* feat(core): gate decision tool prefilter behind decisionAssistance labs and harness capability (#155314, #155316)
- Connect prompt-build prefilter to canonical isDecisionAssistanceEligible Labs consent contract (#155314)
- Gate prefilter evaluation on harnessSupportsTurnScopedToolRestrictions capability declaration (#155316)
- Preserve tool policy when harness is unsupported (e.g. Codex app-server), pending action instructions are present, or attempt is cancelled
- Update test suite with schema-backed decisionAssistance configurations and per-agent decisionModel overrides
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
* feat: align conversational tool filtering with Decision assistance
Preserve the contributor core consumer while binding canonical consent, actual harness support, prepared config, and live run authority. Validate the runtime deadline and real ONNX prompt submission without claiming universal classification or latency gains.
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
* fix: disclose automatic Decision filtering in configuration help
Resolve the remaining configuration-help review finding. Explain explicit consent, inherited Decision models, built-in filtering, preserved required tools, request transfer, and hosted costs. Regenerate the config documentation baseline without changing settings or runtime behavior.
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
* feat: filter conversational follow-ups with bounded Decision context
Use one provider-neutral batch with separate missing-context and next-response tool-need judgments, following the documented Jev/Noul semantics. Preserve first-turn eligibility, safe bounded history, permissions, required tools and per-turn restoration. Measure actual foreground tool definitions with DEBUG-only scalar diagnostics.
Live model-quality testing is deferred to local testing; no classifier-specific thresholds or adapter framework are introduced.
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
* fix: restore prompt-phase CI and simplify Decision assistance copy
Bring the shared prompt-phase fixture and Tool Search assembly override up to the current dispatch diagnostics contract. Type the assembly mock and complete policy result so missing fields fail checking instead of silently blocking the primary stream. Preserve the original four replay/queued-context assertions and cover DEBUG-on/off diagnostics without adding cases.
Construct modified Decision answers on fixture-owned copies rather than mutating the read-only API result. Keep the Labs toggle and configuration help generic; supported uses and full behavior/privacy details remain in canonical docs. No provider, saved-consent, permission, or classifier-policy change.
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
* fix: preserve prompt hook context in tool prefilter
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
* fix: preserve tool and provider availability through Decision filtering
Revalidate Decision eligibility at the foreground dispatch boundary and withdraw only its optional cap when the published opt-in or model selection changes. Restore the current permitted schemas, catalog, callability and owned prompt guidance without discarding independent hook restrictions or required tools.
Keep caller-budget expiry out of shared provider outage accounting while preserving genuine provider failures and physical request settlement. Replace incomplete context-engine hook fixtures with the typed real runner, retain the original assertions, and keep the context limit private for the production dead-code gate.
Preserve the hook-context and rubric-9 change; document and test the separate 6000/8000 UTF-16 evidence-text bounds and the existing forward-looking saved-intent contract. Add failure-first composed final-wire and real loopback HTTP/provider-host regressions, including subsequent explicit evaluation on the same provider.
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
* fix: restore foreground result-budget fixture typecheck
Model the result-budget fixture's foreground-only session explicitly as not compacting. Keep the required production compaction contract and all existing result/persistence assertions intact.
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
* test: consolidate Decision assistance boundary coverage
Remove overlapping test layers and repeated fixtures while retaining the distinct context, policy, and dispatch boundaries. Runtime validation remains pending because the secretless runner broker is unavailable.
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
* fix: stop automatic Decision calls after opt-out
Address MertBasar0’s pre-dispatch opt-out report by reusing the automatic consumer eligibility fence after awaited operator preparation. Preserve explicit Decision calls and root/scoped authority and cancellation checks. Extend the existing composed regression at the operator boundary.
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
* fix: stop Decision evidence after admission is revoked
Carry automatic consumer admission through the existing provider host and TypeSafe adapter to the final synchronous guarded-fetch boundary. Retain observed revocation or authority failure across adapter sanitization and asynchronous cleanup without treating either as a provider outage. Preserve explicit Decision evaluation, caller cancellation, deadlines, and provider retirement.
Rebase the contributor commits onto pinned main and retain their previously published integration fixes and test organization. Prove the cold-load and transport-preparation windows with the registered TypeSafe plugin and a real loopback request counter, including same-host explicit calls after revocations.
Co-authored-by: MertBasar0 <mertbasar0@hotmail.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
* fix(agents): make decision assistance opt-out admission-only
Keep Labs eligibility at provider dispatch while preserving independent live guards for admitted evaluations. Let in-flight results survive opt-out and cover subsequent disabled turns.
* test: close skills watchers in Discord capture fixture
---------
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Co-authored-by: MertBasar0 <mertbasar0@hotmail.com>
Related: #130753, #137832, #147969
## What Problem This Solves
Fixes: some scheduled jobs created by an agent fail for months because a tool list saved by an older OpenClaw build is missing tools the creator actually had, such as the native shell. In our setup, a monthly group job that runs `node <script>` delivered nothing in August, delivered nothing in September (the run still reported `ok`), and posted a blocker in October. Its saved list had 31 tools and no `exec`.
## User Impact
User impact: an agent-created agent-turn job that does not name specific tools now gets the same tools as its owner conversation at run time, like a job an operator creates without `--tools`. Existing jobs with an automatically saved creator snapshot behave the same way from their next run. Nothing stored is rewritten: no migration and no backups. Explicit tool lists, script payloads, condition triggers, and jobs bound to captured Codex app authority keep their stored list.
Tradeoff, approved by the maintainer (Ayaan): a per-sender tool policy on the creating owner, or a plugin hook that narrowed the creating turn, no longer limits these default jobs. Only owners can create automations from chat, and subagents cannot create them.
## Why This Change Was Made
**History of the saved list.**
- #91499 introduced it so a delayed run cannot do more than its creator could.
- #112483 made every agent-created job store one, because runs have no sender.
- #112661 made scheduled runs re-apply the owner session's group policy and every non-sender limit, keeping the stored list as the upper bound.
- #137832 fixed native tool capture for new jobs only, and deliberately did not widen stored lists.
- #147969 added a Doctor advisory. It only fires for claude-cli, so it never covered Codex-harness or built-in OpenAI jobs like ours.
**Root cause.** When no tool list was given, OpenClaw saved a frozen copy of the creating turn's tools instead of treating the job like an operator `*` job. Every capture bug (missing native tools, late configured MCP, renamed tools) then stayed in the job permanently.
**Fix.** This follows Hermes, which keeps no creator snapshot: `cron/scheduler.py` `_resolve_cron_enabled_toolsets` reads toolsets from config at run time.
- **New jobs.** An agent-turn create or update with no list, or `*`, stores `["*"]`. That is the same value operator jobs store, so the job's tools match a normal turn in its owner conversation. Script payloads and condition triggers still store the creator's concrete tools, because a script reaches MCP only through servers its list names. Jobs whose creator captured Codex app authority also keep the concrete list, because that authority is bound to it.
- **Existing jobs.** One helper, `resolveCronRunToolsAllow` in `src/cron/tools-allow.ts`: a stored automatic snapshot (`toolsAllowIsDefault`) runs as `*` when it has a valid scheduled owner policy, no condition trigger, and no Codex app authority. Otherwise it keeps its stored list. Every execution consumer of the stored list uses it: the run payload, the command-prompt preflight, and the scheduled message authority.
- **Script transitions.** A `*` job that becomes a script, or gains a condition trigger, captures the creator's concrete tools.
- **Exec pin.** A `*` list keeps the creator's exec host pin.
- **No new noise:** automatic snapshots stay excluded from the `web_search` provider warning, as on main.
- **Deleted, now pointless:** both Doctor advisories about incomplete automatic snapshots, the run warning about pre-MCP snapshots, and two exports nothing uses anymore.
Review note: on claude-cli, a `*` job runs without a CLI tool cap, so Claude's native tools behave exactly as in a normal chat turn in that conversation. This PR introduces no new path around `tools.deny` that a chat turn doesn't already have.
## Evidence
Live-model Telegram proof (Telegram Test Server DM, leased team credential, live `openai/gpt-6-astra` reached through a forwarding proxy that stands in for the runner's mock provider; the runner harness itself is unchanged). This reproduces the shape of the original incident:
- The tester DMs the bot, which creates the owner conversation.
- A job owned by that conversation is added. Its stored list is an old-style automatic snapshot `["automations","message","read"]` plus `toolsAllowIsDefault: true`, with no `exec`.
- The payload is `Run: node scripts/split-report.mjs and post its output line verbatim`. The workspace script prints a random nonce.
- The job is run once (`cron run --wait`), with announce delivery to the DM.
| Build | `exec` offered | Model action | What arrived in the DM | Run |
|---|---|---|---|---|
| base 94f5a8d (main before this PR) | no | `tool_search` ×2, then gave up | "Could not run node scripts/split-report.mjs: no command-execution tool is available…" | error |
| **this PR, head 5b78cb7** | **yes** | `exec {"command":"node scripts/split-report.mjs"}` | "**SPLIT-REPORT 93C53909**: general 41, design 17, ops 9" (the exact script output, with this run's random nonce) | ok, delivered |
| head 5b78cb7 with `tools.deny: ["exec"]` | no | `tool_search`, `read`, then gave up | "Could not run node scripts/split-report.mjs: no command-execution tool or paired node is available…" | error |
In every run, the stored job kept `["automations","message","read"]` plus the marker. Before and after use the same scenario and driver; only the checkout differs.
Update and live proof: published `openclaw@2026.9.7`, then this branch at the exact head (2f5099d), on the same state directory. Mock provider. Every process ran under a temporary `HOME` and state directory. Each job's message makes the model call `exec` with `touch <effects>/<job>`.
1. 2026.9.7 created both jobs through `cron.add` (scheduled policy `trusted`). With the Gateway stopped, the "stale" job was given the old automatic-snapshot shape `["automations","message","read"]` plus `toolsAllowIsDefault: true`. sha256 of both stored rows: `609358837…`.
2. Runs:
| Build / config | Job | `exec` offered | Side effect | Run |
|---|---|---|---|---|
| 2026.9.7 | stale automatic snapshot | no | absent | error |
| 2026.9.7 | explicit `["read","message"]` | no | absent | error |
| this branch | stale automatic snapshot | **yes** | **created** | ok |
| this branch | explicit `["read","message"]` | no | absent | error |
| this branch, owner policy narrowed to `tools.deny: ["exec"]` | stale automatic snapshot | no | **absent** | error |
| this branch, `tools.deny: ["exec"]` | explicit `["read","message"]` | no | absent | error |
3. After the branch runs, the stored rows were byte-identical (same sha256 `609358837…`), and job ids and lists were unchanged. Nothing was migrated.
An earlier run at e151ea3, with the same harness, also covered a snapshot bound to Codex app authority: `exec` was not offered, the file stayed absent, and the stored row was unchanged.
Tests:
- `run.tools-allow.test.ts`: a stored automatic snapshot `["message","read"]` reaches the embedded run as `["*"]`, with the owner's scheduled policy intact. It fails on main with `["message","read"]`.
- `cron-tool-creator-cap.test.ts`: a default agent turn stores `["*"]`, while a trigger script and a Codex-app creator keep the concrete snapshot.
- `run.tools-allow.test.ts`: snapshots without a valid owner policy, or behind a condition trigger, keep their list. Both cases fail on the previous head.
- `run.tools-allow.test.ts`: no `web_search` warning for an automatic snapshot that kept its list. This fails without the exclusion.
- `run.message-tool-policy.test.ts`: a self-edited automatic snapshot runs on CLI with no cap.
- `run.tools-allow.test.ts`: a legacy `Command to run:` prompt from an automatic snapshot without shell tools now runs instead of being rejected.
- `jobs-tool-policy.test.ts`: scheduled message authority is admitted for an automatic snapshot that lacked `message`.
- `cron-tool-creator-cap.test.ts`: a `*` agent turn converted to a script captures the creator's concrete tools.
- These three regressions fail on the previous head. `node scripts/check-changed.mjs` passes.
- Explicit-list, exec-pin and gateway creator-transport suites pass. `pnpm tsgo:core` passes.
## Bounded cost
No new path triggers a model call or a job run. The change only selects which tool list an already scheduled run uses.
LOC vs main: production +98/-250 (net -152), tests +140/-373, docs +19/-11.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Seven channel plugins hand-wrote the same secret-contract setup, and
Zalouser duplicated migration and prefix parsing that shared helpers
already own. A new Plugin SDK helper, createChannelSecretContract on
openclaw/plugin-sdk/channel-secret-basic-runtime, builds the contract
from each channel's declaration, and Zalouser uses the existing helpers.
Feishu and Matrix stay out because other work is changing them.
The public SDK surface budget grows by exactly this one export (public
exports and callable exports each +1), approved by Peter on 2026-10-01.
clickclack, googlechat, irc, slack, sms and zalo now require OpenClaw
2026.9.8 because they call it; published plugins already require a
matching host, and installs fall back to the newest compatible version.
Registration and secret-contract output are identical to main across
eight channels; 6,619 channel tests pass. Production -110 net.
Release note: The clickclack, Google Chat, IRC, Slack, SMS and Zalo
plugins require OpenClaw 2026.9.8 or newer.
Target GitHub visitor invitations by immutable account ID without requiring a public email. Preserve explicit email invitations, verified identity bindings, grant lifetimes, revocation and restart recovery.
Fixes#114200.
OpenAI Responses paths now send structured output correctly: a raw JSON Schema `responseFormat` is wrapped as `{type: "json_schema", name, schema}` and set as `text.format` for the direct OpenAI and Azure Responses providers. `json_object` and `text` pass through unchanged, Chat Completions is unchanged, and SDK retries stay at 0 so failover keeps owning retries.
Proof: isolated Gateway captured the raw schema on main and the wrapped `json_schema` with this change. Live check on the exact head with a real OpenAI key (gpt-4.1-mini, Responses): the schema turn returned HTTP 200 with schema-conformant JSON, and a control turn without `responseFormat` also succeeded. Regression tests fail on main and pass here.
Co-authored-by: Codex QA <codex-qa@local.invalid>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Retire the five SDK compatibility facades under the approved September 30 owner decision, and migrate in-repository callers onto their focused contracts. Keep implementations with their canonical owners and remove forwarding exports that become unused after the cutover.
BREAKING CHANGE: remove openclaw/plugin-sdk/channel-lifecycle, channel-message, channel-reply-pipeline, config-runtime, and infra-runtime. Use channel-outbound/channel-inbound, config-contracts and focused configuration/infra entrypoints. The new system-event-runtime entrypoint supplies public snapshot inspection and consumption. Update affected external plugins before upgrading the host; this retirement does not certify universal external migration.
Package exports, SDK entry inventories, compatibility tombstones, migration docs, and surface budgets move together. Canonical API comparison confirms exactly five removed entrypoints, 869 removed export paths, 35 focused additions, and no retained-export signature changes. Net production reduction: 1,320 lines.
* feat(agents): expose installed skill search across harnesses
Keep installed-skill eligibility host-owned while applying normal tool policy to discovery and complete instruction reads. Preserve current main's prepared tool surface and consolidated Code Mode coverage. Caller-provided snapshot options supply read paths, not replacement eligibility authority.
* test(agents): preserve hoisted skill harness initialization
Keep mock factory dependencies owned by vi.hoisted when splitting skill fixtures. Assemble skill-specific mocks only when the test consumer requests them. Update two prior expectations for the prepared empty snapshot/resource contracts while preserving sandbox host-skill exclusion.
Exact-head reproduction failed on the original hoisting and resource-array assertions. Nine affected suites covered 61 tests; after the hoisting repair, a stale sandbox snapshot assertion was found and the 32-test owner file passed on focused retry. Fresh lifecycle autoreview found no actionable P0-P2 issues; formatting and whitespace checks pass.
* test(agents): group sandbox skill coverage with policy tests
* test(agents): admit skill tools in client collision coverage
* test(agents): provide complete prepared skill fixtures
* test(agents): match async skill prompt contract
* fix(skills): preserve existing Code Mode read grants and whole reads
* fix(skills): preserve read denials in frozen tool profiles
* fix(codex): refuse partial installed skill instructions
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Restore missing fs-safe prebuilds without relying on Node directory-merge semantics, preserve Bun native-launch diagnostics, and recover malformed Codex JSON without consuming the next valid frame. Keep package custody, bounded diagnostics, warning-only lifecycle failures, and installed-updater ordering intact.
Proof: 53 changed-test cases plus six catalog sibling cases pass on each of Node 24 and Bun; full changed-file validation and both import-cycle checks pass. Published Node-driver upgrade and Node-free Bun lifecycle cells passed on AWS. The published Bun driver's owning-npm preflight limitation remains explicitly documented.
Adds named storage locations as a generic, pluggable capability, with backup as its first consumer.
- Core storage owner (src/storage): storage.locations config, a location marker that binds identity (runtime never creates it, so unplugged disks and different disks at the same path are refused), client-side streaming encryption (scrypt key from a SecretRef passphrase, per-object HKDF keys, AES-256-GCM segments), and a built-in filesystem provider for external disks and mounts.
- Plugin SDK: api.registerStorageProvider plus manifest contracts.storageProviders; providers move opaque bytes only.
- Bundled cloudflare plugin: an r2 provider over the S3 API with conditional writes and bounded multipart uploads; auto-enabled when a location uses provider "r2".
- Backups: backup create --to <location> with verified archives, UTC retention, list/verify/restore --from, Gateway-owned offsite schedules (installed Git schedules unchanged), per-installation namespace claims fenced at publication and deletion, backup record for external jobs, backup.status RPC, Doctor/status hints, and a Systems page Backups section.
No config or state migration; the storage section is new and optional. Proof: live R2 and mounted-disk round trips, namespace takeover trace, and a published 2026.9.7 upgrade cell with an existing Git backup schedule.
Follow-up to #161053 (shared-session emoji reactions).
Reaction writes now run through the SQLite worker admission the sibling
session stores use (runOpenClawAgentWorkerWrite); the native path stays
only for process-held incognito databases the worker cannot reopen by
path, and the handler revalidates live authority around the awaited
write.
The plugin action dispatch path awaits onPlatformSendDispatch right
before the synchronous handoff fence, exactly like the send path, so the
reaction mirror re-reads the conversation binding at the final handoff
and refuses a message whose conversation was rebound while the action
runner prepared delivery.
Channels with one bot reaction per message (Telegram bots, WhatsApp)
declare the new optional ChannelPlugin.capabilities.reactionSlots =
"single"; when a person removes one emoji while others remain, the
mirror re-sets the newest surviving emoji instead of clearing the slot.
Multi-slot channels are unchanged.
Also trims redundant scaffolding in the reaction handler, kernel, UI
component and worker.
Proof: 119 focused tests across store, handler, dispatch and UI; mocked
Gateway reactions e2e; typecheck lanes; database-worker inventory check;
live two-person Gateway proof with the qa-channel mirror reporting
delivered through the final-dispatch hook.
* fix(gateway): keep model metadata available during plugin drains
Keep the active model publication readable while admitted plugin work settles,
then retire it before resource replacement. Preserve execution fencing, auth
revocation, decision cancellation, rollback, and cleanup ownership.
Retain an automatic drain failure only while its original plugin configuration
delta remains unresolved. Allow explicit wait recovery and reversion alongside
changes to a different plugin without retrying unrelated failed work.
Validate on Blacksmith Testbox with real Gateway reader latency proof, 378
focused tests, 145 follow-up tests, negative controls, types, lint, and guards.
* fix(plugins): fence admission while preserving owned cleanup
* test(gateway): preserve plugin record helpers in reload fixture
* test: repair reload mocks and supervised process joins
* test(models): preserve queue receiver in drain observer
Add a per-viewer people filter beside the agent filter on Sessions boards: Everyone (default), Involving me, or a person from the Gateway's people facet. workboard.sessionsBoard.read accepts an optional view { involvingMe, involvingProfileId, includePeople } forwarded to the caller-scoped sessions.list roster; the board's shared columns, classification, placements, and Board agent are unchanged. The choice is remembered per viewer, device, Gateway, and board, and the preference scope reloads on reconnect so a different viewer or Gateway never inherits a stale selection.
* fix: preserve legacy delivery and Telegram queues during upgrades
Route legacy queue normalization through Doctor with exact source backups and durable import receipts. Preserve historical failure times, recover interrupted source claims, and prevent consumed Telegram work from replaying.
* fix: finish legacy queue migration lint cleanup
Construct inventory entries with their status directly and remove the obsolete max-lines suppression after extracting canonical row normalization. Exact lint stripes, core types, and 38 migration tests pass; the matching one-line baseline prune remains blocked by the campaign overlap rule.
* chore: prune the retired storage migration line exemption
Native-reference admission (#158414) re-validated every retained hardlinked target and walked its companion directory on every captureAdmitted()/finish() call, so a Windows 2026.9.6 -> 2026.9.7 candidate Doctor spent 39 minutes in assertPluginNativeReferenceNamespace. Admission now retains directory verdicts per capture, namespace, and target-directory mapping, validates only newly admitted targets, and revalidates on replacement captures, explicit host selection, and recovery copies. Seven-agent, three-plugin fixture: repeated companion walks 2,016 -> 0.
Closes#162047
Summary:
- Moves Agents API onboarding into a dedicated plugin guide and updates navigation, provider references, the plugin README, and release links.
Automerge notes:
- PR branch already contained follow-up commit before automerge: docs: address Agents API onboarding review
Validation:
- ClawSweeper review passed for head af284a9707b38cc6b72094559a4b14ffdac83019.
- Required merge gates passed before the squash merge.
Prepared head SHA: af284a9707b38cc6b72094559a4b14ffdac83019
Review: https://github.com/openclaw/openclaw/pull/162255#issuecomment-5922939944
Co-authored-by: Sarah Fortune <sjf@openai.com>
Approved-by: sjf-oa
Route the two exact process-global Codex diagnostic-log warnings to Gateway warning logs at client receipt, before turn fan-out or replay. Keep other warnings on their existing delivery path and prevent a diagnostic-sink exception from closing the shared transport.
Consolidate duplicate logger-failure test setup without removing startup or active-connection coverage (14 fewer test lines). Real native SQLite-lock and logger-fault probes, 83 focused cases, adjacent coverage, changed checks, and the production build support the repair.
Logging remains best effort: these operator-only diagnostics do not fall back to chat if Gateway warning logging is disabled or broken. This does not repair native SQLite or update Codex.
Worked on by:
- @vyctorbrzezowski
Co-authored-by: vyctorbrzezowski <51521767+vyctorbrzezowski@users.noreply.github.com>
Retire the beta.5 whole-session-store bridge under the approved September 30 SDK cutoff. Remove loadSessionStore, updateSessionStore, resolveSessionFilePath, resolveSessionStoreEntry, LoadSessionStoreOptions, and UpdateSessionStoreOptions from session-store-runtime, plus package-root loadSessionStore/saveSessionStore aliases.
Plugins use getSessionEntry/listSessionEntries, scoped patch/upsert/delete operations, and identity-backed transcript APIs. Preserve the session-store-runtime subpath, resolveStorePath, canonical SQLite storage, and ordinary legacy-state/Doctor migrations. Published Codex and Feishu 2026.9.7 packages use the replacements.
Remove exclusive projection/reconciliation/materialization code, migrate surviving tests, and shrink SDK export and assertion budgets. Focused remote tests, plugin contracts, import boundaries, changed-file checks, build, both zero-cycle checks, exact API-removal audit, and independent review passed.
Share Slack generation and stale-create custody with the existing draft lifecycle while preserving human-reply retention, throttle deadlines, and deferred cleanup. Consolidate Mattermost accepted-delivery failure publication and remove the private Discord chunking wrapper.
Validated with focused and cross-shard tests, plugin contracts, changed checks, both zero-cycle checks, a full build, compatible SDK API diff, independent review, and exact-head hosted CI.
Report settled inspection disposal errors without marking their managed
resources as retained. Keep rejected instance/cache prerequisites and drain
timeouts classified as retained cleanup. Preserve CLI resource release,
original error causes, borrowed ownership, and exactly-once disposal.
Fix the product regression introduced by bfdb432570 (#160181). Update
caller assertions to require the disposal failure while preserving successful
process close and subsequent healthy publication; document the contract.
Validation on an isolated AWS lease at main 0c1952c56e:
- Original 129-file plugin CI composition reproduced both failures before
the fix, then passed 1,257 tests with one existing skip in 59.45 seconds.
- Four affected files passed all 60 tests in three consecutive runs
(40.87s, 23.71s, 23.78s including runner overhead).
- New settled/rejected/pending inspection regressions took 79ms/19ms/17ms
in the final focused run; the settled case fails on the original code.
- Core and plugins-platform test typechecks, scoped lint, oxfmt --check,
git diff --check, and independent P0-P2 review passed.
Pre-commit formatting ran on the lease: oxfmt --check passed for all five
changed files. The local hook is skipped because this sparse worktree has
no node_modules.
* docs(plugins): correct the rendered Control UI descriptor surfaces
registerControlUiDescriptor accepts session, tool, run, settings, tab,
and widget, but only tab and widget are consumed. The overview table
advertised four inert surfaces and omitted widget, one of the two that
actually render.
* docs: include rendered link-reader descriptors
---------
Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
Move draft generation, atomic receipt publication, sticky stale-create retirement, and current-message reset/retirement into the existing shared lifecycle. Discord uses the generation operations; Matrix reuses message reset while keeping its live-marker policy.
Preserve existing SDK parameters and operations; the API report confirms only additive members on the existing helper. Validation: 273 draft/entry-point tests, 1153 plugin contract tests, changed-file checks, both cycle checks at zero, and independent review through P2.
Hosted CI hit the pre-existing chat.abort-errors fixture deadlock also present in main run 36780090668. Main already contains the owning fixture fix, 79f0e9bf12. The PR records this inherited failure and its incidental SDK barrel import; security checks passed.
* fix(cli): release plugin resources when help finishes
Uncached CLI metadata loads now use the existing inspection acquisition when an executable invocation owns them. The source-plugin regression verifies that invocation release joins module disposal. Caller-owned programs retain their existing lifetime.
* fix(plugins): release retired registry preparation scope
Create the aggregate retirement observer outside the registry preparation
scope so it retains only child waiters. Preserve per-observation options,
cleanup ordering, failure identity, and deferred consumer results.
The unchanged cold registry-retention regression failed on the CI Bun
runtime before this change and passes after it. The original seven-file
shard passes all 172 cases, and Node retirement coverage passes 20 cases.
* fix(ci): carry the chat attachment lint repair
Carry the exact two-file fix from 2e95cdba84
(#160159). Move the unchanged image decoder into its existing helper
to restore the 700-line limit without changing attachment behavior.
Targeted lint and all 21 attachment tests pass on this candidate.
Independent review found no actionable defects.
* test: retain complete lifecycle helpers in lease fixtures
* test: isolate local command fixtures and join recovery cleanup
* test(ci): preserve CLI runtime ownership and harness staging
Keep the moved CLI command fixture in its runtime prerequisite group and
preserve complete agent coverage across the core and CLI process owners.
Carry the test-only trusted-harness fixture fix from #160024.
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(tests): scope Vitest preload to test workers
Apply the jsdom adapter only at Vitest worker entry points while preserving
package-local runtime resolution. Ordinary Workers may inherit the preload
without having a Vitest dependency. Exercise default native inheritance and
await worker termination.
Copy the static-check evidence helper into lint fixtures so the real oxlint
entry point can load its current import closure.
Validation: reproduced both original failure causes before repair; all 76
affected tests pass (351.44 seconds in Vitest), as do canonical changed-file
checks and independent review.
* test: isolate public runtime surface planning fixtures
Verify public runtime entries and packaged assets with a synthetic publishable plugin after the Diffs forwarding APIs were retired. Preserve the existing planner assertions without depending on one bundled plugin layout.
* fix(ci): carry shared lint repairs into CLI preparation
* test(tooling): repair extracted PR and npm fixtures
* test: retain gateway cleanup and isolate Windows temp paths
Close the prewarm gateway when client connection fails and retain cleanup
errors during orphaned recovery. Keep Windows-under-Bun coverage on
host-owned temporary directories, matching the fixture fix in #160985.
Validation: 13 focused fixture tests and canonical changed-file checks.
* fix(ci): keep extracted manifest within script contracts
Resolve inherited manifest lint errors without changing job selection. Move its closed true/1 flag parsing to the dependency-free argument owner and include that helper in trusted fixture copies. Validation passed 718 tests with eight existing skips, canonical changed checks, fixture lint, and independent review. The 74-case argument helper file took 1.79 seconds at one worker with warm inputs.
* test(state): apply upstream snapshot custody fixture isolation
Reuse the fixture repair from #161598 (af16b80a22). Keep the real exit callback and every custody assertion while isolating the synthetic cleanup owner from preceding files.
* test(pr): drain fake GitHub streams before exit
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Add a separate Sessions board kind to the Workboard plugin: sessions land in editable columns by ordered state rules (observer health, run state, pull-request state, archived) and, for anything unresolved, the agent's utility model in bounded batches; drag pins a session until its facts change; a Board agent conversation docks beside the board and edits it through workboard_sessions_board_read/update/move. Facts reach the plugin through the lifecycle-bound api.runtime.gateway.readSessionFacts (trusted plugins only, redacted and bounded, incognito and draft sessions excluded). Interactive edits recheck the caller's full Gateway authority before the SQLite write; background classification runs under the plugin service and only for boards viewed in the last 15 minutes. Card boards, tools, and storage are unchanged; sessions-board tools register default-on. Storage adds nullable kind/sessions_spec columns and a placement cache table; rollback to an older release shows a Sessions board as an empty Cards board (documented). Live proof on a dev Gateway with the docked agent adding a column and pinning a session.
Expose the optional ControlUiHost.dock capability for native plugin pages. Reuse the assistant-panel owner, Home chat pane, and existing placement and size persistence. Keep docks across navigation, suppress them on their own session page, and close activation-owned docks without restoring a previous conversation. Carry bounded plugin page details through the existing untrusted work-context capture and Context attached presentation. Closing a plugin dock restores the built-in Home/Ask choice, and the close-on-dispose is retained only once an activation opens a dock.
The Zoom, Teams and Slack huddles transport layers repeated the same
template with platform strings swapped in. Each platform adapter hand-
wrote the manual-action classification switch, origin check, retry and
permission wiring. Each page-script builder assembled status, transcript,
leave and audio-capture scripts the same way. Zoom and Teams prejoin
sources shared guest-name, identity-preservation and virtual-audio
fragments.
Two additive MeetingPlatformAdapter members, createBrowserAdapterOptions
and createPageScripts, plus shared prejoin fragments in the meeting
runtime, now own that scaffolding. Each plugin's transport files keep
only its selectors, URL grammar and origins, page identity, and
platform-only join steps. The shared adapter options type moved to the
platform-adapter leaf contract to keep the import graph acyclic.
This is a refactor of the TypeScript that builds the browser scripts.
6,772 generated script hashes and byte lengths across Zoom, Teams, Slack
huddles and Google Meet, plus all registrations and adapter behavior,
are identical to main.
Zoom meetings, Teams meetings and Slack huddles each carried the same
thirteen-file glue layer (entry, CLI, CLI metadata, config, errors, node
host, node invoke policy, runtime facade, probes, setup, Chrome transport,
types), feeding one platform adapter into a dozen separate
MeetingPlatformAdapter helpers. A new MeetingPlatformAdapter member,
defineBrowserMeetingPlugin, composes those helpers from one declaration,
so each plugin now declares only what is genuinely platform-specific. A
fourth browser meeting platform costs one declaration.
The helpers the factory replaces stay exported and are marked deprecated,
since published plugin versions call them. Tests inject Chrome bindings
through the factory's public spec instead of mocking core internals. The
three plugins drop their unused typebox dependency.
Zoom and Teams now require OpenClaw 2026.9.8 (install and plugin API
floors, catalog, docs), like Slack huddles, because they call a member
2026.9.7 lacks. Released plugin packages already require a matching host.
All plugin registrations and 42 generated page-script hashes are
identical to main.
Release note: Zoom meetings and Microsoft Teams meetings plugins require
OpenClaw 2026.9.8 or newer.
* fix(doctor): detect Codex bwrap loopback denials that unshare misses
The Codex bwrap network-namespace probe ran `unshare --user --map-root-user
--net true`, which never configures loopback. It passed on hosts where Codex's
Bubblewrap sandbox then fails before any shell command with
`bwrap: loopback: Failed RTM_NEWADDR`, the exact symptom doctor documents.
Doctor now asks the Codex plugin to run the configured Codex runtime's own
`codex sandbox` in workspace-write mode with network access disabled, using a
throwaway CODEX_HOME and a bounded timeout. Only Codex's own namespace-denial
lines are diagnosed; other failures are reported as unverified. The plugin
owns binary selection (shared with the managed app-server check); core reaches
it through the existing Codex owner-selection and trust path. The user
namespace probe and its messages are unchanged.
* fix(codex): use the OpenClaw temp root for the sandbox probe
Extension runtime code must not use host tmp defaults; the boundary check requires resolvePreferredOpenClawTmpDir from the plugin SDK, matching the Codex plugin's other temp users.
* fix(doctor): keep Codex bwrap diagnostics out of update Doctor passes
The Codex bwrap namespace diagnostic is advisory and has no update
migration or readiness dependency, but it ran inside doctor:sandbox,
which update Doctor passes execute for registry migration. It now lives
in its own doctor:codex-bwrap contribution with standalone update scope,
so `openclaw update` never spawns the Codex probe or loads a retained
Codex plugin's probe API, and lists it under "Omitted during update".
Probe commands, gating, and messages are unchanged; image repair keeps
its engine validation through a shared enabled-backend helper.
Problem: when large plugins are installed, the Gateway freezes for minutes on model changes. Prepared model runtimes loaded and captured their own copy of every plugin: 3 copies at startup and 2 more on every model republish. A `config.patch` of `agents.defaults.model` froze the Gateway for 68–142 s and ended in "recovery restart required".
Fix: prepared runtimes now borrow unchanged plugin instances from their admitting Gateway instead of capturing another copy. The loader records a load-mode-free identity for Gateway loads. A non-activating load given `borrowRegistry` lists the lender's active, unchanged record. The lender keeps custody of borrowed records through adoption, inspection release, rollback, and retirement. Borrowed channel callbacks are fenced to the borrower's lifetime. Lenders come only from the admitting Gateway's live owner. If a lending Gateway closes, recovery republishes under the surviving owner without borrowing from a process-global sibling. Module identity is never shared: a turn uses the Gateway's live instance or a fresh evaluation.
User impact: model changes republish in about 1 s with no new plugin captures and no recovery restarts. Prepared turns use the Gateway's full-mode registrations for unchanged plugins. A turn that still holds an instance makes `plugins.reload` wait through the existing drain; past the drain timeout, the previous instance keeps serving. The maintainer accepted both tradeoffs: https://github.com/openclaw/openclaw/pull/161267#issuecomment-5907833707
Proof:
- Paired large-plugin run: republish took 0.6–1.4 s with 0 restarts and 0 new captures on this branch, versus 68–142 s with 5/5 recovery restarts on main.
- An installed release-to-candidate upgrade passed with public turns, a model patch, and a plugin reload.
- Post-close public-client proof on the exact head: after the lending Gateway closed, the survivor served `models.list` and real OpenRouter agent turns: https://github.com/openclaw/openclaw/pull/161267#issuecomment-5907830560
- Focused loader, prepared-runtime, reload, and Gateway-close suites pass. `pnpm build` passes.
- CI: every required job passed except one UI e2e timeout in Control UI code this PR does not ship. Attribution: https://github.com/openclaw/openclaw/pull/161267#issuecomment-5908179695
Co-authored-by: roboclaw-bot <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* fix(exec): avoid extra prompts for audit suppression commands
Remove the command-text gate and its dedicated approval/reviewer plumbing. Suppression configuration and audit filtering remain unchanged; normal exec policies still decide execution. Keep only the shipped deprecated SDK signature as a no-op.
Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com>
* fix(exec): avoid extra prompts for audit suppression commands
Worked on by:
- @jesse-merhi
Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com>
OpenClaw-Publication: f1fe3778-77d9-432e-b98f-01f680f2cdd3
* fix(plugins): preserve deprecated suppression approval predicate
Retain the shipped SDK result during infra-runtime retirement without restoring internal execution callers. Document the distinct runtime and plugin migration outcomes.
Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com>
---------
Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com>
* feat(ui): select Ultrafast for supported accounts
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* fix(gateway): preserve Ultrafast compatibility and account authority
Negotiate speed decoding per connection and keep canonical session state intact. Fence personal-account catalog requests at final guarded HTTP dispatch. Refresh the measured UI boot manifest without changing performance limits.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* refactor(openai): keep account catalog outcomes together
Move the existing account-scoped result projection into the already imported catalog helper without changing its behavior. Keep the provider owner below its existing line-count ratchet.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* test: refresh Ultrafast tool prompt fixtures
Regenerate the canonical Codex dynamic-tool fixtures for the authorized Ultrafast speed value. Update only that enum value and its derived size/hash metadata; keep snapshot checks enabled.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* fix: stop account catalog requests after default unlink
Bind automatic account selections to the canonical profile writer's committed link authority and carry the real request scope through final guarded dispatch. Keep explicit retained account selections usable after unlink and evict failed discovery custody. Preserve the existing active Auto Ultrafast opt-in while explicit Fast stays priority.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* refactor(codex): use narrowed Auto activation flag
Keep the reviewed Auto tier predicate while satisfying the typed boolean lint contract.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* fix(ui): share speed applicability for optional Ultrafast
Respect the selected request mapping before offering an optional entitled tier. Preserve all three choices on eligible routes and clearable stored preferences on unsupported routes. Reproduced the contradictory catalog regression and passed 59 unit/Chromium cases; scoped independent review found no actionable P0/P1.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* fix(ci): export manifest in same-revision preflight harness
Restore the trusted file omitted when the inline manifest moved out of ci.yml in 9d75a8fe87. Actual preflight job109720001696 failed before tests with MODULE_NOT_FOUND; real Git push and PR materialization fixtures reproduce the same omission. Keep the canonical index export owner, regenerate its workflow projection, and preserve all source/credential guards. Fifteen materialization variants, five import/size checks, root test types, and focused independent review pass.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* fix(ci): satisfy extracted manifest static contracts
Repair inherited check-lint failures from the manifest extraction without changing CI routing: avoid namespace shadowing, retain error cause and the diagnostic callback string contract, preserve nonmutating shard copies, and apply required branch syntax. All1259 scripts lint clean;45 planner/import/size cases, formatting, UI i18n, styles, ratchet and independent review pass.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* fix(ci): centralize dependency-free workflow flag parsing
Remove the extracted manifest local coercion helper and preserve its exact narrow Boolean grammar under the existing script argument owner. Register the canonical declaration rather than weakening the guard, and carry its runtime through trusted preflight materialization and fixtures.77 argument cases,11 declaration-guard cases,71 scoped integration cases, types, lint, export scans and remaining guard commands pass; independent review has no actionable P0/P1.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* fix(gateway): separate model publication authority from selection scope
Keep the actual request lifetime in selected-account HTTP assertions without treating every anonymous unscoped catalog read as a personal account projection. Restore the established models.list response shape; no assertions weakened.177 model/catalog/session cases and10physical HTTP authority cases pass, together with types, lint, ratchet and independent review.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* fix: preserve session response argument tuples
Forward the original response tuple while projecting successful legacy payloads, without appending optional undefined arguments.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* fix(codex): preserve existing Ultrafast opt-ins
Keep the v2026.9.7 Fast and active Auto opt-in semantics while adding explicit per-session Ultrafast. Standard still clears the tier. Cover cold and warm native turn requests without requiring a migration.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* test(ui): distinguish speed labels from model names
Match the complete Effort and Speed section labels rather than the Speed only fixture model. Retain the independent absence assertions for reasoning and speed controls. All four failures reproduced before the repair; the complete 13-case bundled browser file passes afterward.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* test(openai): intercept the shared transcription socket
Update the two stale socket mock registrations after the upstream transport consolidation. Keep the actual provider/session code, fake peers, assertions, timeouts, and Bun transport guard unchanged. All 86 OpenAI shard files pass: 1263 passed and one existing skip.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* test(gateway): construct complete session reset callers
Replace partial caller objects cast as never with the existing typed session mutation client fixture. Preserve provenance and required-sandbox assertions and the production client capability contract. Both CI failures reproduce before the repair; all 15 reset-model cases pass afterward.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
* fix: confirm the selected Ultrafast command mode
Share the direct command, directive reply, and system-event confirmation formatter so the saved Ultrafast tier is named accurately. Include the accepted manual value in help and docs without advertising an unverified optional native-menu choice. Preserve boolean Fast, Auto, reset, authorization and persistence behavior. Three regressions fail before repair; 274 focused cases pass afterward.
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
---------
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Project ordinary iterator payloads on first consumption and share completed graph inspection only within that synchronous reader chain. Preserve consumer admission, mutable native data, and exceptional-result projection while removing the intermediate Promise mapping.
Reduce sampled allocation by 38-40% across five forwarding layers. Record the read-before-forward limitation, add native-await and lifecycle regressions, and clarify the SDK contract.
Solo Codex turns on installs without native hook admission (relay disabled, or managed-hooks-only) keep native sub-agent delegation again, on fresh and resumed threads, fixing forward the fallback from #160525 that disabled it for every operator turn.
When a Codex thread can reach native spawn but has no spawn admission installed, a different person's message queues as a follow-up instead of steering the running turn, so the turn never becomes multi-person unguarded. One predicate in thread-requests.ts (isCodexNativeDelegationDisabledForRun) drives the thread's delegation config, the optional supportsCrossProfileSteering backend capability, and the attempt-start refusal, so policy-disabled turns (token-sharing OAuth, report-only, restricted, message-only, system-agent-only) keep accepting other people's steering. Same-person steering and installs with native hook admission are unchanged. A rare cross-runtime fallback into an unguarded multi-person attempt refuses with an explicit message.
Maintainer decision: Codex threads created on source-main no-hook installs while #160525 was live may keep delegation disabled on resume; no npm or GitHub release contained #160525.
Release note: Codex keeps native sub-agent spawning in single-person turns when native hooks are unavailable; another person's message queues as a follow-up only when native spawn could not be guarded.
When a plugin hot reload failed partway through activation, the Gateway committed the failed generation without republishing the prepared model runtime. `models.list` then returned "Model catalog is not ready", and every new turn failed with "prepared reply dispatch runtime owner was not published", until the Gateway was restarted. A failed reload now keeps serving the previously published runtime and still reports the activation failure. A successful reload still swaps in the new runtime.
Related to #157814. This proves the plugin-reload trigger, but not the Windows update-restart incident reported there. Thanks @sahilsatralkar.
Proof: a real isolated Gateway with a scripted mock model.
- On main, after a failed plugin reload, `models.list` was unavailable and a fresh agent turn failed with the unpublished-owner error.
- With this change, the activation failure is still reported, a model stays available and the turn gets a reply.
- A successful reload advances to the next generation and keeps replying.
- Three regression tests fail against main's owner, and the dispatch suite (20/20) and consolidated reload handlers (114/114) pass.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Each upstream request inside a GitHub hover preview received its own
eight-second deadline, so the metadata -> commits -> co-author avatar chain
could stack several waits before the card appeared. Carry one abort signal
through visibility checks, redirects, the optional-auth fallback, item and
body reads, commits, and avatars. Slow required metadata keeps the existing
retryable unavailable response; slow optional content is omitted while the
card is preserved. Completed cache hits still make zero network requests
and the full detail reader keeps its own timeout.
Microbenchmark with an abort-aware slow transport: slow avatars after a
700 ms commits page 8,704 -> 2,001 ms wall; stalled required item
8,001 -> 2,002 ms; reopened completed previews unchanged at zero requests.