Commit graph

125 commits

Author SHA1 Message Date
Ayaan Zaidi
41aec3ca92
feat(deepgram): support Flux voice-note transcription (#141836)
Closes #129452.
Replaces #129442 and preserves Safzan Pirani's original Flux contribution.

## Problem and fix

Selecting `flux-general-en` or `flux-general-multi` sends voice-note audio to Deepgram's prerecorded HTTP `/v1/listen` endpoint, which rejects Flux. This change routes those models through the streaming `/v2/listen` protocol. Operators use their existing media model settings; Nova remains the default. Flux requires `ffmpeg`.

The Deepgram plugin owns bounded audio conversion, frame pacing, protocol parsing, and transcript assembly. The shared WebSocket connector applies resolved authentication, private-network policy, proxy routing, and TLS settings. One connection deadline covers DNS preparation, proxy CONNECT, and the opening handshake; Flux keeps its original transcription-attempt deadline after preparation. Cancellation and socket closure release pending connections.

Proxy connections use the existing shared Node agent backed by `@openclaw/proxyline@0.3.12`. [Proxyline #34](https://github.com/openclaw/proxyline/pull/34), now merged, adds prepared proxy DNS/TLS connection options while preserving its existing pending-socket ownership. OpenClaw passes these through `createNodeProxyAgent(...)`; it does not retain a separate proxy-agent implementation or add `https-proxy-agent` as a direct dependency. Proxy TLS and target TLS remain separate.

Configured proxies retain resolved target-address checks before connection. Applicable managed and ambient HTTP(S) proxies retain their existing DNS delegation. `NO_PROXY` bypasses and `ALL_PROXY` alone do not disable address checks. HTTP and WebSocket paths share the managed-proxy predicate.

The query builder combines saved language and explicit query inputs before applying [Deepgram's model contract](https://developers.deepgram.com/reference/speech-to-text/listen-flux). Only `flux-general-multi` receives `language_hint`; explicit query values keep their precedence. The English-only model ignores both language inputs without requiring changes to saved settings.

The documentation follows main's split-page structure: request policy is documented on [Custom providers](https://docs.openclaw.ai/gateway/config-tools/custom-providers), and connection ownership on [Provider voice capabilities](https://docs.openclaw.ai/plugins/sdk-provider-plugins/voice-and-audio). The Deepgram manifest keeps main's categories and the Flux description. No configuration keys or storage changes are added.

## Canonical transport evidence

These results cover the retained compiled candidate with the canonical Proxyline implementation. They are not claims about a newly installed or rebuilt merge head.

- Independent public CLI acceptance saved and read back all four combinations of the two Flux models with either top-level `language: "en"` or `providerOptions.deepgram.language_hint: "en"`. Each ran `openclaw infer audio transcribe --file sample.wav --json` without model or language overrides, returned the expected “Life moves pretty fast” transcript, and exited 0. The original model configuration was restored and verified.
- A saved Nova configuration and a post-fault Flux control returned the expected transcript and exited 0.
- A configured proxy with private-network access explicitly denied produced a visible target-address rejection and exit 1, with no CONNECT admissions, no target connections or bytes, and no remaining proxy connections.
- A stalled CONNECT produced a visible transcription timeout and exit 1 without forced termination. Both admitted connections closed before the CLI exited; zero connections remained. This records two attempts, not a one-second deadline for the whole CLI invocation.
- A successful real-provider transcription through an HTTPS proxy recorded certificate verification enabled, the explicit server name, an authorized client certificate, CONNECT to the intended provider, 570,755 bytes forwarded upstream and 47,863 downstream, and complete peer cleanup. Verification mode was observed from the operator configuration; this public acceptance did not independently inject an invalid server certificate.
- Focused canonical tests passed: 136 WebSocket/HTTP address-policy tests, 3 shared Node-agent tests, and 37 Deepgram tests. Proxyline's 11 connection-control tests passed, including prepared lookup/TLS settings and Node certificate-verification defaults. The retained runtime build, formatting, lint, and documentation checks also passed.
- Proxyline's upstream review and Linux/macOS/Windows, package, and CodeQL checks passed before merge. Its published `0.3.12` package has been inspected: `src` and `dist` are byte-identical to the tested package. Registry metadata identifies release commit `46a8aa2e3c4b8fbed5fc3ccc16a4631cb7ca3d24` and includes package provenance.

## Regression evidence retained

- On baseline `8954f104fb`, the compiled public command failed with HTTP 400 `V2_MODEL_ON_V1_LISTEN_ENDPOINT` and exit 1. The repaired command returned the expected real transcript.
- Five deadline/cancellation cases failed before repair and passed afterward, including socket termination during pending proxy CONNECT. Both English-only language-input cases also failed before their query repair and passed afterward.
- Before the address-policy repair, the forbidden-target fixture received one connection and 1,600 TLS handshake bytes. After repair, it received zero connections and zero bytes. The canonical acceptance above repeats the repaired denial through the public CLI.
- Earlier broad media validation passed 354 tests across 26 files. Earlier SDK surface and import-cycle checks passed. A missing-file public CLI control produced a visible error and exit 1. These are historical coverage, not fresh merge-head results.
- An explicit `undefined` query value exposed by test-type CI was corrected by omitting absent fixture keys. The 11 Flux tests passed afterward; that correction did not change production code or live-proof inputs.

## Review and merge status

The latest ClawSweeper review of `550a7f60d612b1f19efcaec9b94112cf76338931` found no actionable code defect and requested dependency authorization, conflict resolution, and branch readiness. Its suggested direct `https-proxy-agent` addition is superseded by the canonical Proxyline repair above. The existing Proxyline dependency is updated to the published `0.3.12` release; any repository-enforced dependency approval must cover the eventual head.

The maintainer approved an exact-version release-age exception for `@openclaw/proxyline@0.3.12` through **2026-09-15 10:08 UTC**. This exception does not relax the policy for other packages or versions.

The integration preserves main's documentation moves and both manifest fields, and regenerates the config-help digest from the combined inputs. Main leaves the Flux runtime, WebSocket connection owner, and proxy helpers unchanged. Its shared HTTP capture changes require current HTTP integration evidence, including the saved Nova control.

The integrated tree passes a frozen install from the published registry, formatting, targeted lint, generated config and plugin inventory updates, and a fresh compiled CLI build. Fresh tests passed: 138 WebSocket/HTTP address-policy cases, 18 Deepgram cases, and 13 HTTP capture-release/shared-agent cases. A real saved `nova-3` CLI transcription returned the expected sample text with exit 0. The initial direct test configuration excluded the capture-release file; the canonical test router then ran all 13 cases successfully.

Relative to pinned main `412755bd5c`: production TypeScript +559 net, plugin manifest +13, tests/support +714. The growth implements the missing Flux streaming protocol, bounded conversion/transcription, and generic guarded WebSocket transport. Proxyline itself removes 15 net production lines by unifying the proxy dialers. Final exact-head review and CI remain required.

## Separate follow-ups

- The existing HTTP dispatcher maps explicit provider proxy TLS settings to the target TLS hop. The documented settings describe the proxy hop; this WebSocket implementation applies them there. The HTTP mismatch predates this change and needs its own reproduction and repair.
- The CLI's existing `--model` argument does not override an explicit media-model list. Acceptance selects Nova through saved configuration; override semantics remain a separate follow-up.
- Historical [CI run 34193953000](https://github.com/openclaw/openclaw/actions/runs/34193953000) passed test types, browser-extension end-to-end checks, and the other selected children, except [Control UI shard 3](https://github.com/openclaw/openclaw/actions/runs/34193953000/job/101957740234). Its image-handoff failure also reproduced on the exact main parent `64656c24fa` with the same 67 selected files; the standalone case passed. The mocked image scenario does not invoke Deepgram. This is historical evidence of an unrelated failure, not a result or blanket CI exception for the new head. Nine missing-video-encoder errors in that probe were excluded from the recurrence evidence. The earlier [selected-tab browser failure](https://github.com/openclaw/openclaw/actions/runs/34192553828/job/101953552659) remains a separate browser-lifecycle follow-up with its cause unproven.

AI-assisted repair.

Co-authored-by: Safzan Pirani <5602916+safzanpirani@users.noreply.github.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-10 09:45:11 +05:30
RileyJJY
5e30346432
fix(kimi): stop treating quota exhaustion as authentication failure (#142656)
Classify explicit Kimi weekly, seven-day, and quota-reset HTTP 403 responses at the provider owner. Keep genuine access restrictions, invalid credentials, and generic HTTP authentication handling unchanged.

Declare the existing kimi-code alias so lazy provider resolution reaches the same hook. Preserve the contributor repair and add regression coverage for the reused error type, aliases, and status boundaries.

Public Gateway proof with a synthetic key and loopback Kimi HTTP server reproduces the wrong auth guidance and blocked sibling request on pinned main, then verifies rate-limit guidance and successful sibling fallback after repair.

Closes #142524

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-09 08:16:50 +05:30
Peter Steinberger
d3101b5229
feat: add Team Reports plugin for GitHub and Discord activity reports (#139850)
* feat(team-reports): scaffold bundled team activity reports plugin contract

* feat(team-reports): add bundled plugin icon

* feat(team-reports): add team activity reports plugin core

* feat(team-reports): add GitHub and Discord activity sources

Collect activity through cancellable HTTPS clients with pagination, rate-limit
handling, isolated warnings, deterministic ordering, merger attribution,
coauthor parsing, and Discord thread rollup.

Validate 23 source tests, both plugin import guards, production and test
TypeScript checks, and a clean independent review. Preserve the frozen
contract and collection scope.

Full changed checks remain blocked by 11 unused frozen domain exports awaiting
the report core consumers. Scoped lint artifact preparation also rejects the
parent-checkout punycode dependency; an isolated dependency install is needed.
No guard or shared contract was changed to hide these blockers.

* fix(team-reports): resolve lint and dead exports

Bound GitHub issue and review comment titles to a normalized first line
of at most 140 Unicode code points, preserving full bodies for attribution,
duplicate collapsing, and ignore patterns.

Remove unused exports, keep scheduling and summary proof at their runtime
boundaries, and repair plugin lint and strict typecheck findings without
changing contract fields, schemas, dependencies, or baselines.

Validation: 129 plugin tests and 1131 plugin contract tests pass. The full
typecheck and all dead-export scans pass. Repository lint is blocked by
parent-checkout declaration resolution; the build remains deferred under
the requested environment-failure stop condition.

* fix(team-reports): discover GitHub issues by event date, not last update

* fix(team-reports): satisfy type-aware lint rules

* fix(team-reports): reject aborted runs with a typed error message

* fix(team-reports): register plugin secrets, labeler rule, and test fixtures for CI gates

* fix(team-reports): correct fixture import path after rename

* refactor(team-reports): drop test-only seams and simplify helpers

* refactor(team-reports): share source HTTP helpers between clients

* refactor(team-reports): name the source abort labels

* build(team-reports): publish as an official external package

* test(release): count team-reports in the publisher inventory

Marking @openclaw/team-reports publishable adds one npm and one ClawHub package, so the pinned inventory becomes 94 npm and 90 ClawHub.

* refactor(team-reports): share constants and response parsing, consolidate test fixtures

Export DAY_MS and periodSchema from periods.ts and MAX_REPORT_BYTES from limits.ts instead of per-file copies; share the zod response parser between the GitHub and Discord clients; drop the Discord message wrapper in favor of sorting on channel and message IDs; remove the test-only random and nowMs parameters (tests spy on Date.now); share report fixtures across suites. Net -35 lines, all 137 tests and assertions retained.

* fix(team-reports): qualify GitHub issue searches by type for fine-grained tokens

* fix(team-reports): accept advisory credit shapes, skip unreadable advisories, and honor manual day runs

* fix(team-reports): size the summary output budget by roster and surface model fallbacks

* fix(team-reports): accept null advisory credit logins and retry model summaries after a fallback

One of 1780 advisories on a large repository carries credits: [{login: null}], which the credit schema rejected and marked the day stale. With summaries enabled, a stored fallback for unchanged evidence is now retried instead of reused, so a transient model failure does not persist until the evidence changes; disabled runs never carry a model-failure warning forward.

* fix(team-reports): require operator.read on the report route, scan comment-only and advisory-only repositories, and count only closed-window runs for catch-up

* fix(team-reports): extend comment discovery to the current time

The updated: discovery searches used the report window's end, so an issue or pull request edited again after the day closed no longer matched updated:<day> and a comment-only repository was skipped during the 00:05 closed-day run or a historical regeneration. Discovery now bounds updated_at by the current time and the comment endpoints still filter events to the report window.

* fix(team-reports): collect archived private Discord threads
2026-09-06 23:15:10 -07:00
Vincent Koc
633b84f222
docs(plugins): mark generated reference pages and fix their shared template (#140254)
* docs(plugins): mark generated reference pages and fix their shared template

Two generator files change; the other 153 files are their regenerated
output from `pnpm plugins:inventory:gen`.

- Emit a "generated, do not edit" banner naming the regeneration command
  and the manual-block markers. None of the 151 reference pages said they
  were generated, so a contributor edit was silently overwritten.
- Give generated reference titles a `reference` suffix. Eight of them
  collided with a hand-written guide title (beam, geolocation,
  google-meet, logbook, teams-meetings, webhooks, workboard,
  zoom-meetings). Duplicate frontmatter titles across docs/ now total 0.
- Render the surface list as a list instead of a semicolon-joined
  sentence, and use plain conjunctions for install routes. Strict STE
  hard violations across docs/plugins/reference/ drop from 178 to 20.
- Drop the body H1, which duplicated the frontmatter title Mintlify
  already renders.
- Fix a generator bug found while testing: the marker-less fallback in
  `extractManualReferenceSections` matched only the first line under
  `## Surface`, so a second `--write` run captured later bullets into a
  fabricated manual block. `--write` is now idempotent and `--check`
  passes across repeated runs.

All 12 hand-written manual blocks are preserved.

* test(scripts): follow resolvePluginSurface to its list contract

resolvePluginSurface now returns one string per surface item instead of
a semicolon-joined sentence, so the generator can render a list. The
four assertions move from toBe(<joined string>) to toEqual(<array>) and
pick up the capitalised labels.

The "generic fallback" case changes meaning rather than disappearing.
The empty manifest now yields [], and renderSurface() prints "This
plugin declares no channels, providers, commands, or contracts." for an
empty list, which tells a reader more than the old "plugin". The test
is renamed to say what it now checks, with a comment pointing at the
new home of the fallback.

No generated page has an empty Surface section, so no page changes
because of this.
2026-09-07 03:14:31 +08:00
Vincent Koc
617251ad4f
fix(ci): enforce generated preflight checks (#137455) 2026-09-04 02:23:34 +08:00
Vincent Koc
7818c524d3
docs(plugins): align npm-first install guidance (#136605)
* docs(plugins): sync npm-first install routes

* docs(channels): align npm-first install guidance
2026-09-03 06:45:19 +08:00
Peter Steinberger
20677ab7fc
fix(crabbox): retain checkpoint cleanup after warm-image refresh (#132959)
* fix(crabbox): retry checkpoint cleanup after warm-image refresh

* fix(crabbox): keep retirement retries off allocation path
2026-08-29 19:57:01 -07:00
Vincent Koc
d7b0e07f4c
fix(sandbox): revert Daytona cloud sandbox plugin (#130996)
* fix: remove Daytona sandbox plugin

Retire the unshipped cloud sandbox provider and its exclusive dependency closure; preserve Crabbox and standalone Daytona hosting documentation.

Co-authored-by: Peter Steinberger <steipete@gmail.com>

* test(ui): await catalog terminal restore completion

Wait for catalog RPC dispatch and pending-intent completion before asserting exactly one restored terminal. Reuse the request filter without weakening payload, activation, or cache checks.

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-27 22:31:04 -07:00
Mislav Ivanda
3a5cb3847c
feat(sandbox): add Daytona cloud sandbox backend plugin (#121554)
* feat: @openclaw/daytona-sandbox inital version

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>

* feat: plugin config params extended

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>

* feat: implement ClawSweeper review notes

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>

* fix(daytona): honor abort signals and clean up remote staging on failure

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>

* fix(daytona): register launcher as knip entry and refresh manifest schema

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>

* fix(daytona): cancellable session transport and auto-stopped sandbox restart

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>

* fix(daytona): deny egress by default and arm launcher cleanup before startup

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>

* fix(daytona): stop cancelled startup before submission

* test(daytona): satisfy deferred race lint

* test(release): update plugin publisher inventory

* fix(daytona): close provisioning and PTY cleanup gaps

* test(daytona): type PTY launcher mock

* fix(acpx): avoid promise-returning line handler

* fix(daytona): await PTY signal cleanup

* fix(daytona): declare ClawHub install route

* fix(daytona): forward PTY stdin EOF

* fix(daytona): serialize PTY input

* docs(daytona): document sandbox backend config

---------

Signed-off-by: Mislav Ivanda <mislavivanda454@gmail.com>
Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-08-26 20:52:12 -07:00
Peter Steinberger
066f5eb83d
docs(plugins): register imap in generated plugin inventory (#130363)
The bundled imap plugin landed in #130230 without regenerating the plugin
inventory docs, so pnpm plugins:inventory:check failed on main and the plugin
was absent from the reference index. Regenerate, and register its operator
guide in PLUGIN_DOC_ALIASES so the generated reference page links to
/automation/imap (same mechanism codex and firecrawl use).
2026-08-26 13:33:30 -07:00
Peter Steinberger
ad1e946c7a
feat(a2a): add A2A v1.0 agent-interop channel plugin (#130008)
* feat(a2a): add A2A v1.0 agent-interop channel plugin

Adds `a2a` as a bundled, default-off channel plugin so A2A-compliant agents
(Hermes Agent, LangChain, CrewAI, Google ADK) can discover an OpenClaw gateway
and send it tasks, and so OpenClaw can address configured peers.

Inbound HTTP (`/.well-known/agent-card.json`, `/a2a/v1`) runs through the normal
channel ingress path, so peer messages inherit allowlist admission, provenance
framing, and session routing. The agent reply returns through the channel
delivery callback and resolves the A2A task with its artifact, correlated per
(peer, contextId) FIFO so concurrent sends cannot cross-talk.

Wire format follows the canonical v1.0.0 spec sources (a2aproject/A2A
specification/a2a.proto): PascalCase SendMessage/GetTask/CancelTask,
supportedInterfaces[] rather than the 0.3-era top-level url/protocolVersion,
member-presence Part oneof, TASK_STATE_*/ROLE_* enums, no Task.kind. The 0.3
dotted method names are accepted as explicit compat aliases because shipped
Hermes-generation peers still send them.

Security: per-peer bearer tokens compared in constant time, per-peer sliding
window rate limit, 1 MiB body cap, 64 KiB inbound text cap, tasks scoped to
their owning peer, outbound redirect: "error", and no inbound-controlled target
URLs. With no peers configured the channel stays unconfigured and registers no
routes.

Live-verified against a real gateway on an isolated state dir with a mock
provider: 31/31 proofs, including the agent reply round-tripping into the task
artifact. That run caught two defects fixed here:

- The agent card read `cfg.agents.list` directly, so gateways configured with
  the canonical `agents.entries` roster advertised zero skills. Now uses
  listAgentIds/resolveAgentConfig, which read either roster shape.
- `returnImmediately` dispatches inherited the HTTP request's released work-
  admission root and every async task failed as GatewayDrainingError. Now
  reserves an independent root via runDetachedWebhookWork, matching sms/line/zalo.

* fix(a2a): route outbound sends through the SSRF guard and land setup metadata

CI on the first push surfaced four real defects that local `check:changed` did
not cover (it runs no oxlint or typecheck lane):

- Outbound peer sends used raw `fetch()`, tripping the channel/plugin
  no-raw-fetch boundary. They now go through `fetchWithSsrFGuard` with
  `maxRedirects: 0`, so A2A egress carries the same SSRF policy as every other
  plugin call and a redirect cannot re-target a delivered task.
- The channel declared an empty setup contract while its package manifest
  declared none, so the bundled-channel shape contract failed. The wizard now
  collects `advertisedUrl` plus a first peer name/token pair, which is the
  minimum that leaves A2A actually configured (it stays unconfigured until one
  peer credential exists), and the package manifest mirrors that metadata.
- Six lint errors in the test files: base-to-string on `BodyInit` bodies, an
  unused mock, and a shadowed `options` parameter. Request bodies now go through
  a typed reader that asserts the serialized-string contract before parsing.
- A card assertion referenced `capabilities` without declaring it on the local
  response type, failing test typecheck.

Outbound redirect handling moves from `redirect: "error"` to the guard's
`manual` inspection; the adapter tests assert the new shape.

* fix(a2a): drop route replaceExisting, accept configWrites, register runtime api

CI after the interop run surfaced four more gates:

- `registerPluginHttpRoute` used `replaceExisting: true`, tripping the
  GHSA-RQP8-Q22P-5J9Q rule: a duplicate path can silently replace another
  account's handler. A2A owns fixed global paths on a single account, so a
  duplicate registration means a stale or conflicting owner. It now fails loudly
  through `throwOnFailure` instead.
- The channel schema rejected `channels.a2a.configWrites`, which every bundled
  channel must accept; added to the zod schema, the manifest schema, and the
  config type, then regenerated the channel metadata.
- The new `input` assertion in the setup adapter needed a `// SAFETY:` line for
  the assertion ratchet.
- `extensions/a2a/runtime-api.ts` needed registering in the runtime-api
  classification list, like every other bundled channel barrel.

Also repairs a pre-existing incomplete `vi.mock` factory for `../infra/fs-safe.js`
in the agent-delete suites. Both pass in isolation, but the shared-worker lane
lets a sibling importer reach `readLocalFileSafely`, which the partial factory
never exported; adding this channel changed shard composition and surfaced it.
The factory now spreads the real module and overrides only `movePathToTrash`.

* chore(a2a): refresh config baseline for the configWrites surface

* chore(tests): adopt main's fs-safe mock repair for the agent-delete suites

Main landed the same importOriginal spread for the shared-worker mock-defeat
while this branch carried an equivalent fix; take main's version verbatim so the
two do not conflict.

* fix(a2a): isolate peer sessions and stop faking task cancellation

Addresses both ClawSweeper P1 findings.

Peer sessions: inbound routing never passed `dmScope`, so it fell back to
`session.dmScope ?? "main"` and every authenticated peer converged on
`agent:main:main` under default config. Untrusted remote content was joining the
operator's own session, and peers shared conversation history. A2A now pins
`per-account-channel-peer`; the peer id already embeds the A2A contextId, so each
peer+context pair gets its own session regardless of global session config.

Cancellation: `CancelTask` marked the task `TASK_STATE_CANCELED` and discarded
the late reply, but the dispatched agent run kept going and could still use
tools. The peer was told the work stopped when it had not. There is no
plugin-facing seam to abort a live run, so cancellation is refused with -32004
instead of acknowledged, and the store's cancel path plus its canceled-delivery
tombstone are deleted rather than left as a lying terminal state. Documented as
an explicit limitation.

Live-verified on a real gateway: 34/34 proofs, including three distinct
`agent:main:a2a:default:direct:<peer>:<context>` sessions with zero traffic in
`agent:main:main`, and both cancel spellings refused while the task stays
WORKING. Re-ran the official a2a-sdk 1.1.2 Docker interop afterwards: 9/9.
2026-08-26 08:04:53 -07:00
Peter Steinberger
295809df98
feat(geolocation): resolve client addresses to a coarse city via a bundled plugin (#128546)
* feat(geolocation): resolve client addresses to a coarse city via a bundled plugin

The Activity identity card could show a client's IP address but not where it
was, so an operator still had to look the address up by hand.

Add a bundled `geolocation` plugin that owns address-to-place resolution behind
one authenticated route, `GET /plugins/geolocation/lookup?ip=`. It downloads a
MaxMind-format database on first lookup into the state directory, answers from
that local copy, and refreshes it monthly, so a lookup never sends an address
to a third party. The Control UI renders the resolved city on the device row
next to the address and the client-reported time zone.

The default source is DB-IP City Lite under CC BY 4.0. That license requires
attribution, so every response carries the credit and the UI renders it next to
the value; the database is downloaded at runtime and never redistributed.
Plugin code and the `maxmind` reader are MIT. No free city-level IP database is
MIT-licensed, so the obligation lives with the data rather than the code, and
`databaseUrl` plus the attribution fields make the source swappable.

No new core provider kind: with one implementation the plugin owns everything
through the existing HTTP-route seam, keeping core plugin-agnostic. A second
provider is what would justify promoting this to a registry contract.

Availability and lookup failure stay distinguishable: a missing or still
downloading database answers 503, never `found: false`. A failed refresh serves
the cached copy, and a body that does not parse as an MMDB is discarded without
replacing a working database.

* fix(docs): correct geolocation config examples and add zh-CN glossary entries

The config examples used `plugins.<id>` instead of the real
`plugins.entries.<id>.config` shape, which the docs config-example
validator and src/config/docs-config-examples.test.ts both reject.
New doc labels also need zh-CN glossary entries.

* chore(labeler): cover the geolocation extension directory

AGENTS.md requires a labeler entry plus a GitHub label for every new
plugin surface; test/scripts/labeler-extension-coverage.test.ts enforces
the labeler half.

* fix(geolocation): address review findings on caching, download bounds, and scope

Cold-start lookups were permanently suppressed. The loader cached one promise
per address including failures, so the 15s browser deadline expiring against a
first download that takes ~46s cached a blank forever, and a mounted row only
looks up again when its IP changes. Lookups now return a discriminated
located/absent/unavailable result: only definitive answers are cached, and the
element retries an unavailable one on a widening 5s/15s/45s schedule.

Download limits ran after allocation. The size check happened only after
`response.arrayBuffer()` had buffered the whole body, and gunzip had no output
ceiling, so a replaced source or a compression bomb could exhaust Gateway memory
before rejection. The body now streams against a compressed ceiling enforced
per chunk, and inflation uses zlib's maxOutputLength.

Cached placements were not scoped to the Gateway. The cache keyed only by
address while endpoint and credentials come from the shared Gateway context, so
a switch could render the previous Gateway's answer. The shared reset hook now
supports multiple subscribers - a single slot silently dropped whichever
registered first - and the geolocation cache subscribes.

Unresolvable ranges no longer trigger a download. Only loopback suppresses `ip`
at connect, so Tailscale carrier-grade-NAT and LAN addresses are recorded and
displayed. No geolocation database contains them, so a tailnet-only or LAN-only
Gateway was downloading 125 MB to answer nothing. The route now answers those
ranges without loading the database, using the already-public
`isPrivateOrLoopbackHost` seam so the SDK surface budget is unchanged.

The quickstart queried a reserved documentation range while showing a located
response, which cannot happen; it now uses a routable address and documents the
not-found case.

* fix(deps): resync the lockfile after dropping the net-policy dependency

The geolocation plugin briefly depended on @openclaw/net-policy before
switching to the already-public isPrivateOrLoopbackHost SDK seam. The
package.json entry was removed without regenerating the lockfile, so the
frozen-lockfile install failed and every downstream CI job failed with it.
2026-08-24 04:56:03 -07:00
Ayaan Zaidi
ca6fea301b
refactor(plugins)!: remove OpenProse (#128494)
Remove the bundled OpenProse plugin and /prose command now that upstream owns the maintained Agent Skill. Preserve /prose as migration documentation and let Doctor clean stale plugin configuration.

BREAKING CHANGE: The bundled OpenProse plugin and /prose command are removed.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-24 10:39:46 +05:30
Peter Steinberger
ae55a4090c
refactor(canvas): make the panel a widget presenter (#126030)
* refactor(canvas): retire legacy host and commands

* refactor(apple): narrow shared Canvas contracts

* refactor(macos): keep Canvas as widget presenter

* refactor(ios): remove Canvas client

* refactor(android): remove Canvas client

* refactor(linux): remove Canvas client

* fix(ci): isolate native locale artifacts

* fix(linux): regenerate companion lockfile

* fix(canvas): refresh native tool display metadata

* test(canvas): align coverage with presenter surface

* test(canvas): remove obsolete asset root seam

* test(canvas): stabilize retirement CI coverage

* refactor(swift): remove orphaned resource wrapper

* test(ios): remove retired canvas layout assertion

* fix(macos): reserve retired canvas command namespace

* refactor(macos): isolate canvas command policy

* fix(canvas): select only eligible macOS panels

* fix(canvas): keep panel selection plugin-owned
2026-08-19 08:21:07 -07:00
Onur Solmaz
c2de3206d4
feat(llama-cpp): support external llama-server
* feat(llama-cpp): add external server provider

* feat(llama-cpp): document external server setup

* refactor(llama-cpp): harden external provider boundaries

* fix(llama-cpp): support external structured output

* fix(llama-cpp): isolate replacement endpoint credentials

* test(llama-cpp): register external live shard

* fix(llama-cpp): preserve explicit endpoint authorization

* fix(llama-cpp): clear disabled inline credentials

* fix(llama-cpp): preserve external local service configs

* test(llama-cpp): cover retained external configs

* test(llama-cpp): cover authorization precedence
2026-08-19 17:32:00 +03:00
Dallin Romney
adf2ca46bb
chore(release): refresh generated preflight artifacts (#125586) 2026-08-18 07:10:49 -07:00
Peter Steinberger
6aa27d6ecd
refactor: retire August compat windows (embedding API, pi aliases, target parser, spawning hook, setup exports, WhatsApp inbound aliases) (#124416)
* refactor(plugin-sdk): retire embedded Pi aliases

* refactor(channels): retire explicit target compatibility

* refactor(plugins): retire subagent spawning hook

* refactor(plugin-sdk): retire shipped channel setup exports

* refactor(whatsapp): retire inbound callback aliases

Proof: focused build and WhatsApp E2E green; broad WhatsApp suite 188/189 files green. extensions/whatsapp/src/monitor-inbox.policy.test.ts flakes only in the parallel batch and passes isolated (10/10).

* refactor(plugin-sdk): retire memory embedding registrar

Migrate every bundled provider and manifest to registerEmbeddingProvider and contracts.embeddingProviders. Preserve memory-specific batching, local-service acquisition, index identity, and auto-selection through the canonical generic registry adapter, then remove the parallel registrar, registry, diagnostics, contracts, tests, and docs.

* chore(plugin-sdk): tighten retired surface budgets

Pin the post-retirement public SDK surface to 144 entrypoints, 4,312 exports, 2,564 callable exports, and 1,133 deprecated exports; agent-harness-runtime now permits exactly nine deprecated exports.
2026-08-15 22:43:47 -07:00
Peter Steinberger
1348387076
refactor(plugins): replace node-llama-cpp with managed llama-server (#123105)
Move llama.cpp chat and local embeddings onto a verified externally managed llama-server runtime. Remove the in-process native runtime, forked embedding workers, and node-llama-cpp dependency while preserving guided setup, local GGUF models, tool-capable agent runs, diagnostics, and operator docs.
2026-08-13 16:58:20 -07:00
Peter Steinberger
a89b88ec0e
docs(qqbot): point install docs at the Tencent package (#122417)
The externalization left the docs inventory seed and the channel page
advertising the retired @openclaw/qqbot npm package; the catalog already
resolves qqbot to @tencent-connect/openclaw-qqbot. Regenerates the
plugin reference and inventory pages from the corrected seed.
2026-08-11 21:20:00 -07:00
Peter Steinberger
f44c5e2e5e
fix(plugins): surface manifest-only bundled capabilities (#121354)
Use a manifest-first inventory with independent coverage for manifest-only bundled capabilities.

Retire the undocumented thread-ownership plugin while Doctor removes stale references.

Document Talk voice and persist only provider-scoped voice selection.

Closes #121353
2026-08-09 19:43:49 -07:00
Peter Steinberger
c092900e3c
fix(plugins): keep OpenCode Go bundled (#120985)
* fix(plugins): keep OpenCode Go bundled

* fix(plugins): mark OpenCode Go dist bundled

* fix(docs): show OpenCode Go as bundled

* fix(release): defer bundled plugin publication
2026-08-09 03:09:47 -07:00
Peter Steinberger
48639663b0
chore(release): prepare 2026.8.1 (#120375) 2026-08-07 18:44:12 -07:00
Patrick Erichsen
05e3ce11ac fix(release): restore Fish Audio ClawHub publishing 2026-08-06 11:31:37 -07:00
Vincent Koc
52b8f9be80
fix(plugins): migrate Fish Audio plugin identity (#119900)
* fix(plugins): migrate Fish Audio plugin identity

* fix(plugins): tighten Fish Audio identity migration

* fix(plugins): follow catalog selector for id replacements

* fix(plugins): reject replacement install collisions

* refactor(plugins): reuse skipped update outcomes
2026-08-06 20:50:36 +08:00
Dallin Romney
e9b25aa21d
refactor(cua-computer): replace daemon MCP with CUA Driver SDK (#117205)
* chore(cua-computer): freeze CUA Driver 0.14.1 fixtures

* refactor(cua-computer): use CUA Driver SDK directly

* fix(cua-computer): repair CUA CI checks

* style(cua-computer): format driver cleanup

* fix(cua-computer): migrate retired driver path

* fix(cua-computer): start direct driver sessions
2026-08-02 20:03:24 +08:00
Vincent Koc
89aadef6ef
feat(plugins): externalize iMessage channel (#117101) 2026-08-01 08:01:18 +08:00
Vincent Koc
61fb29e90a
feat(plugins): externalize OpenCode Go provider (#117064) 2026-08-01 06:31:19 +08:00
Vincent Koc
e73250f54b
feat(plugins): externalize OpenCode provider (#117035)
* feat(plugins): externalize OpenCode provider

* docs: refresh OpenCode provider map
2026-08-01 05:07:35 +08:00
Vincent Koc
e84e2a49fc
feat(plugins): externalize NovitaAI provider (#117007) 2026-08-01 03:53:46 +08:00
Vincent Koc
8b93fd963c
feat(plugins): externalize Mistral provider (#116919)
* feat(plugins): externalize Mistral provider

* fix(update): preserve external provider models during repair

* chore(release): keep Mistral note in PR body
2026-08-01 03:04:12 +08:00
Vincent Koc
a1026663ae
feat(plugins): externalize ComfyUI provider (#116888) 2026-07-31 23:26:51 +08:00
Vincent Koc
e93de36081
feat(plugins): externalize BytePlus provider (#116866) 2026-07-31 22:37:47 +08:00
Vincent Koc
fab2c33cad
feat(plugins): externalize Xiaomi provider (#116861) 2026-07-31 22:02:07 +08:00
Vincent Koc
b2b62b2e1e
feat(plugins): externalize Volcengine provider (#116854) 2026-07-31 21:28:01 +08:00
Vincent Koc
ba1a6722bb
feat(plugins): externalize Vydra provider (#116813) 2026-07-31 20:35:43 +08:00
Vincent Koc
eb55c8ea8f
feat(plugins): externalize Voyage embeddings (#116785)
* feat(voyage): externalize embedding provider

* fix(voyage): drop stale bundled description
2026-07-31 19:52:33 +08:00
Vincent Koc
6830820755
feat(plugins): externalize DuckDuckGo search (#116740) 2026-07-31 18:49:07 +08:00
Vincent Koc
a550827dad
feat(plugins): externalize Synthetic provider (#116720) 2026-07-31 17:19:43 +08:00
Vincent Koc
fa4cc51a87
feat(plugins): externalize Teams and Zoom meetings (#116682) 2026-07-31 14:10:46 +08:00
Vincent Koc
c892a712e6
feat(plugins): externalize Cohere and Meta providers (#116662)
* feat(plugins): externalize Cohere and Meta providers

* docs(changelog): note provider externalization

* chore: keep changelog release-owned
2026-07-31 12:53:01 +08:00
Peter Steinberger
e8524a5839
feat: add Fish Audio S2.1 and local MLX speech (#115790)
* feat: add Fish Audio speech support

* chore: remove unused speech exports

* chore: keep Fish API constant private

* test: remove stale code mode import

* style: use bracket access for Fish voice ids

* test: restore QA evidence validator import
2026-07-29 05:15:33 -04:00
Peter Steinberger
feb5883089
docs: add Buzz to generated plugin inventory (#114875)
* docs: refresh Buzz plugin inventory

* docs: refresh generated docs map
2026-07-27 22:59:32 -04:00
Peter Steinberger
bbc73c36bd
feat(plugins): beam local coding sessions into a read-only catalog (#112323)
* feat(plugins): add read-only session beam

* test(plugins): tighten Beam HTTP fixtures

* docs(plugins): refresh Beam inventory counts

* fix(plugins): keep Beam wire types private

* fix(plugins): stabilize Beam transcript paging

* fix(plugins): honor Beam Control UI base paths
2026-07-27 15:37:36 -04:00
sallyom
f045f33a62 feat(onepassword): add managed SecretRef integration 2026-07-25 06:03:30 -05:00
Peter Steinberger
f695be341c
feat(cua-computer): add experimental Windows/Linux computer-use fulfiller (#112267)
* feat(cua-computer): add experimental Windows/Linux computer-use fulfiller

Bundled plugin that fulfills the capability-based computer.act + screen.snapshot
node contract on Windows and Linux by supervising a pinned cua-driver 0.10.x
daemon over MCP stdio. macOS keeps the Peekaboo fulfiller; this plugin is
disabled by default and never available on darwin.

Grounded in cua-driver 0.10.0 source (tool schemas, refusal codes, coordinate
spaces, session/daemon lifecycle). Notable safety and correctness properties:
- Deny-by-default env allowlist so OpenClaw secrets (provider/channel tokens,
  CUA_API_KEY) never reach the separately installed daemon; telemetry and
  update checks forced off.
- Version-gated handshake (exact-minor pin + capability/schema version),
  time-bounded so a corrected driver recovers without a node restart.
- Robust daemon supervision: full readiness-budget polling, startup-race
  tolerance, signal-death and spawn-error recovery, shared-daemon lifecycle
  (never killed on dispose).
- Frame authorization preserved within upstream limits (generation + full live
  geometry; capture refused when screen and screenshot geometry diverge).
- Action mapping refuses inputs cua-driver cannot faithfully deliver:
  layout-shifted keys, modifier-held drag/scroll, Linux modifier clicks,
  hold_key/mouse down-up, non-positive scroll; drag duration clamped.

* fix(cua-computer): satisfy lint, test-types, dead-code, and docs-map gates
2026-07-21 05:58:04 -07:00
Peter Steinberger
b4187ced90
feat: add live-validated Zoom meeting guest plugin (#111048)
* feat(zoom-meetings): add browser guest transport

* fix(zoom-meetings): type reusable refresh result

* test(meeting-bot): bind reusable refresh callback

* chore(zoom-meetings): use existing plugin label

* chore(zoom-meetings): defer changelog to release

* test(zoom-meetings): cover node setup through public host
2026-07-18 17:17:26 -07:00
Peter Steinberger
a068d38b66
refactor(plugins)!: remove experimental workspaces plugin (#110416)
* refactor(plugins)!: remove experimental workspaces plugin

* fix(doctor): remove retired workspaces plugin state

* refactor(doctor): settle retired-workspaces cleanup before legacy-state detect

* chore(i18n): leave workflow-owned locale artifacts to the locale refresh

* ci: retrigger after GitHub scheduling outage
2026-07-18 10:56:37 +01:00
Peter Steinberger
ee762ad761
feat(teams-meetings): join Microsoft Teams meetings via the meeting-bot core (#109964)
* feat(teams-meetings): join Microsoft Teams meetings via the meeting-bot core

* fix(teams-meetings): satisfy lint and package-boundary contracts
2026-07-17 13:23:39 +01:00
Peter Steinberger
658b601ee5
feat(llama-cpp): in-process local GGUF text inference provider (#109444)
* feat(llama-cpp): add in-process text inference

* test(llama-cpp): narrow setup provider fixture

* fix(llama-cpp): trim public surface and refresh docs map

* fix(llama-cpp): import Context type in inference test
2026-07-16 18:53:55 -07:00
Peter Steinberger
349f78776d
fix(models): refresh bundled provider catalogs (#109410)
* fix(models): refresh bundled provider catalogs

* docs(models): refresh generated docs map

* fix(xiaomi): keep provider helper private

* chore(release): defer catalog release note
2026-07-16 16:47:25 -07:00