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>
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>
* 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
* 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.
* 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>
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).
* 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.
* 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.
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>
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.
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.
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
* 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
* 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
* 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