Commit graph

24 commits

Author SHA1 Message Date
Peter Steinberger
dab1f08376
feat: give every plugin a compact chat activity icon (#147333)
* feat: give every plugin a compact chat activity icon

Separate package-owned activity SVGs from plugin identity artwork. Ship 154 defaults and seven exact tool overrides, preserving Echo and the progress claw. Include authenticated bounded delivery, safe mask rendering, packaging, and authoring guidance.

* test: declare Vite types for the activity asset browser test

* refactor: keep plugin artwork selection with catalog presentation facts

* test: scope activity browser types and simplify fixture copies
2026-09-13 13:49:51 -07:00
Ayaan Zaidi
b10035faa9
fix(backup): archive unmanaged SQLite files as opaque bytes (#146700)
## What Problem This Solves

An unrelated SQLite file with foreign-key violations could prevent every backup from completing.

## Why This Change Was Made

Whether an included file gets the live-database snapshot path is decided by exactly one mechanism at `src/commands/backup-resource-inventory.ts:336`, from the core set plus declared plugin resources. The online root snapshot supplies the registry used for discovery and traversal, so planning no longer needs a quiet write-ahead log.

## User Impact

Undeclared files survive backup unchanged with filename warnings. Foreign SQLite symbolic links that exceed the link-resolution limit (`ELOOP`), including loops, are skipped with a filename warning. Corrupt managed databases and unavailable plugin SQLite capabilities still stop publication.

## Evidence

- Pinned main `f0817f23e9`: the real CLI exits 1 on a structurally valid foreign database with a foreign-key violation and publishes no archive.
- Candidate `ce85d13530c4`: CLI create with verification, verify, and restore preserve seven foreign files and sidecars byte-for-byte, with one warning each. Corrupt core and unavailable plugin functions still refuse publication. Managed hardlinks include committed WAL data and restore identical images.
- Tested commit `37f9d97a9d65` (capture behavior unchanged): real CLI backup succeeds during 10 ms commits (309 rows during the 3.6-second run) and in the 100 ms control. Verify/restore retain identical valid root/alias images with writes committed during backup. The unrelated symlink loop is skipped with one warning and the archive restores successfully.
- The full architecture check passes with zero import cycles; five formatter/command tests pass. All 10 managed-refusal/older-schema command cases and changed-test checks pass. The separate macOS planning assertion noted below remains a baseline failure. Type checks and the test-partition check remain for CI.

## Compatibility

No schema, plugin fields, flags, or archive format change. Existing `backupResources` declarations define managed plugin data.

## Consumers

- `backup create`: preserves undeclared SQLite files and reports their filenames.
- `backup verify` and `backup restore`: treat files outside the captured core registry as opaque.
- `src/commands/migrate/apply.ts:26-41`: pre-migration backup now accepts unrelated foreign SQLite, returns only the archive path, and does not forward opaque warnings.
- Fleet backup: uses the shared archive metadata adapter to preserve independent hardlink entries without stalling.

- `formatBackupCreateSummary` moved unchanged to `src/commands/backup-summary.ts`; `src/commands/backup.ts` and `src/infra/backup-create.test.ts` import that owner.

census: generic formatBackupCreateSummary reviewed — 2 callers listed

## Invalidation

Ownership is frozen from the captured root registry for each backup; later registrations belong to the next capture. A plugin declaration changed after planning can be archived with payload classified by the earlier declaration.

## Contention

Registry discovery reads the online root snapshot before archive traversal. The 10 ms sustained-write test and 100 ms control both complete, verify, and restore successfully.

## Tests

- `backupCreateCommand`: all eight managed-refusal cases and both older-schema cases in `src/infra/backup-create.test.ts`; older-schema verification also uses `backupVerifyCommand`.
- `backupVerifyCommand`: all three opaque-file/sidecar cases in `src/commands/backup-verify.test.ts`.
- `backupCreateCommand` and `backupRestoreCommand`: opaque loop handling, declared/core loop refusal, and agent registration captured immediately before the root snapshot.

Those tables retain the complete case mapping, fixtures, and assertions. Source bytes are compared after capture or refusal and before the real outcome recorder runs; the recorder still runs unchanged. The unchanged macOS manifest-path assertion reproduces on base. The added source lines separate resource planning from captured ownership and preserve missing-root checks.

Thanks @clawputerlabs for the report.

Closes #144552.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-13 11:31:38 +05:30
Ayaan Zaidi
7292689e7f
fix(signal): start accounts with spaced keys without Doctor (#145750)
## What Problem This Solves

Signal lists `Work Phone` as `work-phone` but misses its authored number/endpoint. Fresh startup reports unconfigured and never contacts that endpoint until Doctor renames the key. Runtime must work without the #138982 cleanup.

## Why This Change Was Made

The decision 'which stored key serves Signal account id X' is made by exactly one mechanism at `src/routing/account-lookup.ts:94`.

Signal declares its rule once in manifest metadata: exact keys win; normalized aliases use authored settings only with a nonempty own number. The task owner accepted own-number transport/settings, whole-root inheritance for aliases without a number, exact collision winners, and Doctor refusing cleanup that activates ignored settings.

Setup preserves restrictions in the canonical default row, even when empty, and restores its number to root. Promotion targets the runtime winner. The second correction removes command singleton/writer raw-key bypasses: selector-owned optional creation; existing selection for delete/clear. Global owner authority stays separate.

## User Impact

Own-number aliases start at their authored endpoint without Doctor. Root inheritance, exact winners and other channels' maps remain stable; adding an account preserves the active account's inherited credentials. No config keys, schema or protocol versions change. Public SDK exports remain. Code `8828dc61b2da6d7ed02b8765080ed8f474ae2567`: 76 files (+1,941/-360) against e76.

Credit: @marmar9615-cloud for the report; adversarial review found the incomplete repair.

## Evidence

Eight runtime scenarios cover both status paths, 35 startup cases and allowed/excluded senders across setup, reload and restart.

| Fresh alias, no Doctor | Pinned base `3f75b33d67` | Candidate `d4e41539b2aee717e105be61522463d76ad74490` |
|---|---|---|
| Both status paths: configured/running | false/false | true/true; connected |
| Authored endpoint | No requests | Account event stream |

Published v2026.9.4 CLI updated isolated authentic old state to merge `174bdff8057e193b01b3bf63ef969a5f676d737d`, exited 0, and a new Gateway passed both status paths/events. Independent acceptance verified the new process and cleanup. That cell permits updater Doctor; the fresh cell does not. Review of 134 later paths supports applicability through actual historical CI merge `4d2cdb6527d9b3e42f4f7085703a20f358b66c01`. Separate package `20090983b5683ff352ed31e50a15143468913084` is not that CI merge.

## Compatibility

| Consumer | Host/operation | Observed result |
|---|---|---|
| Published Signal 2026.9.4 npm artifact; API floor 2026.9.4 | Released 2026.9.4; install/canonical startup/both status paths | External plugin/events pass |
| Same published artifact | Candidate core; same flow | External source/host peer link pass |
| Matching candidate Signal/core | Fresh spaced account, optional Doctor, setup/reload/restart | Retained controls pass |
| Published v2026.9.4 CLI/authentic old state | Actual update/final driver output/new candidate process | Pass, including independent acceptance |

Preflights remain failed: `34683985589` found 1,113 native translation-ID mismatches; `34684811000` fixed those but found 126 different mismatches plus UI fallback drift. Neither compiled declarations. Focused `34686128844` passed candidate compilation but failed released Node-type enrollment; `34686394191` fixes only that environment and passes both. Sealed source/producer hashes remain; no locale source/release guard changed.

Unpublished candidate Signal imports a new SDK export and cannot run on 2026.9.4 SDK; this pairing is neither published nor claimed. Official release sync raises its API floor to matching host before publication; no fallback selector/invented release version.

## Consumers

Grouped paths use braces to enumerate exact filenames; no wildcard is implied.

Paths cover readers, writers, delivery/status, SDK facades and tests. Facades retain public signatures and delegate to the shared owner; their directory does not imply channel-specific use. Tests and Compatibility record execution and the published/candidate matrix. The pinned base `3f75b33d67` has 299 successful queries across 394 files. Historical CI integration `4d2cdb6527d9b3e42f4f7085703a20f358b66c01` has 147 queries across 401 files (2,385 references, 1,826 positions); its parents are main `67e1a8b0f2` and PR `ec1b82bc0c`. The correction at `8cbd800e3d` adds a 12-query supplement across 242 files (836 references, 767 positions) and two predecessor-local queries. All resolve. These supplements are source evidence, not a replacement CI-merge census. Complete raw outputs and omitted-project coverage are retained; scripts queries close the packed-consumer route and manual edges cover native assets and registrations.

Historical `census.json` stays bound to340. New `census-8828dc6-reviewed-row.json` uses the same pinned base and CI run34688444368 checkout `9fcd956a0f3dad9caed524a55968dbbc19f3346d`, tree `00e4c42d2982b3745f7db6175918155fcef51754`, main parent `cb25f00e24`; second parent is882 above. Prior5b census is preserved. Source-query applicability is separate from CI execution.

### Signal, shared owners, writers and final consumers

- `extensions/signal/api.ts` — Retained public operation export; transport writes use the selected key.
- `extensions/signal/config-doctor-api.ts` — Retained Doctor/compatibility registration; repair owner supplies persisted cleanup.
- `extensions/signal/runtime-api.ts` — Retained generic config/setup runtime exports; selection stays with shared owners.
- Under `extensions/signal/src/` (relative paths):
  - `account-key-repair.ts` — Changed Doctor eligibility to selected-key lookup; collision enumeration only reports.
  - `account-selection.ts` — Adapter passes manifest policy/routing normalizer to resolveAccountKey; resolveSignalAccountEntry projects that key. No selection loop.
  - `accounts.ts` — Changed merged account/reply-mode reads to the shared selector with Signal policy.
  - `channel.ts` — Retained registered threading/outbound calls with Signal channel/account identity.
  - `config-compat.ts` — Removed normalized-first selector; exhaustive migration reads winners through Signal adapter.
  - `config-schema.ts` — Changed default selection; validation still checks every authored row.
  - `doctor.ts` — Retained preview hook delegates warnings to key-repair owner; no selection.
  - `monitor.ts` — Retained receive/reply flow; threading/chunk reads use selected fields.
  - `send.ts` — Retained outbound formatting; Markdown uses selected account.
  - `setup-core.ts` — Changed selection/policy forwarding; default restoration keeps canonical winner/settings.
  - `setup-transport.ts` — Changed reserved-port/transport reads and writes to runtime-selected key.
  - `shared.ts` — Changed scoped adapter forwards Signal policy to runtime/writers; operation ownership stays.
- `extensions/signal/src/monitor/event-handler.ts` — Retained inbound reply/reaction/group policies use runtime-selected account.
- `extensions/signal/src/monitor/inbound-context.ts` — Retained inbound context consumes selected policy from shared owner.
- `scripts/fixtures/packed-plugin-sdk-setup-consumer.ts` — Identical released/candidate fixture calls promotion, setup/setup-runtime patch and allowFrom with full inline adapters/contextual callbacks. Compile results: Tests.
- `scripts/plugin-sdk-surface-report.mts` — Retained SDK budget/report owner; approved delta covers selector exports. No account state.
- `scripts/release-check.ts` — Copies both trusted-tooling fixtures, installs/compiles separate released/candidate consumers; packed smoke invokes/cleans up. Query success is not compilation.
- `src/agents/embedded-agent-runner/{compaction-session-execution.ts,run/attempt-history-prepare.ts}` — Retained history consumers use selected settings before unchanged truncation.
- `src/agents/embedded-agent-runner/history.ts` — Changed history-limit selection; precedence stays.
- `src/agents/{embedded-agent-runner/{model.configured-fallback.ts,model.configured-overrides.ts,model.inline-provider.ts,model.registry-resolution.ts},model-discovery-normalize.ts,provider-attribution.ts,provider-request-config.ts}` — Retained provider metadata readers/types; existing owner facts, not channel policy map.
- `src/agents/identity.ts` — Changed reaction/prefix reads to channel-aware selection.
- `src/agents/runtime-capabilities.ts` — Retained capability projection consumes selected account from shared owner.
- `src/{agents/embedded-agent-runner/model.static-catalog.ts,cli/{plugins-authoring-command.ts,plugins-feature-artifact.ts},commands/models/provider-aliases.ts,gateway/control-ui-plugin-assets.ts,plugins/{bundled-sources.ts,install.runtime.ts}}` — Retained plugin/provider/artifact manifest fields; no Signal key selection or policy interpretation.
- `src/agents/subagents/spawn/acp-spawn-parent-stream.ts` — Changed parent progress-stream selection.
- `src/auto-reply/chunk.ts` — Changed chunk size/mode reads carry channel identity.
- `src/auto-reply/command-auth.ts` — Changed explicit/default/sole fallback allowlists select through owner; no raw singleton return. Authorization/global-owner policy stays.
- `src/auto-reply/reply/block-streaming.ts` — Changed coalesce-setting selection.
- `src/auto-reply/reply/reply-threading.ts` — Changed generic reply-mode selection; registered Signal threading passes explicit policy.
- `src/channels/account-config-enabled.ts` — Changed enabled read uses selected entry.
- `src/channels/draft-streaming-chunking.ts` — Changed draft-stream chunk selection.
- `src/channels/join-intro/report-channel-room-join.ts` — Changed join-intro selection.
- Under `src/channels/plugins/` (relative paths):
  - `account-config-mutation.ts` — Active channels.add: normalize ID → shared promotion with execution adapter → applyAccountConfig. Existing lifecycle/config owners commit.
  - `account-helpers.ts` — Changed factory forwards channel key to shared merge; explicit normalizer stays.
  - `config-helpers.ts` — Changed enable creation uses owner-derived destination; delete/clear require selected existing key. No rejected raw-row fallback; unrelated entries stay.
  - `config-write-policy-shared.ts` — Changed configWrites selection before authorization.
  - `helpers.ts` — Changed DM guidance uses selected policy/stored path.
  - `read-only.ts` — Changed manifest-only reader uses selected record policy, never another snapshot owner.
  - `setup-adapter.types.ts` — Extended optional setup policy; existing implementations valid.
  - `setup-contract.ts` — Extended setup contract forwards policy to execution adapter.
  - `setup-helpers.ts` — Changed name/patch/promotion writes use selected spelling. Sole/default inference cannot restore rejected aliases; full-adapter and metadata-only inputs stay.
  - `setup-promotion-discovery.ts` — Retained lightweight discovery forwards promotion/policy declarations; no key selection.
  - `setup-promotion-helpers.ts` — Derives five promotion fields from ChannelSetupAdapter; duplicate callback shape removed. Owns movable fields/root preservation/named filtering, not destination selection.
  - `setup-wizard-helpers.ts` — Changed patch/allowFrom pass full adapter or derived promotion metadata into shared writing/promotion; destination is owner-selected.
  - `setup-wizard-legacy-compat.ts` — Active legacy wizard delegates policy/scoped patches to shared patch/promotion.
  - `setup-wizard-types.ts` — Retained setup type carries optional policy.
  - `setup-wizard.ts` — Active wizard promotes via owner, temporarily sets legacy defaultAccount, then restores operator default. Temporary scope does not select stored winners.
  - `types.adapters.ts` — Retained type barrel; public adapter identity stays.
  - `types.plugin.ts` — Retained plugin shape and setup ownership.
- `src/channels/status/account-state.ts` — Retained disabled-state check agrees with runtime selection.
- `src/cli/plugin-install-config-policy.ts` — Historical recovery-metadata caller now delegates manifest/request planning to src/plugins/install-config.ts; no account selection.
- `src/commands/doctor-config-flow.ts` — Retained Doctor binding orchestration delegates selection to repair owner.
- `src/commands/doctor/shared/legacy-config-binding-repair.ts` — Changed binding lookup; Doctor retains persistence.
- `src/commands/doctor/shared/plugin-metadata-snapshot-scope.ts` — Retained metadata rebase refreshes policy within its scope.
- Under `src/config/` (relative paths):
  - `channel-account-config.ts` — Shared merge forwards normalizer/channel/policy to one key decision; precedence stays.
  - `channel-alias-migration.ts` — Retained migration uses selected inherited-stream account.
  - `channel-capabilities.ts` — Changed capability selection.
  - `channel-doctor-helpers.ts` — Changed inherited-stream selection; full-map migration stays exhaustive.
  - `channel-groups.ts` — Changed merged-group read carries channel identity.
  - `context-visibility.ts` — Changed context-visibility selection.
  - `group-policy.ts` — Changed group path/policy selection before field precedence.
  - `group-scope-tree.ts` — Retained group-tree consumer of selected data.
  - `implicit-mentions.ts` — Changed implicit-mention selection.
  - `io.plugin-metadata.ts` — Retained config metadata rebase refreshes policy with existing facts.
  - `markdown-tables.ts` — Changed table-mode read carries channel identity.
  - `zod-schema.providers-whatsapp.ts` — Retained WhatsApp-only schema lookup; undeclared policy preserves old behavior.
- `src/cron/delivery-channel-validation.ts` — Changed enabled selection before delivery validation.
- `src/gateway/server-channels.ts` — Changed health-setting selection; undeclared compatibility forms stay and agree under declared policy. Manager still owns startup/runtime state.
- `src/gateway/server-chat.ts` — Retained heartbeat visibility consumer of selected policy.
- `src/gateway/server-runtime-state-prepare.ts` — Retained manager construction; startup/runtime state owner stays.
- `src/gateway/server-secrets-reload.ts` — Retained secrets-reload manager contract; no selector.
- `src/infra/event-session-routing.ts` — Changed event allowlist selection before routing.
- `src/infra/heartbeat-runner-execution.ts` — Retained heartbeat execution consumes visibility owner.
- `src/infra/heartbeat-visibility.ts` — Changed heartbeat policy selection.
- Under `src/infra/outbound/` (relative paths):
  - `deliver-core.ts` — Retained outbound chunk caller carries channel identity.
  - `message-account-selection.ts` — Retained disabled check agrees with runtime selection.
  - `message-action-params.ts` — Retained media-limit consumer of selected account.
  - `message-action-send.ts` — Retained response-prefix consumer of selected identity.
- `src/media/configured-max-bytes.ts` — Changed media-limit selection.
- Under `src/plugin-sdk/` (relative paths):
  - `{account-core.ts,account-helpers.ts,account-resolution-runtime.ts,account-resolution.ts,agent-runtime.ts,channel-core.ts,channel-feedback.ts,channel-ingress-runtime.ts,channel-outbound.ts,channel-plugin-common.ts,config-runtime.ts,context-visibility-runtime.ts,discord.ts,markdown-table-runtime.ts,reply-chunking.ts,reply-dispatch-runtime.ts,reply-runtime.ts,routing.ts,runtime-doctor-migrations.ts,setup-runtime.ts,setup.ts}` — Retained public SDK facades/types and argument/declaration identities; typed export/local-binding queries cover forwarding.
  - `allowlist-config-edit.ts` — Changed allowlist destination selection; persistence/validation owners stay.
  - `channel-config-helpers.ts` — Extended adapter/factory policy forwarding to enable/delete/setup writers.
  - `channel-dm-policy.ts` — Active DM getCurrent resolves account; setPolicy computes policy/allowFrom and invokes applyPatch or patchChannelConfigForAccount(setupSurface). Descriptive resolveConfigKeys keeps canonical IDs and has no production caller; live writer is active.
  - `channel-policy.ts` — Retained security exports/account wrappers; separate open-group severity change is not selection.
  - `channel-setup.ts` — Retained setup exports/types/missing-plugin builder; missing adapters reject writes.
  - `core.ts` — Retained exports and active security wrapper forward channelKey/policy to DM path owner.
  - `optional-channel-setup.ts` — Retained missing-plugin guidance; applyAccountConfig throws before account selection/mutation.
- Under `src/plugins/` (relative paths):
  - `bundled-channel-config-metadata.ts` — Retained manifest-bearing loader; no account/winner selection.
  - `bundled-plugin-metadata.ts` — Retained complete manifest transport; optional policy is not interpreted.
  - `discovery.ts` — Retained full PluginManifest transport; policy derivation downstream.
  - `discovery.types.ts` — Retained optional bundledManifest type accepts added metadata.
  - `manifest-capability-normalizers.ts` — Retained providerAuthAliases type use, unrelated to account policy.
  - `manifest-registry.ts` — Changed record builder preserves parsed policy; transport stays.
  - `manifest-registry.types.ts` — Retained manifest-derived types/record transport carry optional metadata.
  - `manifest-setup-normalizers.ts` — Validates declared channel/field shape, drops blocked keys and emits metadata; no account-map selection.
  - `manifest-types.ts` — Added optional channel-owned manifest policy.
  - `manifest.ts` — Changed parser adds normalized policy; existing fields stay.
  - `plugin-cache-files.types.ts` — Retained parsed-manifest cache type; lifecycle invalidation stays.
  - `plugin-metadata-snapshot.ts` — Changed installed-index owner-map derivation; existing rebase/project/load lifecycle rebuilds policy.
  - `plugin-metadata-snapshot.types.ts` — Added optional derived map; provider-reader fields stay.
  - `provider-model-compat.ts` — Retained provider-owner map reader; no channel-policy use.
- `src/plugins/contracts/inventory/bundled-capability-metadata.ts` — Retained type/setup/provider-env projection; no account-policy interpretation.
- `src/plugins/runtime/runtime-channel.ts` — Retained chunk/format runtime facade carries channel identity.
- `src/plugins/runtime/types-channel.ts` — Retained runtime type projection; arguments compatible.
- `src/routing/account-lookup.ts` — Entry helpers delegate to key owner. Declared policy gates aliases and optional allowMissing canonical creation; undeclared raw creation stays.
- `src/status/status-text.ts` — Retained Telegram-only rich-message lookup; Signal cannot reach it.
- `src/system-agent/plugin-artifact.ts` — Retained artifact identity/capability reader; incoming consolidation changes lazy config-owner import, not manifest interpretation.

### Retained non-Signal production consumers

Bundled non-Signal manifests omit Signal eligibility. Existing case-only/normalized reads and ordinary scoped/name/cleanup writes remain. Promotion now writes to the runtime exact-key winner; maps need not stay identical. Groups name readers, writers, delivery and forwarding files.

- `extensions/a2a/src/channel-base.ts` — Retained a2a fixed-root setup; no named account selection.
- `extensions/buzz/src/{gateway.ts,setup-core.ts,types.ts}` — Retained Buzz factory/Markdown delivery; setup preserves root during promotion.
- `extensions/clickclack/src/{accounts.ts,setup-core.ts}` — Retained exact-first normalized reader; registered promotion moves root credentials into runtime winner, preserving active account. Tests covers account-add.
- `extensions/discord/src/{accounts.ts,draft-chunking.ts,monitor/{agent-components.dispatch.ts,agent-components.runtime.ts,message-handler.context.ts,message-handler.process-reactions.ts,message-handler.process-reply-runtime.ts,native-command-agent-reply.ts,native-command-status.ts,native-command.ts,provider.ts},outbound-text.ts,send.outbound.ts,setup-adapter.ts,setup-core.ts,setup-surface.ts,shared.ts,token.ts}` — Retained case-only account/token reads, scoped setup/config, chunk/group/context/ack/outbound. Local calls bind Discord; generic runtime facade and lazy dispatch census stay.
- `extensions/feishu/{runtime-api.ts,src/{accounts.ts,bot.ts,channel.ts,comment-dispatcher.ts,outbound.ts,policy.ts,reply-dispatcher.ts,send.ts,setup-core.ts,setup-surface.ts}}` — Retained account/group/context, scoped setup/config, outbound/formatting. Local calls bind Feishu; generic exports, channel-only Markdown and account chunks stay.
- `extensions/googlechat/src/{accounts.ts,channel-base.ts,monitor-reply-delivery.ts,setup-core.ts,setup-surface.ts}` — Retained factory/setup/channel adapters; reply chunks receive resolved account; writes bind googlechat.
- `extensions/imessage/src/{accounts.ts,monitor/{deliver.runtime.ts,deliver.ts,inbound-processing.ts,monitor-provider.ts},send.ts,setup-core.ts,shared.ts}` — Retained factory/scoped writers/send/monitor. Chunk/ack/context bind iMessage; generic runtime forwarding stays.
- `extensions/irc/src/{accounts.ts,channel.ts,message-adapter.ts,send.ts,setup-core.ts}` — Retained factory/hybrid config/setup/send/message adapter; destructured chunks receive irc/accountId; no Signal policy.
- `extensions/line/{runtime-api.ts,src/{accounts.ts,bot-handlers.ts,config-adapter.ts,gateway.ts,group-keys.ts,outbound.ts,setup-core.ts}}` — Retained account/group/scoped writers/ingress/outbound; local calls bind line; generic exports and optional chunk limit stay.
- `extensions/matrix/src/{account-selection.ts,config-adapter.ts,matrix/{account-config.ts,monitor/{ack-config.ts,handler-context.ts,handler.ts},send/chunking.ts},setup-config.ts,setup-core.ts}` — Retained normalized selection/merge/writers/ack/context/formatting. Logical callback target resolves to runtime winner before promotion; Matrix owns bootstrap.
- `extensions/mattermost/{runtime-api.ts,src/{channel-config-shared.ts,mattermost/{accounts.ts,monitor-activation.ts,monitor-event-plan.ts,monitor-posts.ts,monitor-turn.ts,reply-delivery.ts,send.ts,slash-http.ts},setup-core.ts,setup-surface.ts}}` — Retained account/config/scoped setup/monitor/group/ack/outbound. Local calls bind mattermost/resolved account; generic exports stay.
- `extensions/msteams/src/{messenger.ts,monitor-handler/message-handler.ts,monitor.ts,reply-dispatcher.ts,send.ts,setup-core.ts}` — Retained Teams setup/context/ack/send/formatting; channel-only calls remain without new account argument or Signal policy.
- `extensions/nextcloud-talk/src/{accounts.ts,channel.adapters.ts,gateway.ts,send.runtime.ts,send.ts,setup-core.ts}` — Retained factory/scoped writers/gateway/send bind nextcloud-talk; send.runtime remains generic facade.
- `extensions/nostr/src/{gateway.ts,setup-adapter.ts,types.ts}` — Retained factory/setup/Markdown; both gateway calls keep their resolved account IDs.
- `extensions/qa-channel/src/{accounts.ts,channel-base.ts}` — Retained synthetic factory/fixed channel contract; no Signal policy.
- `extensions/raft/src/{accounts.ts,setup.ts}` — Retained factory/channel-bound setup/promotion; no Signal policy.
- `extensions/reef/src/setup.ts` — Retained fixed-channel setup/promotion declarations.
- `extensions/slack/src/{accounts.ts,actions.ts,config-adapter.ts,message-action-dispatch.ts,monitor/{config.runtime.ts,message-handler/{dispatch.ts,prepare.ts},provider.ts,slash-dispatch.runtime.ts,slash.ts},send.ts,setup-core.ts,setup-shared.ts,setup-surface.ts}` — Retained account/config/scoped setup/actions/ingress/send; slash chunk/Markdown bind slack/route.accountId; generic facades stay.
- `extensions/sms/src/{accounts.ts,channel.ts}` — Retained SMS-bound factory/channel account/config contract.
- `extensions/synology-chat/src/{accounts.ts,channel.ts,setup-surface.ts}` — Retained Synology factory/channel/setup binding and promotion.
- `extensions/telegram/{runtime-api.ts,src/{account-config.ts,account-selection.ts,bot-handlers.message-pipeline.ts,bot-message-context.session.ts,bot-message-context.ts,bot-message-dispatch.runtime.ts,bot-message-dispatch.ts,bot-message.ts,bot-native-command-dispatch.ts,bot-native-commands.runtime.ts,channel.ts,config-adapter.ts,draft-chunking.ts,send-edit.ts,send-message.ts,send.runtime.ts,setup-core.ts,setup-surface.helpers.ts,setup-surface.ts,text-chunk-limit.ts,token.ts}}` — Retained normalized account/token, scoped writers, message/native context, drafts/send/formatting. Calls bind telegram; generic facades/root-default precedence/token promotion stay.
- `extensions/tlon/src/{channel.ts,monitor/utils.ts,setup-core.ts,types.ts}` — Retained factory/hybrid config/scoped setup/name/monitor mentions bind tlon; URL/code promotion stays.
- `extensions/twitch/src/{config.ts,setup-surface.ts,token.ts}` — Retained normalized account/token/root-first default and scoped setup; access-token promotion stays.
- `extensions/whatsapp/src/{account-config.ts,account-ids.ts,auto-reply/{config.runtime.ts,monitor/{inbound-dispatch.ts,process-message.ts,runtime-api.ts}},send.ts,setup-core.ts,shared.ts}` — Retained listing/merge/account-only writers; alwaysUseAccounts/authDir promotion stays. Context/chunk/Markdown bind whatsapp; one inbound text-limit call still omits accountId; generic exports stay.
- `extensions/zalo/{runtime-api.ts,src/{accounts.ts,channel.ts,monitor.ts,setup-core.ts,test-support/lifecycle-test-support.ts,token.ts}}` — Retained factory/case-only token/scoped writers/monitor formatting bind zalo; generic setup exports stay.
- `extensions/zalouser/src/{accounts.ts,channel.adapters.ts,monitor.ts,setup-core.ts,setup-surface.ts,shared.ts}` — Retained profile factory/scoped writers/group-path/chunks bind zalouser/resolved account; empty extra promotion-key declaration stays.

- `extensions/clickclack/src/{channel.ts,channel.setup.ts,setup-core.test.ts}` — Both registered surfaces install ClickClack setup contract; account-add regression preserves exact ops credential (Tests).
- `extensions/matrix/src/setup-contract.ts` — Logical callback target is resolved by shared owner before inheritance copy; callback does not write map.

### Test and fixture consumers

These contract/test-support files assert behavior through production owners; none selects production stored keys. Tests records changed assertions, registered entry points, runtime proof and incomplete checks. Listing a retained test does not claim it was rerun.

- Signal: `extensions/signal/{doctor-contract-api.test.ts,src/{account-policy.test.ts,setup-transport.test.ts}}`.
- Other channel tests and fixtures: `extensions/{feishu/src/delivery-trace.test.ts,matrix/src/{channel.setup.test.ts,delivery-trace.test.ts,matrix/monitor/replies.formatting.test.ts},mattermost/src/delivery-trace.test.ts,msteams/src/delivery-trace.test.ts,slack/src/channel-actions-setup-status.contract.test.ts,telegram/src/{bot-native-command-executors.test-support.ts,bot-native-commands.test-helpers.ts,send.test-harness.ts,setup-surface.test.ts}}`.
- Agent tests and fixtures: `src/agents/{agent-command.compaction.test-support.ts,agent-command.live-model-switch.test.ts,ai-transport-runtime-host.test.ts,conversation-capability-profile.test.ts,embedded-agent-runner/{compact.hooks.harness.ts,history.test.ts,run/runtime-preparation.thinking.test.ts},identity.per-channel-prefix.test.ts,identity.test.ts,model-catalog-view.test.ts,models-config.providers.implicit.discovery-scope.test.ts,provider-auth-aliases.test.ts,provider-request-config.test.ts}`.
- Shared account, config and SDK tests: `src/{auto-reply/{chunk.test.ts,reply/commands-allowlist.test.ts},channels/plugins/{account-config-mutation.test.ts,account-helpers.test.ts,account-key-policy.test.ts,config-helpers.test.ts,helpers.test.ts,read-only.test.ts,setup-contract.test.ts,setup-helpers.test.ts,setup-promotion-helpers.test.ts,setup-wizard-helpers.test.ts,setup-wizard.test.ts},config/{channel-capabilities.test.ts,context-visibility.test.ts,group-policy.test.ts,implicit-mentions.test.ts,markdown-tables.test.ts},plugin-sdk/{channel-config-helpers.test.ts,channel-outbound.draft-chunking.test.ts,test-helpers/plugin-runtime-mock.ts},routing/account-lookup.test.ts}`.
- Command, Gateway and delivery tests: `src/{commands/{agents.providers.test.ts,channels.add.test.ts,channels.adds-non-default-telegram-account.test.ts,channels.remove.test.ts,doctor-config-flow.missing-default-account-bindings.integration.test.ts},flows/bundled-health-checks.test.ts,gateway/{server-channels.approval-bootstrap.test.ts,server-channels.test.ts,server-chat.agent-events.test.ts,server-methods/channels.start.test.ts,server-plugin-reload.activation.test-support.ts,server-plugin-reload.recovery.test-support.ts,server-plugin-reload.recovery.test.ts,server-plugin-reload.suspension.test-support.ts,server-reload-channel-restart.test.ts,server-reload-handlers.test.ts,server.chat.gateway-server-chat-b.test.ts},infra/heartbeat-visibility.test.ts}`.
- Metadata and release tests: `{src/plugins/{bundled-plugin-categories.test.ts,bundled-plugin-metadata.test.ts,contracts/runtime-import-side-effects.contract.test.ts,dashboard-capabilities.test.ts,loader.prefer-over.test.ts,manifest-backup-resources.test.ts,manifest-categories.test.ts,manifest-control-ui.test.ts,manifest-metadata-scan.test.ts,manifest-model-catalog.test.ts,manifest-registry-installed.test.ts,manifest-transcript-sources.test.ts,manifest.json5-tolerance.test.ts,manifest.reserved-id.test.ts,plugin-metadata-account-key-policies.test.ts,plugin-metadata.test-support.ts,plugin-policy-id.test.ts,provider-model-compat.prepared.test.ts,provider-model-routes.installed.test.ts},test/release-check.test.ts}`.

### Manual strings, registration and final pipeline edges

- Under `extensions/signal/src/` (relative paths):
  - `aliases.ts` — Retained aliases consume resolved Signal account.
  - `{approval-auth.ts,approval-native.ts,approval-handler.runtime.ts}` — Retained account-scoped approval auth/capability/handling; existing owner retains approval authority.
  - `{message-actions.ts,reaction-level.ts,send-reactions.ts}` — Retained action/reaction policy/RPC use selected account; no alternate key choice.
  - `{rpc-context.ts,signal-ingress.ts,client.ts}` — Selected number/endpoint reach ingress/RPC/event URL; recording endpoint proves final handoff.
  - `setup-surface.ts` — Retained setup status reads resolved account.
- `src/{gateway/server-methods/channels.ts,commands/channels/status.ts}` — Registered RPC/CLI status consume manager/adapters; UI/native receive canonical runtime maps, not authored maps. Both entrypoints have runtime proof.
- `src/commands/channels/add.ts` — Registered account-add calls account-config-mutation/shared promotion before persistence; Tests covers absent-default regression.
- `scripts/fixtures/packed-plugin-sdk-type-smoke.ts` — Relative import includes copied setup fixture in candidate smoke; same release-check copy owner.
- `test/scripts/release-check.test.ts` — Generated string calls fixture generator from separate tooling checkout; both copies checked against stale target fixtures.
- `.github/workflows/openclaw-npm-preflight.yml` — Four sparse tooling roots include scripts/both fixtures; copy owner/source stay.
- `src/plugins/install-config.ts` — Receives former CLI manifest read; manifest.id plans recovery and install metadata gates eligibility. No account-policy/route selection.

Full-map validation and migration enumeration in `extensions/signal/src/config-schema.ts`, `extensions/signal/src/config-compat.ts`, `extensions/signal/src/account-key-repair.ts`, and `src/config/channel-doctor-helpers.ts` inspect authored rows for validation, cleanup or diagnostics. Their selection-dependent operations use the shared owner; enumeration is not route selection.

### Pinned-base census dispositions

The pinned-base comparison includes incoming main changes. Rows trace removed declarations/properties; markers count files, except the worker path's two references. Unrelated `type` keywords and `meta` fields are not the removed local properties.

| Census row | Source disposition |
|---|---|
| `anyOf` | The local MCP union conversion moved to `packages/normalization-core/src/json-schema.ts:178`. `src/agents/mcp-json-schema-validator.ts:45` calls that shared normalizer. Remaining schema keywords and fixtures retain their JSON Schema contract. |
| `type` | The removed inventory property is the same MCP converter's `{ type: entry }`. The shared normalizer still emits it at `packages/normalization-core/src/json-schema.ts:178`. No global `type` contract was removed. |
| `AWS_SECRET_ACCESS_KEY_VALUE_PATTERN` | The old regex export and imports were removed. `src/logging/redact-patterns.ts:96,202,368` now supplies the matcher through the default pattern list. No old external reference remains. |
| `config/sessions/session-model-context.worker` | Both remaining references are the source/dist paths in `src/infra/runtime-process-entrypoints.ts:69-70`. Runtime URL resolution and `scripts/lib/runtime-process-core-build-entries.mts:4-24` consume this table; `tsdown.config.ts:423` spreads the derived build entries. The worker remains packaged; these two unchanged main-side readers are retained. |
| `currentModuleUrl` | The inline session-model worker property moved to `runtimeProcessEntrypoints.sessionModelContext`. Its source-relative path was adjusted for the table location. Runtime and build derive from that same table; other worker entries retain the property. |
| `sourceWorkerName` | The source worker name moved into the same table. Both URL resolution and build generation consume it. Other workers' names remain valid. |
| `distWorkerPath` | The same table retains `config/sessions/session-model-context.worker.js`. Runtime joins it under `dist`; build generation derives its output key. The artifact was not retired. |
| `DoctorConfigPreflightResult` | The type moved to `src/commands/doctor/shared/config-migration-result.ts:10`. `src/cli/program/config-guard.ts:6` and `doctor-config-preflight.ts:59` import the new owner. No old import remains. |
| `cronCodexRuntimePolicyTargets` | The moved type retains this field at `config-migration-result.ts:15`. Preflight still records/returns targets, and `doctor-config-flow.ts:332-347` consumes them for repair and persistence. |
| `stateMigrationStepReceipts` | Preflight still records/returns receipts. `config-migration-result.ts:54,61` forwards them; Doctor flow awaits/spreads the result. `doctor-health-contribution-runners.state.ts:149-150` retains the health handoff. |
| `postSessionPluginMigration` | Preflight still returns the prepared plan. `config-migration-result.ts:55,62` forwards it, and `doctor-health-contribution-runners.state.ts:142-143` supplies it to the transcript migration owner. |
| `postSessionPluginMigrationPlanBound` | The preflight flag remains on the moved type and is forwarded by `config-migration-result.ts:56,63`, the health runner at145-146, and the transcript migration entry point at `doctor-session-transcripts.ts:199-214`. |
| `lastTouchedVersion` | Doctor's inline read moved to `config-migration-result.ts:26-27`, before repairs. It still produces `sourceLastTouchedVersion`; the stored metadata key and native/config readers remain valid. |
| `meta` | The removed inventory field was Doctor's inline metadata cast. `config-migration-result.ts:26` now reads typed `snapshot.sourceConfig.meta`. Other metadata identities were not retired. |
| `resolveClaudeCliSessionFilePath` | The synchronous helper remains private at `cli-session-history.claude.ts:455` for its internal synchronous readers. The external snapshot reader imports/awaits the async owner at `cli-session-history.claude-snapshot.ts:15,135`. No stale external import remains. |
| `runSerializedPreparedModelRuntimeTask` | The helper and its only caller/import were removed. `prepared-model-runtime.build.ts` now uses `createFullModelCatalogAccess`; its owner at `prepared-model-runtime.catalog-access.ts:420-440` retains the generation, limits discovery concurrency, and checks the current generation around awaited work. No old caller remains. |

The exact source-dispositioned owner/consumer files counted by each marker are:

| Symbol | Files |
|---|---|
| `anyOf` | `src/agents/mcp-json-schema-validator.ts`; `packages/normalization-core/src/json-schema.ts` |
| `type` | `src/agents/mcp-json-schema-validator.ts`; `packages/normalization-core/src/json-schema.ts` |
| `currentModuleUrl` | `{src/{config/sessions/session-model-context-worker-runtime.ts,infra/{runtime-process-entrypoints.ts,runtime-worker-url.ts}},scripts/lib/runtime-process-core-build-entries.mts}` |
| `sourceWorkerName` | `{src/{config/sessions/session-model-context-worker-runtime.ts,infra/{runtime-process-entrypoints.ts,runtime-worker-url.ts}},scripts/lib/runtime-process-core-build-entries.mts}` |
| `distWorkerPath` | `{src/{config/sessions/session-model-context-worker-runtime.ts,infra/{runtime-process-entrypoints.ts,runtime-worker-url.ts}},scripts/lib/runtime-process-core-build-entries.mts}` |
| `DoctorConfigPreflightResult` | `src/{commands/{doctor/shared/config-migration-result.ts,doctor-config-preflight.ts},cli/program/config-guard.ts}` |
| `cronCodexRuntimePolicyTargets` | `src/commands/{doctor/shared/config-migration-result.ts,doctor-config-preflight.ts,doctor-config-flow.ts}` |
| `stateMigrationStepReceipts` | `src/{commands/{doctor/shared/config-migration-result.ts,doctor-config-preflight.ts,doctor-config-flow.ts},flows/doctor-health-contribution-runners.state.ts}` |
| `postSessionPluginMigration` | `src/{commands/{doctor/shared/config-migration-result.ts,doctor-config-preflight.ts,doctor-config-flow.ts,doctor-session-transcripts.ts},flows/doctor-health-contribution-runners.state.ts}` |
| `postSessionPluginMigrationPlanBound` | `src/{commands/{doctor/shared/config-migration-result.ts,doctor-config-preflight.ts,doctor-config-flow.ts,doctor-session-transcripts.ts},flows/doctor-health-contribution-runners.state.ts}` |
| `lastTouchedVersion` | `src/commands/{doctor/shared/config-migration-result.ts,doctor-config-flow.ts}` |
| `meta` | `src/commands/{doctor/shared/config-migration-result.ts,doctor-config-flow.ts}` |

census: generic `config/sessions/session-model-context.worker` reviewed — 2 callers listed
census: generic anyOf reviewed — 2 callers listed
census: generic type reviewed — 2 callers listed
census: generic currentModuleUrl reviewed — 4 callers listed
census: generic sourceWorkerName reviewed — 4 callers listed
census: generic distWorkerPath reviewed — 4 callers listed
census: generic DoctorConfigPreflightResult reviewed — 3 callers listed
census: generic cronCodexRuntimePolicyTargets reviewed — 3 callers listed
census: generic stateMigrationStepReceipts reviewed — 4 callers listed
census: generic postSessionPluginMigration reviewed — 5 callers listed
census: generic postSessionPluginMigrationPlanBound reviewed — 5 callers listed
census: generic lastTouchedVersion reviewed — 2 callers listed
census: generic meta reviewed — 2 callers listed

### Additional normalization consumers

The source supplement adds 126 files to 401 typed: 527 total. Matrix setup-contract was already named; these 125 were absent. These are existing normalizer consumers, not new production changes; braces enumerate paths.

- `extensions/a2a/src/accounts.ts` — Retained account-context identity normalization.
- `extensions/discord/src/{account-inspect.ts,actions/runtime.messaging.shared.ts,client.ts,directory-cache.ts,directory-config.ts,monitor/{model-picker-preferences.ts,thread-bindings.config.ts,thread-bindings.manager.ts,thread-bindings.session-shared.ts,thread-bindings.state.ts},secret-config-contract.ts,setup-account-state.ts,voice/transcripts-source.ts}` — Retained inspection/setup/client/directory identity, message comparisons, voice secret/transcript scope, model/thread cache/manager/persisted keys.
- `extensions/feishu/src/{bot-identity-cache.ts,config-schema.ts,dynamic-agent.ts,secret-contract.ts,thread-bindings.ts}` — Retained bot/dynamic-agent/thread keys, default schema and secret identity.
- `extensions/googlechat/src/secret-contract.ts` — Retained account secret-owner identity.
- `extensions/imessage/src/approval-native.ts` — Retained approval-target identity/same-account suppression.
- `extensions/matrix/{doctor-contract-api.ts,src/{approval-handler.runtime.ts,approval-reactions.ts,auth-precedence.ts,cli-account.ts,cli-shared.ts,env-vars.ts,matrix/{accounts.ts,client/{config.ts,env-auth.ts,storage.ts},config-paths.ts,config-update.ts,credentials-read.ts,credentials-state.ts,credentials.ts,monitor/reaction-events.ts,read-policy.ts,session-store-metadata.ts},onboarding.ts,profile-update.ts,secret-contract.ts,session-route.ts,setup-dm-policy.ts,storage-paths.ts}}` — Retained Doctor credential identity; account/auth/config/CLI/onboarding/profile/env scope; storage/credential compatibility; session/read routes; approval-reaction registration/lookup/cleanup.
- `extensions/mattermost/src/mattermost/read.ts` — Retained requester-account comparisons for message reads.
- `extensions/msteams/src/approval-native.ts` — Retained named-account approval transport gating.
- `extensions/nostr/src/{channel.setup.ts,secret-contract.ts}` — Retained setup/default-account and secret-assignment identity.
- `extensions/policy/src/doctor/strictness.ts` — Retained Doctor routing-policy canonicalization.
- `extensions/signal/src/{approval-reaction-routes.ts,approval-reactions.ts,question-reactions.ts}` — Retained approval-route matching and delivered approval/question account identity.
- `extensions/slack/src/{account-inspect.ts,action-runtime.ts,channel-migration.ts,directory-config.ts,group-policy.ts,installation-identity-state.ts,monitor/enterprise-install.ts}` — Retained account/directory/group identity, requester checks, migration and installation/enterprise keys.
- `extensions/telegram/src/{account-inspect.ts,accounts.ts,bot/helpers.ts,directory-config.ts,dm-session-key.ts,group-migration.ts,message-topic-binding.ts,miniapp/{command.ts,routes.ts},poll-registry.ts,thread-bindings.ts}` — Retained account/token/action gates; directory/group/DM routes; requester/topic matching; Mini App scope; poll/thread keys.
- `extensions/whatsapp/{auth-presence.ts,src/{accounts.ts,agent-tools-call.ts,auto-reply/monitor/group-activation.ts,group-session-key.ts}}` — Retained auth/call storage, group activation and account session keys.
- `extensions/zalouser/runtime-api.ts` — Retained public normalizer forwarding export.
- `src/agents/agent-tools.policy.ts` — Retained normalization/account matching. Historical merge4d throws for unavailable accounts; prepush8c denies all. Separate base/source behavior, not promotion repair or new CI proof.
- `src/acp/persistent-bindings.types.ts` — Retained persistent-binding/session account identity.
- `src/agents/{subagents/announce/subagent-announce-origin.ts,tools/{message-tool-discovery.ts,message-tool-execution.ts,message-tool-group-thread.ts,sessions-send-tool.ts}}` — Retained announcement origin, message discovery/echo/group-thread and session-send matching.
- `src/auto-reply/{group-thread-dispatch.ts,group-thread.ts,reply/{route-reply.ts,source-turn-id.ts}}` — Retained group dispatch/source-turn/reply-source identity.
- `src/channels/{message/outbound-echo.ts,plugins/{configured-binding-match.ts,media-limits.ts,message-action-dispatch.ts},thread-bindings-policy.ts}` — Retained echo/binding keys, media account input, conversation comparisons and thread policy.
- `src/cli/message-secret-scope.ts` — Retained message secret-account scope.
- `src/commands/{agents.providers.ts,channels/{add-mutators.ts,remove.ts},doctor/shared/{allowlist-policy-repair.ts,default-account-warnings.ts,legacy-config-migrations.runtime.config-tranche.ts}}` — Retained provider indexing, add/remove identity, Doctor allowlist/default/migration scope.
- `src/cron/isolated-agent/delivery-target.ts` — Retained isolated delivery account identity.
- `src/flows/{channel-setup.prompts.ts,channel-setup.ts}` — Retained setup/removal account input normalization.
- `src/gateway/{conversation-route-ownership.ts,server-methods/{cron-caller-scope.ts,send.ts}}` — Retained conversation ownership, cron caller/declaration matching and message routes.
- `src/infra/outbound/{account-scoped-conversation-bindings.ts,session-binding-normalization.ts,source-reply-mirror.ts,targets.ts}` — Retained conversation/session keys, reply matching and heartbeat account/secret scope.
- `src/pairing/pairing-challenge.ts` — Retained optional challenge account scope.
- `src/plugin-sdk/{account-id.ts,approval-client-helpers.ts,approval-native-helpers.ts,pairing-access.ts}` — Retained normalizer export/approval-recipient/native route/pairing comparisons.
- `src/routing/{account-id.test.ts,account-id.ts,binding-scope.ts,channel-route-targets.ts,resolve-route.ts,session-key.ts}` — Retained normalizer/test, bindings/routes/account lists and peer/group session keys.
- `src/secrets/channel-secret-basic-runtime.ts` — Retained account secret-owner and assignment identity.
- `src/state/openclaw-agent-db-session-migrations.ts` — Retained migrated conversation account identity.
- `src/tts/tts-config.ts` — Retained account normalization supplied to TTS override lookup.

Of these 125 files, 124 match the historical integration byte-for-byte. The agent tool-policy source difference is stated above; its normalization and account matching remain. The normalizer still owns lowercase/safe account IDs and default handling. These callers retain their existing lookup, comparison, storage, routing or test duties; they add no Signal eligibility decision.

### Complete fallback audit and authorization consumers

Audit before correction: 73 calls/54 production files (four owner delegations); 160 lexical references/67 files include facades/types/comments/local names. Factory/Signal aliases were traced separately. Coordinates below identify the audited340 source. Commit5b50754 fixes both families; registered red/green is recorded under Tests.

- **Owner/facades:** `src/routing/account-lookup.ts:34,44,60,72` and `src/config/channel-account-config.ts:76,83` consume the selected key; no rescan. `src/channels/plugins/account-helpers.ts:115` forwards generated resolvers. Public `src/plugin-sdk/{account-core,account-helpers,account-resolution,account-resolution-runtime,routing}.ts` retain owner forwarding.
- **Correction sites:** `src/auto-reply/command-auth.ts:454,471-472` now selects inferred sole accounts through the owner. `src/channels/plugins/config-helpers.ts:88,126,177,184` remove raw fallback: delete/clear need existing selection; optional allowMissing creation belongs to selector. Public helpers accept raw IDs; Signal CRUD normalizes at `src/plugin-sdk/channel-config-helpers.ts:227-238`. Undeclared-channel raw creation remains the shipped contract.
- **Retained writer creation:** `src/channels/plugins/setup-helpers.ts:50,93,311,370` and `src/plugin-sdk/allowlist-config-edit.ts:197` normalize creation intent. Promotion selects inferred/default candidates; only explicit callback targets may create a missing normalized key. `src/channels/plugins/helpers.ts:57,63` reads selected/default values; path descriptions do not read raw rows.
- **Root/default field inheritance only:** `src/media/configured-max-bytes.ts:39`; `src/channels/{account-config-enabled.ts:13,draft-streaming-chunking.ts:33,join-intro/report-channel-room-join.ts:74}`; `src/channels/plugins/{config-write-policy-shared.ts:71,read-only.ts:209}`; `src/config/{channel-groups.ts:32,channel-capabilities.ts:51,markdown-tables.ts:57,group-policy.ts:75,104,context-visibility.ts:54,implicit-mentions.ts:29}`; `src/infra/{event-session-routing.ts:113,heartbeat-visibility.ts:54}`; `src/auto-reply/{chunk.ts:49,92,reply/reply-threading.ts:51,reply/block-streaming.ts:60}`; `src/agents/{identity.ts:33,117,subagents/spawn/acp-spawn-parent-stream.ts:124,embedded-agent-runner/history.ts:192}`. No raw retry; group/peer scans stay inside selected maps.
- **Selection or validation only:** `src/cron/delivery-channel-validation.ts:99` and `src/commands/doctor/shared/legacy-config-binding-repair.ts:99` consume selected enabled flags; `src/config/channel-doctor-helpers.ts:196` selects inheritance, migrates all rows. `src/config/zod-schema.providers-whatsapp.ts:126` selects default, validates all rows. `src/status/status-text.ts:97` is Telegram-only. `src/gateway/server-channels.ts:407,415` retains two undeclared forms; declared policy gives the same winner.
- **Plugin direct calls:** `extensions/{zalo/src/token.ts:42,discord/src/token.ts:70,discord/src/accounts.ts:52,line/src/accounts.ts:88,line/src/group-keys.ts:52,imessage/src/accounts.ts:47,slack/src/accounts.ts:93,sms/src/accounts.ts:103,googlechat/src/accounts.ts:71,clickclack/src/accounts.ts:86,122,feishu/src/policy.ts:303,whatsapp/src/account-config.ts:13,33,telegram/src/token.ts:125,telegram/src/account-config.ts:15,matrix/src/matrix/account-config.ts:81,117,matrix/src/account-selection.ts:130,twitch/src/token.ts:64,twitch/src/config.ts:68,116,signal/src/account-selection.ts:10,signal/src/accounts.ts:69}`. Selected values inherit root/shared-default/environment or stop. Twitch reuses selected default; Signal reply/setup/transport/Doctor wrappers use explicit policy (Consumers).
- **Factory aliases:** active: `extensions/{discord,clickclack,line,imessage,slack,googlechat,sms,zalo,zalouser,qa-channel,nextcloud-talk,irc,feishu,raft,synology-chat}/src/accounts.ts`, `extensions/mattermost/src/mattermost/accounts.ts`, and `extensions/tlon/src/types.ts`; factory-owned. List/default-only: `extensions/{nostr/src/types.ts,buzz/src/types.ts,whatsapp/src/account-ids.ts,signal/src/accounts.ts,twitch/src/config.ts,telegram/src/account-selection.ts}`.
- **Separate source-only follow-ups:** `src/channels/thread-bindings-policy.ts:123` and `extensions/discord/src/monitor/thread-bindings.config.ts:18,31` retain exact reads; Signal lacks that field. `extensions/synology-chat/src/accounts.ts:51,113` retains exact webhook/policy reads beside merged config. Both are source-only follow-ups. Runtime maps, all-row scans and pairing's resolved/configured singleton do not restore rejected config.

Authorization scope closes the full additional inventory:

- **Account-derived `isAuthorizedSender` pipeline:** `src/auto-reply/reply/{commands-context,commands-core,commands-handlers.runtime,get-reply-directives,get-reply-native-slash-fast-path,fast-approve,dispatch-from-config.context,dispatch-from-config.prepare-operation,session-reset-command,session,commands-reset,abort-operation,abort}.ts`. Dispatch/reset/abort share corrected auth owner.
- **Flag consumers/handlers:** `src/auto-reply/reply/{command-gates,get-reply-directives-apply,get-reply-directives-routing,get-reply-inline-actions,get-reply-run-context,get-reply,commands-status,commands-plugin,commands-system-agent,commands-diagnostics,commands-session,commands-acp,commands-bash,commands-approve,commands-login}.ts`. `/acp help` proves the flag gate; approve/login/privileged actions retain independent approval/global-owner gates.
- **Facades/type projections:** `src/auto-reply/reply/{commands,abort.runtime,fast-approve.runtime,get-reply-run.types,commands-types}.ts`; no local account selection.
- **Global `senderIsOwner` consumers:** `src/gateway/{talk-client-agent-consult,server-methods/chat-send-message-injection}.ts` and `src/auto-reply/reply/dispatch-from-config.prepare-operation.ts:235-253`. Alias fallback never grants this field; last file also dispatches fast account-authorized commands.
- **Retained tests/fixtures:** `src/auto-reply/{command-control.test,command-auth.owner-default.test}.ts`; `src/auto-reply/test-helpers/{command-auth-registry-fixture,command-auth-registry-fixture.test}.ts`; `src/auto-reply/reply/{commands.test-harness,get-reply.test-mocks,get-reply.test-fixtures,directive-handling.mixed-inline.test-helpers,commands-export-trajectory.test-support,commands-compact.test-support,commands-login.harness-test-support}.ts`. `src/gateway/node-invoke-plugin-policy.ts:291` is an unrelated local closure. `src/gateway/server-plugin-reload.recovery.test-support.ts` was already inventoried.

### Round-2 typed authorization supplement

Candidate `5b5075411706bf8ee1992125335c0ac84a30a6b2`, tree `7029d0d267619b6511f3a7c29c225375cf7dd5ff`: 69 queries (29 root, 18 scripts, 18 test-root, 4 locals/copied CommandContext properties). Eight separate pinned-base auth/context queries supplement the historical 299/147/12 batches; all five new batches exit 0. Candidate: 1,257 references/708 positions/160 files; base supplement: 443/443/87. Added files: 84 (37 production, 47 tests/fixtures), preserving 527 for a union of 611. These are source queries, not a new CI-merge census. Full source evidence stays private; braces enumerate all added paths.

- `extensions/slack/src/monitor/events/interactions.block-actions.ts` — Shared admission when commands.allowFrom is set; retains Slack policy.
- `extensions/telegram/src/{bot-handlers.inbound-authorization,bot-native-command-login}.ts` — Callbacks use command admission when configured, otherwise channel admission; login requires current admission AND global owner.
- Under `src/auto-reply/reply/`:
  - `abort-operation.ts`, `commands-{acp,bash,session,status,system-agent}.ts`, `get-reply-native-slash-fast-path.ts` — Admission before abort, ACP, bang alias, session, both status branches, system-agent and native fast routing.
  - `command-gates.ts` — Admission and global-owner checks remain distinct.
  - `commands-{diagnostics,plugin}.ts` — Gate admission; forward both facts.
  - `commands-approve.ts` — Admission or existing explicit approval authority.
  - `commands-login.ts` — Recheck current admission AND global owner.
  - `commands-plugins.ts` — Privileged work still requires global owner or Gateway admin.
  - `commands-{btw,compact,learn}.ts`, `commands-acp/diagnostics.ts` — Forward global-owner fact; ACP diagnostics use it for visible/current entries.
  - `commands-{context,types}.ts` — Resolver copies two distinct facts into CommandContext; both receiving declarations were queried.
  - `commands-reset.ts`, `session-reset-command.ts` — Shared reset admission, including upstream commandAuthorized.
  - `dispatch-from-config.prepare-operation.ts` — Resolve current authorization; forward owner fact and dispatch fast commands.
  - `get-reply-directives{,-apply}.ts` — Gate directives/status on admission; retain literal-command suppression and separate owner branches/forwarding.
  - `get-reply-inline-actions.ts` — Gate skill/commands on admission; forward owner fact.
  - `get-reply-run-context.ts` — Upstream commandAuthorized and resolved admission both apply.
  - `get-reply-run-execute.ts` — Forward owner fact; retain existing Gateway-admin alternative.
  - `get-reply.ts` — Reset hooks need admission; ordinary replies forward owner fact.
- `src/gateway/{server-methods/chat-send-message-injection,talk-client-agent-consult}.ts`, `src/system-agent/rescue-message.ts` — Derive/forward global-owner fact; account access cannot grant it.
- `src/plugin-sdk/command-auth{,-native}.ts` — Re-export shared resolver. `src/plugin-sdk/command-status.runtime.ts` constructs CommandContext from separately supplied flags.

All 47 tests/fixtures below retain admission/global-owner inputs or assertions; none owns production selection. The new account-policy test covers registered command/reset regression. Inventory does not claim all listed tests reran.

- `extensions/discord/src/monitor/monitor.test.ts`
- `src/acp/control-plane/spawn.test.ts`
- `src/auto-reply/{command-auth.owner-default.test,command-control.test}.ts`
- `src/auto-reply/reply/{abort.target-owner.test,directive-handling.mixed-inline.test-helpers,directive-handling.model.test,get-reply-directives-apply.test,get-reply-inline-actions.skip-when-config-empty.test,get-reply-native-compact-authorization.test,get-reply-run.media-only.test,session.test,commands-{abort-trigger.test,account-policy.test,acp.test,approve.test,bash-alias.test,btw.test,compact.test,diagnostics.test,export-trajectory.test-support,gating.test,handlers.registration.test,info.test,learn.test,login.consent.test,login.harness-test-support,login.test,loop.test,mcp.test,plugin.test,plugins.install.test,plugins.test,reset-hooks.test,session-lifecycle.test,session-restart.test,steer.test,stop-target.test,subagents-routing.test,update.test}}.ts`
- `src/gateway/server-methods/{chat-send-user-turn.test,gateway-client-identity.test,talk.test}.ts`
- `src/plugin-sdk/command-auth.test.ts`
- `src/system-agent/{rescue-channel.live.test,rescue-message.test}.ts`
- `src/tts/tts-entry-delivery.test.ts`

Seven manual scope paths stay outside typed611: `scripts/check-ingress-agent-owner-context.mts` is diagnostic text; `scripts/dev/test-device-pair-telegram.ts` supplies literal plugin-command admission. `test/{helpers/agents/happy-path-prompt-snapshots.ts,transcripts-tool.discord-lifecycle.integration.test.ts,loopback-ask-user-telegram-channel.test.ts,canonical-descendant.integration.test.ts}` supply synthetic prompt/speaker/tool-authority/descendant owner facts. `scripts/e2e/system-agent-rescue-docker-client.ts` supplies literal flags in a client excluded from the scripts project. No competing selector or new execution claim.

## Invalidation

Selection is synchronous, uncached and reads supplied config. Doctor retains scoped metadata, preview without writes, backed-up repair and idempotence. Existing Signal reload prefix/restart refresh account state. Installed-record load/rebase/project rebuild policy; publication, retirement and caches own lifetime. Manifest adapters retain their selected record. Runtime proof covers Doctor preview/repair/repeat, restart, endpoint reload and old-source config boot without Doctor.

## Contention

No new lock, queue, transaction, async wait, listener or state store. Existing config/lifecycle owners retain effects.

## Tests

- Round 2: real admitted-group `buildCommandContext`→`handleCommands` `/acp help` and `resolveAuthorizedSessionResetCommand` prove the optional-resolveAllowFrom path in `src/auto-reply/reply/commands-account-policy.test.ts`. On source340, Alice loses help/reset and Bob gains both, with/without defaultAccount; senderIsOwner stays false. Four dispatch/reset cases fail while all eight eligible sole/default, undeclared, exact/case and global-owner controls pass. Split enable/delete/clear tests add three intended failures: final red 7 failed/27 passed. Source `5b5075411706bf8ee1992125335c0ac84a30a6b2` passes 146 tests (106 focused + 40 sibling), syntax lint/format/max-lines/assertion/SDK surface/diff checks. Native review passes. CI34688067951 found a test cleanup returning number;8828dc6 returns void, with14 command/abort tests passing. Assertions/production stay unchanged; CI/review receipts remain separate.
- Reset uses the actual session reset gate/result. Fast abort at `src/auto-reply/reply/abort-operation.ts:183-190` calls the same authorization owner and rejects before target/cancellation work. This is shared-entry authorization coverage, not executed cancellation side effects or a callback-backed Signal/global-owner escalation claim.
- Promotion: real `channelsAddCommand` plus cold contract tables reproduce root credentials/ignored access moving into absent-default spaced aliases. Corrected controls preserve root identity/restrictions/ignored rows across absent/present defaults, exact/case keys, explicit callback creation, `Default` and collisions: 125/125; earlier broader suite 143/143. Signal's adapter suppresses this generic promotion. `src/channels/plugins/setup-contract.test.ts` covers full/metadata-only contracts; `src/commands/channels.add.test.ts` covers registered add. No test deletion.
- SDK: [run 34686394191](https://github.com/openclaw/openclaw/actions/runs/34686394191), jobs `103534002152`/`103534002115`, compiles released 2026.9.4 and sealed candidate. Identical full inline adapters/contextual callbacks exercise promotion, setup/setup-runtime patch and allowFrom. TypeScript 6.0.3: NodeNext, strict/noEmit, skipLibCheck:false, types:[node]. Package source `20090983b5683ff352ed31e50a15143468913084`, SHA-256 `0b25b612693be3b4629231ff4445342f859bc0399f207d98b5d884c19caa6e6c`; checker `340142aaf7`. Runtime-only promotion correction retains signatures/fixture bytes. Both release-check files pass 60 cases; `test/scripts/release-check.test.ts` proves trusted-tooling copies against stale targets. Declaration proof is not runtime/release qualification. Round2 keeps this fixture/checker and public setup signatures unchanged. Its selector gains an optional final creation argument; prior arguments/inference/returns stay valid. The writer regression executes that mode and new-head CI owns its typecheck; the sealed200 compiler run is not represented as compiling the new mode.
- `extensions/clickclack/src/setup-core.test.ts:484`: registered setup+real reader preserve active exact ops credentials; pinned base fails while 19 controls pass, candidate 20/20.
- `extensions/signal/src/account-selection.test.ts`: registered config/threading/setup/delete cover own-number/inheritance, collision reads/writes, container URL, managed-port and Doctor default-transport collisions in both orders. `src/channels/plugins/account-key-policy.test.ts`: manifest adapter, scoped binding repair, outbound media collision, explicit SDK merge. Metadata tests cover persisted reconstruction, enablement, projection, replacement with/without declaration and no runtime execution.
- Retained receipts: 200 Signal-owner passes; routing/Gateway/metadata/policy/startup checks; final 35-case startup corpus; CI fixture 25/25 and 27/27 with provider-alias/Doctor sequencing assertions retained; setup metadata 142; SDK surface 10 (two exports/one callable); restored two released full-adapter callbacks; setup/contract 83 before metadata-only addition; revised cold-promotion/tooling tests. CI owns typechecking. Two macOS read-only `/var` versus `/private/var` assertions fail identically on base/candidate; neither assertions nor production changed.
- Test ledger: no baseline test deleted. `extensions/signal/src/setup-core.test.ts:144` and `:355` retire deletion of an emptied default row; the canonical empty row now preserves the exact winner. `extensions/signal/src/account-selection.test.ts:16` replaces that cleanup shape with effective identity/restriction continuity for exact and alias winners, including empty collision winners. `src/channels/plugins/setup-contract.test.ts:20` covers cold contract-to-promotion policy forwarding. `extensions/signal/doctor-contract-api.test.ts:45`: Doctor's former own-number inheritance fixture now omits the number per the accepted contract; `extensions/signal/doctor-contract-api.test.ts:87`: its own-number default case now expects optional key cleanup while keeping root transport authoritative. `src/plugins/loader.prefer-over.test.ts:110`: existing preferred-plugin assertions remain and cover both policy variants. `src/agents/provider-auth-aliases.test.ts` retains real installed-record selection in its I/O mock; `src/commands/doctor/repair-sequencing.test.ts` uses existing complete snapshots through `runDoctorRepairSequence`, removing two fixture casts without changing assertions. New metadata coverage uses a separate file; an intermediate clone-only assertion was removed because persisted-index reconstruction already covers the meaningful boundary.
- Runtime controls: exact collision before/after Doctor, own-number plus root, unchanged no-own-number base/head inheritance, optional Doctor/unchanged Telegram map, endpoint reload/restart, legacy default collision, and old config from `3a9d69db30` booting without Doctor. Managed-native inheritance lacks signal-cli; base/head share configured state/fallback/spawn error while root external transport runs. No live Signal messages; recording endpoints prove transport.
- Fresh external npm install on sealed200 passes maintained install/inspect, new Gateway, both status paths, account events/probes and cleanup. Source comparison to actual later merge `4b2f53900bc91b72b4afddaeb219251cda804f26` supports ordinary install/fresh startup only; new batch deferral is unused. Not merge4b package execution. Cold-start event-loop degraded metrics and disabled unrelated config warning remain recorded; no steady-state/live-service/batch/hot-reload claim. Historical merge4b lifecycle job103534548382 passed all nine reload tests, including watcher restart followed by settings persistence after reload closes. Its complete receipt is retained; this is not future-merge execution.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-12 16:35:19 +05:30
Peter Steinberger
f9ccd07ded
fix: skip official setup approvals and default to Astra (#145646)
* fix: skip official setup approvals and default to Astra

* test: align default-model expectations across consumers

* test: align attachment catalog with Astra default

* fix: preserve configured Codex catalog model selections
2026-09-12 00:34:25 -07:00
Peter Steinberger
fe056539fd
fix(plugins): keep rebuilt bundled plugins on git installs instead of npm refreshes with a mismatched SDK (#145356)
Git/dev installations keep the plugin rebuilt from the running source checkout instead of refreshing a same-version npm build with a different Plugin SDK. Doctor records why the registry artifact was not admitted.

Manifest-registry selection owns bundle precedence and dormant install detection. Named plugin updates, --all, and stable/beta release-cohort convergence carry that decision through package-mutation preflight while retaining strict checks for active packages. Existing npm records remain available, explicit plugin paths retain priority, and package-host updates retain their registry behavior.

Fixes #145266

Thanks @DonnieFi for reporting the artifact skew and providing the reproduction evidence.
2026-09-11 21:50:11 -07:00
Vincent Koc
dda6470454
feat(desktop): match virtual worker displays to the viewport (#143938)
* feat(desktop): match virtual worker displays to the viewport

Keep Fit as the default and gate Match on provider-owned resize permission and authenticated controller ownership. Preserve sizing choices during reconnects and retire resize authority with input control.

Pass resolved SSH VNC credentials through optional auth metadata and reconcile lost physical modifier releases after native popups. Add instrumented production Gateway/XFCE resize proof and native selection regressions.

* fix(desktop): align renderer exports and suppression inventory

* test(desktop): cover real node carrier resize qualification

* test(workers): align repository access provider fixture

* fix(ci): run real desktop resize proof with an upstream fixture

* fix(ci): bind desktop proof to raw merge identity

* fix(ci): retain safe desktop proof failure diagnostics

* fix(ci): expose safe desktop SSH setup diagnostics

* fix(ci): keep desktop proof phase tuple private

* fix(ci): retain desktop proof phase after timeout

* fix(ci): observe Gateway startup at desktop proof timeout

* fix(ci): run desktop proof against built Gateway

* fix(ci): assert visible desktop recovery state

* fix(ci): remove retired desktop spy allowance

* test(ui): model targeted desktop status in sizing fixtures
2026-09-12 11:26:46 +08:00
Ayaan Zaidi
8ebd18d84c
fix(doctor): preserve consumers of renamed auth profiles (#145136)
Doctor can rename a saved credential while leaving account selections and model references pointing at its deleted ID. This can make a new conversation use another account, send a profile ID as a literal API key, or leave an automation unable to run. A saved session can also restore the deleted credential after a failed model change, and an older stored pin with surrounding spaces can lose its selected account. Ordinary interactive Doctor also imported and archived credentials without completing all their consumers.

Both Doctor entry paths now use one auth-completion operation. It completes verified imports through the existing credential, account-link, config, session, and automation owners. Planned collision mappings are kept separate from completed-import and verified recovery facts. No database schema, runtime alias, plugin callback contract, or public config option is added.

Current signed PR head: `97b7f558aa772e06825e3aece4abf7ae84ecf19c`. The latest commit removes a redundant empty-session return and its helper; the existing preview and repair paths already produce the same summary. Current merge-tree checks use `0fc267c94c12f7138e674762c0e19f51a0dd963b`, whose second parent is this head. Earlier runtime, released-state, and published-updater captures below retain their original source identities.

## Consumers

| Stored representation | Repair owner | Runtime consumer |
| --- | --- | --- |
| Credential IDs, auth order, last-good and usage keys | Existing auth importer, alias allocator, and receipt owners | Credential selection and rotation |
| Personal `model-accounts` links | `state/user-model-accounts.ts` inside the shared receipt transaction | New conversations and normal turns |
| Provider `apiKey` profile reference | Explicit Doctor config slot using the reader's secret normalization | Provider authentication |
| Media `profile` / `preferredProfile` | Explicit Doctor config slots with exact ID matching | Media and image model selection |
| Plugin `authProfileId` / `defaultAuthProfileId` | Bounded plugin/channel config adapter | Registered LLM Task and older installed plugins |
| MCP OAuth and legacy model-runtime profile IDs | Existing typed config slots, following reader trimming | MCP authentication and runtime policy |
| Configured `@profile` model suffixes, model-map keys, and allow policies | Existing model parser and slot traversal | Agent/default/utility/media models, heartbeat, subagents, compaction, reviewers, hooks, and channel overrides |
| Active session `authProfileOverride`, including accepted surrounding spaces | Session owner with explicit profile-only repair admission; lookup follows the reader’s `.trim()` rule | `agents/auth-profiles/session-override.ts` account selection |
| Saved session `modelFallback.prevAuthProfileOverride` | Same session owner and verified rename map, independently of the active pin | Agent-selected model change snapshots the pin; failed-turn auto-revert restores it; session account selection consumes it |
| Saved automation primary/fallback suffixes | Existing cron changed-row writer after config persistence | Registered automation execution |

`doctor/auth-profile-repair.ts` joins the explicit repair sequence and ordinary health contribution. Successful imports report completed IDs; failed owners report blocked IDs. Config credentials and AWS metadata are updated only after import verification and archival. An empty completed map remains visible so the health flow does not repeat an operation already completed by the explicit sequence.

The private `emptyRepairSummary` helper and its sole call are removed. Empty session stores use the existing preview or repair summary path; no public interface or persisted shape changes.

The removed importer result `configOwnerMigrationApplied` is replaced by `migratedProfileIds` and `blockedProfileIds`. The shared completion operation consumes these facts; source-owner, canonical-key, flat-profile, sequencing, health-flow and fast-path fixtures mirror that return contract. Config and session runners consume the completed map, including an explicitly empty map.

Recovery uses current verified archive/credential evidence after the importer has resumed pending receipts. It can recover the recorded target even when the initial plan was empty. A before-state plan is not completion authority. A declined live source cannot borrow permission from an old archive or an unrelated receipt.

Session preview remains read-only when given a map. The registered health flow explicitly admits profile-only session updates after auth completion. The session owner repairs both active and rollback pins using the same completed map. It trims only lookup input and leaves unmapped saved strings unchanged. Marker timestamps, source, previous model/provider choices, credential provenance and compaction count, runtime bindings, and protected-harness state remain intact. Full repair retains its existing behavior.

The rollback producer is `src/config/sessions/session-model-fallback.ts:54-59`, entered through the registered sessions tool’s model patch at `src/agents/tools/sessions-tool.ts:496-515` and persisted by `src/gateway/sessions-patch.ts:547-550,643-644`. `src/agents/session-model-auto-revert.ts:84-102` restores the previous credential after a definitive failed turn; `src/agents/auth-profiles/session-override.ts:332` trims that restored pin before account selection. `src/config/sessions/types.ts:521-524` and `store-entry-shape.ts:56-80,186` retain both saved forms, and `session-entry-projection.ts:38-45,60-64` carries the rollback marker through projection. Doctor changes these saved references; no later runtime alias table is added.

Automation repair reads stored config together with runtime state, changes only matching reference slots, and submits only changed jobs. It retains same-job legacy config such as notify markers and excludes embedded runtime-authority fields from the config overlay. General cron normalization and unrelated repairs retain their own admission.

The census also follows the link writer in `src/gateway/model-account-connect.ts` through `src/agents/auth-profiles/session-override.ts` and `src/gateway/session-create-service.ts`; provider lookup in `src/agents/model-auth-provider-config.ts`; media entry/image runtimes; `src/agents/mcp-auth-profile.ts`; the released LLM Task and Reef readers; the model-ref parser and `packages/model-catalog-core/src/configured-model-refs.ts`; and cron model selection, preparation, and dispatch.

`ui/src/pages/profile/model-accounts.ts` and `ui/src/pages/new-session/model-control.ts` consume Gateway IDs; generated native protocol models project those IDs. They add no independent durable auth-selection store. UI inventory retains its existing reopen/reconnect lifecycle. Config contracts remain in `src/config/types.auth.ts`, `src/config/types.tools.ts`, `src/config/zod-schema.core.ts`, `src/config/zod-schema.mcp-server.ts`, and `docs/plugins/manifest/surfaces.md`. Browser/tool profiles, human profile IDs, secret references, prompts, and historical replay/binding fingerprints remain distinct contracts.

Literal credentials, structured secret references, user text, explicit disconnects, link timestamps, ownership, and collision decisions remain intact. Historical CLI/plugin credential fingerprints keep their existing mismatch and reuse rules.

## Contention

Auth aliases and personal links use the existing shared receipt transaction and auth-store locks. The link owner validates participating records before its first write and changes only their stored value. Separate agent databases retain their receipt-based recovery contract.

The automation writer rechecks changed-job definitions and existence inside its transaction. It preserves concurrent additions and merges current scheduler state and runtime authority. No prompt or import runs inside that write transaction.

The session correction adds no lock or queue. Both slots are changed inside the existing session replacement operation after its current-entry read. Its runtime proof uses an idle saved session and stopped Gateway during Doctor; it does not claim a Doctor-versus-in-flight-turn race.

The existing shared-store owner tests passed all three selected cases: scheduler state survives stale CRUD views, stale edits are rejected, and stale deletion is rejected. Three selected authority-preservation tests also passed, covering concurrent runtime/authority state, authority clearing, and older embedded authority. Signed Doctor controls preserved the unrelated dreaming row and all non-reference fields on the affected job. These owner tests and Doctor controls are separate observations, not a claim that the Doctor fixture injected a concurrent authority change.

## Invalidation

Credential owners continue to clear auth snapshots after mutation. Personal links read current SQLite state. Config publication stays with the existing config writer, before downstream automation repair. Session updates use the existing session writer and recency rules; the cron writer advances its existing committed revision. The saved rollback pin is derived state that can become authoritative on a later failed turn. Verified credential rename invalidates it together with the active pin. The correction preserves the existing success/rollback marker lifecycle, preview admission, and restart/reload owners.

Prior CI merge `532b42292bcf23fbb1b0f01978649df8e92b062b` passed all six flows on copied v2026.9.4 state without reseeding credentials. The actual registry-installed v2026.9.4 updater then installed the validated candidate package; rehearsal, Doctor lint, config validation, plugin resolution, continuation, Gateway canary, and installed Doctor all succeeded. The resulting installed Gateway used the selected credential for a new conversation and the released LLM Task plugin. The update used `--no-restart`, so this proves subsequent installed-Gateway startup, not automatic service restart. Historical Telegram restart/reload evidence retains its original head and covers the unchanged runtime publication owners.

## Tests

All three packaged Doctor cases are new relative to the PR base: free destination with padded references, occupied destination, and ordinary interactive Doctor through a real terminal. The suite retains the older installed-plugin fixture and all prior assertions, adds saved automation primary/fallback checks to every case, and checks the second run. The interactive case stores the legacy credential only in JSON, accepts the actual config/import prompts, and does not use `--fix` or `--yes`. Each case now also checks active and rollback pins, rollback-only repair, unmapped padded values, protected harness state, marker preservation, and unchanged second-run snapshots. These assertions pass on the corrected source and combined CI tree.

The automation proof invokes registered `cron.run` and correlates `cron.runs` by run ID after the real Doctor command. New-conversation proof uses `sessions.create` and `chat.send`; the registered LLM Task proof uses `/tools/invoke`; utility proof uses `sessions.title.prepare`.

Historical proof retains its original source identities:

- Main baseline `c06f21dd` reproduced failures through `sessions.create` / `chat.send`, provider-key lookup, explicit/preferred media, registered LLM Task, and `sessions.title.prepare`. Candidate `95b0009c` repaired those six flows, including copies of state created by `v2026.9.4` (`3a9d69db`).
- The older installed LLM Task gap was reproduced on `06c9ae94`; the plugin-boundary correction and preservation runs belong to `e52f2f5b`.
- Signed `83130fa5` passed the padded provider and registered LLM Task flows. Ordinary interactive Doctor on that same revision reproduced the missing account/automation completion addressed here.
- These earlier captures are historical evidence. Final correction results and any reuse decision are recorded separately below.

| Prior completed validation (before the session correction) | Result |
| --- | --- |
| Source | Signed PR head `e8a3653f0f394f946ca2f28b40a16ea9f815ee2c`; hosted and native builds identify CI merge `532b42292bcf23fbb1b0f01978649df8e92b062b`. |
| Focused owners | Command owners: 163 tests; auth recovery/preservation: 28; final sequencing: 27; final health/session runners: 152. All pass. Counts overlap and are not a distinct-test total. |
| Registered Doctor CLI | All three packaged cases pass on merge532, including ordinary interactive confirmation and second-run assertions. |
| Registered automation triggers | Prior-merge primary and fallback runs finish successfully with the selected credential observed at the fake provider. The new conversation also uses that account. Intentional unavailable-primary attempts remain separate from successful fallback selection. |
| Padding | Prior-merge padded provider-key and released LLM Task requests use the selected credential; LLM Task returns HTTP 200 with the expected JSON. |
| Admission and recovery | Signed `216855f4` controls show that declined import, recorded-before-import plan plus decline, and noninteractive-without-fix preserve state. Completed archive recovery passes. Constructed post-archive/pre-receipt-completion state recovers from an empty initial map through both plain Doctor and `--fix`. These are two entry paths through one crash window, not observed crashes. The later brace-only change preserves these conditions and effects; the original capture identity is retained. |
| Raw preservation and repeat | Signed `216855f4` controls preserve same-job legacy notify/delivery fields and an unrelated dreaming row after declined cron repair. Ownerless jobs retain no owner; their second-run snapshots are identical. |
| Previous release | All six flows pass on copied v2026.9.4 state, including the released installed plugin. |
| Published updater | Registry release commit `3a9d69db30` upgrades to the merge532 package; installed new-session and LLM Task requests use the selected credential. |
| Packaging | Runtime-only packaging first failed validation because SDK declarations were absent. The canonical SDK declaration build and repeated full package integrity check passed; only the validated package was installed. |

Full CI, the merge-tree census and structural checks, and the exact-head landing review remain required before merge.

## Session rollback and padded-pin validation

The two session failures are red on signed source/build `e8a3653f0f394f946ca2f28b40a16ea9f815ee2c`. A real agent turn invoked the registered sessions tool to change its model, producing the saved rollback marker. With the session idle, registered Doctor completed successfully but left the rollback ID and a separate padded active pin unchanged. A definitive failed turn restored the deleted alias. The following restored-model turn and padded-session turn both reached the recording provider with the control credential instead of the selected work credential. Failed-model attempts are recorded separately and include multiple credentials; the final restored-model request is the account-selection signal.

The first packaged regression run exposed a fixture lifecycle error: the parent test kept an agent database open while explicit Doctor attempted offline maintenance. The interactive case reached the intended stale rollback assertion. The fixture now closes its own database handles before each CLI launch. This changes test setup only and does not bypass lease checks or suppress command failure. The corrected baseline rerun fails all three cases on stale references. All three pass with the owner correction, including on the new CI merge tree.

The corrected rollback and padded-session flows use the selected work credential. Focused session owners pass 131 tests, the preview owner passes 2, and the legacy-runtime sibling passes 13. All three packaged CLI cases pass. Signed-head previous-release proof uses the actual v2026.9.4 Gateway and registered sessions tool to create the rollback marker, preserves complete saved sessions during noninteractive preview, then observes the selected credential after repair and rollback. No credentials are reseeded. The same flow also passes on CI merge `1cf05930a67df9fd44c9c204cdf4fd9ee071ca80`. Its three packaged Doctor cases, suppression and assertion ratchets, unused-export scan, formatting, SDK declarations, and full package validation pass. Hosted lint later found the line-count overrun addressed below. The actual registry-installed v2026.9.4 updater installs this merge package with all nine update steps successful; subsequent installed-Gateway and released LLM Task requests use the work credential. The update uses `--no-restart`, so automatic service restart is not claimed. Those executions retain their merge1cf identity. The later cleanup changes only the zero-target summary path; populated session processing is unchanged.

## Empty-session summary cleanup

CI run `34653949895`, attempt `1`, found that the session repair owner exceeded the existing line limit. The follow-up removes the duplicate zero-target return and its single-use helper. Existing preview and repair returns produce the same values without extra I/O.

Focused lint, including the actual max-lines rule, passes with zero errors. The 131 owner tests, all three current packaged CLI cases, and all three original empty-session CLI cases pass on the cleanup. The empty-session fixture is an unchanged copy of the earlier CLI fixture used for private validation; current tracked tests and assertions remain intact. On merge `0fc267c94c12f7138e674762c0e19f51a0dd963b`, both CLI suites pass, the real session-tool/Doctor/failed-turn flow restores the work account, and both final restored-model and padded-session requests use its credential. Focused lint, the file-size and assertion ratchets, unused exports, formatting, and removed-symbol scans pass. The earlier published-updater proof retains merge1cf identity; the empty-target equivalence and new CLI/runtime evidence support that bounded reuse. CI run `34656199411`, attempt `1`, and a fresh exact-head review remain required before merge.

## Test changes and deletion ledger

- Added all three registered Doctor CLI cases relative to the PR base; the session correction extends each with active/rollback reference preservation. No existing assertion is removed or weakened. The fixture closes its own agent database handles before both child CLI paths so offline Doctor can acquire its maintenance lease; the repeated invocation uses the same lifecycle.
- Removed `retains the exact auth profile map after import for later session-owner repair` from `doctor/repair-sequencing.test.ts`. It asserted Map object identity and the old config-before-import call order. The real CLI cases protect persisted account/reference behavior; existing ordering tests retain sidecar/import/model-validation sequencing.
- `doctor-auth-flat-profiles.test.ts` now asserts blocked and completed profile IDs in the partial-import case; source, warning, and later-owner assertions remain.
- `doctor-auth-canonical-api-key-alias.test.ts` and `doctor-auth-source-owner.test.ts` update repeat/decline result shapes to the completed/blocked-ID contract.
- `doctor-retired-models.test.ts`, `doctor-retired-models.ordering.test.ts`, and `doctor/cron/schema-safety.test.ts` explicitly request broad model migration where their existing scenarios require it.
- `doctor/repair-sequencing.test.ts`, `doctor-health-contributions.test.ts`, and `doctor.fast-path-mocks.ts` update importer fixtures and exact adapter arguments. No remaining behavior assertion was removed to make the correction pass.

## Limits

Independent agent databases do not become one atomic store. If one owner fails, its IDs cannot borrow another owner's success. Existing partial-import recovery can still require operator repair. Broader provider retirement and historical runtime-session reuse keep their prior policies.

The session proof covers idle saved sessions across offline maintenance. It does not claim an in-flight turn spanning maintenance, automatic managed-service restart, reload-mode-off recovery, immediate live UI inventory propagation, or unrelated xAI/warm-picker behavior. The first introducing commits for the two session omissions remain unknown; the retained red flow establishes their behavior at the stated head.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-12 05:03:07 +05:30
Ayaan Zaidi
a48c47a09c
docs: align model catalog and provider login guides (#144591)
Related: #136257

## What Problem This Solves

The setup guide still says credentials are saved only after a successful live test, and the Models guide describes API keys as inline configuration. These statements conflict with the landed credential and activation flow. Catalog guidance also mixes user tasks with internal loader details.

## Why This Change Was Made

Document the current shared Gateway catalog, explicit refresh, compatible last-good results, session model search, credential-only login, model-access consent, shared API-key management, and saved-credential recovery. Preserve the Models page's first-picker discovery request and distinguish legacy auth refresh from model-list fallback.

## User Impact

Users can choose between connecting and activating a model, retry a saved sign-in after failed setup, and understand when credentials were saved but the Gateway has not applied them. Plugin authors can declare the login choices that these surfaces actually support. Runtime behavior is unchanged.

## Evidence

Current-main source audit covers all eight changed pages. `pnpm docs:list`, `pnpm check:docs`, and `git diff --check` pass. The optional anchor check reports the same 53 pre-existing failures on the candidate and the untouched baseline; no new anchor failure is introduced.

Focused source-mode validation: 73 tests pass across credential editing/removal, refresh and older-Gateway recovery, manifest login choices, setup activation/retry, and session-selection scope. No tests were added or changed.

Live CLI checks cover model listing/refresh, provider filtering, API-key save/list/remove, and the hosted catalog's disabled result. Telegram Test Server proof captures the provider buttons for `/login`, the `/models` menu, and OpenRouter's missing-secure-address recovery message. The chat run made zero model requests and cleaned up successfully. Completed browser authorization is source-verified, not claimed as live proof.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-11 08:08:13 +05:30
Ayaan Zaidi
e52d47f89a
feat(auth): sign in to OpenRouter from private chat (#144491)
Related: #136257

## What Problem This Solves

Private-chat `/login openrouter` required a Control UI handoff instead of completing browser sign-in from chat. Browser callback waits could also hide the sign-in URL or keep cancellation behind the pending login.

## Why This Change Was Made

OpenRouter now supplies its PKCE authorization URL and key exchange to a core-owned HTTPS callback. Core binds the callback to the managed Tailscale origin, a deadline, cancellation and Gateway restart; it consumes each response once. The callback acknowledges receipt without granting Control UI access or claiming credentials were saved.

Setup and login share one auth-context owner, including current caller authority. The wizard chooses hosted completion only when the connected browser origin matches the managed HTTPS origin; localhost and other remote connections retain manual redirect completion. Existing local and remote CLI completion remains available. No new operator setting, database schema or protocol version is introduced.

## User Impact

- `/login openrouter`, or OpenRouter in the bare `/login` menu, sends a **Sign in with OpenRouter** URL action before completion.
- `/login cancel` cancels that chat's pending login. Other chat sessions retain their own login.
- Missing managed HTTPS publication gives Tailscale Serve and CLI guidance. Serve requires the browser to have tailnet access.
- Wizard browser waits show the existing sign-in link without an extra Continue action. Credential success follows persistence.

## Evidence

- Baseline regressions reproduce the missing direct chat link and missing wizard browser-wait delivery.
- Targeted Vitest covers callback state, deadlines, denial, replay, restart, cancellation, owner isolation, persistence ordering, CLI siblings, native Telegram and UI presentation. The command-ticket regression also preserves executable-command ordering.
- Real HTTP requests through an isolated Gateway prove callback receipt, actual credential persistence, cancellation and replay rejection.
- Live Telegram Test Server proof covers the direct command and an actual OpenRouter menu-button click, URL delivery before completion, chat cancellation and subsequent HTTP 410. No model request was needed.
- Actual Models-screen proof uses the running Gateway and a temporary HTTPS proxy: select a provider, observe the waiting link without Continue, accept the callback, observe saved credentials, and reject replay. A localhost session retains the OpenRouter redirect input.
- Formatting, syntax lint and runtime build passed. Independent public Gateway acceptance passed, including two-session cancellation, saved credentials after completion, replay rejection, timeout, restart and fresh-client authentication rejection.

The approved extraction scope from #136257 leaves real OpenRouter account approval as a manual check. Actual Tailscale route provisioning is also unexercised. Automated evidence uses the existing managed-origin contract, a temporary HTTPS proxy for full UI completion, a synthetic credential provider, and the real OpenRouter plugin for live Telegram delivery. Live owner revocation hot-reloads the channel, invalidates its pending callback before any key exchange, leaves no OpenRouter profile, and rejects a subsequent non-owner login.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-11 05:54:30 +05:30
Ayaan Zaidi
b34254154b
feat(models): separate provider login from model activation (#144329)
## Problem

Models sign-in also runs model setup, which can activate a provider's starter model. Bare `/login` starts OpenAI device authentication before the user chooses a provider.

## Solution

Add credential-only provider login in Models and one shared provider menu for chat. Plugins declare optional login choices in their manifests. Core resolves provider and method choices and produces existing command buttons or copyable commands. Bare `/login` always asks for a provider, even when only one is available. API-key and local setup methods remain available through explicit provider commands. `/login codex` retains its device-code flow.

The credential-only flow uses the shared persistence and auth-refresh owner from merged #144181. It preserves the selected model, restrictions, concurrent settings and existing account pins. Models publishes the saved credential through the current Gateway context. Cancellation locks at the real persistent-effect boundary; confirmed saves remain visible through later errors. Caller authority is checked at credential and session commits, and disconnected clients cannot finish pending login. Qualified choices reject stale or ambiguous plugin owners. Leaving Models closes its wizard input through the existing close operation and waits for admission to settle. This uses optional `closeInput` on `wizard.cancel`; ordinary cancellation still refuses to interrupt a protected save. Separate pending requests keep page disposal independent of an outstanding Cancel response.

The final conflict resolution also preserves the shared refresh outcomes from merged #144436: rejected or unreachable refresh reports saved credentials with recovery guidance. One shared error module carries saved-credential and settings-write failure facts, and the provider-neutral runtime owns completion/failure wording for both chat paths.

Channels render core-owned command actions. Provider-neutral recovery replaces the old OpenAI-specific recovery path. OpenAI, MiniMax and xAI declare their supported credential-only choices; methods requiring starter discovery retain setup.

## Impact

- No new configuration keys or database schema.
- Optional metadata preserves setup for plugins that omit it.
- Connecting a provider does not activate its default model.
- Chat selection rechecks current owner permission and availability.

## Evidence

Original baseline: `d35cefd9b7`. Integrated base after #144181: `7d2296e333`. The later required conflict resolution integrates #144436 at `39b671dc74`.

- Real Telegram Test Server baseline: bare `/login` issued a device code before selection. Candidate: provider buttons, second method menu, synthetic device code, saved result and rejection of a stale qualified selection. Telegram transport and user actions are real; provider credentials are synthetic.
- Real Gateway baseline: `models.authLogin` was unknown. Candidate: saved fixture credential with the same default model and restrictions.
- Independent public-client checks cover singleton webchat selection, wrong-connection denial, unavailable choices, disconnect rejection and positive connected-owner completion. An existing CLI setup flow with omitted metadata still saves credentials and honors explicit `--set-default`.
- Integrated public-client cancellation proof: cancelling before save leaves no credential; cancelling after profile visibility while a provider note is pending returns running, and acknowledging the note produces the saved terminal result. A public settings edit during login survives; the saved profile appears without restarting the Gateway.
- Browser captures show Connect, explicit provider selection, the sign-in URL/code and saved result with the selected model unchanged. Captures were inspected.
- Current integrated focused Vitest: 243 passing tests across the auth producer and choice resolution, shared login runtime, core chat, native Telegram, Gateway wizard ownership, Models UI and the CLI sibling. Earlier retained checks cover method ordering, setup, MiniMax/xAI and provider recovery. The Telegram fixture cleanup preserves all test names, parameter rows and assertions; its 25 cases pass after cleanup.
- Page-disposal regression: the original Gateway kept waiting after a saved note, and the original Models page left the wizard unsettled on removal. The correction passes 33 Gateway and 49 UI tests, including disposal during a pending Cancel and a second login on the same connection. Fresh real-Gateway proof rejects a peer disposal, preserves the saved credential, returns truthful closure status, and immediately admits the same owner’s next login.
- Focused lint, assertion safety, runtime build and whitespace checks pass. Full typechecking and full CI run on GitHub.

This extracts Auth C+E from #136257 and replaces closed #142943. Auth A was merged before integration; its implementation was not copied into this branch.

## Deletion and rewrite ledger

`src/auto-reply/codex-login-recovery.ts` moved to `src/auto-reply/provider-login-recovery.ts`. Its `buildCodexLoginRecovery`, `CodexLoginRecoveryEvidence`, and `CodexLoginRecovery` symbols moved to `buildProviderLoginRecovery` (`:23`), `ProviderLoginRecoveryEvidence` (`:5`), and `ProviderLoginRecovery` (`:11`). The fixed OpenAI/Codex recovery action is replaced by the shared provider menu. No recovery test definition was deleted; the nine renamed or parameterized definitions are mapped below against head `73cfa4b319b9640a6711b2dc8e871d9ff4aea035`.

| Removed test name | Disposition | New test name and location |
| --- | --- | --- |
| `adds Codex login recovery to raw forwarded refresh failures` | **behavior moved** | `adds provider login recovery to raw forwarded refresh failures` — `src/auto-reply/reply/agent-runner-execution-auth-failures.test.ts:51` |
| `keeps Codex login recovery actionable on Control UI turns` | **behavior moved** | `keeps provider login recovery actionable on Control UI turns` — `src/auto-reply/reply/agent-runner-execution-auth-failures.test.ts:72` |
| `preserves Codex login recovery in known failure payloads` | **behavior moved** | `preserves provider login recovery in known failure payloads` — `src/auto-reply/reply/agent-runner-execution-auth-failures.test.ts:118` |
| `keeps disabled OpenAI OAuth profiles actionable on later turns` | **behavior moved**; original OpenAI row retained, xAI and MiniMax rows added | `keeps disabled %s OAuth profiles actionable on later turns` — `src/auto-reply/reply/agent-runner-execution-auth-failures.test.ts:228` |
| `does not offer Codex login for $label` | **behavior moved**; API-key and transient OAuth negative rows retained, provider-name-only negative row added | `does not offer provider login for $label` — `src/auto-reply/reply/agent-runner-execution-auth-failures.test.ts:277` |
| `keeps non-OpenAI OAuth refresh failures on provider-specific terminal guidance` | **behavior moved and expanded**; terminal guidance retained, shared login action added | `adds provider login while retaining non-OpenAI terminal guidance` — `src/auto-reply/reply/agent-runner-execution-auth-failures.test.ts:289` |
| `turns returned OpenAI refresh failures into Codex login recovery` | **behavior moved**; original OpenAI row retained, xAI and MiniMax rows added | `turns returned %s refresh failures into provider login recovery` — `src/agents/embedded-agent-runner/run/payloads.errors.test.ts:121` |
| `adds Codex login recovery to OpenAI OAuth refresh failures` | **behavior moved** | `adds provider login recovery to OpenAI OAuth refresh failures` — `src/cron/service.failure-alert.test.ts:845` |
| `does not offer Codex login for non-OAuth authentication failures` | **behavior moved** | `does not offer provider login for non-OAuth authentication failures` — `src/cron/service.failure-alert.test.ts:884` |

The obsolete provider restriction in `buildCodexLoginRecovery` is intentionally removed: typed non-OpenAI OAuth failures now receive the shared action while keeping their provider-specific terminal repair command. The old `/login codex` recovery presentation becomes `/login`; explicit `/login codex` remains supported. Typed OAuth evidence, negative authentication cases, private profile handling, and complete recovery-presentation assertions remain covered.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-11 04:18:48 +05:30
Vincent Koc
efefa622e2
docs: fix 20 link defects confirmed live in the verified backlog (#144133)
Mis-pointed targets where a correct target exists, first mentions of
documented surfaces that were never linked, and pages with no Related
section at all. Adds 57 internal links, none broken.

These sat in the ledger's verified bucket, which an earlier round did not
have in scope when it reported this category closed.

Co-authored-by: Vincent Koc <vincent@openclaw.org>
2026-09-11 03:48:10 +08:00
Vincent Koc
e98a5c15f3
docs: scope version-locked claims across plugins, platforms, providers, and gateway (#144032)
* docs: scope version-locked claims across plugins, platforms, providers, and gateway

Resolves 78 accuracy-audit rows across five directory batches. Most changes
replace time-relative wording ("currently", "not yet", "previously") with
either a release number established by tag containment or a restatement as a
present-tense limit where no release record exists.

Also marks placeholder identifiers in SDK samples that read as exported APIs,
completes one command sample that was a bare object fragment, and dates two
deprecation notes from the compatibility registry.

* docs(automation): cite the beta tag and its stable release for the SQLite cutover

v2026.5.30 was never released as a stable tag; only v2026.5.30-beta.1 and
-beta.2 exist, and the first stable release containing the migration commit
d115fb4cf9 is v2026.6.1.

Uses the form already established at
docs/reference/database-schemas/agent-schema-history.md:13, which names the
beta tag and the stable release separately.

* docs(automation): the SQLite cutover shipped in beta.1, not beta.2

The earlier commit named beta.2 because the local tag set was incomplete
when the containment query ran. With tags fetched, v2026.5.30-beta.1 also
contains d115fb4cf9.

---------

Co-authored-by: Vincent Koc <vincent@openclaw.org>
2026-09-10 21:33:28 +08:00
Vincent Koc
c41730baf0
docs: scope version-locked claims in tools and plugins pages (#143982)
Close the open accuracy findings for docs/tools/ and docs/plugins/ that
were still valid after the recent page splits.

- exec-approvals: date the non-directory-bound generated-entry migration
  to 2026.8.1 (#129636)
- exec: date the sessions.patch execSecurity/execAsk retirement to
  2026.8.1 (#132740)
- sdk-channel-inbound: date the runtime.channel.turn.* alias removal to
  2026.5.27, and state the runPreparedReply compatibility record
- sdk-channel-plugins: scope the retainNativeCatalog deprecation to
  2026.9.2 from its own @deprecated annotation
- sdk-runtime/state-and-system: date the plugin-state lease removal to
  2026.8.1 (#121140)
- sdk-runtime, sdk-runtime/gateway-and-nodes, sdk-runtime/media,
  sdk-provider-plugins/{media-and-search,runtime-hooks}: mark
  caller-owned helpers in samples as placeholders, not SDK exports
- sdk-overview/tools-and-commands: make the agentPromptGuidance sample a
  complete registerCommand call
- sdk-provider-plugins/runtime-hooks: state that
  resolveWebSocketSessionPolicy carries no compatibility-registry record
- architecture-internals/provider-hooks: give augmentModelCatalog its
  2026-10-01 removal gate from src/plugins/compat
- architecture-internals/load-pipeline: complete the activation consumer
  list with the onAgentHarnesses and onConfigPaths consumers
- hooks: replace "before the next major release" with the per-surface
  compatibility-registry contract
- manifest/surfaces: drop the undated "open" proposal status
- beam: drop "Current" from the automatic-mirror sentence
2026-09-10 19:27:32 +08:00
Vincent Koc
3b61516187
docs: fix information-architecture findings in plugins and channels docs (#143926)
Some checks are pending
Native App Locale Refresh / Refresh native ja-JP (push) Blocked by required conditions
Native App Locale Refresh / Refresh native ko (push) Blocked by required conditions
Native App Locale Refresh / Refresh native nl (push) Blocked by required conditions
Native App Locale Refresh / Refresh native pl (push) Blocked by required conditions
Native App Locale Refresh / Refresh native pt-BR (push) Blocked by required conditions
Native App Locale Refresh / Refresh native ru (push) Blocked by required conditions
Native App Locale Refresh / Refresh native sv (push) Blocked by required conditions
Native App Locale Refresh / Refresh native th (push) Blocked by required conditions
Native App Locale Refresh / Refresh native tr (push) Blocked by required conditions
Native App Locale Refresh / Refresh native uk (push) Blocked by required conditions
Native App Locale Refresh / Refresh native vi (push) Blocked by required conditions
Native App Locale Refresh / Refresh native zh-CN (push) Blocked by required conditions
Native App Locale Refresh / Refresh native zh-TW (push) Blocked by required conditions
Native App Locale Refresh / Commit native locale refresh (push) Blocked by required conditions
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Waiting to run
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Blocked by required conditions
Plugin Init Scaffold Validation / Validate provider scaffold (push) Waiting to run
Plugin NPM Release / preview_plugins_npm (push) Waiting to run
Plugin NPM Release / Validate release publish approval (push) Blocked by required conditions
Plugin NPM Release / preview_plugin_pack (push) Blocked by required conditions
Plugin NPM Release / Preflight plugin npm package () (push) Blocked by required conditions
Plugin NPM Release / Seal prepared plugin npm release (push) Blocked by required conditions
Plugin NPM Release / Trusted publisher OIDC exchange (push) Blocked by required conditions
Plugin NPM Release / publish_plugins_npm (push) Blocked by required conditions
Plugin NPM Release / verify_plugins_npm (push) Blocked by required conditions
Vitest Cache Warm / warm (linux) (push) Waiting to run
Vitest Cache Warm / warm (macos) (push) Waiting to run
Workflow Sanity / no-tabs (push) Waiting to run
Workflow Sanity / actionlint (push) Waiting to run
Workflow Sanity / generated-doc-baselines (push) Waiting to run
Structural-only pass over the open `ia` audit rows for docs/plugins/ and
docs/channels/. No prose was rewritten; the only content additions are
headings, one Related section, and one section index.

- channels/whatsapp: group 24 flat H2 sections under four parent H2s
  (Setup, Access control, Messaging and delivery, Reactions and typing)
  by demoting contiguous siblings to H3. No sections reordered.
- channels/clickclack: add a Configuration H2 so the JSON5/config-keys/
  hostname references stop nesting under Quick setup, give the stray
  plugin-allowlist paragraph its own H3, and add a Related section.
- channels/groups: drop the two redirect-only H2 stubs and carry their
  links into Related; both old ids kept as authored anchor stubs.
- plugins/bundles: promote "MCP for embedded OpenClaw" to H2 and its
  children to H3, removing the H5 depth under "Supported now".
- plugins/dependency-resolution: add eight H3s inside "Install roots".
- plugins/manifest/setup-and-auth: put the `setup` object table before
  its child `setup.providers` table.
- plugins/google-meet: rename the "Notes" H2 to "Audio bridge
  architecture" (old `#notes` id kept as an anchor stub) and move
  "Realtime session health" under it, out of Quick start.
- plugins/sdk-overview: move the session-discussion paragraph below the
  registration table it was interrupting.
- plugins/sdk-setup: fold "ClawHub publishing" into "Publishing and
  installing" as an H3 and add a section index after the intro.
- plugins/codex-harness-reference: link the five unlinked config-surface
  table rows to the child pages that document them.
- plugins/architecture: sidebar title "Internals" -> "Architecture".
- docs.json: order channels/groups before channels/group-messages, and
  move plugins/install-overrides into the maintainer reference group.

Anchor proof: parseDocsDocument id sets before/after over all 11 changed
pages -- 0 ids lost, 0 collisions, 16 ids added.
2026-09-10 18:22:51 +08:00
Peter Steinberger
fec7692fbe
fix(update): finish the update with recorded warnings on recoverable hiccups (#143767)
* fix(update): retain recoverable maintenance warnings

Let disposable validation cleanup, retired derived-cache cleanup, and Git
tracking setup finish with actionable warnings. Preserve migration ownership,
canonical update timing, failed imports, cancellation, and Gateway boot gates.
Carry approved Doctor warnings through IPC, progress, and update history.

Release-note context: updates finish with recorded warnings for recoverable
maintenance hiccups; unsafe or unverified updates still fail.

* fix(update): preserve Doctor advisory shape and warning records

Keep the existing package Doctor advisory kind/message contract. Carry
complete bounded warning records on the update step and forward them through
progress into the ledger, preserving each reason and repair command without
misusing Doctor IPC diagnostic details.

Prove the existing package post-core path and multi-warning history/reporting,
including normal exit 0 and explicit advisory exit 86. Hard failures retain
their original classification.
2026-09-10 02:25:04 -07:00
Vincent Koc
d84f5e8cbd
docs(plugins): fix accuracy findings across the plugin docs (#143857)
Closes the open `accuracy` audit findings scoped to `docs/plugins/`, each
verified against source before editing.

Corrections where the docs understated or misstated the code:
- Telephony support listed 2 of 9 bundled providers that implement
  `synthesizeTelephony`.
- `allowInvalidConfigRecovery` named 2 of the 5 recovery cases in
  `isAllowedPluginRecoveryIssue`.
- The `activation-command-hint` row omitted the live
  `manifest-cli-command-owner` reason.
- A `js`/`export default` config example was neither a real config surface nor
  checked by `check-docs-config-examples` (that fence language is skipped);
  converted to `json5`, now validated.
- `npm:@openclaw/google-meet` skips the official-catalog install plan; the
  package is in the catalog, so the bare spec is correct.

Version scope added only where a release or dated compat record exists:
`2026.4.22` (guardian env removal), `2026.5.2` (Meet access-type control),
`2026.8.1` (Teams/Zoom live validation), `2026.8.2` (Beam named links),
`2026.9.2` (legacy doctor selector), and the dated `removeAfter` records in
`src/plugins/compat/`. Elsewhere the time-relative wording is dropped and
present behaviour stated, rather than a version guessed.

`docs/plugins/plugin-inventory.md` is generated: the fix is in
`scripts/generate-plugin-inventory-doc.mts`, regenerated, `plugins:inventory:check` rc=0.
2026-09-10 16:54:53 +08:00
Vincent Koc
cf41c606df
docs: close remaining one-way link findings in cli, plugins, tools, providers (#143855)
* docs: close remaining one-way link findings in cli, plugins, tools, providers

Adds the back-links and anchors that PR #143157 did not cover, and gives two
"see below" tables real headings to link to.

- Related back-links: cli/tui -> resume, cli/doctor -> status,
  configuration-reference -> configure, voice-call -> voicecall CLI,
  onepassword -> secrets CLI, acp-agents-setup -> acpx reference,
  llama-cpp -> llama-cpp reference, cli/policy -> policy reference,
  ollama -> LM Studio and Memory LanceDB, image/video generation -> OpenRouter,
  google-meet -> ElevenLabs, media-understanding -> Mistral,
  tts service links -> Fish Audio, tools/secrets -> ask_user.
- manifest/config-and-secrets: H3 headings for the dangerousFlags and
  secretInputs detail tables; the two "See below" cells now link to them.
- sdk-overview/capabilities: new "Worker providers" heading; the manifest
  worker-provider contract link now lands on it instead of 36 lines above.
- providers/openrouter: the model-list Note pointed at /concepts/model-providers,
  which carries no OpenRouter catalog; it now points at OpenRouter's own catalog
  and keeps a separate pointer to OpenClaw model selection.
- glossary.zh-CN: 7 sources for the new list-item link labels.

* docs(google): link the Gemini CLI runtime tab to the CLI backends page

r3-2107. `google-gemini-cli` is the CLI backend id the bundled Google plugin
registers, so the tab that configures it should point at the page documenting
its argv, JSONL dialect, and session behavior. The Related card alone left the
tab itself unlinked.

---------

Co-authored-by: Vincent Koc <vincent@openclaw.org>
2026-09-10 16:15:08 +08:00
Peter Steinberger
b98b1589d3
fix(configure): stop Moonshot China auth reinstall loops (#143742) 2026-09-09 23:24:16 -07:00
Peter Steinberger
a4440c941d
fix(update): avoid false Memory Core migration refusals (#143138) 2026-09-09 07:17:32 -07:00
Vincent Koc
58394c97dc
docs(plugins,providers): close open link-audit findings for plugins and providers (#143021)
Fixes the open `link`-kind audit findings filed against `docs/plugins/` and
`docs/providers/`: missing reciprocal "Related" links, one link to a page that
moved in an earlier split, one card pointing at the wrong route, and one
duplicate card title.

Link-only: no prose was rewritten. The one prose-adjacent change wraps existing
words in a link (`senseaudio.md` "Voice Call", `nodes/computer-use.md`
"Codex Computer Use").

`docs/docs.json` and `docs/.i18n/glossary.zh-CN.json` are untouched.

Co-authored-by: Vincent Koc <vincent@openclaw.org>
2026-09-09 19:49:52 +09:00
Vincent Koc
c61d773282
docs(tools): split the code mode page by reader job (#142425)
The page was 79,412 characters with 43 headings in a flat run of 32 H2
sections, mixing a how-to quickstart, explanation, reference for exec,
wait, the guest API, error codes, and telemetry, plus a contributor test
plan. It is now an index with eight children, one per reader job.

Children:

- /tools/code-mode/quickstart - enable it, override one model, recover
  from tool errors, verify the surface, Swarm fan-out
- /tools/code-mode/configuration - fields, automatic per-model
  activation, the preferred-model list, activation precedence
- /tools/code-mode/tool-surface - model-visible tools, exec, wait, the
  hidden catalog, Tool Search interaction, name collisions
- /tools/code-mode/guest-api - guest globals, catalog handles, MCP
  namespaces, virtual API declarations, paginated file data
- /tools/code-mode/output - declared output contracts and the output API
- /tools/code-mode/internals - runtime status, scope, terms, nested tool
  execution, snapshot lifecycle, QuickJS-WASI, TypeScript, security
- /tools/code-mode/troubleshooting - error codes, telemetry, debugging
- /tools/code-mode/maintainers - implementation layout, validation
  checklist, E2E test plan

Anchor strategy: per-anchor routes are impossible because redirectSource()
rejects any source containing [?#]. All 44 pre-split IDs computed with
parseDocsDocument stay alive on the parent index instead: 4 remain
natively published (what-it-does, why-use-it, technical-tour, related)
and 40 became authored <a id="..." /> stubs in a "Where each section
moved" list, each linking to the page that now holds the content. The one
punctuated heading emits both an encoded and a cleaned ID; both are
stubbed. No ID the index still publishes itself is stubbed, so there is no
duplicate authored/canonical ID collision. A script enumerated the
pre-split IDs and asserted each resolves end to end: 44/44 pass, and the
9 pages report 0 collisions. External deep links such as the CHANGELOG
and appcast entries to #guest-runtime-api still land correctly.

Losslessness: 43 of 43 section bodies are byte-identical at their
destination, and all 23 code fences are character-identical on both info
string and body. Words 10,630 -> 11,081, links 18 -> 65, table rows
40 -> 52, fences 23 -> 23; every delta is accounted for by the index's two
child tables (12 rows, 8 links) and the 39-entry moved list. The empty
"## Quickstart" heading became the quickstart page title, and its five H3
children were promoted to H2 with identical heading text, so their IDs are
unchanged.

Prose was not rewritten. Five orphaned cross-references were repaired:
four same-page fragment links that now point at the child holding the
target, and the "Technical tour" signpost, which said "The rest of this
page covers" and now says "These pages cover". Six inbound deep links from
other docs pages were retargeted at the children.

Closes audit findings: r3-0783, r3-0784
2026-09-09 02:55:26 +08:00
Ayaan Zaidi
f7d2fa7402
fix(models): preserve authored rows during generated catalog refresh (#142302)
Preserve manual root models during generated catalog refresh and require current authentication for deferred generated inventory. Keep root authorship separate from disposable cache membership, reuse the shared owner/endpoint filter, and consolidate generated-marker detection.

Compiled CLI regressions and controls, focused source checks, and independent acceptance verify the behavior.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-08 22:25:52 +05:30
Ayaan Zaidi
f9ea6dbb69
feat(models): scope implicit catalogs to configured provider endpoints (#142158)
## What Problem This Solves

A provider configured with a custom endpoint still advertises native models that the endpoint might not support. A retained generated catalog can also restore native rows or the old native URL during catalog preparation.

This implements the approved endpoint inventory outcome from #136257. Explicit model declarations remain authoritative, including models that reuse native or previously generated IDs.

## Why This Change Was Made

One provider-owned eligibility rule now covers manifest planning, implicit discovery, static preparation and resolution, runtime augmentation, generated catalog reads, and recovery. It reuses the existing normalized URL, declared host/suffix, alias, and native model URL semantics.

Providers without native endpoint declarations keep their discovery behavior. Model-level URL overrides alone do not exclude provider inventory. Shared hooks retain eligible sibling identities. Explicit model definitions, credentials, defaults, aliases, headers, costs, and token settings retain their existing owners.

Recovery and registry reads share the generated-provider filter. Request classification and inventory share the endpoint matcher. Static and authored runtime preparation share the inline-model completion owner. No configuration switch, persistence schema, or request authorization policy is added.

## User Impact

For a proxy under an existing provider ID, declare its supported models in `models.providers.<id>.models`. A nonmatching provider-level `baseUrl` excludes implicit rows, including retained generated rows. Native endpoint configurations remain eligible.

Catalog membership does not prove that a credential or model request will succeed.

## Evidence

- Baseline: `openclaw models list --all --provider deepseek --json --refresh` returned the three published native DeepSeek models at both native and synthetic custom endpoints. The retained baseline build was `e3d987e97d1bb260f0e4937b3b75393df9d3fd99`, from raw CI revision `f12624aed3ee3bf2b67b63db0cc8769fc77d7e45`. Affected catalog owners matched pinned main `0ed187312e`; this was not a newly built main binary.
- Candidate: CI run34233266724 produced artifact10058822670 from merge build `b04489ff47cbf3cf9d3fb4823ac70f36dbc7f6b8`, containing head `76089cab111170c490b481d5ca71607e5d4f0f98`. The exercised endpoint/catalog owners match that head. Artifact digest, complete archive inventory, paths, links and build identity were verified.
- Compiled CLI: native inventory3; custom endpoint0; explicitly authored unique/native-named rows2 through refresh and replace; empty replace0; native restoration3. Authored names, context sizes, defaults and aliases remain correct, with unchanged config readback.
- Compiled Gateway: provider-config and refreshed views retain the two authored DeepSeek rows; the all-provider view also retains the unrelated authored model. Config readback is unchanged and the owned Gateway exited cleanly.
- Persisted upgrade: a synthetic historical generated catalog was written using the baseline's compiled storage owner. The real baseline CLI exposed its three native rows and an extra historical sentinel at the custom endpoint. The candidate read an identical copy of the per-agent database and excluded all four. After explicit declarations reused the sentinel and a native ID, refresh retained their authored names, 24576/49152 context sizes, alias and settings.
- Shared-provider control: the actual Google plugin retains ten Vertex models under its native regional endpoint in scoped and unscoped listings while excluding Google rows under a custom endpoint.

All public behavior checks used isolated state, fixed synthetic credentials and blocked external networking. No vendor request or inference was performed. The runtime used a verified read-only dependency namespace; these are not standalone installation results.

Independent source-blind acceptance passes all five behavior clauses, with a fresh supplemental judgment of both corrected persisted-cache cases. The original report and its withdrawn cache inference remain in the evidence history.

Focused manifest, generated registry, recovery, discovery, endpoint and static checks pass. Ten normalization checks and93 shared discovery/static/attribution checks pass. The existing PDF caller test reproduced an authored runtime omission and passes after the owner repair;17 startup/static siblings pass without weakened assertions. Format, lint and whitespace checks pass.

CI completed production checks, hosted lint/type checks, docs, security and the build. Nineteen child jobs reported lost self-hosted runner communication with no recorded steps; the aggregate failed those categories. Full annotations and the aggregate log are retained. These infrastructure failures require the existing guarded unrelated-failure disposition; CI is not claimed green.

Earlier failures remain recorded: a zero-test routing attempt was corrected and never counted as passing; two initial type errors and the shared-hook/endpoint-normalization review findings were repaired; the preceding run's PDF defect and22 lost-runner failures are retained separately. The initial CLI state copy contained no persisted generated catalog and did not prove upgrade safety; the explicit persisted-catalog control above replaces that claim.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-09-08 19:53:49 +05:30
Vincent Koc
9da1f244bf
docs(plugins): split the manifest reference by domain (#140504)
The plugin manifest reference had grown to 158,349 characters across 38
H2 sections, mixing model, provider, setup, auth, capability, host
surface, config, and packaging metadata on one page. Split it into seven
child pages under docs/plugins/manifest/, keeping plugins/manifest as a
short index that still carries the whole top-level field table.

Children:

- plugins/manifest/models - modelSupport, modelCatalog,
  modelIdNormalization, modelPricing, OpenClaw Provider Index
- plugins/manifest/providers - generation provider metadata,
  mediaUnderstandingProviderMetadata, providerEndpoints, providerRequest
- plugins/manifest/setup-and-auth - setup.nativeSessionCatalog,
  providerAuthChoices, setup, uiHints
- plugins/manifest/capabilities - contracts, toolMetadata, activation
- plugins/manifest/surfaces - plugin icon and doctorContract,
  transcriptSources, backupResources, mcpServers, controlUi, dashboard,
  catalog, cliCommands, commandAliases, qaRunners, channelConfigs
- plugins/manifest/config-and-secrets - configContracts,
  secretProviderIntegrations
- plugins/manifest/package-json - manifest versus package.json,
  discovery precedence

Anchor strategy: pointer sections plus authored <a id> stubs on the
parent, the pattern already used for gateway/configuration-reference,
rather than the per-anchor routes used for web/control-ui. docs.json
redirects match on pathname only - redirectSource() in
scripts/lib/docs-redirects.mjs throws on /[?#]/ - so no redirect can
carry a fragment, and a per-anchor route only helps a link written as a
route. Other pages deep-link into this reference with #fragments, so the
parent keeps every id alive instead: the new "Where each field is
documented" section carries one authored stub per moved id, sitting on
the list item that links to the child section that now owns it.

Anchor and inbound-link table, produced by script rather than counted by
hand:

- 49 heading ids exposed before the split; 49 still resolve on the
  parent after it, with 0 id collisions on the parent or any child
- 47 route references to /plugins/manifest across 26 files, 8 of them
  carrying a fragment; all 8 repointed at the owning child
- 6 same-page fragment links inside the top-level field table; the 5
  whose target moved were repointed at the child, and
  #capability-catalogs stayed on the parent

Losslessness:

- 38 H2 blocks in, 38 out, each assigned to exactly one page
- 15,481 words of H2 body before; 2,394 kept plus 13,087 moved after
- 33 code fences before; 3 kept plus 30 moved after
- 237 documented field rows before and after, 0 lost
- top-level field table: 51 rows before and after, in the same order
- every section body concatenates back to the original text unchanged

Nav places plugins/manifest and its child group directly after
plugins/sdk-overview instead of last in the group.

No generator emits links into this page. The 151 pages produced by
scripts/generate-plugin-inventory-doc.mts and
scripts/lib/plugin-inventory-doc.mts contain no /plugins/manifest
reference, and the six references in the generated
docs/maturity/taxonomy.md are all fragment-free.

Prose findings against this page are deliberately deferred so a
structural split stays reviewable: r3-0498 (526-word worker-provider
paragraph), r3-0499 (49-row prose table), r3-0500 (STE violation rate),
r3-0503, r3-0504, r3-0505, r3-1409, r3-1892, r3-1893, r3-1894, r3-2017,
r3-2022, r5-0109, r5-0110, r5-0111, r5-0113, r5-0114, r5-0115, r5-0116,
r5-0117, r5-0118, r5-0119, r5-0120, r5-0121. r3-0502 is only partly
addressed here: the explanation sections now have their own pages, but
the runtime hook contracts still need to move to the SDK pages.

The zh-CN glossary gains seven entries for the new page titles. Those
translations are unreviewed.

Closes audit findings: r3-0501, r3-1895
2026-09-07 10:13:42 +08:00