Compare commits

...

350 Commits

Author SHA1 Message Date
Josh Lehman
78a7ac9826 fix: make sqlite session entry writes atomic 2026-06-08 12:09:37 -07:00
Vincent Koc
f4e746bdfc fix(memory-wiki): render native links relative to generated pages 2026-06-09 02:04:26 +09:00
Shakker
4094ef4dcb test: isolate ACP Matrix plugin routing 2026-06-08 17:53:18 +01:00
Shakker
009ae442a4 test: refresh cron prompt snapshots 2026-06-08 17:53:18 +01:00
Ayaan Zaidi
e7f1b24d9d fix(delivery): treat internal artifacts as silent skips 2026-06-08 22:16:33 +05:30
joelnishanth
f658abae50 fix(delivery): suppress Codex/Harmony internal protocol artifacts from user-facing channels (#88128) 2026-06-08 22:16:33 +05:30
Vincent Koc
81234fbf12 feat(skills): expose content versions in skill prompts 2026-06-09 01:45:42 +09:00
Ayaan Zaidi
47fc1c288b test(reply-queue): cover overflow mutation during drain 2026-06-08 22:03:11 +05:30
yetval
51dbc2c60f fix(reply-queue): remove the drained item by reference instead of front index
drainNextQueueItem captured items[0], awaited the run, then shift()-ed
index 0 assuming it still held the item it ran. Concurrent inbound
messages mutate the same shared items array, and at or over cap
applyQueueDropPolicy splices items off the front, so a burst arriving
while item[0] is in flight can shift a different, still-undelivered
survivor into index 0. shift() then deletes that survivor: it is never
run and is not counted in the overflow summary, so the agent silently
ignores a message it should have answered.

Remove the item that actually ran by identity via a new
removeQueuedItemsByRef helper, and apply the same reference-based
removal to the collect path in drain.ts, which had the same positional
splice(0, groupItems.length) assumption after an awaited group run.
2026-06-08 22:03:11 +05:30
openperf
2ffbea20d2 fix(agents): drop stale exec approval followups after session rebind
Exec approval followups were dispatched by sessionKey only. When /new or
/reset rotates the sessionId under that key while an approval is pending,
the resolved followup landed in the new session, surfacing stale approval
output (or 'Exec denied' / continuation text) in a fresh conversation.

Capture the session UUID active when the approval is requested and drop the
followup once the key has been rebound to a different sessionId:
- agent-run followups: carry the expected id on the agent request and drop it
  at the gateway as an early preflight, before the handler touches the rebound
  session (session-store write, chat/agent run + active-run registration,
  dedupe, accepted ack) — not just before model dispatch. Covers elevated and
  non-elevated.
- denied / direct fallback followups: resolve the key's current sessionId from
  the session store and drop before the channel send.

Fixes #59349.
2026-06-08 17:29:15 +01:00
ly-wang19
303873e835 refactor(cron): replace store-load double casts with raw-boundary record types
ensureLoaded cast persisted rows through `as unknown as CronJob[]` and then
back to `Record<string, unknown>` per item, which mislabeled unvalidated data
as CronJob. Treat the rows as raw records at the store boundary and apply a
single trusted CronJob cast only after getInvalidPersistedCronJobReason passes,
preserving the normalize/validate/quarantine flow. Drops two redundant casts
and two lines with no behavior change.

Fixes #91314

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(cherry picked from commit e9e494c7fe)
2026-06-09 01:15:18 +09:00
Sasan Sotoodehfar
22bda60cbe fix(memory): rebind qmd collections when a collection root changes
ensureCollections() never rebound a managed collection after its root
path changed (e.g. an agent workspace repoint): listCollectionsBestEffort()
read `qmd collection list`, whose output carries no filesystem path, so
listed.path was always undefined and shouldRebindCollection took its
defensive branch and skipped the rebind. The collection stayed pinned to
the stale root and recall kept resolving the old location.

Enrich the listed collections with their path via `qmd collection show`
so path-change detection works and the rebind fires.

Closes #91251

(cherry picked from commit 4a225011e9)
2026-06-09 01:10:19 +09:00
Andy Ye
57633c42b6 Fix CLI silent reply fallback policy
(cherry picked from commit 2f3762d229)
2026-06-09 00:49:51 +09:00
宇宙熊Yzx
cfeaf6897f fix(cron): clear payload model overrides
(cherry picked from commit 87af108140)
2026-06-09 00:46:36 +09:00
Ayaan Zaidi
7a602c7385 fix(reply): forward queued commentary progress (#89834) (thanks @anagnorisis2peripeteia) 2026-06-08 21:13:22 +05:30
Ayaan Zaidi
66c9feb41d fix(cli): type claude live commentary flag (#89834) (thanks @anagnorisis2peripeteia) 2026-06-08 21:13:22 +05:30
Ayaan Zaidi
817a0910f3 fix(cli): suppress claude commentary answer partials 2026-06-08 21:13:22 +05:30
Ayaan Zaidi
26983877d7 fix(channels): keep commentary progress bounded 2026-06-08 21:13:22 +05:30
Cameron Beeley
5fef91f1de fix: apply backend output transforms to commentary progress text 2026-06-08 21:13:22 +05:30
Cameron Beeley
3a04c9a4bb fix: emit only new commentary segment, add text-tool-text-tool regression test 2026-06-08 21:13:22 +05:30
Cameron Beeley
d03952ccd4 feat: add commentary text emission to Claude CLI streaming parser
Detect text accumulated before tool_use blocks in the Claude CLI
streaming parser and emit it as commentary via a new onCommentaryText
callback. This enables the same commentary progress display that the
Codex backend already provides through preamble item events.

- Add onCommentaryText optional callback to createCliJsonlStreamingParser
- Flush accumulated assistantText as commentary when content_block_start
  with tool_use type is encountered
- Track last flushed position to avoid duplicate emissions on consecutive
  tool_use blocks without intervening text
- Wire the callback in both execute.ts (regular CLI spawn + live session)
  and claude-live-session.ts, emitting AgentItemEventData with
  kind=preamble and progressText
- Add 3 test cases covering: text before tool_use, empty text before
  tool_use, and consecutive tool_use dedup
2026-06-08 21:13:22 +05:30
Cameron Beeley
c1300455d9 fix(channels): render inter-tool commentary in full
Commentary lines carry noCompact so the progress-draft renderer does not compact
them like tool lines — assistant prose renders in full, spilling to a new message
at the channel limit rather than truncating mid-sentence.
2026-06-08 21:13:22 +05:30
Pavan Kumar Gondhi
53357e8e7f fix: neutralize browser media directives (#91422) 2026-06-08 21:11:14 +05:30
宇宙熊Yzx
0911f86916 fix(cron): keep session model overrides strict
(cherry picked from commit 4562a3850b)
2026-06-09 00:21:47 +09:00
宇宙熊Yzx
14430ca588 fix(cron): inherit default fallbacks for string agent jobs
(cherry picked from commit 4b5cb68d39)
2026-06-09 00:21:47 +09:00
Vincent Koc
67dc805314 fix(agents): retry post-tool empty replies
Post-tool empty OpenAI-compatible stop turns no longer qualify as intentional silence, so replay-safe attempts use the existing empty-response retry and unsafe attempts surface the existing incomplete-turn error instead of disappearing.

Fixes #91394
2026-06-09 00:18:04 +09:00
Kevin Lin
6cc6f5e210 docs: add maturity scorecard mirror (#91317)
* docs: add maturity scorecard mirror

* docs: format maturity scorecard mirror

* docs: drop stray maturity note

* docs: fix maturity scorecard docs checks
2026-06-08 08:07:32 -07:00
Ben Badejo
60d716e652 fix: normalize Codex dynamic tool progress results
Normalize Codex dynamic tool progress result payloads to TUI-compatible content arrays after sanitization, while stripping protocol-only fields from the emitted event.

Includes regression coverage for sanitized dynamic tool text/image progress output.

Thanks @bdjben.
2026-06-08 16:05:16 +01:00
Vincent Koc
d46dc39b18 fix(memory): rebuild missing index metadata safely
Gateway/background sync now repairs missing memory index metadata with the existing full reindex path when the configured embedding provider is available, while preserving dirty/paused state instead of downgrading semantic chunks when embeddings are unavailable.

Fixes #90338
2026-06-08 23:58:10 +09:00
rudi193-cmd
e3ef136bca fix(memory): keep FTS keyword search model agnostic
Make lexical FTS/LIKE search ignore embedding model identity so exact keyword recall survives provider/model changes. Vector search remains model-scoped, and refreshed or stale FTS rows are cleaned by path/source with live-chunk filtering to prevent old orphan rows from surfacing.

Fixes #48300
2026-06-08 23:38:46 +09:00
Mason Huang
7499a020d9 docs: preserve LINE across localized docs glossaries (#91442)
Summary:
- The PR adds `LINE -> LINE` entries to 16 localized docs glossary JSON files so generated localized docs preserve the LINE brand term.
- PR surface: Docs +64. Total +64 across 16 files.
- Reproducibility: not applicable. this is a docs glossary maintenance PR, not a runtime bug report. The relevant checks are source inspection, PR-head JSON validation, and docs-i18n policy alignment.

Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.

Validation:
- ClawSweeper review passed for head 2ef712ff7a.
- Required merge gates passed before the squash merge.

Prepared head SHA: 2ef712ff7a
Review: https://github.com/openclaw/openclaw/pull/91442#issuecomment-4649882666

Co-authored-by: Mason Huang <masonxhuang@tencent.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: hxy91819
Co-authored-by: hxy91819 <8814856+hxy91819@users.noreply.github.com>
2026-06-08 14:33:41 +00:00
张贵萍0668001030
3e0f7e4931 fix(memory-core): preserve sqlite-vec table on unsafe reindex 2026-06-08 23:29:42 +09:00
Vincent Koc
355a9cbf35 fix(memory): fall back to sqlite-vec platform variant 2026-06-08 23:25:24 +09:00
Ayaan Zaidi
6d7eb9bb84 fix(android): use connected device foreground service 2026-06-08 19:53:25 +05:30
Dave Lutz
7d357a75fd fix(android): avoid data sync fgs for node service 2026-06-08 19:53:25 +05:30
Vincent Koc
9c5ac9f42d fix(memory): verify sqlite-vec loads usable functions 2026-06-08 23:16:54 +09:00
Shakker
da401341b6 fix: preserve fallback approval runtime auth 2026-06-08 14:49:01 +01:00
Shakker
f366922e01 fix: limit approval runtime token to local clients 2026-06-08 14:49:01 +01:00
Shakker
1c28c3914a fix: require stable approval requester identity 2026-06-08 14:49:01 +01:00
fuller-stack-dev
43acf3a4a2 fix(gateway): gate env approval runtime auth 2026-06-08 14:49:01 +01:00
fuller-stack-dev
2affecc720 fix(gateway): share approval runtime socket token 2026-06-08 14:49:01 +01:00
Mason Huang
9a82b60024 docs: preserve channel brand terms in Chinese i18n (#91419)
Summary:
- Adds Simplified and Traditional Chinese docs i18n glossary mappings to preserve channel and product brand terms in generated Chinese translations.
- PR surface: Docs +144. Total +144 across 2 files.
- Reproducibility: not applicable. this is a docs i18n glossary maintenance PR, not a bug report. The relevant check is source inspection plus PR-head JSON and coverage validation.

Automerge notes:
- PR branch already contained follow-up commit before automerge: docs: preserve channel brand terms in Chinese i18n

Validation:
- ClawSweeper review passed for head 45d54b370f.
- Required merge gates passed before the squash merge.

Prepared head SHA: 45d54b370f
Review: https://github.com/openclaw/openclaw/pull/91419#issuecomment-4649184716

Co-authored-by: Mason Huang <masonxhuang@tencent.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: hxy91819
Co-authored-by: hxy91819 <8814856+hxy91819@users.noreply.github.com>
2026-06-08 13:25:30 +00:00
Shakker
a04de1a0ce test: scope subagent attachment home env 2026-06-08 14:19:41 +01:00
Shakker
ca7047e460 fix: restore cli prepare session env 2026-06-08 14:19:41 +01:00
Shakker
226341e847 test: scope bundle mcp harness env 2026-06-08 14:19:41 +01:00
Shakker
1621e58ff1 fix: restore cli runner session env 2026-06-08 14:19:41 +01:00
Shakker
9403ea805d test: scope models config host env 2026-06-08 14:19:41 +01:00
Shakker
71f6620ba3 fix: scope model auth marker env 2026-06-08 14:19:41 +01:00
Shakker
35eb63e692 test: stabilize agent SIGTERM tests 2026-06-08 14:11:29 +01:00
Ayaan Zaidi
2858c629bd build(plugin-sdk): refresh api baseline for cli commentary bridge 2026-06-08 18:06:18 +05:30
Ayaan Zaidi
e1ac2d0925 refactor(cli): unify agent event bridge cleanup 2026-06-08 18:06:18 +05:30
Cameron Beeley
d7b9b21fb8 fix(cli): bridge inter-tool commentary events to channel progress
Inter-tool commentary (assistant text emitted before a tool call, surfaced by
the CLI parser as a stream:"item", kind:"preamble" agent event) landed on the
agent-event bus with no subscriber and was silently dropped: runCliAgentWithLifecycle
bridges the assistant, reasoning, and tool streams to channel callbacks, but the
item/preamble stream had no bridge. Add createCommentaryEventBridge to forward it
to onItemEvent, so CLI commentary reaches the channel's commentary render hook.

This is the CLI-dispatch delivery half of the commentary feature: the parser
emission (claude-cli) feeds the bus; this bridge delivers it to the channel.
Addresses the claude-cli case of intermediate-text-lost (#87326 / #84486).
2026-06-08 18:06:18 +05:30
Mason Huang
439dcbde3b fix: clarify provider quota errors (#91390)
Summary:
- The branch adds provider error classification for generic HTTP 429 runtime failures and Volcengine `InvalidSubscription` billing errors, plus focused regression tests and SIGTERM test stabilization.
- PR surface: Source +62, Tests +137. Total +199 across 8 files.
- Reproducibility: yes. at source level. Current main lacks the HTTP 429 metadata classifier and Volcengine subscription billing matcher, and the PR body reports a live Volcengine failure shape plus after-fix tests.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix: clarify provider quota errors

Validation:
- ClawSweeper review passed for head 5e10848a37.
- Required merge gates passed before the squash merge.

Prepared head SHA: 5e10848a37
Review: https://github.com/openclaw/openclaw/pull/91390#issuecomment-4647819660

Co-authored-by: Mason Huang <masonxhuang@tencent.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: hxy91819
Co-authored-by: hxy91819 <8814856+hxy91819@users.noreply.github.com>
2026-06-08 11:12:56 +00:00
Ayaan Zaidi
310d28f719 test(telegram): trim block rotation coverage 2026-06-08 15:30:35 +05:30
Ayaan Zaidi
5b0061e7a2 refactor(telegram): distill streamed block rotation cleanup 2026-06-08 15:30:35 +05:30
Alexazhu
4f31967141 Preserve stale Telegram block drafts before rotation
Queued answer block rotations can split assistant messages while the previous Telegram preview is stale. Materialize the previous block through the existing delivery fallback before resetting the draft lane, and keep assistant-message correlation on the internal dispatcher path instead of expanding the public Plugin SDK payload API.

Constraint: ClawSweeper flagged missing live Telegram proof and a public Plugin SDK helper surface; the code path must stay a Telegram/channel bugfix without adding a third-party SDK contract.

Rejected: Export getReplyPayloadAssistantMessageIndex from openclaw/plugin-sdk/reply-payload | it exposes internal reply metadata solely for this Telegram fix.

Rejected: Match queued block rotations only by text | plugin rewrites, repeated text, and media-only transformed block deliveries need assistant-message correlation.

Confidence: high

Scope-risk: narrow

Directive: Keep intermediate block materialization non-durable unless it is the actual final answer path, and keep assistant-message correlation off public reply-payload SDK exports.

Tested: OPENCLAW_HEAVY_CHECK_LOCK_SCOPE=worktree node scripts/run-vitest.mjs extensions/telegram/src/bot-message-dispatch.test.ts extensions/telegram/src/lane-delivery.test.ts src/auto-reply/reply/before-deliver.test.ts src/auto-reply/dispatch.test.ts src/auto-reply/reply/dispatch-from-config.test.ts src/plugins/wired-hooks-reply-payload-sending.test.ts

Tested: node_modules/.bin/oxlint --tsconfig config/tsconfig/oxlint.extensions.json extensions/telegram/src/bot-message-dispatch.ts extensions/telegram/src/bot-message-dispatch.test.ts extensions/telegram/src/lane-delivery-text-deliverer.ts extensions/telegram/src/lane-delivery.test.ts

Tested: node_modules/.bin/oxlint --tsconfig config/tsconfig/oxlint.core.json src/auto-reply/reply/reply-dispatcher.ts src/auto-reply/reply/before-deliver.test.ts src/auto-reply/dispatch.ts src/auto-reply/dispatch.test.ts src/auto-reply/reply/dispatch-from-config.ts src/auto-reply/reply/dispatch-from-config.test.ts src/auto-reply/reply/reply-payload-sending-hook.ts

Tested: git diff --check origin/main

Not-tested: Redacted live Telegram Bot API/Desktop proof; no Telegram credentials or chat target are configured in this local environment.

Not-tested: tsgo extensions test command was attempted locally and terminated after running over six minutes without output; prior known local run failed on unrelated Discord voice libopus-wasm errors.
2026-06-08 15:30:35 +05:30
Alexazhu
3fdc17b921 Preserve Telegram block previews across assistant boundaries
Constraint: Telegram draft previews are mutable until stopped, while OpenClaw block streaming can emit multiple chunks for one assistant message and separate assistant messages around tool calls.
Rejected: Finalizing every answer block | same-message chunks would become duplicate standalone Telegram messages before the final payload.
Rejected: Exposing full reply payload metadata through the public plugin SDK | Telegram only needs assistant block identity, and broader metadata would make internal dispatch fields a third-party API contract.
Rejected: Leaving queued block rotations as FIFO text-only state | delivery hooks can rewrite, cancel, skip, or remove answer text after queueing.
Confidence: high
Scope-risk: moderate
Directive: Keep same-message block chunks in one draft; rotate only at assistant-message/tool-progress boundaries, and expire cancelled, skipped, or non-answer queued blocks without deleting still-pending earlier rotations.
Tested: git diff --check origin/main; /Users/alex/PR/projects/openclaw__openclaw/repo/node_modules/oxfmt/bin/oxfmt --check --threads=1 docs/.generated/plugin-sdk-api-baseline.sha256 extensions/telegram/src/bot-message-dispatch.test.ts extensions/telegram/src/bot-message-dispatch.ts extensions/telegram/src/draft-stream.test-helpers.ts extensions/telegram/src/lane-delivery-text-deliverer.ts extensions/telegram/src/lane-delivery.test.ts src/auto-reply/dispatch.test.ts src/auto-reply/dispatch.ts src/auto-reply/reply/before-deliver.test.ts src/auto-reply/reply/dispatch-from-config.test.ts src/auto-reply/reply/dispatch-from-config.ts src/auto-reply/reply/reply-dispatcher.ts src/auto-reply/reply/reply-payload-sending-hook.ts src/plugin-sdk/reply-payload.ts; ./node_modules/.bin/oxlint extensions/telegram/src/bot-message-dispatch.test.ts extensions/telegram/src/bot-message-dispatch.ts extensions/telegram/src/lane-delivery-text-deliverer.ts extensions/telegram/src/lane-delivery.test.ts extensions/telegram/src/draft-stream.test-helpers.ts src/plugin-sdk/reply-payload.ts src/auto-reply/dispatch.ts src/auto-reply/dispatch.test.ts src/auto-reply/reply/before-deliver.test.ts src/auto-reply/reply/dispatch-from-config.ts src/auto-reply/reply/dispatch-from-config.test.ts src/auto-reply/reply/reply-dispatcher.ts src/auto-reply/reply/reply-payload-sending-hook.ts; node --max-old-space-size=8192 --import tsx scripts/generate-plugin-sdk-api-baseline.ts --check; CI=1 NODE_OPTIONS=--max-old-space-size=4096 node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.extensions.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/extensions-test.tsbuildinfo; CI=1 NODE_OPTIONS=--max-old-space-size=4096 node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.core.test.non-agents.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/core-test-non-agents.tsbuildinfo; OPENCLAW_HEAVY_CHECK_LOCK_SCOPE=worktree node scripts/run-vitest.mjs extensions/telegram/src/bot-message-dispatch.test.ts; OPENCLAW_HEAVY_CHECK_LOCK_SCOPE=worktree node scripts/run-vitest.mjs extensions/telegram/src/bot-message-dispatch.test.ts extensions/telegram/src/lane-delivery.test.ts extensions/telegram/src/draft-stream.test.ts src/plugin-sdk/reply-payload.test.ts src/plugins/contracts/plugin-sdk-index.test.ts; OPENCLAW_HEAVY_CHECK_LOCK_SCOPE=worktree node scripts/run-vitest.mjs src/auto-reply/reply/before-deliver.test.ts src/auto-reply/dispatch.test.ts; OPENCLAW_HEAVY_CHECK_LOCK_SCOPE=worktree node scripts/run-vitest.mjs src/auto-reply/reply/dispatch-from-config.test.ts -t "forwards payload metadata into onBlockReplyQueued context"
Not-tested: Live Telegram bot roundtrip with real credentials. codex review --base origin/main was run twice but did not complete a final verdict after hitting local heavy-check lock, unsupported --runInBand, and .vite-temp EPERM in the review tool sandbox; no actionable P1/P2 finding was emitted before termination.
2026-06-08 15:30:35 +05:30
Ayaan Zaidi
b75d1a0b85 fix(telegram): keep forum topic sessions stable 2026-06-08 14:50:48 +05:30
Cody's OpenClaw
e2db55373d fix(telegram): satisfy topic reply lint
Co-authored-by: Codex <codex@openai.com>
2026-06-08 14:50:48 +05:30
Cody's OpenClaw
733152127b fix(telegram): route account-scoped topic agents
Co-authored-by: Codex <codex@openai.com>
2026-06-08 14:50:48 +05:30
Omar Shahine
fc6400ede3 fix(imessage): always-on inbound recovery and dedupe (#91335)
* feat(imessage): always-on inbound recovery, deprecate catchup

Replaces the opt-in catchup subsystem with always-on inbound replay
protection that brings iMessage in line with the other channels, and
fixes #89237 (stale backlog dispatched as fresh after bridge recovery).

- New inbound-dedupe.ts: persistent claimable GUID dedupe (claim/commit/
  release) plus a stale-backlog age fence that suppresses live rows whose
  send date is materially older than arrival (logged, never silent).
- monitor-provider: claim at ingestion, carry the exact claimed key on the
  debouncer entry, commit on successful flush / release on dispatch failure
  (per-unit so a coalesced bucket cannot strand a sibling claim). Keeps the
  local startup since_rowid watermark so startup-window rows are not skipped.
- Deprecate catchup: delete catchup.ts + catchup-bridge.ts, remove the
  channels.imessage.catchup schema, cursor migration, and config-guard nag.
  Back-compat: strip the retired key before validation; new imessage doctor
  contract reports + removes it on doctor --fix.
- Docs updated for the new recovery model.

Net -947 prod LOC.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(imessage): recover downtime messages via since_rowid replay

Builds downtime recovery on the new inbound dedupe instead of restoring the
old catchup subsystem. On startup the monitor passes the last dispatched rowid
(a persisted per-account cursor) to imsg watch.subscribe as since_rowid, so imsg
replays the messages that landed while the gateway was down, then tails live.
The GUID dedupe drops anything already handled, so no cursor/retry bookkeeping
is needed.

- recovery-cursor.ts: minimal persisted per-account lastDispatchedRowid.
- monitor-provider: since_rowid = cursor (capped to the most recent
  IMESSAGE_RECOVERY_MAX_ROWS); split the age fence on the startup rowid boundary
  so replayed rows (<= boundary) use the wider recovery window and live rows
  (> boundary) keep the tight #89237 fence; advance the cursor on commit.
- Local only: remote SSH cliPath cannot read chat.db, so it tails from the
  current rowid (suppress-and-move-on) as before.

Restores missed-message recovery that the catchup removal dropped, with no
config and a fraction of the old LOC.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(imessage): make recovery cursor advance failure- and suppression-safe

Addresses two cursor-state regressions in the downtime-recovery path:

- Failed replay rows could be skipped forever: a released (failed) row keeps
  its dedupe claim for retry, but a later successful row in the same flush
  advanced the cursor past it, so the next startup's since_rowid skipped it.
  Hold a per-session floor at the lowest released rowid and never advance the
  cursor past it.
- Suppressed live backlog could be re-delivered after a restart: a live row
  suppressed under the tight live fence was not recorded, so after a restart it
  fell under the wider recovery window (its rowid now below the new boundary)
  and was delivered. Commit its dedupe key on suppression so the recovery
  replay treats it as already handled.

Both caught by Codex autoreview. Adds regression tests for the floor and the
suppression record.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(imessage): bound the GUID-less replay key length

Hash the composite fallback key's variable parts (conversation, sender,
created_at, text) so the key is length-bounded regardless of message text.
The persistent dedupe store already hashes keys internally, so this was not a
live overflow, but the bounded key removes the dependency on that and keeps the
fallback fail-open. Flagged by autoreview.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(imessage): recover downtime messages on remote cliPath setups too

The since_rowid replay runs over the imsg RPC client, so driving it from the
persisted recovery cursor (not the local chat.db boundary) makes downtime
recovery work for remote SSH cliPath gateways — the topology the old RPC-based
catchup served and that the rowid-boundary-only version regressed. Local setups
keep the wider, capped recovery window via the chat.db boundary; remote uses the
live age-fence window. Flagged by autoreview.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(imessage): seed recovery cursor from retired catchup cursor on upgrade

A one-time, self-cleaning migration: when the recovery cursor is empty on the
first startup after upgrade, seed it from the retired imessage.catchup-cursors
lastSeenRowid and consume the legacy entry. Without this a user who had catchup
enabled would not replay messages missed across the upgrade restart. Flagged by
autoreview.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(imessage): preserve catchup recovery on upgrade

---------

Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-06-08 16:54:10 +09:00
Peter Steinberger
538d36eaaa refactor: move session metadata to SQLite (#91322)
* refactor: move session metadata to sqlite

* test: seed session stores with sqlite fixtures

* test: seed remaining session stores with sqlite fixtures

* fix: stabilize sqlite session cache freshness

* test: seed cli transcript metadata in sqlite
2026-06-07 23:17:35 -07:00
mushuiyu_xydt
b2c1de77ac fix #90452: Regression: Heartbeat exec completion still shows generic fallback text instead of actual output (#90897)
Summary:
- The PR threads heartbeat trigger state into embedded-runner payload formatting so heartbeat exec-like failures include captured error details, with a focused regression test.
- PR surface: Source +12, Tests +18. Total +30 across 3 files.
- Reproducibility: yes. Source inspection shows current main and v2026.6.1 only include raw exec details for v ... follows the generic warning path; I did not run the wall-clock heartbeat scenario in this read-only review.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix #90452: Regression: Heartbeat exec completion still shows generic…

Validation:
- ClawSweeper review passed for head 85c5d6fb9f.
- Required merge gates passed before the squash merge.

Prepared head SHA: 85c5d6fb9f
Review: https://github.com/openclaw/openclaw/pull/90897#issuecomment-4638158130

Co-authored-by: 杨浩宇0668001029 <yang.haoyu@xydigit.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
2026-06-08 05:47:53 +00:00
Yzx
ccb9f2ca2b fix(anthropic): drop reasoning_content replay signatures (#91231)
Summary:
- The PR filters persisted OpenAI-compatible `reasoning_content` thinking placeholders from direct Anthropic replay payloads and updates the focused Anthropic provider test.
- PR surface: Source +1, Tests -4. Total -3 across 2 files.
- Reproducibility: yes. from source: current main serializes `thinkingSignature: "reasoning_content"` as a nat ... rror. The PR body also provides after-fix captured outbound payload proof for the production provider path.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(anthropic): drop reasoning_content replay signatures

Validation:
- ClawSweeper review passed for head 6eaa72f3a3.
- Required merge gates passed before the squash merge.

Prepared head SHA: 6eaa72f3a3
Review: https://github.com/openclaw/openclaw/pull/91231#issuecomment-4643786130

Co-authored-by: 宇宙熊Yzx <53250620+849261680@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
2026-06-08 05:45:48 +00:00
snowzlm
a4f0e508df fix(gateway): preserve stale channel restart diagnostics (#90937)
Summary:
- This PR sanitizes status patches from aborted channel tasks in the gateway manager and adds regression tests for stale restart diagnostics.
- PR surface: Source +56, Tests +78. Total +134 across 2 files.
- Reproducibility: yes. Source inspection and the PR's before-fix regression show the sequence: non-manual sto ... while the stale task remains, then a late `connected=true` / `lastError=null` status patch on current main.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(gateway): preserve stale restart diagnostics
- PR branch already contained follow-up commit before automerge: fix(gateway): preserve stale channel restart diagnostics

Validation:
- ClawSweeper review passed for head 53b37e5073.
- Required merge gates passed before the squash merge.

Prepared head SHA: 53b37e5073
Review: https://github.com/openclaw/openclaw/pull/90937#issuecomment-4638942823

Co-authored-by: snowzlm <snowzlm@noreply.codeberg.org>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
2026-06-08 05:29:11 +00:00
Mariano
b8adc11977 feat(cron): support command jobs
Add command-backed cron jobs with timeout-safe process-tree cleanup for shell wrappers. Ensures POSIX command jobs run in a killable process group, adds Windows tree cleanup fallback handling, and covers timeout cleanup behind sh -lc.
2026-06-08 12:06:16 +09:00
Marcus Castro
181238fb53 feat(whatsapp): expand live QA coverage (#90480)
* feat(whatsapp): expand qa driver message support

* feat(qa-lab): add deterministic whatsapp mock replies

* feat(qa-lab): expand whatsapp live qa scenarios

* docs(qa): document whatsapp live qa coverage
2026-06-08 00:03:23 -03:00
Yzx
4780546c12 fix(cron): preserve isolated agent turn payload message (#91230)
Summary:
- The PR changes isolated cron agent prompt construction to read agentTurn text from `job.payload.message` and adds regression coverage for malformed dispatch messages plus SQLite-rehydrated manual runs.
- PR surface: Source +8, Tests +60. Total +68 across 3 files.
- Reproducibility: yes. source-level: current main interpolates `input.message` into the isolated cron prompt, ... release report supplies operator repro evidence; I did not run it locally because this review is read-only.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(cron): preserve isolated agent turn payload message

Validation:
- ClawSweeper review passed for head 4d33607efd.
- Required merge gates passed before the squash merge.

Prepared head SHA: 4d33607efd
Review: https://github.com/openclaw/openclaw/pull/91230#issuecomment-4643779241

Co-authored-by: 宇宙熊Yzx <53250620+849261680@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
2026-06-08 02:23:02 +00:00
Yzx
766c5b3d32 fix(codex): preserve native subagent completion results (#91235)
Summary:
- The branch updates the Codex plugin native subagent parser, monitor, and tests so successful null or blank c ... final result and transcript reconciliation can override early empty notifications before fallback delivery.
- PR surface: Source +92, Tests +176. Total +268 across 4 files.
- Reproducibility: yes. at source level: current main maps successful null/blank Codex completions to `(no out ... n recover final text. I did not run a live current-main Telegram/Codex reproduction in this read-only pass.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(codex): preserve native subagent completion results

Validation:
- ClawSweeper review passed for head f9270c28e7.
- Required merge gates passed before the squash merge.

Prepared head SHA: f9270c28e7
Review: https://github.com/openclaw/openclaw/pull/91235#issuecomment-4643854708

Co-authored-by: 宇宙熊Yzx <53250620+849261680@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
2026-06-08 02:22:05 +00:00
Omar Shahine
9caff5f873 fix(imessage): gate split-send coalescing on imsg balloon metadata with back-compat (#90858)
Gate iMessage same-sender DM split-send coalescing on imsg's structural
`balloon_bundle_id` URL-balloon marker (openclaw/imsg#137) instead of timing/
text-shape inference, with a session capability latch and a back-compat path:

- URL-balloon marker present -> merge (precise split-send).
- Build known to emit balloon metadata (session latch) -> keep non-marker
  buckets separate (the precision win).
- Build that never emits balloon metadata (older imsg) -> preserve the legacy
  unconditional merge, so split-send users do not regress to two turns.

Never merges more than shipped main already did. Verified live end-to-end: the
patched gateway, watching a real chat.db via an imsg #137 build, merged a real
iPhone-sent `Dump <url>` split-send into one turn. Client-side removal once imsg
coalesces upstream is tracked in #91243 (openclaw/imsg#141).

Closes #90795
2026-06-07 19:14:13 -07:00
Chunyue Wang
f2530de832 fix(agents): do not refresh lastUsedAt on MCP lease release (#91124)
Summary:
- The PR removes release-time `lastUsedAt` refresh from session MCP runtime lease cleanup and adds regression tests for idle eviction after a lease expires while active.
- PR surface: Source 0, Tests +74. Total +74 across 2 files.
- Reproducibility: yes. from source inspection: current main refreshes `lastUsedAt` in the release callback, a ...  timestamp after active leases drop to zero. I did not execute the focused Vitest in this read-only review.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(agents): do not refresh lastUsedAt on MCP lease release

Validation:
- ClawSweeper review passed for head c9144789fd.
- Required merge gates passed before the squash merge.

Prepared head SHA: c9144789fd
Review: https://github.com/openclaw/openclaw/pull/91124#issuecomment-4641967555

Co-authored-by: openperf <16864032@qq.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
2026-06-08 02:08:28 +00:00
Yzx
75c1790b50 fix(outbound): preserve retries for budget-deferred deliveries (#91241)
Summary:
- The branch removes the budget-deferred `failDelivery` path from outbound recovery and updates the `maxRecoveryMs` regression expectation so unattempted deliveries keep retry counts at zero.
- PR surface: Source -11, Tests -1. Total -12 across 2 files.
- Reproducibility: yes. at source level: current main reaches `failDelivery` from the exhausted recovery-budge ...  in this read-only review, but the PR body also supplies terminal output showing the after-fix queue state.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(outbound): preserve retries for budget-deferred deliveries

Validation:
- ClawSweeper review passed for head aff2b9d16e.
- Required merge gates passed before the squash merge.

Prepared head SHA: aff2b9d16e
Review: https://github.com/openclaw/openclaw/pull/91241#issuecomment-4644024479

Co-authored-by: 宇宙熊Yzx <53250620+849261680@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
2026-06-08 01:43:02 +00:00
Vincent Koc
b16a43597d fix(agents): guard prompt cache tool names
Make prompt-cache observability collect diagnostic tool names through guarded descriptor reads so an unreadable tool-name getter cannot abort cache tracing/debug collection. Preserve readable trimmed names and keep runtime tool registration/schema behavior strict and unchanged.
2026-06-08 10:36:50 +09:00
Vincent Koc
8b03fd1f5f fix(agents): compact lean local tool catalogs
Default localModelLean runs to compact Tool Search controls when the operator has not configured tools.toolSearch, while preserving explicit Tool Search settings and direct message-tool delivery semantics.

Verification: local focused Vitest/docs/format/lint/diff/autoreview proof; GitHub CI, CodeQL/Security High, CodeQL Critical Quality, OpenGrep PR Diff, Real behavior proof, Dependency Guard, and Workflow Sanity passed on 6153fb5ecb.

Refs https://github.com/openclaw/openclaw/issues/86599
2026-06-08 10:33:41 +09:00
Vincent Koc
3ffb3609a1 fix(codex): quarantine unreadable dynamic tools (#90022) 2026-06-08 10:30:13 +09:00
joshavant
5c5391836b fix(android): remove inert appearance palette preview 2026-06-07 17:43:21 -05:00
Pavan Kumar Gondhi
2a21de6322 fix: gate owner-only HTTP tools (#90261)
* fix: gate owner-only HTTP tools

* fix: inherit HTTP owner tool denies

* fix: use mutable HTTP owner deny policy

* fix: preserve RPC owner tool access

* docs: clarify owner-only gateway tool allowlist

---------

Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
2026-06-07 17:26:12 -05:00
Voscko
3c73ff7689 feat(android): add theme mode selection (#90752)
* feat(android): add theme mode selection

* refine Android theme mode handling

---------

Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
2026-06-07 17:24:57 -05:00
Jason (Json)
57e0bdaabe feat: add live provider model catalog helper
Summary:
- Add a shared live provider catalog runtime for SDK-backed providers.
- Route OpenAI, xAI, OpenCode Go, Chutes, DeepInfra, Venice, NVIDIA, and Vercel AI Gateway live model discovery through the shared helper.
- Remove duplicated provider-local live catalog caching and harden auth marker stripping, empty live-result retries, and OpenAI custom-base-url handling.

Verification:
- node scripts/run-vitest.mjs extensions/openai/openai-provider.test.ts src/plugin-sdk/provider-catalog-live-runtime.test.ts src/commands/models/list.source-plan.test.ts extensions/opencode-go/index.test.ts extensions/nvidia/provider-catalog.test.ts
- pnpm plugin-sdk:api:check
- pnpm lint --threads=8
- pnpm run lint:extensions:bundled
- pnpm run test:extensions:package-boundary:compile
- pnpm check:import-cycles
- pnpm exec oxfmt --check extensions/openai/openai-provider.ts extensions/openai/openai-provider.test.ts
- git diff --check origin/main...HEAD
- autoreview clean: no accepted/actionable findings reported
- AWS Crabbox focused remote proof: run_364680d1bff8 / cbx_2456fffafe01
- Earlier same-PR AWS Crabbox live proof: run_1f05ccab368e / cbx_7375c79fcf9b

Known proof gap:
- Final current-code true live-provider smoke was blocked by Crabbox secret hydration, documented in the PR proof comment.
2026-06-07 14:16:00 -07:00
Omar Shahine
6c35c0d965 fix(imessage): self-explaining private-API failures and dedicated send timeout (#91041)
Append imsg's own status message (SIP / library validation / macOS 26 AMFI gate)
to iMessage private-API blocked-action errors so operators see the real blocker
instead of a generic "run imsg launch". Add a dedicated 150s default timeout for
iMessage send RPCs (explicit opts and probeTimeoutMs still win) so macOS 26
bridge stalls are not aborted mid-send.

Staged mitigation: the longer wait fully activates once the companion bridge
timeout (openclaw/imsg#139) ships; on current imsg the bridge still returns at
its own 10s, so there is no regression. Diagnostics half is live-proven; the
delayed-send timeout is covered by source + unit proof + maintainer waiver.
2026-06-07 14:07:31 -07:00
Peter Steinberger
af79cd6a9d fix: preserve live Ollama catalog metadata 2026-06-07 14:00:09 -07:00
brokemac79
3b6bcbfb50 fix: make sandbox skills readable in writable sandboxes
Materializes prompt-visible skills into a protected sandbox-readable workspace for rw sandboxes, refreshes Docker/SSH/OpenShell views, and hardens stale or poisoned remote skill copies. Fixes #90410.
2026-06-07 13:47:56 -07:00
clawsweeper[bot]
e498d39bed fix(agents): prevent ReDoS in background-session name derivation (#91233)
Summary:
- The PR updates background-session command tokenization to avoid catastrophic regex backtracking and adds `deriveSessionName` regression tests for quoted and backslash-heavy commands.
- PR surface: Source 0, Tests +26. Total +26 across 2 files.
- Reproducibility: yes. with high confidence from source inspection and supplied terminal proof: current `main ...  shows before/after timing for the production helper. I did not run tests because this review is read-only.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(agents): treat backslash as literal inside single-quoted session …
- PR branch already contained follow-up commit before automerge: fix(agents): prevent ReDoS in background-session name derivation

Validation:
- ClawSweeper review passed for head 0a38952fc8.
- Required merge gates passed before the squash merge.

Prepared head SHA: 0a38952fc8
Review: https://github.com/openclaw/openclaw/pull/91233#issuecomment-4643821335

Co-authored-by: yetval <yetvald@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
2026-06-07 20:30:56 +00:00
Shakker
0c33f4e078 fix: stabilize docker stats heartbeat test 2026-06-07 19:06:24 +01:00
Nimrod Gutman
47dbc675e9 feat(ios): clarify talk realtime fallback (#91201)
Merged via squash.

Prepared head SHA: b6fd32ed6e

Local prep note: pnpm build passed. pnpm check hit the npm shrinkwrap guard because @anthropic-ai/sdk@0.100.1 is no longer resolvable before 2026-05-24T20:18:43Z; the same shrinkwrap guard failure reproduces on current origin/main at 66b91d78fe, and this PR does not touch dependency manifests or lockfiles.

Co-authored-by: ngutman <1540134+ngutman@users.noreply.github.com>
Reviewed-by: @ngutman
2026-06-07 20:21:34 +03:00
Vincent Koc
66b91d78fe fix(e2e): bound release user journey JSON artifacts 2026-06-07 12:45:43 +02:00
zenglingbiao
3753c5e2c8 fix(inbound-meta): preserve reply-context body tails
Preserve actionable tail content in long reply-context bodies before they enter prompt JSON or inline reply context formatting.

- Apply UTF-16-safe head+tail truncation to ReplyChain JSON bodies and fallback ReplyToBody JSON blocks.
- Use the same body-aware truncation for Telegram inline ReplyToBody fallback and chat_window message bodies, so those paths cannot suppress the JSON fallback and still lose the tail.
- Adds regression coverage for ReplyChain, fallback ReplyToBody, Telegram inline ReplyToBody, chat_window reply targets, and emoji-heavy heads.

Verification:
- node scripts/run-vitest.mjs src/auto-reply/reply/inbound-meta.test.ts
- node_modules/.bin/oxfmt --check --threads=1 src/auto-reply/reply/inbound-meta.ts src/auto-reply/reply/inbound-meta.test.ts
- node scripts/run-oxlint.mjs src/auto-reply/reply/inbound-meta.ts src/auto-reply/reply/inbound-meta.test.ts
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- Testbox-through-Crabbox check:changed: provider=blacksmith-testbox id=tbx_01ktgthbb5xa9d5ap58h4134s0 exit=0
- PR CI: 158/158 completed, no failures, MERGEABLE/CLEAN

Fixes #91042
2026-06-07 19:45:00 +09:00
Vincent Koc
9bafa2a2b6 fix(e2e): bound release scenario JSON artifacts 2026-06-07 12:43:33 +02:00
Vincent Koc
1703fbc2ad fix(e2e): bound browser snapshot diagnostics 2026-06-07 12:39:48 +02:00
Chunyue Wang
afcbdd7416 fix(infra/agents): session-routing guard for coalesced gateway restart continuations (#86742) (#87323)
* fix(infra/agents): session-routing guard for coalesced gateway restart continuations (#86742)

When two sessions issue gateway.restart with continuationMessage close
together, the scheduler Path B updatePendingRestartEmitHooks
unconditionally overwrote the existing pending hooks, silently dropping
the first sessions continuation and potentially routing the second
sessions continuation back to the first session (CWE-200 finding
flagged by aisle-research-bot on prior attempt #74443).

Add a session-routing guard: scheduleGatewaySigusr1Restart now accepts
an optional sessionKey and tracks the pending restarts owning session.
Coalesced callers from a different session are rejected at the hook-
update step and the new ScheduledRestart.emitHooksQueued: false field
surfaces the drop to the caller. The gateway tool propagates this as
continuationQueued: false in the tool response, matching #83370 narrow
report-only surface.

Same-session debounce/replace and legacy hookless callers behave the
same as before.

Refs #86742

* fix(infra): preserve queued restart continuation on forced bypass

* fix(infra): make forced restart hook preservation explicit

* fix(infra): guard restart continuation ownership before reschedule

* fix(infra): report hookless coalesced restarts accurately

* fix(infra): trust runtime session for restart sentinel routing

* fix(infra): preserve earlier restart reschedule semantics

* fix(agents): trust runtime session for update continuations

* fix(infra): preserve hookless forced restart continuations

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-06-07 03:38:58 -07:00
Vincent Koc
2b43315933 fix(tooling): bound extension boundary source reads 2026-06-07 12:36:13 +02:00
Vincent Koc
f5935bbca1 fix(e2e): cancel timed out response reads 2026-06-07 12:32:56 +02:00
liuweiqin
a1af47e5da fix(codex): surface lastToolError on degraded orphan-tool delivery
Completed turns with deliverable assistant text still synthesize failed
tool.result rows but no longer set promptError. Record lastToolError on
that degraded path and treat whitespace-only assistant items as non-
deliverable so orphan tools still fail closed.

Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit cb6fbe36c73982e1043186b983f0c03334989b34)
2026-06-07 19:27:58 +09:00
weiqinl
ed3a0241f3 fix(codex): deliver assistant reply when orphan tool.call lacks result
Keep synthesizing failed tool.result records for transcript consistency,
but skip promptError when a completed Codex turn has deliverable assistant
text so channel users still receive the composed reply.

Fixes #91067

Co-authored-by: Cursor <cursoragent@cursor.com>

(cherry picked from commit ffac77ce811eab528bcce81eec99fd8bd6c70cca)
2026-06-07 19:27:58 +09:00
Vincent Koc
b00e1b2e7b fix(diagnostics): make memory pressure logs actionable
Log warning memory pressure at WARN level, add readable RSS/heap/threshold units, threshold ratio, and a concrete operator next step while preserving raw byte fields for diagnostics consumers.

Fixes #90783
2026-06-07 19:27:29 +09:00
Vincent Koc
cfe5d24889 fix(test): bound remaining child output collectors 2026-06-07 12:25:30 +02:00
Vincent Koc
bae607b9f1 fix(test): execute docker observability proof 2026-06-07 12:22:10 +02:00
Peter Steinberger
f08ee9eb54 fix(protocol): refresh generated send params 2026-06-07 03:21:26 -07:00
Jason Yao
d1cb6cd0b5 fix(media-understanding): preserve native vision skip with imageModel fallback
Fixes #91084

(cherry picked from commit 8aa5148338)
2026-06-07 19:19:27 +09:00
Vincent Koc
8291cfc2f4 fix(test): bound child output buffers 2026-06-07 12:19:08 +02:00
Vincent Koc
bf27221753 fix(tooling): bound source scan file reads 2026-06-07 12:14:09 +02:00
Vincent Koc
88c1af0a2c fix(tooling): bound generated formatter execution 2026-06-07 12:11:21 +02:00
Vincent Koc
6a0fdea90a fix(outbound): materialize buffer-only sends
Fixes #90768

Incorporates the send-buffer materialization shape proposed in #90794 by @LiuwqGit, with maintainer fixes for dry-run, gateway delivery, byte-cap, target-validation, and downstream plugin dispatch paths.
2026-06-07 19:09:49 +09:00
Vincent Koc
85840eb10e fix(dev): align gateway smoke auth contract 2026-06-07 12:07:05 +02:00
Vincent Koc
f7f2532cac test(agents): widen overflow model mock 2026-06-07 19:05:27 +09:00
Vincent Koc
e2524e0438 fix(ci): break plugin import cycles 2026-06-07 19:03:38 +09:00
兰之
58bab0c276 fix(agents): dispatch subagent spawn in process (#90612)
* fix(agents): dispatch subagent spawn in process

* docs: update subagent gateway dispatch note

* fix(gateway): keep in-process dispatch timeout budget

* test(gateway): avoid promise executor timer returns

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-06-07 03:01:51 -07:00
Vincent Koc
48da8d83d9 fix(e2e): bound parallels update logs 2026-06-07 12:00:57 +02:00
Vincent Koc
363c6923a1 fix(e2e): bound web search smoke logs 2026-06-07 11:57:04 +02:00
Vincent Koc
be617fdd62 fix(e2e): bound telegram docker logs 2026-06-07 11:54:47 +02:00
Vincent Koc
8dff529587 fix(e2e): bound corrupt update logs 2026-06-07 11:52:39 +02:00
Vincent Koc
901f963f62 fix(e2e): bound cleanup smoke logs 2026-06-07 11:50:33 +02:00
Vincent Koc
cdbf6d95ac fix(e2e): bound scenario client logs 2026-06-07 11:48:05 +02:00
Vincent Koc
5d7e0b73a7 fix(e2e): bound mcp client logs 2026-06-07 11:44:55 +02:00
Peter Steinberger
bab18d567b refactor(plugin-sdk): persist dedupe state in sqlite 2026-06-07 02:41:45 -07:00
Vincent Koc
a4e78aec4b fix(test): bound group report child output 2026-06-07 11:40:47 +02:00
Vincent Koc
0f855ea71a fix(e2e): require dashboard smoke assets 2026-06-07 11:38:25 +02:00
Vincent Koc
a7d5d92989 fix(e2e): require zai fallback evidence 2026-06-07 11:33:36 +02:00
Peter Steinberger
6f2b3830f1 fix(qqbot): migrate group tool policy config (#91128)
* fix(qqbot): migrate group tool policy config

* test: stabilize changed check lanes

* style: format changed main files

* test: align CI matrix expectations
2026-06-07 02:33:06 -07:00
clawsweeper[bot]
58b68e92f2 fix(outbound): keep Discord runtime adapters resolvable (#91119)
Summary:
- The branch changes outbound channel bootstrap and resolution so delivery paths prefer send-capable runtime a ...  avoid setup-only shells for runtime delivery, retry non-send-capable bootstraps, and add regression tests.
- PR surface: Source +121, Tests +294. Total +415 across 4 files.
- Reproducibility: yes. The linked stable-release report supplies the user-visible Discord failure, and curren ... p-shell/direct-registry path that can satisfy runtime resolution before a send-capable adapter is verified.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(outbound): keep Discord runtime adapters resolvable
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): reconcile automerge-openclaw-openclaw-90198 with ma…

Validation:
- ClawSweeper review passed for head 8711ada0c4.
- Required merge gates passed before the squash merge.

Prepared head SHA: 8711ada0c4
Review: https://github.com/openclaw/openclaw/pull/91119#issuecomment-4641934231

Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: thewilloftheshadow
Co-authored-by: thewilloftheshadow <35580099+thewilloftheshadow@users.noreply.github.com>
2026-06-07 09:30:41 +00:00
Vincent Koc
dcba17d019 fix(e2e): stream installer session scans 2026-06-07 11:28:31 +02:00
Peter Steinberger
c2d825ae53 fix: migrate legacy agent registry schema via doctor
Move the shipped legacy shared-state agent database registry repair into doctor. Runtime now fails fast with a doctor repair hint when the old primary-key shape remains.
2026-06-07 02:23:51 -07:00
Vincent Koc
f36e54cd68 fix(e2e): require secret probe success 2026-06-07 11:22:43 +02:00
Vincent Koc
3dc6ac3802 fix(qa): fail closed on skipped suite summaries 2026-06-07 11:20:25 +02:00
Peter Steinberger
ce015cef57 refactor: store sandbox registry in sqlite
Move sandbox registry runtime state to SQLite and migrate legacy JSON registry files/directories via doctor.
2026-06-07 02:05:28 -07:00
Chunyue Wang
e06f6ffc3e fix(doctor): merge legacy codex models safely
Merge disjoint legacy openai-codex model entries into the canonical openai provider without losing safe per-model metadata, params, or models-add markers.

Unsafe provider-level defaults, auth/header/request state, and blocked normalized legacy providers are now preserved for manual cleanup with doctor preview warnings instead of being silently copied into models or repeatedly reported as auto-fixable.

Fixes #90047

Co-authored-by: openperf <16864032@qq.com>
2026-06-07 01:59:31 -07:00
Peter Steinberger
3e4b10fa1c fix: strip Google provider prefix from Gemini paths (#91125)
* fix: strip Google provider prefix from Gemini paths

* test: align qa exit code type
2026-06-07 01:49:45 -07:00
Vincent Koc
e5a9c60851 fix(e2e): bound codex live failure logs 2026-06-07 10:44:06 +02:00
Vincent Koc
677358f4a9 fix(e2e): bound telegram desktop proof logs 2026-06-07 10:42:28 +02:00
Vincent Koc
9e87d316c7 fix(e2e): bound telegram rtt mock logs 2026-06-07 10:41:07 +02:00
Vincent Koc
8cba5f7efd fix(e2e): bound upgrade survivor logs 2026-06-07 10:38:29 +02:00
Vincent Koc
440f315e83 fix(e2e): bound update channel logs 2026-06-07 10:33:14 +02:00
Vincent Koc
b9d530e292 fix(e2e): bound doctor switch logs 2026-06-07 10:31:53 +02:00
Vincent Koc
9b85b36d92 fix(qa): fail whatsapp skipped scenarios 2026-06-07 10:30:15 +02:00
Vincent Koc
9fb8d87f91 fix(e2e): bound plugin update logs 2026-06-07 10:26:59 +02:00
Peter Steinberger
248dfb22ec fix: preserve Foundry Responses reasoning replay ids
Preserve Microsoft Foundry encrypted reasoning replay item ids for Responses continuations while leaving chat-completions streams untouched.

Fixes #91033.
2026-06-07 01:24:07 -07:00
Vincent Koc
e64f2324b9 fix(dev): bound anthropic prompt log tails 2026-06-07 10:22:50 +02:00
Vincent Koc
eae4d284e7 fix(e2e): bound shared helper log output 2026-06-07 10:19:16 +02:00
Vincent Koc
3643a68e49 fix(qa): verify config after restart races 2026-06-07 10:17:06 +02:00
Vincent Koc
a58a6f63ca fix(qa): stream session transcript summaries 2026-06-07 10:14:45 +02:00
Vincent Koc
a931884eb5 fix(qa): require runtime tool failure proof 2026-06-07 10:09:38 +02:00
Peter Steinberger
7a3d24e70c refactor(memory-wiki): store import runs in sqlite (#91108) 2026-06-07 01:04:43 -07:00
Peter Steinberger
d6dffd6ef8 fix: align Xiaomi completions replay compat
Fixes #91106.

Behavior:
- Xiaomi MiMo OpenAI-compatible completions now replay assistant tool-call messages with `reasoning_content: ""` and DeepSeek-style thinking format.
- Adds a provider payload regression for the outbound Xiaomi request.
- Includes a small script fixture lint repair needed after rebasing onto current main.

Proof:
- `OPENCLAW_VITEST_MAX_WORKERS=1 node scripts/run-vitest.mjs src/llm/providers/openai-completions.test.ts src/agents/openai-completions-compat.test.ts`
- `node scripts/run-oxlint.mjs --tsconfig config/tsconfig/oxlint.scripts.json scripts/e2e/lib/fixtures/workspace.mjs`
- `pnpm check:test-types`
- `pnpm tsgo:core`
- `pnpm exec oxfmt --check --threads=1 src/llm/providers/openai-completions.ts src/llm/providers/openai-completions.test.ts scripts/e2e/lib/fixtures/workspace.mjs`
- `git diff --check`
- `/Users/steipete/Projects/agent-skills/skills/autoreview/scripts/autoreview --mode branch --base origin/main`
- `gh pr checks 91113 --watch --fail-fast`
2026-06-07 01:00:59 -07:00
Jason (Json)
cf378e4cc8 fix(codex): preserve post-tool reasoning liveness
Preserve the Codex post-tool continuation guard for raw reasoning completions and streamed reasoning progress so valid post-tool synthesis stays on the intended completion watchdog instead of falling through to terminal idle behavior.

Verified with focused Codex watchdog tests, test typecheck, scripts lint, autoreview, and CI run 27086637988.

Thanks @fuller-stack-dev.

Co-authored-by: FullerStackDev <263060202+fuller-stack-dev@users.noreply.github.com>
2026-06-07 00:57:14 -07:00
Vincent Koc
589ea28dab test(gateway): smoke real websocket client 2026-06-07 09:46:00 +02:00
Vincent Koc
451765ad27 fix(e2e): require live tool result proof 2026-06-07 09:44:35 +02:00
Vincent Koc
4f9f7e20d4 fix(test): bound otel collector output 2026-06-07 09:42:15 +02:00
Dallin Romney
ebabf5022f perf(qqbot): narrow tool discovery cold load (#90780)
* perf: narrow qqbot tool discovery load

* fix(qqbot): load bridge entries through sidecars
2026-06-07 00:41:11 -07:00
Vincent Koc
1de4a3e9ea fix(test): stream group report logs 2026-06-07 09:40:46 +02:00
Vincent Koc
ea3a915cb5 fix(e2e): bound plugin fixture logs 2026-06-07 09:34:36 +02:00
Vincent Koc
ef52798254 fix(e2e): require tool-search session proof 2026-06-07 09:31:21 +02:00
Vincent Koc
78f2af9ac9 fix(e2e): bound workspace fixture output 2026-06-07 09:27:18 +02:00
Christine Yan
22276e6de0 fix(lmstudio): preserve wizard prompter binding
Bind LM Studio wizard prompter callbacks before storing them so class-backed gateway setup sessions keep their receiver and no longer crash when selecting LM Studio.

Thanks @christineyan4.

Co-authored-by: Christine Yan <christine.yan4@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-07 00:26:39 -07:00
Chunyue Wang
697d2d040c fix(agents): block message send loops with volatile delivery ids
Fixes #89090.

Release note: prevent repeated visible message sends from bypassing loop blocking when delivery results include fresh message, file, poll, receipt, run, idempotency, or timestamp fields. Normalizes send-like result hashing for the core message tool, sessions_send, and provider-docked messaging tools while preserving stable routing and outcome facts.

Verification:
- node scripts/run-vitest.mjs src/agents/tool-loop-detection.test.ts src/agents/tools/message-tool.test.ts
- .agents/skills/autoreview/scripts/autoreview --mode local --parallel-tests "node scripts/run-vitest.mjs src/agents/tool-loop-detection.test.ts src/agents/tools/message-tool.test.ts"
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main --parallel-tests "node scripts/run-vitest.mjs src/agents/tool-loop-detection.test.ts src/agents/tools/message-tool.test.ts"
- gh pr checks 89109 --watch --interval 30

Co-authored-by: openperf <16864032@qq.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-06-07 00:24:58 -07:00
Vincent Koc
0a2cad7e68 fix(e2e): bound live plugin transcript scans 2026-06-07 09:24:31 +02:00
Vincent Koc
0bf487e4cb fix(e2e): bound kitchen sink fixture logs 2026-06-07 09:22:36 +02:00
Vincent Koc
a3ab0e2534 fix(e2e): stream kitchen sink log scans 2026-06-07 09:20:45 +02:00
Vincent Koc
ab33fe33d1 fix(e2e): invoke kitchen sink image job 2026-06-07 09:18:22 +02:00
Yzx
054312672a fix(llm): preserve LM Studio Responses tool arguments
Preserve streamed Responses tool-call argument deltas when the final done event omits or sends empty arguments, fixing LM Studio argument-bearing tools from arriving as `{}`.

Fixes #90585.

Thanks @849261680.
2026-06-07 00:13:26 -07:00
Vincent Koc
c9f884fb28 fix(e2e): restrict degraded runtime readiness 2026-06-07 09:11:36 +02:00
Vincent Koc
f8db47e340 fix(e2e): verify bundled plugin source roots 2026-06-07 09:07:27 +02:00
Vincent Koc
cd1a90b310 fix(e2e): verify kitchen sink inspect-all 2026-06-07 09:03:48 +02:00
Vincent Koc
fff3b15fd7 fix(e2e): bound kitchen sink failure logs 2026-06-07 09:01:52 +02:00
Yuval Dinodia
bb27cbd46d fix(agents): decode xai and venice tool-call arguments exactly once
Decode HTML-entity escaped xAI and Venice tool-call arguments through the shared core compat path exactly once, preventing literal entities such as &amp; from being over-decoded before tool execution and transcript persistence. Removes xAI's duplicate provider-local decoder and keeps regression coverage for the shared core wrapper, xAI stream wrapper, and Venice compat path. Thanks @yetval for the fix.
2026-06-06 23:59:01 -07:00
Vincent Koc
8cb018e1f7 fix(e2e): require strict survivor readiness 2026-06-07 08:58:53 +02:00
Peter Steinberger
0566b96927 refactor(matrix): store crypto sidecars in sqlite (#91100) 2026-06-06 23:57:10 -07:00
Vincent Koc
b38e7105ec fix(e2e): bound parallels log version reads 2026-06-07 08:51:41 +02:00
Vincent Koc
6f35f96274 fix(dev): lazy-load telegram pairing smoke 2026-06-07 08:48:40 +02:00
Vincent Koc
f7aea2ad33 fix(e2e): report skipped secret proofs 2026-06-07 08:44:26 +02:00
Andi Liao
97d68b6902 fix(google): handle compressed Vertex ADC token responses
Decode Google Vertex authorized_user ADC OAuth token refresh responses from bytes so gzip-compressed token payloads still expose access_token. Adds a regression test for the compressed token response path while preserving plain JSON handling and the custom fetch seam.

Proof: OPENCLAW_VITEST_MAX_WORKERS=1 node scripts/run-vitest.mjs extensions/google/transport-stream.test.ts; pnpm exec oxfmt --check extensions/google/vertex-adc.ts extensions/google/transport-stream.test.ts; pnpm tsgo:extensions; git diff --check origin/main...HEAD; autoreview --mode branch --base origin/main. PR CI check-test-types failure was reproduced on current origin/main 607bbe4f5c and is unrelated to this two-file Google provider change.

Thanks @liaoandi for the fix and live Google Vertex ADC proof.
2026-06-06 23:42:35 -07:00
Vincent Koc
2fe7b5e8c9 fix(dev): harden smoke log diagnostics 2026-06-07 08:42:16 +02:00
Dallin Romney
a77d0fdd97 fix(test): type overflow resolver mock (#91098) 2026-06-06 23:40:37 -07:00
Vincent Koc
607bbe4f5c fix(dev): validate ios node smoke payloads 2026-06-07 08:36:06 +02:00
Vincent Koc
cd9c643dc6 fix(e2e): require causal telegram rtt canary 2026-06-07 08:25:11 +02:00
Vincent Koc
6bfd47af38 fix(e2e): clean interrupted docker harness runs 2026-06-07 08:17:16 +02:00
Peter Steinberger
08ae0e6d29 refactor: store Zalo hosted media in plugin state
Move Zalo hosted outbound media metadata and expiry into plugin state, add SDK chunked hosted media storage, and keep CI/type/lint gates green after rebase.
2026-06-06 22:56:48 -07:00
Vinayaka Jyothi
443ac732a1 fix(minimax): keep thinking active for M3
Fix MiniMax-M3 Anthropic-compatible requests so OpenClaw no longer sends the disabled-thinking payload that makes M3 return empty content. M3 defaults now stay on MiniMax's omitted/adaptive thinking path, explicit `/think off` is still respected, and MiniMax-M2.x keeps the disabled-thinking default that prevents reasoning_content leaks.

Also wires the MiniMax thinking policy through bundled provider-policy loading so pre-runtime and configless embedded-agent paths resolve the same defaults.

Thanks @IamVNIE for the live MiniMax API repro and initial patch.
2026-06-06 22:56:17 -07:00
Vincent Koc
0880fd94c6 fix(e2e): preserve dist during bun restore 2026-06-07 07:54:05 +02:00
Vincent Koc
ab41e25b2c fix(dev): require telegram final message id 2026-06-07 07:48:13 +02:00
Vincent Koc
03ae553ecd fix(e2e): bound telegram package logs 2026-06-07 07:46:26 +02:00
Vincent Koc
d943d36677 fix(e2e): require cron cleanup success status 2026-06-07 07:43:33 +02:00
Vincent Koc
b6c45a9301 fix(e2e): require web search success marker 2026-06-07 07:42:24 +02:00
Vincent Koc
5ab3104a15 test(e2e): cover openwebui chat smoke 2026-06-07 07:38:04 +02:00
Vincent Koc
d59d3f87b0 fix(e2e): bound bundled lifecycle log output 2026-06-07 07:36:51 +02:00
Vincent Koc
d801bb5be0 fix(e2e): report resource breaches on failed runs 2026-06-07 07:34:21 +02:00
Vincent Koc
3597cfc7bc fix(qa): allow gauntlet observations to fail 2026-06-07 07:31:56 +02:00
Vincent Koc
6590f764b5 fix(e2e): bound docker failure log printing 2026-06-07 07:29:35 +02:00
Vincent Koc
0b0893aa21 fix(e2e): bound kitchen sink log traversal 2026-06-07 07:25:46 +02:00
Vincent Koc
251bd61e22 fix(e2e): reject kitchen sink rpc error envelopes 2026-06-07 07:23:33 +02:00
Vincent Koc
db24e8e76b fix(e2e): validate bundled runtime health smoke 2026-06-07 07:21:34 +02:00
Vincent Koc
22466240a5 fix(e2e): run openwebui release chat smoke 2026-06-07 07:18:56 +02:00
Peter Steinberger
e8348c0dc8 refactor(matrix): store sync cache in sqlite
Move Matrix sync cache state into plugin SQLite storage, with startup and doctor migrations for readable legacy bot-storage.json files.\n\nVerification: focused Matrix and QA tests passed locally; focused touched-file oxlint and git diff --check passed; autoreview clean. CI failures are current main/unrelated: lint/type/madge/gateway-watch issues outside the Matrix diff.
2026-06-06 22:17:41 -07:00
Vincent Koc
690a04f81e fix(e2e): require gateway network health payload 2026-06-07 07:16:19 +02:00
Vincent Koc
ed7f259ce7 fix(e2e): bound onboard log polling reads 2026-06-07 07:14:37 +02:00
Vincent Koc
892dbb5ebb fix(dev): require gateway smoke health payload 2026-06-07 07:11:21 +02:00
Vincent Koc
7e7ea0fed1 fix(qa): validate rpc rtt smoke payloads 2026-06-07 07:09:01 +02:00
Vincent Koc
fa614d0907 fix(qa): tighten kitchen sink rpc proof 2026-06-07 07:03:43 +02:00
Vincent Koc
1d2bebbb41 fix(mac): scope restart app cleanup 2026-06-07 06:58:46 +02:00
Vincent Koc
ab645aca31 fix(test): require enabled live shard proof 2026-06-07 06:47:02 +02:00
Vincent Koc
1f0cf074cf fix(mac): scope build-and-run cleanup 2026-06-07 06:35:44 +02:00
Vincent Koc
03f1bf9a4d fix(qa-lab): fail missing parity tool results 2026-06-07 06:25:28 +02:00
Vincent Koc
0e1d2b3ef4 fix(qa-lab): require tool evidence in parity metrics 2026-06-07 06:20:16 +02:00
Vincent Koc
9b1c0dac68 fix(e2e): bound codex live assertion reads 2026-06-07 06:15:42 +02:00
Vincent Koc
cfaae7761d fix(e2e): fail lifecycle resource spikes 2026-06-07 06:11:15 +02:00
Vincent Koc
a372429a96 fix(e2e): cap docker harness resources 2026-06-07 06:06:12 +02:00
Vincent Koc
a737826320 fix(qa-lab): require live runtime tool evidence 2026-06-07 05:57:37 +02:00
Peter Steinberger
f4098e64e4 docs(config): document reasoning content compat flag 2026-06-07 04:52:37 +01:00
Krasimir Kralev
c45295cc33 fix(config): honor reasoning content compat flag
Allow custom OpenAI-compatible providers to opt into the existing DeepSeek assistant reasoning replay contract through persisted model compat config. Closes #89660.
2026-06-06 20:50:50 -07:00
Vincent Koc
e7b09fba37 fix(e2e): guard openwebui docker resources 2026-06-07 05:48:01 +02:00
wsyjh8
a1f1895b1b fix(config): allow thinkingLevelMap in persisted model schema
Allow persisted provider model entries to carry strict thinkingLevelMap values so Microsoft Foundry Entra onboarding can save generated reasoning model config. Closes #91011.
2026-06-06 20:44:15 -07:00
Mukunda Rao Katta
78135c3a29 fix(agents): suppress DeepSeek thinking for Foundry aliases
Fix Microsoft Foundry DeepSeek V4 alias providers by suppressing the DeepSeek-native `thinking` fallback and stripping DeepSeek replay fields on Foundry/non-native compat paths while preserving native DeepSeek and OpenRouter/Together reasoning controls.

Verified with focused embedded-runner tests, formatting/diff checks, autoreview, passing real-behavior proof gate, CI embedded-agent shard, issue #90520 reporter live A/B proof, and a local attempted gateway probe blocked before provider dispatch by model allowlist.

Known red CI lanes are unrelated to the touched files and documented in the pre-merge PR comment.

Fixes #90520
2026-06-06 20:37:52 -07:00
Vincent Koc
f94d3b1d8c fix(e2e): scan gateway readiness logs incrementally 2026-06-07 05:36:12 +02:00
Vincent Koc
586cf18b8d fix(e2e): bound docker log printing 2026-06-07 05:30:00 +02:00
Vincent Koc
c7a9dc1cc2 fix(qa-lab): link runtime tool output to planned calls 2026-06-07 05:27:34 +02:00
Omar Shahine
203dee9033 docs(imessage): clarify macOS library validation setup
Clarify that modern macOS iMessage private-API injection needs SIP disabled plus Library Validation relaxed, and document the verified macOS 26.5.1 Tahoe behavior without publishing unsupported AMFI boot-args guidance.\n\nVerification:\n- pnpm docs:list\n- git diff --check\n- pnpm check:docs\n\nThanks @omarshahine!
2026-06-06 20:20:57 -07:00
Vincent Koc
480c9a97b6 fix(e2e): tighten kitchen sink rpc assertions 2026-06-07 05:15:12 +02:00
Vincent Koc
cfeed10b01 fix(e2e): bound live plugin agent output 2026-06-07 05:09:31 +02:00
Peter Steinberger
3006b85db0 fix(openrouter): reconcile streamed generation cost
Fix OpenRouter streamed billing reconciliation by replacing the streamed estimated cost with the provider generation metadata total when the final streamed response includes a response id.

Verified with focused OpenRouter tests, full OpenRouter extension tests, formatting/diff checks, autoreview, official OpenRouter generation metadata docs, and a live OpenRouter API stream plus delayed generation lookup. Remaining CI failures were inspected and are unrelated existing failures outside the OpenRouter surface.

Fixes #68066
2026-06-06 20:06:57 -07:00
Peter Steinberger
a4236bd6fa refactor(memory-wiki): store source sync state in sqlite
* refactor(memory-wiki): store source sync state in sqlite

* fix(memory-wiki): satisfy source sync migration lint
2026-06-06 20:04:27 -07:00
Vincent Koc
ec55179504 fix(parallels): stream host command logs 2026-06-07 05:00:48 +02:00
Vincent Koc
416008dd10 fix(qa): reject empty gauntlet pass evidence 2026-06-07 04:56:32 +02:00
Vincent Koc
f55433bf31 fix(qa-lab): require runtime tool output evidence 2026-06-07 04:52:10 +02:00
Vincent Koc
f8f53de45a fix(e2e): bound kitchen sink log scans 2026-06-07 04:47:23 +02:00
Vincent Koc
6c9e7de04d fix(e2e): bound plugin assertion log reads 2026-06-07 04:42:15 +02:00
Vincent Koc
bcfd7164de fix(e2e): bound plugin update log assertions 2026-06-07 04:40:38 +02:00
Vincent Koc
e32707458d fix(e2e): bound secret configure pty output 2026-06-07 04:37:41 +02:00
Vincent Koc
7f7614276b fix(gateway): timeout stalled mcp bodies 2026-06-07 04:32:33 +02:00
Vincent Koc
c40d2c45bf fix(e2e): honor kitchen sink output caps 2026-06-07 04:26:55 +02:00
Vincent Koc
4911615e72 fix(e2e): bound credential payload chunks 2026-06-07 04:24:35 +02:00
Vincent Koc
326c4e0e35 fix(e2e): bound secret resolver stdin 2026-06-07 04:19:16 +02:00
Vincent Koc
06e8a74473 fix(apns): bound response body capture 2026-06-07 04:12:59 +02:00
Vincent Koc
801df108f0 fix(cli): bound exec approvals stdin 2026-06-07 04:08:13 +02:00
Vincent Koc
51b64b8198 fix(proxy): stream debug capture bodies 2026-06-07 04:06:26 +02:00
Vincent Koc
b804d20da7 refactor(test): share channel contract file discovery 2026-06-07 03:59:03 +02:00
Vincent Koc
2b7d7841d2 fix(test): bound prompt capture bodies 2026-06-07 03:55:48 +02:00
Peter Steinberger
0551af92b0 fix(gemini): accept empty grounding metadata
Fixes #88528.

Gemini web_search now accepts successful Google Search grounding responses that include candidate text and an empty `groundingMetadata` object without `groundingChunks`, returning wrapped content with `citations: []` instead of throwing `Gemini API error: malformed JSON response`.

Proof: live direct Gemini API reproduced the empty-grounding response shape; live OpenClaw provider failed before and succeeded after; `node scripts/run-vitest.mjs extensions/google/web-search-provider.test.ts`; `pnpm lint:web-search-provider-boundaries`; targeted oxfmt check; `git diff --check`; autoreview clean.

CI note: admin bypass used for unrelated failures in memory-core/device-pair/scripts, an existing core architecture cycle, and gateway-watch; PR diff touched only the two Gemini files.
2026-06-06 18:54:18 -07:00
Vincent Koc
344db67a00 fix(test): flush prompt probe gateway logs 2026-06-07 03:52:45 +02:00
Vincent Koc
08e3846470 fix(test): close otel receiver sockets 2026-06-07 03:49:48 +02:00
Vincent Koc
dbde27af4b fix(test): flush static artifact logs 2026-06-07 03:46:44 +02:00
alkor2000
2bd1c7b1c9 fix(vertex): route eu/us multi-region to .rep.googleapis.com host
Fixes #89891.

Route Google Vertex `eu` and `us` multi-region locations to the REP hosts used by `@google/genai`, and keep native Vertex endpoint trust exact to those two hosts.

Verification before merge:
- Live 1Password-backed GCP service-account probe: `eu-aiplatform.googleapis.com` returned Google HTML 404; `aiplatform.eu.rep.googleapis.com` reached Vertex JSON `PERMISSION_DENIED` with the same token.
- `node scripts/run-vitest.mjs src/agents/provider-attribution.test.ts extensions/google/vertex-multi-region-host.test.ts extensions/google/api.test.ts` passed.
- `git diff --check` passed.
- `autoreview --mode branch --base origin/main` clean.
- Real behavior proof passed on latest head.
- ClawSweeper re-review: ready for maintainer review, proof sufficient.

CI note: merged with maintainer approval despite red CI because the failures were unrelated to this PR and reproduced on untouched paths: `extensions/acpx/doctor-contract-api.ts`, `extensions/device-pair/notify.ts`, script lint, and existing architecture/gateway-watch checks.

Co-authored-by: alkor2000 <200923177@qq.com>
2026-06-06 18:39:49 -07:00
Peter Steinberger
3f5e001844 fix: store memory-core dreams state in sqlite (#91056) 2026-06-06 18:38:45 -07:00
Vincent Koc
1222f7a6bc fix(test): wait for cross os command logs 2026-06-07 03:35:54 +02:00
Vincent Koc
c4bc366a4c fix(test): clean up codex bind startup failures 2026-06-07 03:31:12 +02:00
Vincent Koc
50437d02c1 fix(test): clean up codex harness startup failures 2026-06-07 03:28:56 +02:00
Vincent Koc
61bb7d5523 fix(test): clean up cli backend live startup failures 2026-06-07 03:27:37 +02:00
Vincent Koc
3060ebf052 fix(test): restore tool search gateway e2e env 2026-06-07 03:24:24 +02:00
Vincent Koc
ecec1b9a59 fix(test): clean up acp live startup failures 2026-06-07 03:21:46 +02:00
Vincent Koc
7f885d5a39 fix(test): restore mcp code mode e2e env 2026-06-07 03:17:46 +02:00
Vincent Koc
3a6696951e fix(test): close rpc rtt websocket on failure 2026-06-07 03:14:57 +02:00
Vincent Koc
1d371eb5ae fix(qa): stop queued gateway rpc calls 2026-06-07 03:12:03 +02:00
Vincent Koc
e75d7cda8f fix(test): fail live gateway startup skips 2026-06-07 03:09:19 +02:00
Vincent Koc
919befbbb6 fix(qa): gate character eval on suite summary 2026-06-07 03:04:53 +02:00
Vincent Koc
d034e9698a fix(qa): trust parity scenario rows for metrics 2026-06-07 02:59:19 +02:00
Vincent Koc
51848de462 fix(test): gate package telegram on summary failures 2026-06-07 02:57:24 +02:00
Vincent Koc
e12141fa9f fix(qa): gate live transport exits on summaries 2026-06-07 02:53:50 +02:00
Vincent Koc
6d2566682a fix(qa): fail suite on summary scenario failures 2026-06-07 02:49:02 +02:00
Vincent Koc
154ee9fd23 fix(test): require source summary scenario evidence 2026-06-07 02:43:27 +02:00
Vincent Koc
0a37f797f2 fix(test): validate gauntlet qa summary counts 2026-06-07 02:41:39 +02:00
Vincent Koc
b45e3028e0 fix(test): require kitchen sink install rss proof 2026-06-07 02:38:58 +02:00
Matt H
983b65b0e0 feat(parallel): add free Parallel Search MCP as the zero-config default web_search provider (#90849)
* feat(parallel): add free Parallel Search MCP as the zero-config default web_search provider

Registers two Parallel web_search providers in the parallel plugin:
- parallel-free: keyless, always the free hosted Search MCP (search.parallel.ai/mcp);
  the zero-config default (autoDetectOrder 76) so web_search works with no key.
- parallel: the existing paid v1 REST API (requires PARALLEL_API_KEY).

Shared query/result normalization lives in parallel-search-normalize.ts (used by both
transports); a minimal Streamable-HTTP JSON-RPC client (parallel-mcp-search.runtime.ts)
backs the free path. UI brands the tool-call chip 'Parallel Web Search' on the free path
via a searchTransport marker; setup default mirrors runtime auto-detect.

* chore(parallel): register parallel-free in doctor legacy-web-search owners

parallel-free is a bundled web_search provider, so add it to the doctor's
exhaustive BUNDLED_LEGACY_WEB_SEARCH_OWNERS map (owned by the parallel plugin)
and the NON_MIGRATED set — it has no legacy tools.web.search.* shape, so this is
a no-op for migration, matching paid parallel/tavily. Keeps the registry
complete. (Spotted by diffing the earlier local WIP branch.)

* docs(parallel): restore concise frontmatter summary

* docs(parallel): clearer, professional copy; drop v1 REST jargon and UI-label claim

- Frame the two providers as Parallel Search (Free) vs paid Parallel Search;
  remove internal 'v1 REST API' wording.
- Remove conversational/overstated phrasing ('out of the box for everyone').
- Remove the 'labeled Parallel Web Search in the UI' claim (only renders in the
  Control UI, not the TUI). Scope the searchTransport code comment accordingly.

* revert(parallel): drop the "Parallel Web Search" tool-call branding

The label only rendered in the Control UI, never the TUI (a separate renderer
via src/agents/tool-display.ts). Extending it would put provider-specific
labeling into a shared/core display path, against the plugin-agnostic-core rule.

Reverts the Control-UI labelOverride wiring and removes the now-orphaned
searchTransport marker from the free provider's result. The result still carries
provider: "parallel-free".

* fix(parallel): cap free Search MCP session_id at its 100-char tools/list contract

The free parallel-free provider reused the paid ParallelSearchSchema, whose
session_id allows 1000 chars, but the live Search MCP tools/list schema caps
session_id at 100. Parameterize normalizeParallelSessionId(value, maxLength);
the free path passes 100 (paid keeps 1000) and advertises the tighter bound in
its own ParallelFreeSearchSchema. An over-limit caller id is dropped and a
fresh in-contract id is minted. Updates tests and docs accordingly.
2026-06-06 17:36:28 -07:00
Vincent Koc
8516f37563 fix(scripts): reject loose run env kill delay 2026-06-07 02:35:08 +02:00
Vincent Koc
e28dd6dd6e fix(test): reject loose startup memory limits 2026-06-07 02:33:15 +02:00
Vincent Koc
b32d769069 fix(test): require live shard file evidence 2026-06-07 02:30:18 +02:00
Vincent Koc
824d5d44cd fix(test): require advisory cli provider skips 2026-06-07 02:23:27 +02:00
Vincent Koc
e12136a7bb fix(test): require strict codex models proof 2026-06-07 02:21:23 +02:00
Vincent Koc
1ce84627e8 fix(test): reject loose release journey limits 2026-06-07 02:11:49 +02:00
Vincent Koc
4dae3b3071 fix(ci): require kova partial evidence 2026-06-07 02:10:23 +02:00
Vincent Koc
5378cf527e fix(test): require live model successes 2026-06-07 02:08:40 +02:00
Vincent Koc
ba46d00589 fix(test): fail secret proof signal exits 2026-06-07 02:06:21 +02:00
Vincent Koc
b6cbb4b861 fix(test): fail acp bind auth errors 2026-06-07 02:04:18 +02:00
Vincent Koc
cb5c513d58 fix(test): fail acp bind missing transcript 2026-06-07 02:02:12 +02:00
Vincent Koc
029e8f0153 fix(test): retry cli backend codex timeouts 2026-06-07 02:00:12 +02:00
Peter Steinberger
05c3325b0a fix: store acpx process state in sqlite
Move ACPX gateway identity and live process leases into SQLite-backed plugin state. Add doctor migration for legacy runtime state and preserve process cleanup identity checks across the storage move.
2026-06-06 16:49:47 -07:00
Vincent Koc
157da3621a fix(gateway): close slow direct response consumers 2026-06-07 01:45:47 +02:00
Vincent Koc
ee7cafafeb fix(test): retry codex harness live timeouts 2026-06-07 01:40:44 +02:00
Vincent Koc
172c3f6064 fix(gateway): classify mcp json-rpc failures 2026-06-07 01:35:40 +02:00
Vincent Koc
46e12e7aff fix(gateway): cap mcp loopback tool cache 2026-06-07 01:27:55 +02:00
Vincent Koc
5f7cfd6451 fix(test): require perf budget source 2026-06-07 01:24:10 +02:00
Vincent Koc
bb0384e884 fix(test): require gateway smoke history evidence 2026-06-07 01:21:49 +02:00
Vincent Koc
e74d98bd65 fix(ci): fail release qa verifier closed 2026-06-07 01:17:58 +02:00
Vincent Koc
2accfeedc7 fix(test): require resource evidence in perf scripts 2026-06-07 01:11:24 +02:00
Peter Steinberger
ba447d5afc fix: store device-pair notify state in sqlite
Move Device Pair notify subscribers and delivery dedupe state into SQLite-backed plugin state. Add doctor migration for legacy notify subscribers; request-id delivery state is cache-only and rebuilt.
2026-06-06 16:10:16 -07:00
Vincent Koc
76435679f5 fix(test): require live runtime parity evidence 2026-06-07 01:05:44 +02:00
Vincent Koc
f5c345b3fe fix(test): require native live shard proof 2026-06-07 00:57:51 +02:00
Vincent Koc
369793d9ab fix(test): require telegram rtt samples 2026-06-07 00:54:13 +02:00
Vincent Koc
a9706ddef2 fix(ci): require live kova evidence 2026-06-07 00:48:54 +02:00
Vincent Koc
9564ee25b2 fix(test): reject failed agent reply markers 2026-06-07 00:45:00 +02:00
Vincent Koc
c3dca12274 fix(test): ignore error reply payload markers 2026-06-07 00:36:02 +02:00
Vincent Koc
441a73c492 fix(ci): require docker e2e summaries 2026-06-07 00:35:03 +02:00
Vincent Koc
c575b9782e fix(test): require group report evidence 2026-06-07 00:31:52 +02:00
Vincent Koc
8ac2ffde2a fix(ci): require source performance artifacts 2026-06-07 00:27:48 +02:00
Vincent Koc
d5ef040e65 fix(test): require live media providers 2026-06-07 00:25:05 +02:00
Vincent Koc
84bcae95a0 fix(ci): fail closed on partial kova reports 2026-06-07 00:17:20 +02:00
Vincent Koc
84275d6608 fix(test): forward kitchen rpc env knobs 2026-06-07 00:13:02 +02:00
Vincent Koc
2876906da5 fix(ci): require kova resource metrics 2026-06-07 00:10:37 +02:00
Vincent Koc
368f687735 fix(test): require tool search execution proof 2026-06-07 00:07:07 +02:00
Vincent Koc
1b6bc2ef7d fix(test): require native web search proof 2026-06-07 00:02:29 +02:00
Vincent Koc
2102166f86 fix(test): normalize docker stats heartbeat 2026-06-06 23:59:33 +02:00
Vincent Koc
f7b6ee48fd fix(test): require chat tool-call-only proof 2026-06-06 23:53:08 +02:00
Vincent Koc
a5fcab9ff8 fix(test): reject malformed plugin uninstall config 2026-06-06 23:50:30 +02:00
Vincent Koc
39e27c8276 fix(test): require lifecycle uninstall config proof 2026-06-06 23:46:46 +02:00
Vincent Koc
a78234a5d3 fix(test): validate kitchen sink rpc outputs 2026-06-06 23:45:00 +02:00
Vincent Koc
1f6eabb09f fix(test): require live tool transcript evidence 2026-06-06 23:41:52 +02:00
Shakker
caae4c9109 test: manage update startup env 2026-06-06 22:10:40 +01:00
Shakker
c6bbb55fb5 fix: scope llm api key env 2026-06-06 22:10:01 +01:00
Vincent Koc
7f4ddf62ea fix(test): validate auth profile env refs 2026-06-06 23:09:43 +02:00
Shakker
f6b6cf6d6c test: manage chat reply media state 2026-06-06 22:08:30 +01:00
joshp123
5d5bc5c84d Revert "Fix talk config secret resolution"
This reverts commit 4500f02fe6.
2026-06-06 23:07:03 +02:00
Shakker
de4ef48323 fix: manage skill autocapture state 2026-06-06 22:04:17 +01:00
Shakker
133585d97f test: wrap install download state fixture 2026-06-06 22:03:49 +01:00
Vincent Koc
a33077d9c6 fix(test): reject missing numeric flag values 2026-06-06 23:01:21 +02:00
Shakker
78f67fa85f fix: manage diagnostic session state 2026-06-06 21:59:54 +01:00
Shakker
27406dc6fb test: scope logging config env 2026-06-06 21:59:24 +01:00
joshp123
4500f02fe6 Fix talk config secret resolution 2026-06-06 22:58:55 +02:00
Shakker
86792c0319 fix: manage gateway skills state fixtures 2026-06-06 21:58:04 +01:00
Vincent Koc
a3e969101c fix(perf): reject missing cpu scenario values 2026-06-06 22:57:19 +02:00
Vincent Koc
5b88ddfb99 fix(perf): reject ambiguous changed bench args 2026-06-06 22:55:56 +02:00
Vincent Koc
c5f40275f5 fix(rpc): reject missing rtt option values 2026-06-06 22:54:07 +02:00
Shakker
4a46da7499 test: scope subagent resume state env 2026-06-06 21:53:29 +01:00
Vincent Koc
ba5fa16907 fix(perf): require group report numeric values 2026-06-06 22:52:29 +02:00
Shakker
ca40b3cdc6 test: manage workshop state fixtures 2026-06-06 21:51:01 +01:00
Vincent Koc
5ecfee04f8 fix(ci): reject ambiguous run timing args 2026-06-06 22:50:49 +02:00
Vincent Koc
125b0fc279 fix(ci): reject unknown kova summary flags 2026-06-06 22:49:29 +02:00
Shakker
aa9c5209fc test: restore ssh sandbox env snapshots 2026-06-06 21:49:10 +01:00
Vincent Koc
a470daad12 fix(test): require docker timing limit values 2026-06-06 22:48:19 +02:00
Shakker
f86dd6c0af test: scope session read media env 2026-06-06 21:47:39 +01:00
Vincent Koc
fbcd27e258 fix(perf): require source summary path values 2026-06-06 22:47:00 +02:00
Vincent Koc
8ff0a20744 fix(docs): require sync provenance values 2026-06-06 22:43:04 +02:00
Vincent Koc
bdc317f4a6 fix(release): require validation dispatch values 2026-06-06 22:39:58 +02:00
Vincent Koc
1c0d7c8a57 fix(package): require docker package option values 2026-06-06 22:36:47 +02:00
Vincent Koc
b6b50d893c fix(security): require docker attestation platform values 2026-06-06 22:33:56 +02:00
Vincent Koc
b5b73bd362 fix(plugin): require package manifest run target 2026-06-06 22:31:42 +02:00
Vincent Koc
3f3b757e50 fix(plugin): require runtime build package targets 2026-06-06 22:29:12 +02:00
Vincent Koc
bd7f65d445 fix(package): require package root values 2026-06-06 22:26:53 +02:00
Vincent Koc
b8f5950fe3 fix(ci): reject missing merge diff refs 2026-06-06 22:25:06 +02:00
Vincent Koc
f47f32db46 fix(docs): reject missing glossary diff refs 2026-06-06 22:23:09 +02:00
Vincent Koc
1549172816 fix(ci): reject missing changed scope refs 2026-06-06 22:21:14 +02:00
Vincent Koc
9f5fc45593 fix(security): require audit severity values 2026-06-06 22:19:18 +02:00
Vincent Koc
bedb3e61c6 fix(report): require ownership markdown path 2026-06-06 22:18:03 +02:00
Vincent Koc
c2af0475fe fix(test): require vitest profile output dir 2026-06-06 22:16:53 +02:00
Vincent Koc
66a1cfb7be fix(ci): reject missing release metadata refs 2026-06-06 22:15:34 +02:00
Vincent Koc
457c76964d fix(test): reject missing report artifact paths 2026-06-06 22:13:37 +02:00
Vincent Koc
4b19f820e1 fix(test): reject missing dependency evidence values 2026-06-06 22:12:08 +02:00
Vincent Koc
4bce355318 fix(test): reject missing group report values 2026-06-06 22:09:33 +02:00
Vincent Koc
53044e8717 fix(report): reject missing dependency report paths 2026-06-06 22:07:31 +02:00
Vincent Koc
98c45aa8b5 fix(test): reject missing env mutation roots 2026-06-06 22:05:23 +02:00
Vincent Koc
71f8b9c41e fix(rtt): reject missing path option values 2026-06-06 22:03:50 +02:00
Vincent Koc
59a8137b04 fix(docs): reject missing mdx report paths 2026-06-06 22:02:20 +02:00
Vincent Koc
a791636160 fix(test): reject missing startup memory paths 2026-06-06 22:00:38 +02:00
Vincent Koc
e3d402427c fix(test): reject zero startup RSS samples 2026-06-06 21:59:09 +02:00
1697 changed files with 149055 additions and 14172 deletions

View File

@@ -2,19 +2,14 @@
What problem does this PR solve?
Why does this matter now?
What is the intended outcome?
What is intentionally out of scope?
What does success look like?
What should reviewers focus on?
<details>
@@ -75,13 +70,10 @@ Be mindful of private information like IP addresses, API keys, phone numbers, no
Which commands did you run?
What regression coverage was added or updated?
What failed before this fix, if known?
If no test was added, why not?
<details>
@@ -95,16 +87,12 @@ List focused commands, not every incidental check. CI is useful support, but ext
Did user-visible behavior change? (`Yes/No`)
Did config, environment, or migration behavior change? (`Yes/No`)
Did security, auth, secrets, network, or tool execution behavior change? (`Yes/No`)
What is the highest-risk area?
How is that risk mitigated?
<details>
@@ -118,10 +106,8 @@ Use this for author judgment that is not obvious from the diff. ClawSweeper can
What is the next action?
What is still waiting on author, maintainer, CI, or external proof?
Which bot or reviewer comments were addressed?
<details>

View File

@@ -722,7 +722,7 @@ jobs:
if [ "$RUN_GATEWAY_WATCH" = "true" ]; then
start_check "gateway-watch" \
node scripts/check-gateway-watch-regression.mjs --skip-build --ready-timeout-ms 5000
node scripts/check-gateway-watch-regression.mjs --skip-build
fi
for index in "${!pids[@]}"; do

View File

@@ -1139,7 +1139,16 @@ jobs:
summary:
name: Verify full validation
needs: [resolve_target, docker_runtime_assets_preflight, normal_ci, plugin_prerelease, release_checks, npm_telegram, performance]
needs:
[
resolve_target,
docker_runtime_assets_preflight,
normal_ci,
plugin_prerelease,
release_checks,
npm_telegram,
performance,
]
if: always()
runs-on: ubuntu-24.04
timeout-minutes: 5

View File

@@ -887,7 +887,7 @@ jobs:
summary=".artifacts/docker-tests/release-${DOCKER_E2E_CHUNK}/summary.json"
if [[ ! -f "$summary" ]]; then
echo "Docker chunk summary missing: \`$summary\`" >> "$GITHUB_STEP_SUMMARY"
exit 0
exit 1
fi
node .release-harness/scripts/docker-e2e.mjs summary "$summary" "Docker E2E chunk: ${DOCKER_E2E_CHUNK:-unknown}" >> "$GITHUB_STEP_SUMMARY"
@@ -897,7 +897,7 @@ jobs:
with:
name: docker-e2e-${{ matrix.chunk_id }}
path: .artifacts/docker-tests/
if-no-files-found: ignore
if-no-files-found: error
plan_docker_lane_groups:
needs: validate_selected_ref
@@ -1147,7 +1147,7 @@ jobs:
summary=".artifacts/docker-tests/targeted-${{ steps.plan.outputs.artifact_suffix }}/summary.json"
if [[ ! -f "$summary" ]]; then
echo "Docker targeted summary missing: \`$summary\`" >> "$GITHUB_STEP_SUMMARY"
exit 0
exit 1
fi
node .release-harness/scripts/docker-e2e.mjs summary "$summary" "Docker E2E targeted lanes" >> "$GITHUB_STEP_SUMMARY"
@@ -1157,7 +1157,7 @@ jobs:
with:
name: docker-e2e-${{ steps.plan.outputs.artifact_suffix }}
path: .artifacts/docker-tests/
if-no-files-found: ignore
if-no-files-found: error
validate_docker_openwebui:
needs: [validate_selected_ref, prepare_docker_e2e_image]
@@ -1274,7 +1274,7 @@ jobs:
summary=".artifacts/docker-tests/release-openwebui/summary.json"
if [[ ! -f "$summary" ]]; then
echo "Docker Open WebUI summary missing: \`$summary\`" >> "$GITHUB_STEP_SUMMARY"
exit 0
exit 1
fi
node .release-harness/scripts/docker-e2e.mjs summary "$summary" "Docker E2E chunk: openwebui" >> "$GITHUB_STEP_SUMMARY"
@@ -1284,7 +1284,7 @@ jobs:
with:
name: docker-e2e-openwebui
path: .artifacts/docker-tests/
if-no-files-found: ignore
if-no-files-found: error
prepare_docker_e2e_image:
needs: validate_selected_ref
@@ -1918,7 +1918,7 @@ jobs:
profiles: stable full
- suite_id: native-live-src-gateway-core
label: Native live gateway core
command: node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-core
command: OPENCLAW_LIVE_CODEX_HARNESS=1 OPENCLAW_LIVE_CODEX_HARNESS_AUTH=api-key node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-core
timeout_minutes: 60
profile_env_only: false
profiles: beta minimum stable full
@@ -2038,7 +2038,7 @@ jobs:
profiles: full
- suite_id: native-live-src-gateway-backends
label: Native live gateway backends
command: node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-backends
command: OPENCLAW_LIVE_CODEX_HARNESS=1 OPENCLAW_LIVE_CODEX_HARNESS_AUTH=api-key node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-backends
timeout_minutes: 60
profile_env_only: false
profiles: stable full

View File

@@ -244,8 +244,8 @@ jobs:
run: |
set -euo pipefail
if [[ -z "${OPENAI_API_KEY:-}" ]]; then
echo "OPENAI_API_KEY is not configured; live GPT 5.5 lane will be skipped." >> "$GITHUB_STEP_SUMMARY"
exit 0
echo "OPENAI_API_KEY is not configured; live GPT 5.5 lane cannot run without live evidence." >> "$GITHUB_STEP_SUMMARY"
exit 1
fi
kova setup --ci --json
kova setup --non-interactive --auth env-only --provider openai --env-var OPENAI_API_KEY --json
@@ -262,11 +262,6 @@ jobs:
set -euo pipefail
mkdir -p "$REPORT_DIR" "$BUNDLE_DIR" "$SUMMARY_DIR"
if [[ "$MATRIX_LIVE" == "true" && -z "${OPENAI_API_KEY:-}" ]]; then
echo "skipped=true" >> "$GITHUB_OUTPUT"
exit 0
fi
repeat="$REQUESTED_REPEAT"
if [[ "$MATRIX_REPEAT" != "input" ]]; then
repeat="$MATRIX_REPEAT"
@@ -360,6 +355,28 @@ jobs:
exit "$effective_status"
fi
- name: Validate Kova evidence
if: ${{ always() && steps.lane.outputs.run == 'true' }}
shell: bash
run: |
set -euo pipefail
missing=0
if ! find "$REPORT_DIR" -maxdepth 1 -type f -name '*.json' -size +0c -print -quit | grep -q .; then
echo "::error::Kova JSON report is missing for ${LANE_ID}."
missing=1
fi
if [[ ! -s "$BUNDLE_DIR/bundle.json" ]]; then
echo "::error::Kova bundle evidence is missing for ${LANE_ID}."
missing=1
fi
if [[ ! -s "$SUMMARY_DIR/${LANE_ID}.md" ]]; then
echo "::error::Kova summary evidence is missing for ${LANE_ID}."
missing=1
fi
if [[ "$missing" != "0" ]]; then
exit 1
fi
- name: Fetch previous source performance baseline
if: ${{ steps.lane.outputs.run == 'true' && matrix.lane == 'mock-provider' && steps.clawgrit.outputs.present == 'true' }}
env:
@@ -530,7 +547,7 @@ jobs:
.artifacts/kova/bundles/${{ matrix.lane }}
.artifacts/kova/summaries/${{ matrix.lane }}.md
.artifacts/openclaw-performance/source/${{ matrix.lane }}
if-no-files-found: ignore
if-no-files-found: error
retention-days: ${{ matrix.deep_profile == 'true' && 14 || 30 }}
- name: Prepare clawgrit reports checkout

View File

@@ -346,6 +346,7 @@ jobs:
discord_selected=false
whatsapp_selected=false
slack_selected=false
disabled_required_lanes=()
IFS=', ' read -r -a filter_tokens <<< "$filter"
for token in "${filter_tokens[@]}"; do
@@ -361,6 +362,9 @@ jobs:
discord_selected="$qa_live_discord_ci_enabled"
whatsapp_selected="$qa_live_whatsapp_ci_enabled"
slack_selected="$qa_live_slack_ci_enabled"
[[ "$qa_live_discord_ci_enabled" == "true" ]] || disabled_required_lanes+=("qa-live-discord")
[[ "$qa_live_whatsapp_ci_enabled" == "true" ]] || disabled_required_lanes+=("qa-live-whatsapp")
[[ "$qa_live_slack_ci_enabled" == "true" ]] || disabled_required_lanes+=("qa-live-slack")
;;
qa-live-non-slack|qa-non-slack|non-slack|no-slack|without-slack)
qa_filter_seen=true
@@ -368,6 +372,8 @@ jobs:
telegram_selected=true
discord_selected="$qa_live_discord_ci_enabled"
whatsapp_selected="$qa_live_whatsapp_ci_enabled"
[[ "$qa_live_discord_ci_enabled" == "true" ]] || disabled_required_lanes+=("qa-live-discord")
[[ "$qa_live_whatsapp_ci_enabled" == "true" ]] || disabled_required_lanes+=("qa-live-whatsapp")
;;
qa-live-matrix|qa-matrix|matrix)
qa_filter_seen=true
@@ -380,18 +386,27 @@ jobs:
qa-live-discord|qa-discord|discord)
qa_filter_seen=true
discord_selected="$qa_live_discord_ci_enabled"
[[ "$qa_live_discord_ci_enabled" == "true" ]] || disabled_required_lanes+=("qa-live-discord")
;;
qa-live-whatsapp|qa-whatsapp|whatsapp)
qa_filter_seen=true
whatsapp_selected="$qa_live_whatsapp_ci_enabled"
[[ "$qa_live_whatsapp_ci_enabled" == "true" ]] || disabled_required_lanes+=("qa-live-whatsapp")
;;
qa-live-slack|qa-slack|slack)
qa_filter_seen=true
slack_selected="$qa_live_slack_ci_enabled"
[[ "$qa_live_slack_ci_enabled" == "true" ]] || disabled_required_lanes+=("qa-live-slack")
;;
esac
done
if [[ "${#disabled_required_lanes[@]}" -gt 0 ]]; then
echo "live_suite_filter explicitly requested disabled QA live lane(s): ${disabled_required_lanes[*]}" >&2
echo "Enable the matching OPENCLAW_RELEASE_QA_*_LIVE_CI_ENABLED repo variable or remove the lane from live_suite_filter." >&2
exit 1
fi
if [[ "$qa_filter_seen" == "true" ]]; then
qa_live_matrix_enabled="$matrix_selected"
qa_live_telegram_enabled="$telegram_selected"
@@ -1481,7 +1496,7 @@ jobs:
qa_live_discord_release_checks:
name: Run QA Lab live Discord lane
needs: [resolve_target]
if: contains(fromJSON('["all","qa","qa-live"]'), needs.resolve_target.outputs.rerun_group) && needs.resolve_target.outputs.qa_live_discord_enabled == 'true' && vars.OPENCLAW_RELEASE_QA_DISCORD_LIVE_CI_ENABLED == 'true'
if: contains(fromJSON('["all","qa","qa-live"]'), needs.resolve_target.outputs.rerun_group) && needs.resolve_target.outputs.qa_live_discord_enabled == 'true'
continue-on-error: true
runs-on: ubuntu-24.04
timeout-minutes: 60
@@ -1621,7 +1636,7 @@ jobs:
qa_live_whatsapp_release_checks:
name: Run QA Lab live WhatsApp lane
needs: [resolve_target]
if: contains(fromJSON('["all","qa","qa-live"]'), needs.resolve_target.outputs.rerun_group) && needs.resolve_target.outputs.qa_live_whatsapp_enabled == 'true' && vars.OPENCLAW_RELEASE_QA_WHATSAPP_LIVE_CI_ENABLED == 'true'
if: contains(fromJSON('["all","qa","qa-live"]'), needs.resolve_target.outputs.rerun_group) && needs.resolve_target.outputs.qa_live_whatsapp_enabled == 'true'
continue-on-error: true
runs-on: ubuntu-24.04
timeout-minutes: 60
@@ -1764,7 +1779,7 @@ jobs:
qa_live_slack_release_checks:
name: Run QA Lab live Slack lane
needs: [resolve_target]
if: contains(fromJSON('["all","qa","qa-live"]'), needs.resolve_target.outputs.rerun_group) && needs.resolve_target.outputs.qa_live_slack_enabled == 'true' && vars.OPENCLAW_RELEASE_QA_SLACK_LIVE_CI_ENABLED == 'true'
if: contains(fromJSON('["all","qa","qa-live"]'), needs.resolve_target.outputs.rerun_group) && needs.resolve_target.outputs.qa_live_slack_enabled == 'true'
continue-on-error: true
runs-on: ubuntu-24.04
timeout-minutes: 60
@@ -2022,10 +2037,6 @@ jobs:
;;
esac
fi
if [[ "$name" == qa_* ]]; then
echo "::warning::${name} ended with ${result}; QA release-check lanes are advisory and do not block release validation."
continue
fi
echo "::error::${name} ended with ${result}"
failed=1
fi

View File

@@ -40,6 +40,11 @@ Docs: https://docs.openclaw.ai
- Security/config/tooling: guard MCP HTTP redirects, protect global agent config defaults, and keep release/test/tooling proof failures bounded and explicit. (#89732, #90145)
- Channels: WhatsApp restarts when per-account config changes, bounds background startup waits, closes failed sockets, and preserves reconnect behavior; Mattermost slash commands keep their state on `globalThis`; Feishu streaming cards preserve full merged content; voice-call tracks Twilio streams after connect; ClickClack reply tools respect `toolsAllow`. (#87951, #87965, #90486, #68113, #90534, #90181, #90607, #89500) Thanks @MukundaKatta, @mcaxtr, @infoanton, @mushuiyu886, and @sahibzada-allahyar.
- Release/CI/E2E: main CI guard drift, PR merge diff scoping, live Docker credential staging, base-image qualification, installer Docker classification, Playwright dependency install recovery, API-key auth for Codex live Docker lanes, Parallels option terminators, and JSON-mode progress handling are tighter so release proof fails cleaner. (#90532, #90287, #90058) Thanks @RomneyDa, @hxy91819, and @mrunalp.
- Release/CI/E2E: Docker E2E and live Docker harness runs now apply default memory, CPU, and process ceilings while preserving explicit per-lane overrides.
- Release/CI/E2E: plugin lifecycle matrix resource sampling now fails phases that exceed RSS, wall-clock, or CPU ceilings instead of only logging the measurements.
- Release/CI/E2E: Codex npm plugin live assertions now cap transcript discovery and diagnostic log reads so failure proof stays bounded.
- Tests/state isolation: QA Lab valid-tool-call metrics now require runtime tool-call evidence when runtime parity data is available instead of counting tool-backed scenario pass status alone.
- Tests/state isolation: QA Lab runtime parity now fails planned-only tool-call rows without matching tool results instead of treating matching mock plans as real tool evidence.
- Tests/state isolation: provider, media, auth, cron, task, session, sandbox, Gateway, and Codex timeout fixtures now scope more home/state/env data per test, reducing cross-test leakage and making release validation failures less noisy. (#90027, #89974)
## 2026.6.2

View File

@@ -1,8 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission
@@ -50,7 +51,7 @@
<service
android:name=".NodeForegroundService"
android:exported="false"
android:foregroundServiceType="dataSync|microphone" />
android:foregroundServiceType="connectedDevice|microphone" />
<service
android:name=".node.DeviceNotificationListenerService"
android:label="@string/app_name"

View File

@@ -0,0 +1,25 @@
package ai.openclaw.app
/** User-selectable app theme mode for Android appearance settings. */
enum class AppearanceThemeMode(
val rawValue: String,
val displayLabel: String,
) {
System(rawValue = "system", displayLabel = "System"),
Dark(rawValue = "dark", displayLabel = "Dark"),
Light(rawValue = "light", displayLabel = "Light"),
;
fun isDark(systemDark: Boolean): Boolean =
when (this) {
System -> systemDark
Dark -> true
Light -> false
}
companion object {
fun fromRawValue(value: String?): AppearanceThemeMode = entries.firstOrNull { it.rawValue == value?.trim()?.lowercase() } ?: Dark
fun fromDisplayLabel(label: String): AppearanceThemeMode = entries.firstOrNull { it.displayLabel.equals(label.trim(), ignoreCase = true) } ?: Dark
}
}

View File

@@ -14,6 +14,7 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -64,8 +65,16 @@ class MainActivity : ComponentActivity() {
activeViewModel = readyViewModel
}
OpenClawTheme {
activeViewModel?.let { RootScreen(viewModel = it) } ?: StartupSurface()
val currentViewModel = activeViewModel
if (currentViewModel == null) {
OpenClawTheme {
StartupSurface()
}
} else {
val appearanceThemeMode by currentViewModel.appearanceThemeMode.collectAsState()
OpenClawTheme(themeMode = appearanceThemeMode) {
RootScreen(viewModel = currentViewModel)
}
}
}
}

View File

@@ -172,6 +172,7 @@ class MainViewModel(
val canvasDebugStatusEnabled: StateFlow<Boolean> = prefs.canvasDebugStatusEnabled
val installedAppsSharingEnabled: StateFlow<Boolean> = prefs.installedAppsSharingEnabled
val speakerEnabled: StateFlow<Boolean> = prefs.speakerEnabled
val appearanceThemeMode: StateFlow<AppearanceThemeMode> = prefs.appearanceThemeMode
val voiceCaptureMode: StateFlow<VoiceCaptureMode> = runtimeState(initial = VoiceCaptureMode.Off) { it.voiceCaptureMode }
val micEnabled: StateFlow<Boolean> = runtimeState(initial = false) { it.micEnabled }
@@ -440,6 +441,10 @@ class MainViewModel(
ensureRuntime().setSpeakerEnabled(enabled)
}
fun setAppearanceThemeMode(mode: AppearanceThemeMode) {
prefs.setAppearanceThemeMode(mode)
}
fun refreshGatewayConnection() {
viewModelScope.launch(Dispatchers.Default) {
ensureRuntime().refreshGatewayConnection()

View File

@@ -23,7 +23,6 @@ import kotlinx.coroutines.launch
class NodeForegroundService : Service() {
private val scope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
private var notificationJob: Job? = null
private var didStartForeground = false
private var voiceCaptureMode = VoiceCaptureMode.Off
override fun onCreate() {
@@ -183,13 +182,7 @@ class NodeForegroundService : Service() {
private fun startForegroundWithTypes(notification: Notification) {
val serviceTypes = foregroundServiceTypesForVoiceMode(voiceCaptureMode)
if (didStartForeground) {
// Re-issue startForeground when Talk mode toggles so Android sees the microphone service type.
ServiceCompat.startForeground(this, NOTIFICATION_ID, notification, serviceTypes)
return
}
ServiceCompat.startForeground(this, NOTIFICATION_ID, notification, serviceTypes)
didStartForeground = true
}
companion object {
@@ -200,19 +193,16 @@ class NodeForegroundService : Service() {
private const val ACTION_SET_VOICE_CAPTURE_MODE = "ai.openclaw.app.action.SET_VOICE_CAPTURE_MODE"
private const val EXTRA_VOICE_CAPTURE_MODE = "ai.openclaw.app.extra.VOICE_CAPTURE_MODE"
/** Starts the persistent node foreground service from UI lifecycle code. */
fun start(context: Context) {
val intent = Intent(context, NodeForegroundService::class.java)
context.startForegroundService(intent)
}
/** Requests disconnect through the service action path so notification actions and UI share behavior. */
fun stop(context: Context) {
val intent = Intent(context, NodeForegroundService::class.java).setAction(ACTION_STOP)
context.startService(intent)
}
/** Updates Android's foreground-service type before voice capture mode changes require microphone access. */
fun setVoiceCaptureMode(
context: Context,
mode: VoiceCaptureMode,
@@ -231,11 +221,8 @@ class NodeForegroundService : Service() {
}
}
/**
* Foreground-service type mask required by Android for the current voice capture mode.
*/
internal fun foregroundServiceTypesForVoiceMode(mode: VoiceCaptureMode): Int {
val base = ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
val base = ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
return if (mode == VoiceCaptureMode.TalkMode) {
base or ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
} else {
@@ -243,9 +230,6 @@ internal fun foregroundServiceTypesForVoiceMode(mode: VoiceCaptureMode): Int {
}
}
/**
* Compact notification suffix for voice state; kept pure for service-notification tests.
*/
internal fun voiceNotificationSuffix(
mode: VoiceCaptureMode,
manualMicEnabled: Boolean,

View File

@@ -42,6 +42,7 @@ class SecurePrefs(
private const val notificationsForwardingSessionKeyKey = "notifications.forwarding.sessionKey"
private const val installedAppsSharingEnabledKey = "device.apps.sharing.enabled"
private const val voiceMicEnabledKey = "voice.micEnabled"
private const val appearanceThemeModeKey = "appearance.themeMode"
}
private val appContext = context.applicationContext
@@ -181,6 +182,10 @@ class SecurePrefs(
private val _speakerEnabled = MutableStateFlow(plainPrefs.getBoolean("voice.speakerEnabled", true))
val speakerEnabled: StateFlow<Boolean> = _speakerEnabled
private val _appearanceThemeMode =
MutableStateFlow(AppearanceThemeMode.fromRawValue(plainPrefs.getString(appearanceThemeModeKey, null)))
val appearanceThemeMode: StateFlow<AppearanceThemeMode> = _appearanceThemeMode
fun setLastDiscoveredStableId(value: String) {
val trimmed = value.trim()
plainPrefs.edit { putString("gateway.lastDiscoveredStableID", trimmed) }
@@ -525,6 +530,11 @@ class SecurePrefs(
_speakerEnabled.value = value
}
fun setAppearanceThemeMode(mode: AppearanceThemeMode) {
plainPrefs.edit { putString(appearanceThemeModeKey, mode.rawValue) }
_appearanceThemeMode.value = mode
}
private fun loadNotificationForwardingPackages(): Set<String> {
val raw = plainPrefs.getString(notificationsForwardingPackagesKey, null)?.trim()
if (raw.isNullOrEmpty()) {

View File

@@ -41,27 +41,27 @@ internal data class MobileColors(
internal fun lightMobileColors() =
MobileColors(
surface = Color(0xFFF6F7FA),
surfaceStrong = Color(0xFFECEEF3),
surface = Color(0xFFFAFBFC),
surfaceStrong = Color(0xFFEFF3F8),
cardSurface = Color(0xFFFFFFFF),
border = Color(0xFFE5E7EC),
borderStrong = Color(0xFFD6DAE2),
text = Color(0xFF17181C),
textSecondary = Color(0xFF5D6472),
textTertiary = Color(0xFF99A0AE),
accent = Color(0xFF1D5DD8),
accentSoft = Color(0xFFECF3FF),
accentBorderStrong = Color(0xFF184DAF),
success = Color(0xFF2F8C5A),
successSoft = Color(0xFFEEF9F3),
warning = Color(0xFFC8841A),
warningSoft = Color(0xFFFFF8EC),
danger = Color(0xFFD04B4B),
dangerSoft = Color(0xFFFFF2F2),
codeBg = Color(0xFF15171B),
codeText = Color(0xFFE8EAEE),
codeBorder = Color(0xFF2B2E35),
codeAccent = Color(0xFF3FC97A),
border = Color(0xFFDDE3EC),
borderStrong = Color(0xFFC7D0DC),
text = Color(0xFF16181D),
textSecondary = Color(0xFF505B6A),
textTertiary = Color(0xFF8E98A7),
accent = Color(0xFF1B5ACB),
accentSoft = Color(0xFFEAF2FF),
accentBorderStrong = Color(0xFF174CA9),
success = Color(0xFF287F52),
successSoft = Color(0xFFEAF7F0),
warning = Color(0xFFAF7418),
warningSoft = Color(0xFFFFF4DF),
danger = Color(0xFFC94343),
dangerSoft = Color(0xFFFFECEC),
codeBg = Color(0xFFEFF3F8),
codeText = Color(0xFF172033),
codeBorder = Color(0xFFD7DDE7),
codeAccent = Color(0xFF287F52),
chipBorderConnected = Color(0xFFCFEBD8),
chipBorderConnecting = Color(0xFFD5E2FA),
chipBorderWarning = Color(0xFFEED8B8),

View File

@@ -34,6 +34,7 @@ import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -130,7 +131,9 @@ fun OnboardingFlow(
viewModel: MainViewModel,
modifier: Modifier = Modifier,
) {
ClawDesignTheme {
val appearanceThemeMode by viewModel.appearanceThemeMode.collectAsState()
val onboardingDark = appearanceThemeMode.isDark(systemDark = isSystemInDarkTheme())
ClawDesignTheme(dark = onboardingDark) {
val context = LocalContext.current
val statusText by viewModel.statusText.collectAsState()
val gatewayConnectionProblem by viewModel.gatewayConnectionProblem.collectAsState()
@@ -159,6 +162,8 @@ fun OnboardingFlow(
var connectAttemptStartedAtMs by rememberSaveable { mutableLongStateOf(0L) }
var recoveryNowMs by remember { mutableLongStateOf(SystemClock.elapsedRealtime()) }
OpenClawSystemBarAppearance(lightAppearance = !onboardingDark && step != OnboardingStep.Welcome)
val qrScannerOptions =
remember {
GmsBarcodeScannerOptions
@@ -223,10 +228,12 @@ fun OnboardingFlow(
when (step) {
OnboardingStep.Welcome ->
WelcomeScreen(
modifier = modifier,
onConnect = { step = OnboardingStep.Gateway },
)
ClawDesignTheme(dark = true) {
WelcomeScreen(
modifier = modifier,
onConnect = { step = OnboardingStep.Gateway },
)
}
OnboardingStep.Gateway ->
GatewaySetupScreen(
modifier = modifier,

View File

@@ -1,5 +1,6 @@
package ai.openclaw.app.ui
import ai.openclaw.app.AppearanceThemeMode
import android.app.Activity
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
@@ -8,34 +9,51 @@ import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val LocalOpenClawDarkTheme = staticCompositionLocalOf { true }
/**
* App theme wrapper that installs dynamic Material colors and legacy mobile color tokens.
*/
@Composable
fun OpenClawTheme(content: @Composable () -> Unit) {
fun OpenClawTheme(
themeMode: AppearanceThemeMode = AppearanceThemeMode.Dark,
content: @Composable () -> Unit,
) {
val context = LocalContext.current
val isDark = isSystemInDarkTheme()
val isDark = themeMode.isDark(systemDark = isSystemInDarkTheme())
val colorScheme = if (isDark) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
val mobileColors = if (isDark) darkMobileColors() else lightMobileColors()
OpenClawSystemBarAppearance(lightAppearance = !isDark)
CompositionLocalProvider(
LocalMobileColors provides mobileColors,
LocalOpenClawDarkTheme provides isDark,
) {
MaterialTheme(colorScheme = colorScheme, content = content)
}
}
@Composable
internal fun OpenClawSystemBarAppearance(lightAppearance: Boolean) {
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
val window = (view.context as? Activity)?.window ?: return@SideEffect
WindowCompat
.getInsetsController(window, window.decorView)
.isAppearanceLightStatusBars = !isDark
.isAppearanceLightStatusBars = lightAppearance
WindowCompat
.getInsetsController(window, window.decorView)
.isAppearanceLightNavigationBars = lightAppearance
}
}
CompositionLocalProvider(LocalMobileColors provides mobileColors) {
MaterialTheme(colorScheme = colorScheme, content = content)
}
}
/**
@@ -44,9 +62,9 @@ fun OpenClawTheme(content: @Composable () -> Unit) {
@Composable
fun overlayContainerColor(): Color {
val scheme = MaterialTheme.colorScheme
val isDark = isSystemInDarkTheme()
val isDark = LocalOpenClawDarkTheme.current
val base = if (isDark) scheme.surfaceContainerLow else scheme.surfaceContainerHigh
// Light mode: background stays dark (canvas), so clamp overlays away from pure-white glare.
// Light mode keeps overlays away from pure-white glare on the app canvas.
return if (isDark) base else base.copy(alpha = 0.88f)
}

View File

@@ -217,7 +217,7 @@ private fun SessionRow(
compact: Boolean,
onClick: () -> Unit,
) {
Surface(onClick = onClick, color = ClawTheme.colors.canvas, contentColor = ClawTheme.colors.text) {
Surface(onClick = onClick, color = Color.Transparent, contentColor = ClawTheme.colors.text) {
Column {
Row(
modifier = Modifier.fillMaxWidth().heightIn(min = 58.dp).padding(vertical = 5.dp),

View File

@@ -1,5 +1,6 @@
package ai.openclaw.app.ui
import ai.openclaw.app.AppearanceThemeMode
import ai.openclaw.app.BuildConfig
import ai.openclaw.app.GatewayAgentSummary
import ai.openclaw.app.GatewayCronJobSummary
@@ -146,7 +147,7 @@ internal fun SettingsDetailScreen(
SettingsRoute.Notifications -> NotificationSettingsScreen(viewModel = viewModel, onBack = onBack)
SettingsRoute.PhoneCapabilities -> PhoneCapabilitiesScreen(viewModel = viewModel, onBack = onBack)
SettingsRoute.Gateway -> GatewaySettingsScreen(viewModel = viewModel, onBack = onBack)
SettingsRoute.Appearance -> AppearanceSettingsScreen(onBack = onBack)
SettingsRoute.Appearance -> AppearanceSettingsScreen(viewModel = viewModel, onBack = onBack)
SettingsRoute.Health -> HealthLogsSettingsScreen(viewModel = viewModel, onBack = onBack)
SettingsRoute.About -> AboutSettingsScreen(viewModel = viewModel, onBack = onBack)
}
@@ -914,22 +915,40 @@ private fun GatewaySettingsScreen(
}
@Composable
private fun AppearanceSettingsScreen(onBack: () -> Unit) {
private fun AppearanceSettingsScreen(
viewModel: MainViewModel,
onBack: () -> Unit,
) {
val themeMode by viewModel.appearanceThemeMode.collectAsState()
SettingsDetailFrame(title = "Appearance", subtitle = "A calm, high-contrast OpenClaw interface.", icon = Icons.Default.Palette, onBack = onBack) {
SettingsMetricPanel(
rows =
listOf(
SettingsMetric("Theme", "Dark"),
SettingsMetric("Theme", appearanceThemeSummary(themeMode)),
SettingsMetric("Contrast", "High"),
SettingsMetric("Typography", "Readable"),
),
)
ClawPanel {
Text(text = "OpenClaw uses a fixed premium dark theme so it stays consistent across devices.", style = ClawTheme.type.body, color = ClawTheme.colors.textMuted)
Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
Text(text = "Theme", style = ClawTheme.type.section, color = ClawTheme.colors.text)
ClawSegmentedControl(
options = appearanceThemeOptions(),
selected = appearanceThemeSummary(themeMode),
onSelect = { selected -> viewModel.setAppearanceThemeMode(appearanceThemeModeForLabel(selected)) },
)
}
}
}
}
internal fun appearanceThemeSummary(mode: AppearanceThemeMode): String = mode.displayLabel
internal fun appearanceThemeOptions(): List<String> = AppearanceThemeMode.entries.map { it.displayLabel }
internal fun appearanceThemeModeForLabel(label: String): AppearanceThemeMode = AppearanceThemeMode.fromDisplayLabel(label)
/** Converts raw gateway connection text into stable settings metric labels. */
private fun gatewayStatusLabel(
statusText: String,

View File

@@ -22,6 +22,7 @@ import androidx.activity.compose.BackHandler
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -114,7 +115,10 @@ fun ShellScreen(
viewModel: MainViewModel,
modifier: Modifier = Modifier,
) {
ClawDesignTheme {
val appearanceThemeMode by viewModel.appearanceThemeMode.collectAsState()
val shellDark = appearanceThemeMode.isDark(systemDark = isSystemInDarkTheme())
OpenClawSystemBarAppearance(lightAppearance = !shellDark)
ClawDesignTheme(dark = shellDark) {
var activeTab by rememberSaveable { mutableStateOf(Tab.Overview) }
var settingsRoute by rememberSaveable { mutableStateOf(SettingsRoute.Home) }
var returnToOverviewFromSettings by rememberSaveable { mutableStateOf(false) }
@@ -751,7 +755,7 @@ private fun RecentSessionRowContent(
metadata: String,
onClick: () -> Unit,
) {
Surface(color = ClawTheme.colors.canvas, contentColor = ClawTheme.colors.text) {
Surface(color = Color.Transparent, contentColor = ClawTheme.colors.text) {
Row(
modifier =
Modifier
@@ -849,6 +853,7 @@ private fun SettingsShellScreen(
val nodesDevicesSummary by viewModel.nodesDevicesSummary.collectAsState()
val channelsSummary by viewModel.channelsSummary.collectAsState()
val dreamingSummary by viewModel.dreamingSummary.collectAsState()
val appearanceThemeMode by viewModel.appearanceThemeMode.collectAsState()
LaunchedEffect(isConnected) {
if (isConnected) {
@@ -910,7 +915,7 @@ private fun SettingsShellScreen(
SettingsRow("Notifications", if (notificationForwardingEnabled) "Smart delivery" else "Off", Icons.Default.Notifications, route = SettingsRoute.Notifications),
SettingsRow("Phone Capabilities", if (cameraEnabled) "Camera enabled" else "Locked", Icons.Default.Lock, status = !cameraEnabled, route = SettingsRoute.PhoneCapabilities),
SettingsRow("Gateway", gatewaySummary(statusText, isConnected), Icons.Default.Cloud, status = isConnected, route = SettingsRoute.Gateway),
SettingsRow("Appearance", "Dark", Icons.Default.Palette, route = SettingsRoute.Appearance),
SettingsRow("Appearance", appearanceThemeSummary(appearanceThemeMode), Icons.Default.Palette, route = SettingsRoute.Appearance),
SettingsRow("Health", "Diagnostics", Icons.Default.Settings, status = isConnected, route = SettingsRoute.Health),
SettingsRow("About", "Version and update", Icons.Default.Storage, route = SettingsRoute.About),
),

View File

@@ -1,5 +1,8 @@
package ai.openclaw.app.ui.design
import ai.openclaw.app.ui.LocalMobileColors
import ai.openclaw.app.ui.darkMobileColors
import ai.openclaw.app.ui.lightMobileColors
import ai.openclaw.app.ui.mobileFontFamily
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
@@ -110,22 +113,22 @@ private val ClawDarkColors =
private val ClawLightColors =
ClawColors(
canvas = Color(0xFFF7F7F7),
surface = Color(0xFFFFFFFF),
canvas = Color(0xFFFAFBFC),
surface = Color(0xFFFFFEFB),
surfaceRaised = Color(0xFFFFFFFF),
surfacePressed = Color(0xFFEDEDED),
border = Color(0xFFE0E0E0),
borderStrong = Color(0xFFBDBDBD),
text = Color(0xFF070707),
textMuted = Color(0xFF595959),
textSubtle = Color(0xFF8A8A8A),
primary = Color(0xFF050505),
surfacePressed = Color(0xFFE9EDF3),
border = Color(0xFFDDE3EC),
borderStrong = Color(0xFFC7D0DC),
text = Color(0xFF111318),
textMuted = Color(0xFF505865),
textSubtle = Color(0xFF8993A2),
primary = Color(0xFF111827),
primaryText = Color(0xFFFFFFFF),
success = Color(0xFF157A3E),
successSoft = Color(0xFFEAF8EF),
warning = Color(0xFF9A6A12),
warningSoft = Color(0xFFFFF5DD),
danger = Color(0xFFB42323),
success = Color(0xFF217747),
successSoft = Color(0xFFE9F7EF),
warning = Color(0xFFA56F17),
warningSoft = Color(0xFFFFF3DC),
danger = Color(0xFFB82929),
dangerSoft = Color(0xFFFFE9E9),
)
@@ -168,10 +171,12 @@ internal fun ClawDesignTheme(
content: @Composable () -> Unit,
) {
val colors = if (dark) ClawDarkColors else ClawLightColors
val mobileColors = if (dark) darkMobileColors() else lightMobileColors()
val typography = clawTypography(mobileFontFamily)
CompositionLocalProvider(
LocalClawColors provides colors,
LocalMobileColors provides mobileColors,
LocalClawSpacing provides ClawSpacing(),
LocalClawRadii provides ClawRadii(),
LocalClawTypography provides typography,

View File

@@ -34,15 +34,15 @@ class NodeForegroundServiceTest {
@Test
fun foregroundServiceTypesForVoiceMode_addsMicrophoneOnlyForTalkMode() {
assertEquals(
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC,
ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE,
foregroundServiceTypesForVoiceMode(VoiceCaptureMode.Off),
)
assertEquals(
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC,
ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE,
foregroundServiceTypesForVoiceMode(VoiceCaptureMode.ManualMic),
)
assertEquals(
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC or ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE,
ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE or ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE,
foregroundServiceTypesForVoiceMode(VoiceCaptureMode.TalkMode),
)
}

View File

@@ -77,6 +77,31 @@ class SecurePrefsTest {
assertTrue(plainPrefs.getBoolean("device.apps.sharing.enabled", false))
}
@Test
fun appearanceThemeMode_defaultsDarkForExistingInstalls() {
val context = RuntimeEnvironment.getApplication()
val plainPrefs = context.getSharedPreferences("openclaw.node", Context.MODE_PRIVATE)
plainPrefs.edit().clear().commit()
val prefs = SecurePrefs(context)
assertEquals(AppearanceThemeMode.Dark, prefs.appearanceThemeMode.value)
assertFalse(plainPrefs.contains("appearance.themeMode"))
}
@Test
fun setAppearanceThemeMode_persistsSelectedMode() {
val context = RuntimeEnvironment.getApplication()
val plainPrefs = context.getSharedPreferences("openclaw.node", Context.MODE_PRIVATE)
plainPrefs.edit().clear().commit()
val prefs = SecurePrefs(context)
prefs.setAppearanceThemeMode(AppearanceThemeMode.Light)
assertEquals(AppearanceThemeMode.Light, prefs.appearanceThemeMode.value)
assertEquals("light", plainPrefs.getString("appearance.themeMode", null))
assertEquals(AppearanceThemeMode.Light, SecurePrefs(context).appearanceThemeMode.value)
}
@Test
fun saveGatewayBootstrapToken_persistsSeparatelyFromSharedToken() {
val context = RuntimeEnvironment.getApplication()

View File

@@ -1,5 +1,6 @@
package ai.openclaw.app.ui
import ai.openclaw.app.AppearanceThemeMode
import ai.openclaw.app.GatewayChannelSummary
import ai.openclaw.app.GatewayChannelsSummary
import ai.openclaw.app.GatewayNodesDevicesSummary
@@ -17,6 +18,28 @@ class ShellScreenLogicTest {
assertFalse(shellBottomNavVisible(keyboardVisible = false, commandOpen = true))
}
@Test
fun appearanceThemeModeDefaultsToDarkForExistingInstalls() {
assertEquals(AppearanceThemeMode.Dark, AppearanceThemeMode.fromRawValue(null))
assertEquals(AppearanceThemeMode.Dark, AppearanceThemeMode.fromRawValue("unknown"))
}
@Test
fun appearanceThemeLabelsRoundTripFromSettingsOptions() {
assertEquals(listOf("System", "Dark", "Light"), appearanceThemeOptions())
assertEquals(AppearanceThemeMode.System, appearanceThemeModeForLabel("System"))
assertEquals(AppearanceThemeMode.Dark, appearanceThemeModeForLabel("Dark"))
assertEquals(AppearanceThemeMode.Light, appearanceThemeModeForLabel("Light"))
}
@Test
fun appearanceThemeModeResolvesAgainstSystemPreference() {
assertFalse(AppearanceThemeMode.System.isDark(systemDark = false))
assertTrue(AppearanceThemeMode.System.isDark(systemDark = true))
assertTrue(AppearanceThemeMode.Dark.isDark(systemDark = false))
assertFalse(AppearanceThemeMode.Light.isDark(systemDark = true))
}
@Test
fun homeAttentionRowsSurfaceGatewayWhenDisconnected() {
val rows =

View File

@@ -57,6 +57,7 @@ struct SettingsProTab: View {
@State var notificationActionText = "Request Access"
@State var diagnosticsLastRunText = "Not run"
@State var diagnosticsIssueCount: Int?
@State var showTalkIssueDetails = false
var body: some View {
NavigationStack {
@@ -129,6 +130,11 @@ struct SettingsProTab: View {
})
}
}
.sheet(isPresented: self.$showTalkIssueDetails) {
if let issue = self.appModel.talkMode.gatewayTalkCurrentFallbackIssue {
TalkRuntimeIssueDetailsSheet(issue: issue)
}
}
.sheet(isPresented: self.$showQRScanner) {
NavigationStack {
QRScannerView(

View File

@@ -610,6 +610,21 @@ extension SettingsProTab {
return self.appModel.talkMode.gatewayTalkApiKeyConfigured ? "Configured" : "Not configured"
}
var gatewayTalkActiveVoiceDetail: String {
let title = self.appModel.talkMode.gatewayTalkActiveModeTitle.trimmingCharacters(in: .whitespacesAndNewlines)
let subtitle = (self.appModel.talkMode.gatewayTalkActiveModeSubtitle ?? "")
.trimmingCharacters(in: .whitespacesAndNewlines)
if title.isEmpty { return "Not active" }
if subtitle.isEmpty { return title }
return "\(title)\(subtitle)"
}
var gatewayTalkLastIssueDetail: String? {
let detail = (self.appModel.talkMode.gatewayTalkLastIssueText ?? "")
.trimmingCharacters(in: .whitespacesAndNewlines)
return detail.isEmpty ? nil : detail
}
func gatewayDetailLines(_ gateway: GatewayDiscoveryModel.DiscoveredGateway) -> [String] {
var lines: [String] = []
if let lanHost = gateway.lanHost { lines.append("LAN: \(lanHost)") }

View File

@@ -792,26 +792,44 @@ extension SettingsProTab {
}
var talkVoiceSettingsCard: some View {
ProCard(radius: SettingsLayout.cardRadius) {
VStack(alignment: .leading, spacing: 12) {
Picker("Provider", selection: self.talkProviderSelectionBinding) {
ForEach(TalkModeProviderSelection.allCases) { option in
Text(option.label).tag(option.rawValue)
}
}
if self.shouldShowRealtimeVoicePicker {
Picker("Realtime Voice", selection: self.talkRealtimeVoiceSelectionBinding) {
Text("Gateway Default").tag("")
ForEach(TalkModeRealtimeVoiceSelection.voices, id: \.self) { voice in
Text(TalkModeRealtimeVoiceSelection.label(for: voice)).tag(voice)
VStack(alignment: .leading, spacing: 10) {
if self.gatewayConnected,
let issue = self.appModel.talkMode.gatewayTalkCurrentFallbackIssue
{
TalkRuntimeIssueBanner(
issue: issue,
onOpenSettings: nil,
onShowDetails: {
self.showTalkIssueDetails = true
})
}
ProCard(radius: SettingsLayout.cardRadius) {
VStack(alignment: .leading, spacing: 12) {
Picker("Provider", selection: self.talkProviderSelectionBinding) {
ForEach(TalkModeProviderSelection.allCases) { option in
Text(option.label).tag(option.rawValue)
}
}
if self.shouldShowRealtimeVoicePicker {
Picker("Realtime Voice", selection: self.talkRealtimeVoiceSelectionBinding) {
Text("Gateway Default").tag("")
ForEach(TalkModeRealtimeVoiceSelection.voices, id: \.self) { voice in
Text(TalkModeRealtimeVoiceSelection.label(for: voice)).tag(voice)
}
}
}
self.detailRow("Voice Mode", value: self.appModel.talkMode.gatewayTalkVoiceModeTitle)
Divider()
self.detailRow("Active Voice", value: self.gatewayTalkActiveVoiceDetail)
if let issue = self.gatewayTalkLastIssueDetail {
Divider()
self.detailRow("Last Voice Issue", value: issue)
}
Divider()
self.detailRow("Transport", value: self.appModel.talkMode.gatewayTalkTransportLabel)
Divider()
self.detailRow("API Key", value: self.talkApiKeyStatus)
}
self.detailRow("Voice Mode", value: self.appModel.talkMode.gatewayTalkVoiceModeTitle)
Divider()
self.detailRow("Transport", value: self.appModel.talkMode.gatewayTalkTransportLabel)
Divider()
self.detailRow("API Key", value: self.talkApiKeyStatus)
}
}
.padding(.horizontal, OpenClawProMetric.pagePadding)

View File

@@ -8,6 +8,7 @@ struct TalkProTab: View {
TalkDefaults.speakerphoneEnabledByDefault
@AppStorage("talk.background.enabled") private var talkBackgroundEnabled: Bool = false
@State private var showPermissionPrompt = false
@State private var showTalkIssueDetails = false
var openSettings: () -> Void
private var state: TalkProState {
@@ -30,6 +31,15 @@ struct TalkProTab: View {
ScrollView {
VStack(alignment: .leading, spacing: 10) {
self.header
if let fallbackIssue = self.fallbackIssue {
TalkRuntimeIssueBanner(
issue: fallbackIssue,
onOpenSettings: self.openSettings,
onShowDetails: {
self.showTalkIssueDetails = true
})
.padding(.horizontal, OpenClawProMetric.pagePadding)
}
self.voiceHeroCard
self.conversationCard
self.voiceModeCard
@@ -62,6 +72,14 @@ struct TalkProTab: View {
.presentationDetents([.medium, .large])
.openClawSheetChrome()
}
.sheet(isPresented: self.$showTalkIssueDetails) {
if let fallbackIssue = self.fallbackIssue {
TalkRuntimeIssueDetailsSheet(
issue: fallbackIssue,
onOpenSettings: self.openSettings)
.openClawSheetChrome()
}
}
.onAppear { self.alignPersistedTalkState() }
}
@@ -173,9 +191,21 @@ struct TalkProTab: View {
.padding(.horizontal, 12)
.padding(.top, 11)
.padding(.bottom, 3)
self.infoRow(icon: "waveform", title: "Mode", value: self.appModel.talkMode.gatewayTalkVoiceModeTitle)
self.infoRow(
icon: "waveform",
title: "Configured",
value: self.appModel.talkMode.gatewayTalkVoiceModeTitle)
Divider().padding(.leading, 54)
self.infoRow(
icon: "waveform",
title: "Active now",
value: self.activeModeText)
Divider().padding(.leading, 54)
self.infoRow(icon: "antenna.radiowaves.left.and.right", title: "Transport", value: self.transportText)
if let issueText = self.talkIssueText {
Divider().padding(.leading, 54)
self.infoRow(icon: "exclamationmark.triangle.fill", title: "Last issue", value: issueText)
}
Divider().padding(.leading, 54)
self.infoRow(icon: "key.fill", title: "Permission", value: self.permissionText)
Divider().padding(.leading, 54)
@@ -287,6 +317,11 @@ struct TalkProTab: View {
GatewayStatusBuilder.build(appModel: self.appModel) == .connected
}
private var fallbackIssue: TalkRuntimeIssue? {
guard self.gatewayConnected else { return nil }
return self.appModel.talkMode.gatewayTalkCurrentFallbackIssue
}
private var headerSubtitle: String {
let mode = self.appModel.talkMode.gatewayTalkVoiceModeTitle.trimmingCharacters(in: .whitespacesAndNewlines)
let agent = self.appModel.chatAgentName.trimmingCharacters(in: .whitespacesAndNewlines)
@@ -317,6 +352,21 @@ struct TalkProTab: View {
return "\(provider)\(transport)"
}
private var activeModeText: String {
let title = self.appModel.talkMode.gatewayTalkActiveModeTitle.trimmingCharacters(in: .whitespacesAndNewlines)
let subtitle = (self.appModel.talkMode.gatewayTalkActiveModeSubtitle ?? "")
.trimmingCharacters(in: .whitespacesAndNewlines)
if title.isEmpty { return "Not active" }
if subtitle.isEmpty { return title }
return "\(title)\(subtitle)"
}
private var talkIssueText: String? {
let text = (self.appModel.talkMode.gatewayTalkLastIssueText ?? "")
.trimmingCharacters(in: .whitespacesAndNewlines)
return text.isEmpty ? nil : text
}
private var permissionText: String {
if let failure = self.appModel.talkMode.gatewayTalkPermissionState.failureMessage {
return failure

View File

@@ -0,0 +1,142 @@
import SwiftUI
import UIKit
struct TalkRuntimeIssueBanner: View {
@Environment(\.colorScheme) private var colorScheme
let issue: TalkRuntimeIssue
var onOpenSettings: (() -> Void)?
var onShowDetails: (() -> Void)?
var body: some View {
VStack(alignment: .leading, spacing: 10) {
HStack(alignment: .top, spacing: 10) {
Image(systemName: self.iconName)
.font(.headline.weight(.semibold))
.foregroundStyle(self.tint)
.frame(width: 20)
.padding(.top, 2)
VStack(alignment: .leading, spacing: 5) {
HStack(alignment: .firstTextBaseline, spacing: 8) {
Text(self.issue.fallbackBannerTitle)
.font(.subheadline.weight(.semibold))
.multilineTextAlignment(.leading)
Spacer(minLength: 0)
Text(self.issue.fallbackBannerOwnerLabel)
.font(.caption.weight(.semibold))
.foregroundStyle(.secondary)
}
Text(self.issue.fallbackBannerMessage)
.font(.footnote)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
Text(self.issue.displayMessage)
.font(.caption.weight(.medium))
.foregroundStyle(self.tint)
.fixedSize(horizontal: false, vertical: true)
}
}
HStack(spacing: 10) {
if let onOpenSettings {
Button("Open Settings", action: onOpenSettings)
.buttonStyle(.borderedProminent)
.controlSize(.small)
}
if let onShowDetails {
Button("Details", action: onShowDetails)
.buttonStyle(.bordered)
.controlSize(.small)
}
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(13)
.background {
RoundedRectangle(cornerRadius: 16, style: .continuous)
.fill(.ultraThickMaterial)
.overlay {
RoundedRectangle(cornerRadius: 16, style: .continuous)
.strokeBorder(Color.primary.opacity(self.colorScheme == .dark ? 0.12 : 0.07), lineWidth: 1)
}
.shadow(color: .black.opacity(self.colorScheme == .dark ? 0.16 : 0.07), radius: 16, y: 7)
}
}
private var iconName: String {
"exclamationmark.triangle.fill"
}
private var tint: Color {
.orange
}
}
struct TalkRuntimeIssueDetailsSheet: View {
@Environment(\.dismiss) private var dismiss
let issue: TalkRuntimeIssue
var onOpenSettings: (() -> Void)?
@State private var copyFeedback: String?
var body: some View {
NavigationStack {
List {
Section {
VStack(alignment: .leading, spacing: 10) {
Text(self.issue.fallbackBannerTitle)
.font(.title3.weight(.semibold))
Text(self.issue.fallbackBannerMessage)
.font(.body)
.foregroundStyle(.secondary)
Text(self.issue.displayMessage)
.font(.footnote.weight(.semibold))
.foregroundStyle(.secondary)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.vertical, 4)
}
Section("Technical details") {
Text(verbatim: self.issue.technicalDetails)
.font(.system(.footnote, design: .monospaced))
.foregroundStyle(.secondary)
.textSelection(.enabled)
Button("Copy diagnostics") {
UIPasteboard.general.string = self.issue.technicalDetails
self.copyFeedback = "Copied diagnostics"
}
}
if let copyFeedback {
Section {
Text(copyFeedback)
.font(.footnote)
.foregroundStyle(.secondary)
}
}
}
.navigationTitle("Talk fallback")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
if let onOpenSettings {
Button("Open Settings") {
self.dismiss()
onOpenSettings()
}
}
}
ToolbarItem(placement: .topBarTrailing) {
Button("Done") {
self.dismiss()
}
}
}
}
}
}

View File

@@ -103,6 +103,12 @@ final class RealtimeTalkRelaySession {
let failed: Bool
}
private enum StartupWaitResult {
case ready
case failed(TalkRuntimeIssue)
case cancelled
}
private nonisolated static let expectedInputEncoding = "pcm16"
private nonisolated static let expectedOutputEncoding = "pcm16"
private nonisolated static let defaultSampleRateHz = 24000
@@ -110,16 +116,23 @@ final class RealtimeTalkRelaySession {
private nonisolated static let bargeInRmsThreshold: Float = 0.08
private nonisolated static let bargeInCooldownMs: Double = 900
private nonisolated static let minOutputBeforeBargeInMs: Double = 250
private nonisolated static let startupReadyTimeoutSeconds = 12
private let gateway: GatewayNodeSession
private let options: Options
private let pcmPlayer: PCMStreamingAudioPlaying
private let logger = Logger(subsystem: "ai.openclaw", category: "RealtimeTalkRelay")
private let onStatus: (String) -> Void
private let onIssue: (TalkRuntimeIssue) -> Void
private let onSpeakingChanged: (Bool) -> Void
private let audioEngine = AVAudioEngine()
private var relaySessionId: String?
private var hasReceivedReady = false
private var hasReceivedFailure = false
private var startupIssue: TalkRuntimeIssue?
private var startupWaiter: CheckedContinuation<StartupWaitResult, Never>?
private var pendingPreRelayEvents: [EventFrame] = []
private var inputSampleRateHz = Double(RealtimeTalkRelaySession.defaultSampleRateHz)
private var outputSampleRateHz = Double(RealtimeTalkRelaySession.defaultSampleRateHz)
private var eventTask: Task<Void, Never>?
@@ -151,34 +164,53 @@ final class RealtimeTalkRelaySession {
options: Options,
pcmPlayer: PCMStreamingAudioPlaying,
onStatus: @escaping (String) -> Void,
onIssue: @escaping (TalkRuntimeIssue) -> Void = { _ in },
onSpeakingChanged: @escaping (Bool) -> Void)
{
self.gateway = gateway
self.options = options
self.pcmPlayer = pcmPlayer
self.onStatus = onStatus
self.onIssue = onIssue
self.onSpeakingChanged = onSpeakingChanged
}
func start() async throws {
self.isClosed = false
self.hasReceivedReady = false
self.hasReceivedFailure = false
self.startupIssue = nil
self.startupWaiter = nil
self.pendingPreRelayEvents.removeAll()
self.onStatus("Connecting realtime…")
let result = try await self.createRelaySession()
guard let relaySessionId = result.relaysessionid?.trimmingCharacters(in: .whitespacesAndNewlines),
!relaySessionId.isEmpty
else {
throw NSError(domain: "RealtimeTalkRelay", code: 1, userInfo: [
NSLocalizedDescriptionKey: "Gateway did not return a realtime relay session",
])
}
self.relaySessionId = relaySessionId
let eventStream = await self.gateway.subscribeServerEvents(bufferingNewest: 200)
self.startEventPump(stream: eventStream)
do {
let result = try await self.createRelaySession()
guard let relaySessionId = result.relaysessionid?.trimmingCharacters(in: .whitespacesAndNewlines),
!relaySessionId.isEmpty
else {
throw NSError(domain: "RealtimeTalkRelay", code: 1, userInfo: [
NSLocalizedDescriptionKey: "Gateway did not return a realtime relay session",
])
}
self.relaySessionId = relaySessionId
self.audioSender = RealtimeAudioSender(gateway: self.gateway, relaySessionId: relaySessionId)
let eventStream = await self.gateway.subscribeServerEvents(bufferingNewest: 200)
self.startEventPump(stream: eventStream)
self.configureAudioContract(result.audio)
try self.startMicrophonePump()
self.onStatus("Listening (Realtime)")
self.onStatus("Waiting for realtime")
await self.drainPendingPreRelayEvents()
switch await self.waitForStartupResult(timeoutSeconds: Self.startupReadyTimeoutSeconds) {
case .ready:
return
case let .failed(issue):
self.close(sendClose: true)
throw NSError(domain: "RealtimeTalkRelay", code: 6, userInfo: [
NSLocalizedDescriptionKey: issue.displayMessage,
])
case .cancelled:
return
}
} catch {
let createdRelaySessionId = self.relaySessionId
self.close(sendClose: false)
@@ -196,6 +228,7 @@ final class RealtimeTalkRelaySession {
private func close(sendClose: Bool) {
guard !self.isClosed else { return }
self.isClosed = true
self.finishStartupWait(.cancelled)
self.stopMicrophonePump()
self.eventTask?.cancel()
self.eventTask = nil
@@ -299,14 +332,21 @@ final class RealtimeTalkRelaySession {
guard event.event == "talk.event",
let payload = event.payload?.dictionaryValue
else { return }
if let relaySessionId,
payload["relaySessionId"]?.stringValue != relaySessionId
{
guard let relaySessionId else {
self.pendingPreRelayEvents.append(event)
if self.pendingPreRelayEvents.count > 200 {
self.pendingPreRelayEvents.removeFirst(self.pendingPreRelayEvents.count - 200)
}
return
}
if payload["relaySessionId"]?.stringValue != relaySessionId {
return
}
guard let type = payload["type"]?.stringValue else { return }
switch type {
case "ready":
self.hasReceivedReady = true
self.finishStartupWait(.ready)
self.onStatus("Listening (Realtime)")
case "audio":
guard let base64 = payload["audioBase64"]?.stringValue,
@@ -331,17 +371,107 @@ final class RealtimeTalkRelaySession {
await self.handleToolCall(payload)
case "error":
let message = payload["message"]?.stringValue ?? "Realtime failed"
let issue = Self.issue(
payload: payload,
fallbackMessage: message,
fallbackProvider: self.options.provider,
fallbackModel: self.options.model)
GatewayDiagnostics.log("talk realtime: error=\(Self.safeLogMessage(message))")
self.hasReceivedFailure = true
self.startupIssue = issue
self.onIssue(issue)
self.finishStartupWait(.failed(issue))
self.onStatus(message)
case "close":
GatewayDiagnostics.log("talk realtime: close")
self.onStatus("Ready")
if self.hasReceivedReady {
self.onStatus("Ready")
} else if !self.hasReceivedFailure {
let issue = TalkRuntimeIssue(
code: .realtimeUnavailable,
message: "Realtime closed before it became ready.",
provider: self.options.provider,
model: self.options.model,
transport: "gateway-relay",
phase: "connect")
self.onIssue(issue)
self.startupIssue = issue
self.finishStartupWait(.failed(issue))
self.onStatus("Realtime failed before connecting")
}
self.close(sendClose: false)
default:
return
}
}
private func waitForStartupResult(timeoutSeconds: Int) async -> StartupWaitResult {
if self.isClosed { return .cancelled }
if self.hasReceivedReady { return .ready }
if let startupIssue { return .failed(startupIssue) }
return await withCheckedContinuation { continuation in
if self.isClosed {
continuation.resume(returning: .cancelled)
return
}
self.startupWaiter = continuation
Task { [weak self] in
try? await Task.sleep(nanoseconds: UInt64(max(0, timeoutSeconds)) * 1_000_000_000)
await self?.timeoutStartupWaiterIfNeeded()
}
}
}
private func drainPendingPreRelayEvents() async {
let pendingEvents = self.pendingPreRelayEvents
self.pendingPreRelayEvents.removeAll()
for event in pendingEvents {
await self.handleGatewayEvent(event)
}
}
private func finishStartupWait(_ result: StartupWaitResult) {
guard let waiter = self.startupWaiter else { return }
self.startupWaiter = nil
waiter.resume(returning: result)
}
private func timeoutStartupWaiterIfNeeded() {
guard !self.isClosed, self.startupWaiter != nil, !self.hasReceivedReady, self.startupIssue == nil else {
return
}
let issue = TalkRuntimeIssue(
code: .realtimeUnavailable,
message: "Realtime did not become ready in time.",
provider: self.options.provider,
model: self.options.model,
transport: "gateway-relay",
phase: "connect")
self.hasReceivedFailure = true
self.startupIssue = issue
self.onIssue(issue)
self.onStatus(issue.displayMessage)
self.finishStartupWait(.failed(issue))
}
private static func issue(
payload: [String: AnyCodable],
fallbackMessage: String,
fallbackProvider: String?,
fallbackModel: String?) -> TalkRuntimeIssue
{
let provider = payload["provider"]?.stringValue ?? fallbackProvider
let model = payload["model"]?.stringValue ?? fallbackModel
let transport = payload["transport"]?.stringValue ?? "gateway-relay"
let phase = payload["phase"]?.stringValue
return TalkRuntimeIssue.realtimeUnavailable(
message: fallbackMessage,
provider: provider,
model: model,
transport: transport,
phase: phase)
}
private func recordOutputAudioChunk(byteCount: Int) {
self.outputAudioChunkCount += 1
self.outputAudioByteCount += byteCount
@@ -804,6 +934,25 @@ final class RealtimeTalkRelaySession {
}
extension RealtimeTalkRelaySession {
func _test_setRelaySessionId(_ relaySessionId: String) {
self.relaySessionId = relaySessionId
}
func _test_handleGatewayEvent(_ event: EventFrame) async {
await self.handleGatewayEvent(event)
}
func _test_waitForStartupCancelled(timeoutSeconds: Int) async -> Bool {
if case .cancelled = await self.waitForStartupResult(timeoutSeconds: timeoutSeconds) {
return true
}
return false
}
func _test_startupReadyTimeoutSeconds() -> Int {
Self.startupReadyTimeoutSeconds
}
func _test_markOutputAudioStarted(nowMs: Double) {
self.markOutputAudioStarted(byteCount: 4800, nowMs: nowMs)
}

View File

@@ -7,6 +7,96 @@ enum TalkModeExecutionMode {
case realtimeRelay
}
struct TalkRuntimeIssue: Equatable {
enum Code: String {
case realtimeUnavailable = "realtime_unavailable"
}
let code: Code
let message: String
let provider: String?
let model: String?
let transport: String?
let phase: String?
let occurredAt: Date
init(
code: Code,
message: String,
provider: String? = nil,
model: String? = nil,
transport: String? = nil,
phase: String? = nil,
occurredAt: Date = Date())
{
self.code = code
self.message = message.trimmingCharacters(in: .whitespacesAndNewlines)
self.provider = provider?.trimmingCharacters(in: .whitespacesAndNewlines)
self.model = model?.trimmingCharacters(in: .whitespacesAndNewlines)
self.transport = transport?.trimmingCharacters(in: .whitespacesAndNewlines)
self.phase = phase?.trimmingCharacters(in: .whitespacesAndNewlines)
self.occurredAt = occurredAt
}
var displayMessage: String {
if !self.message.isEmpty { return self.message }
return "Realtime voice did not start."
}
var fallbackStatusText: String {
"Listening (iOS Speech fallback)"
}
var fallbackBannerTitle: String {
"Using iOS Speech fallback"
}
var fallbackBannerOwnerLabel: String {
"Fallback active"
}
var fallbackBannerMessage: String {
"Realtime voice did not start. Talk is running with iOS speech recognition and TTS."
}
var technicalDetails: String {
var lines = [
"code: \(self.code.rawValue)",
"message: \(self.displayMessage)",
]
if let provider, !provider.isEmpty { lines.append("provider: \(provider)") }
if let model, !model.isEmpty { lines.append("model: \(model)") }
if let transport, !transport.isEmpty { lines.append("transport: \(transport)") }
if let phase, !phase.isEmpty { lines.append("phase: \(phase)") }
return lines.joined(separator: "\n")
}
var diagnosticSummary: String {
var parts = [self.displayMessage]
if let provider, !provider.isEmpty { parts.append("provider: \(provider)") }
if let model, !model.isEmpty { parts.append("model: \(model)") }
if let transport, !transport.isEmpty { parts.append("transport: \(transport)") }
if let phase, !phase.isEmpty { parts.append("phase: \(phase)") }
return parts.joined(separator: "")
}
static func realtimeUnavailable(
message: String,
provider: String? = nil,
model: String? = nil,
transport: String? = nil,
phase: String? = nil) -> TalkRuntimeIssue
{
TalkRuntimeIssue(
code: .realtimeUnavailable,
message: message,
provider: provider,
model: model,
transport: transport,
phase: phase)
}
}
struct TalkVoiceModeDescriptor: Equatable {
let title: String
let subtitle: String?

View File

@@ -60,6 +60,10 @@ final class TalkModeManager: NSObject {
var gatewayTalkVoiceModeTitle: String = "Not loaded"
var gatewayTalkVoiceModeSubtitle: String?
var gatewayTalkVoiceModeAccessibilityValue: String = "Not loaded"
var gatewayTalkActiveModeTitle: String = "Not active"
var gatewayTalkActiveModeSubtitle: String?
var gatewayTalkLastIssueText: String?
var gatewayTalkCurrentFallbackIssue: TalkRuntimeIssue?
var gatewayTalkPermissionState: TalkGatewayPermissionState = .unknown
var isGatewayConnected: Bool {
@@ -77,6 +81,12 @@ final class TalkModeManager: NSObject {
case pushToTalk
}
private enum RealtimeStartResult {
case started
case unavailable(TalkRuntimeIssue)
case ignored
}
private var isStarting = false
private var startAttemptID = 0
private var captureMode: CaptureMode = .idle
@@ -129,6 +139,8 @@ final class TalkModeManager: NSObject {
voiceId: nil,
transport: nil,
isRealtime: false)
private var pendingRealtimeIssue: TalkRuntimeIssue?
private var realtimeRelayStartIssue: TalkRuntimeIssue?
private var apiKey: String?
private var voiceAliases: [String: String] = [:]
private var interruptOnSpeech: Bool = true
@@ -192,6 +204,8 @@ final class TalkModeManager: NSObject {
}
} else {
self.stopRealtimeSession()
self.gatewayTalkActiveModeTitle = "Not active"
self.gatewayTalkActiveModeSubtitle = nil
if self.isEnabled, !self.isSpeaking {
self.statusText = "Offline"
}
@@ -299,11 +313,15 @@ final class TalkModeManager: NSObject {
return
}
if self.realtimeWebRTCEnabled {
let started = self.executionMode == .realtimeRelay
let realtimeStart = self.executionMode == .realtimeRelay
? await self.startRealtimeRelayIfAvailable()
: await self.startRealtimeIfAvailable()
if started {
switch realtimeStart {
case .started, .ignored:
return
case let .unavailable(issue):
self.pendingRealtimeIssue = issue
self.gatewayTalkLastIssueText = issue.diagnosticSummary
}
}
@@ -324,7 +342,11 @@ final class TalkModeManager: NSObject {
self.captureMode = .continuous
try self.startRecognition()
self.isListening = true
self.statusText = "Listening"
if let issue = self.pendingRealtimeIssue {
self.markNativeFallbackActive(after: issue)
} else {
self.markNativeTalkActive()
}
self.startSilenceMonitor()
await self.subscribeChatIfNeeded(sessionKey: self.mainSessionKey)
self.logger.info("listening")
@@ -379,6 +401,11 @@ final class TalkModeManager: NSObject {
self.isPushToTalkActive = false
self.captureMode = .idle
self.statusText = "Off"
self.pendingRealtimeIssue = nil
self.gatewayTalkCurrentFallbackIssue = nil
self.gatewayTalkActiveModeTitle = "Not active"
self.gatewayTalkActiveModeSubtitle = nil
self.gatewayTalkLastIssueText = nil
self.lastTranscript = ""
self.lastHeard = nil
self.silenceTask?.cancel()
@@ -425,6 +452,8 @@ final class TalkModeManager: NSObject {
self.isPushToTalkActive = false
self.captureMode = .idle
self.statusText = "Paused"
self.gatewayTalkActiveModeTitle = "Paused"
self.gatewayTalkActiveModeSubtitle = nil
self.lastTranscript = ""
self.lastHeard = nil
self.silenceTask?.cancel()
@@ -1047,8 +1076,10 @@ final class TalkModeManager: NSObject {
}
}
private func startRealtimeIfAvailable() async -> Bool {
guard let gateway else { return false }
private func startRealtimeIfAvailable() async -> RealtimeStartResult {
guard let gateway else {
return .unavailable(self.realtimeIssue(message: "Gateway not connected", phase: "start"))
}
let startedAt = Self.nowSeconds()
if self.prefetchedRealtimeSession == nil, let prefetchTask = self.realtimePrefetchTask {
GatewayDiagnostics.log("talk.timeline realtime awaiting in-flight prefetch")
@@ -1069,49 +1100,53 @@ final class TalkModeManager: NSObject {
prefetchedSession: prefetchedSession)
guard self.realtimeSession === session, self.isEnabled else {
session.stop()
return true
return .ignored
}
self.isListening = true
self.captureMode = .continuous
self.statusText = "Listening"
self.markRealtimeActive()
GatewayDiagnostics.log(
"talk.timeline realtime start ready elapsedMs=\(Self.elapsedMs(since: startedAt))")
GatewayDiagnostics.log("talk realtime: started direct OpenAI WebRTC session")
return true
return .started
} catch {
guard self.realtimeSession === session, self.isEnabled else {
session.stop()
return true
return .ignored
}
self.stopRealtimeSession()
let issue = self.realtimeIssue(from: error, phase: "start")
GatewayDiagnostics
.log("talk realtime: unavailable; falling back to speech pipeline error=\(error.localizedDescription)")
GatewayDiagnostics.log(
"talk.timeline realtime start failed elapsedMs=\(Self.elapsedMs(since: startedAt)) "
+ "error=\(error.localizedDescription)")
return false
return .unavailable(issue)
}
}
private func startRealtimeRelayIfAvailable() async -> Bool {
guard let gateway else { return false }
private func startRealtimeRelayIfAvailable() async -> RealtimeStartResult {
guard let gateway else {
return .unavailable(self.realtimeIssue(message: "Gateway not connected", phase: "start"))
}
guard self.foregroundAudioCaptureAllowed else {
self.statusText = "Paused"
GatewayDiagnostics.log("talk realtime ignored: app backgrounded")
return true
return .ignored
}
if self.realtimeRelaySession != nil {
self.captureMode = .continuous
self.isListening = true
GatewayDiagnostics.log("talk realtime ignored: already active")
return true
return .started
}
guard !self.realtimeRelayStartInFlight else {
GatewayDiagnostics.log("talk realtime ignored: already starting")
return true
return .ignored
}
self.realtimeRelayStartInFlight = true
defer { self.realtimeRelayStartInFlight = false }
self.prepareRealtimeRelayStart()
GatewayDiagnostics.log("talk.timeline realtime relay start attempt sessionKey=\(self.mainSessionKey)")
let startedAt = Self.nowSeconds()
let relaySession = RealtimeTalkRelaySession(
@@ -1124,13 +1159,15 @@ final class TalkModeManager: NSObject {
pcmPlayer: self.pcmPlayer,
onStatus: { [weak self] status in
guard let self else { return }
self.statusText = status
self.isListening = status.localizedCaseInsensitiveContains("listening")
if status.localizedCaseInsensitiveContains("thinking") {
self.isListening = false
self.isSpeaking = false
self.isUserSpeechDetected = false
}
self.handleRealtimeRelayStatus(status)
},
onIssue: { [weak self] issue in
guard let self else { return }
self.realtimeRelayStartIssue = issue
self.pendingRealtimeIssue = issue
self.gatewayTalkLastIssueText = issue.diagnosticSummary
self.gatewayTalkActiveModeTitle = "Realtime unavailable"
self.gatewayTalkActiveModeSubtitle = issue.displayMessage
},
onSpeakingChanged: { [weak self] speaking in
guard let self else { return }
@@ -1145,23 +1182,35 @@ final class TalkModeManager: NSObject {
try await relaySession.start()
guard self.realtimeRelaySession === relaySession, self.isEnabled else {
relaySession.stop()
return true
return .ignored
}
if let issue = self.realtimeRelayStartIssue {
self.realtimeRelaySession = nil
relaySession.stop()
GatewayDiagnostics.log(
"talk.timeline realtime relay start unavailable elapsedMs=\(Self.elapsedMs(since: startedAt)) "
+ "issue=\(issue.code.rawValue)")
return .unavailable(issue)
}
self.isListening = true
self.captureMode = .continuous
self.realtimeRelayStartIssue = nil
GatewayDiagnostics.log(
"talk.timeline realtime relay start ready elapsedMs=\(Self.elapsedMs(since: startedAt))")
return true
return .started
} catch {
guard self.realtimeRelaySession === relaySession, self.isEnabled else {
relaySession.stop()
return true
return .ignored
}
self.realtimeRelaySession = nil
let issue = self.realtimeRelayStartIssue
?? self.realtimeIssue(from: error, phase: "start")
self.realtimeRelayStartIssue = nil
GatewayDiagnostics.log(
"talk.timeline realtime relay start failed elapsedMs=\(Self.elapsedMs(since: startedAt)) "
+ "error=\(error.localizedDescription)")
return false
return .unavailable(issue)
}
}
@@ -2363,6 +2412,103 @@ extension TalkModeManager {
self.gatewayTalkVoiceModeAccessibilityValue = descriptor.accessibilityValue
}
private func markRealtimeActive() {
self.pendingRealtimeIssue = nil
self.gatewayTalkCurrentFallbackIssue = nil
self.gatewayTalkLastIssueText = nil
self.gatewayTalkActiveModeTitle = self.configuredVoiceModeDescriptor.title
self.gatewayTalkActiveModeSubtitle = self.configuredVoiceModeDescriptor.subtitle
self.statusText = "Listening (Realtime)"
}
private func handleRealtimeRelayStatus(_ status: String) {
if status == "Listening (Realtime)" {
self.markRealtimeActive()
} else {
self.statusText = status
if status == "Ready" {
self.realtimeRelaySession = nil
self.gatewayTalkActiveModeTitle = "Not active"
self.gatewayTalkActiveModeSubtitle = nil
self.isListening = false
self.isSpeaking = false
self.isUserSpeechDetected = false
}
}
self.isListening = status.localizedCaseInsensitiveContains("listening")
if status.localizedCaseInsensitiveContains("thinking") {
self.isListening = false
self.isSpeaking = false
self.isUserSpeechDetected = false
}
}
private func prepareRealtimeRelayStart() {
self.realtimeRelayStartIssue = nil
self.pendingRealtimeIssue = nil
self.gatewayTalkCurrentFallbackIssue = nil
}
private func markNativeTalkActive() {
self.pendingRealtimeIssue = nil
self.gatewayTalkCurrentFallbackIssue = nil
self.gatewayTalkActiveModeTitle = "iOS Speech + TTS"
self.gatewayTalkActiveModeSubtitle = nil
self.statusText = "Listening"
}
private func markNativeFallbackActive(after issue: TalkRuntimeIssue) {
self.gatewayTalkActiveModeTitle = "iOS Speech fallback"
self.gatewayTalkActiveModeSubtitle = issue.displayMessage
self.gatewayTalkCurrentFallbackIssue = issue
self.gatewayTalkLastIssueText = issue.diagnosticSummary
self.statusText = issue.fallbackStatusText
}
private func realtimeIssue(message: String, phase: String) -> TalkRuntimeIssue {
TalkRuntimeIssue.realtimeUnavailable(
message: message,
provider: self.realtimeProvider,
model: self.realtimeModelId,
transport: self.executionMode == .realtimeRelay ? "gateway-relay" : "webrtc",
phase: phase)
}
private func realtimeIssue(from error: Error, phase: String) -> TalkRuntimeIssue {
if let gatewayError = error as? GatewayResponseError,
let issue = Self.talkRuntimeIssue(
from: gatewayError,
fallbackProvider: self.realtimeProvider,
fallbackModel: self.realtimeModelId,
fallbackTransport: self.executionMode == .realtimeRelay ? "gateway-relay" : "webrtc",
fallbackPhase: phase)
{
return issue
}
return self.realtimeIssue(message: error.localizedDescription, phase: phase)
}
private static func talkRuntimeIssue(
from gatewayError: GatewayResponseError,
fallbackProvider: String?,
fallbackModel: String?,
fallbackTransport: String?,
fallbackPhase: String) -> TalkRuntimeIssue?
{
guard let rawIssue = gatewayError.details["talkIssue"]?.dictionaryValue else { return nil }
let message = rawIssue["message"]?.stringValue ?? gatewayError.message
let provider = rawIssue["provider"]?.stringValue ?? fallbackProvider
let model = rawIssue["model"]?.stringValue ?? fallbackModel
let transport = rawIssue["transport"]?.stringValue ?? fallbackTransport
let phase = rawIssue["phase"]?.stringValue ?? fallbackPhase
return TalkRuntimeIssue.realtimeUnavailable(
message: message,
provider: provider,
model: model,
transport: transport,
phase: phase)
}
private func restoreConfiguredVoiceModeDescriptor() {
self.applyVoiceModeDescriptor(self.configuredVoiceModeDescriptor)
}
@@ -2836,7 +2982,11 @@ extension TalkModeManager: TalkRealtimeWebRTCSessionDelegate {
func realtimeSession(_ session: TalkRealtimeWebRTCSession, didChangeStatus status: String) {
guard session === self.realtimeSession else { return }
GatewayDiagnostics.log("talk.timeline realtime status=\(status)")
self.statusText = status
if status == "Listening" {
self.markRealtimeActive()
} else {
self.statusText = status
}
self.isListening = status == "Listening"
self.isSpeaking = status == "Speaking"
if status == "Thinking" {
@@ -2877,6 +3027,8 @@ extension TalkModeManager: TalkRealtimeWebRTCSessionDelegate {
self.isListening = false
self.isSpeaking = false
self.isUserSpeechDetected = false
self.gatewayTalkActiveModeTitle = "Not active"
self.gatewayTalkActiveModeSubtitle = nil
if self.isEnabled {
self.statusText = self.gatewayConnected ? "Ready" : "Offline"
}
@@ -2909,6 +3061,49 @@ extension TalkModeManager {
self.gatewayTalkUsesRealtimeRelay
}
func _test_markNativeFallbackActive(after issue: TalkRuntimeIssue) {
self.markNativeFallbackActive(after: issue)
}
func _test_recordRealtimeIssue(_ issue: TalkRuntimeIssue) {
self.pendingRealtimeIssue = issue
self.gatewayTalkLastIssueText = issue.diagnosticSummary
self.gatewayTalkActiveModeTitle = "Realtime unavailable"
self.gatewayTalkActiveModeSubtitle = issue.displayMessage
}
func _test_handleRealtimeRelayStatus(_ status: String) {
self.handleRealtimeRelayStatus(status)
}
func _test_prepareRealtimeRelayStart() {
self.prepareRealtimeRelayStart()
}
func _test_realtimeIssue(from error: Error, phase: String) -> TalkRuntimeIssue {
self.realtimeIssue(from: error, phase: phase)
}
func _test_hasPendingRealtimeIssue() -> Bool {
self.pendingRealtimeIssue != nil
}
func _test_gatewayTalkActiveModeTitle() -> String {
self.gatewayTalkActiveModeTitle
}
func _test_gatewayTalkActiveModeSubtitle() -> String? {
self.gatewayTalkActiveModeSubtitle
}
func _test_gatewayTalkLastIssueText() -> String? {
self.gatewayTalkLastIssueText
}
func _test_gatewayTalkCurrentFallbackIssue() -> TalkRuntimeIssue? {
self.gatewayTalkCurrentFallbackIssue
}
func _test_seedTranscript(_ transcript: String) {
self.lastTranscript = transcript
self.lastHeard = Date()

View File

@@ -21,6 +21,7 @@ Sources/Design/SettingsProTab.swift
Sources/Design/SettingsProTabSupport.swift
Sources/Design/SettingsProTabSections.swift
Sources/Design/SettingsProTabActions.swift
Sources/Design/TalkRuntimeIssueBanner.swift
Sources/Design/CommandCenterSupport.swift
Sources/Design/AgentProTab+Overview.swift
Sources/Design/AgentProTab+Destinations.swift

View File

@@ -1,5 +1,6 @@
import Foundation
import OpenClawKit
import OpenClawProtocol
import Testing
@testable import OpenClaw
@@ -37,4 +38,70 @@ private final class UnusedPCMStreamingAudioPlayer: PCMStreamingAudioPlaying {
session._test_markOutputAudioStarted(nowMs: 500)
#expect(session._test_outputStartedAtMs() == 500)
}
@Test func closeAfterClassifiedErrorDoesNotReplaceIssue() async {
var issues: [TalkRuntimeIssue] = []
var statuses: [String] = []
let session = RealtimeTalkRelaySession(
gateway: GatewayNodeSession(),
options: .init(sessionKey: "main", provider: "openai", model: "gpt-realtime-2", voice: nil),
pcmPlayer: UnusedPCMStreamingAudioPlayer(),
onStatus: { statuses.append($0) },
onIssue: { issues.append($0) },
onSpeakingChanged: { _ in })
session._test_setRelaySessionId("relay-1")
await session._test_handleGatewayEvent(EventFrame(
type: "event",
event: "talk.event",
payload: AnyCodable([
"relaySessionId": "relay-1",
"type": "error",
"message": "OpenAI API key rejected with 401",
"code": "realtime_unavailable",
"provider": "openai",
"model": "gpt-realtime-2",
"transport": "gateway-relay",
"phase": "connect",
]),
seq: nil,
stateversion: nil))
await session._test_handleGatewayEvent(EventFrame(
type: "event",
event: "talk.event",
payload: AnyCodable([
"relaySessionId": "relay-1",
"type": "close",
"reason": "error",
]),
seq: nil,
stateversion: nil))
#expect(issues.map(\.code) == [.realtimeUnavailable])
#expect(statuses == ["OpenAI API key rejected with 401"])
}
@Test func closedRelayDoesNotWaitForStartupReady() async {
let session = RealtimeTalkRelaySession(
gateway: GatewayNodeSession(),
options: .init(sessionKey: "main", provider: "openai", model: "gpt-realtime-2", voice: nil),
pcmPlayer: UnusedPCMStreamingAudioPlayer(),
onStatus: { _ in },
onSpeakingChanged: { _ in })
session.stop()
#expect(await session._test_waitForStartupCancelled(timeoutSeconds: 1))
}
@Test func startupReadyWaitCoversGatewayConnectBudget() {
let session = RealtimeTalkRelaySession(
gateway: GatewayNodeSession(),
options: .init(sessionKey: "main", provider: "openai", model: "gpt-realtime-2", voice: nil),
pcmPlayer: UnusedPCMStreamingAudioPlayer(),
onStatus: { _ in },
onSpeakingChanged: { _ in })
#expect(session._test_startupReadyTimeoutSeconds() >= 12)
}
}

View File

@@ -1,4 +1,5 @@
import Foundation
import OpenClawKit
import Testing
@testable import OpenClaw
@@ -183,6 +184,132 @@ import Testing
#expect(manager._test_gatewayTalkUsesRealtimeRelay())
}
@Test func buildsGenericRealtimeFallbackIssueForDisplay() {
let issue = TalkRuntimeIssue.realtimeUnavailable(
message: "OpenAI API key rejected with 401",
provider: "openai",
model: "gpt-realtime-2",
transport: "gateway-relay",
phase: "start")
#expect(issue.code == .realtimeUnavailable)
#expect(issue.displayMessage == "OpenAI API key rejected with 401")
#expect(issue.diagnosticSummary.contains("provider: openai"))
#expect(issue.diagnosticSummary.contains("model: gpt-realtime-2"))
#expect(issue.fallbackStatusText == "Listening (iOS Speech fallback)")
#expect(issue.fallbackBannerTitle == "Using iOS Speech fallback")
#expect(issue.fallbackBannerOwnerLabel == "Fallback active")
#expect(issue
.fallbackBannerMessage ==
"Realtime voice did not start. Talk is running with iOS speech recognition and TTS.")
#expect(issue.technicalDetails.contains("code: realtime_unavailable"))
}
@Test func nativeFallbackKeepsRealtimeIssueVisible() {
let manager = TalkModeManager(allowSimulatorCapture: true)
let issue = TalkRuntimeIssue(
code: .realtimeUnavailable,
message: "Realtime closed before it became ready.",
provider: "openai",
model: "gpt-realtime-2",
transport: "gateway-relay",
phase: "connect")
manager._test_markNativeFallbackActive(after: issue)
#expect(manager.statusText == "Listening (iOS Speech fallback)")
#expect(manager._test_gatewayTalkActiveModeTitle() == "iOS Speech fallback")
#expect(manager._test_gatewayTalkActiveModeSubtitle() == "Realtime closed before it became ready.")
#expect(manager._test_gatewayTalkLastIssueText()?.contains("phase: connect") == true)
#expect(manager._test_gatewayTalkCurrentFallbackIssue() == issue)
}
@Test func gatewayTalkIssueDetailsDriveRealtimeFailureDisplay() {
let manager = TalkModeManager(allowSimulatorCapture: true)
let error = GatewayResponseError(
method: "talk.session.create",
code: "UNAVAILABLE",
message: "Error: OpenAI API key rejected with 401",
details: [
"talkIssue": AnyCodable([
"code": "realtime_unavailable",
"message": "OpenAI API key rejected with 401",
"provider": "openai",
"model": "gpt-realtime-2",
"transport": "gateway-relay",
"phase": "request",
]),
])
let issue = manager._test_realtimeIssue(from: error, phase: "start")
#expect(issue.code == .realtimeUnavailable)
#expect(issue.displayMessage == "OpenAI API key rejected with 401")
#expect(issue.provider == "openai")
#expect(issue.model == "gpt-realtime-2")
#expect(issue.transport == "gateway-relay")
#expect(issue.phase == "request")
}
@Test func relayStartupIssueSurvivesUntilReadyStatus() {
let manager = TalkModeManager(allowSimulatorCapture: true)
let issue = TalkRuntimeIssue(
code: .realtimeUnavailable,
message: "OpenAI API key rejected with 401",
provider: "openai",
model: "gpt-realtime-2",
transport: "gateway-relay",
phase: "connect")
manager._test_recordRealtimeIssue(issue)
manager._test_handleRealtimeRelayStatus("Connecting realtime…")
#expect(manager._test_gatewayTalkActiveModeTitle() == "Realtime unavailable")
#expect(manager._test_gatewayTalkLastIssueText()?.contains("OpenAI API key rejected") == true)
manager._test_handleRealtimeRelayStatus("Listening (Realtime)")
#expect(manager.statusText == "Listening (Realtime)")
#expect(manager._test_gatewayTalkLastIssueText() == nil)
#expect(manager._test_gatewayTalkCurrentFallbackIssue() == nil)
}
@Test func relayCloseClearsActiveRealtimeMode() {
let manager = TalkModeManager(allowSimulatorCapture: true)
manager._test_handleRealtimeRelayStatus("Listening (Realtime)")
#expect(manager.statusText == "Listening (Realtime)")
#expect(manager._test_gatewayTalkActiveModeTitle() != "Not active")
manager._test_handleRealtimeRelayStatus("Ready")
#expect(manager.statusText == "Ready")
#expect(manager._test_gatewayTalkActiveModeTitle() == "Not active")
#expect(manager._test_gatewayTalkActiveModeSubtitle() == nil)
}
@Test func relayRetryClearsStaleFallbackTriggerButKeepsLastIssueVisible() {
let manager = TalkModeManager(allowSimulatorCapture: true)
let issue = TalkRuntimeIssue(
code: .realtimeUnavailable,
message: "Realtime closed before it became ready.",
provider: "openai",
model: "gpt-realtime-2",
transport: "gateway-relay",
phase: "connect")
manager._test_recordRealtimeIssue(issue)
manager._test_markNativeFallbackActive(after: issue)
#expect(manager._test_hasPendingRealtimeIssue())
#expect(manager._test_gatewayTalkCurrentFallbackIssue() == issue)
manager._test_prepareRealtimeRelayStart()
#expect(!manager._test_hasPendingRealtimeIssue())
#expect(manager._test_gatewayTalkCurrentFallbackIssue() == nil)
#expect(manager._test_gatewayTalkLastIssueText()?.contains("Realtime closed before") == true)
}
@Test func mapsWebRTCRealtimeTransportToGatewayRelayOnIOS() {
let config: [String: Any] = [
"talk": [

View File

@@ -595,6 +595,9 @@ public struct SendParams: Codable, Sendable {
public let message: String?
public let mediaurl: String?
public let mediaurls: [String]?
public let buffer: String?
public let filename: String?
public let contenttype: String?
public let asvoice: Bool?
public let gifplayback: Bool?
public let channel: String?
@@ -613,6 +616,9 @@ public struct SendParams: Codable, Sendable {
message: String?,
mediaurl: String?,
mediaurls: [String]?,
buffer: String? = nil,
filename: String? = nil,
contenttype: String? = nil,
asvoice: Bool?,
gifplayback: Bool?,
channel: String?,
@@ -630,6 +636,9 @@ public struct SendParams: Codable, Sendable {
self.message = message
self.mediaurl = mediaurl
self.mediaurls = mediaurls
self.buffer = buffer
self.filename = filename
self.contenttype = contenttype
self.asvoice = asvoice
self.gifplayback = gifplayback
self.channel = channel
@@ -649,6 +658,9 @@ public struct SendParams: Codable, Sendable {
case message
case mediaurl = "mediaUrl"
case mediaurls = "mediaUrls"
case buffer
case filename
case contenttype = "contentType"
case asvoice = "asVoice"
case gifplayback = "gifPlayback"
case channel
@@ -753,6 +765,7 @@ public struct AgentParams: Codable, Sendable {
public let bootstrapcontextrunkind: AnyCodable?
public let acpturnsource: String?
public let internalruntimehandoffid: String?
public let execapprovalfollowupexpectedsessionid: String?
public let internalevents: [[String: AnyCodable]]?
public let inputprovenance: [String: AnyCodable]?
public let suppresspromptpersistence: Bool?
@@ -794,6 +807,7 @@ public struct AgentParams: Codable, Sendable {
bootstrapcontextrunkind: AnyCodable?,
acpturnsource: String?,
internalruntimehandoffid: String?,
execapprovalfollowupexpectedsessionid: String?,
internalevents: [[String: AnyCodable]]?,
inputprovenance: [String: AnyCodable]?,
suppresspromptpersistence: Bool?,
@@ -834,6 +848,7 @@ public struct AgentParams: Codable, Sendable {
self.bootstrapcontextrunkind = bootstrapcontextrunkind
self.acpturnsource = acpturnsource
self.internalruntimehandoffid = internalruntimehandoffid
self.execapprovalfollowupexpectedsessionid = execapprovalfollowupexpectedsessionid
self.internalevents = internalevents
self.inputprovenance = inputprovenance
self.suppresspromptpersistence = suppresspromptpersistence
@@ -876,6 +891,7 @@ public struct AgentParams: Codable, Sendable {
case bootstrapcontextrunkind = "bootstrapContextRunKind"
case acpturnsource = "acpTurnSource"
case internalruntimehandoffid = "internalRuntimeHandoffId"
case execapprovalfollowupexpectedsessionid = "execApprovalFollowupExpectedSessionId"
case internalevents = "internalEvents"
case inputprovenance = "inputProvenance"
case suppresspromptpersistence = "suppressPromptPersistence"

View File

@@ -1,4 +1,4 @@
60c0700719fd2fe3f7cec4c35da10227b681d87ed1a3876ef830eb6bd80d43f2 config-baseline.json
2ed21fa4a416ac2cec55eb2b6d1b11859aa04b40bd78c6ed9f3eb45b7240261c config-baseline.core.json
0637c9bdcb9517f56049dd786563366877458d35df575328a6b80a890c8bc915 config-baseline.channel.json
e6a1d6f51f0d9c04bd92d51deebfaca8c7917dd28d7998d225c0074e0a095348 config-baseline.plugin.json
37b56008790612b8293930b6a29d74490e98daa90f954fca9d133fcc28645c4c config-baseline.json
75b64c2ea081369ba4306493313a8a4cd48b784145f92fed995e6b77a5df350d config-baseline.core.json
17d64c9799dfa239a49493413f1100bdd9237e9b67aaeae331a4604dbc227023 config-baseline.channel.json
f9d1f50bfa8403891e76cd99dc1357cdece4a71e8ae18a39b190c2a14e6f97b0 config-baseline.plugin.json

View File

@@ -1,2 +1,2 @@
a3ab01b572937539e563aa320ad80135bc701e20fffc43c0351d799590b7a0e0 plugin-sdk-api-baseline.json
9d49587923f8fc4abb16d981bcab54acbf90a3e74ab05933761049e2da0cffe1 plugin-sdk-api-baseline.jsonl
de06fd99257e4b010e54578ea46605c3bc631c31cac5f68aaed4e301f924f8af plugin-sdk-api-baseline.json
1c7a5420c4bcb1ec08544ff43b83fa4d43f3c0dcda597a5a25aa5f5bab0cb199 plugin-sdk-api-baseline.jsonl

View File

@@ -35,6 +35,10 @@
"source": "Heartbeat",
"target": "Heartbeat"
},
{
"source": "LINE",
"target": "LINE"
},
{
"source": "Mintlify",
"target": "Mintlify"

View File

@@ -35,6 +35,10 @@
"source": "Heartbeat",
"target": "Heartbeat"
},
{
"source": "LINE",
"target": "LINE"
},
{
"source": "Mintlify",
"target": "Mintlify"

View File

@@ -35,6 +35,10 @@
"source": "Heartbeat",
"target": "Heartbeat"
},
{
"source": "LINE",
"target": "LINE"
},
{
"source": "Mintlify",
"target": "Mintlify"

View File

@@ -35,6 +35,10 @@
"source": "Heartbeat",
"target": "Heartbeat"
},
{
"source": "LINE",
"target": "LINE"
},
{
"source": "Mintlify",
"target": "Mintlify"

View File

@@ -35,6 +35,10 @@
"source": "Heartbeat",
"target": "Heartbeat"
},
{
"source": "LINE",
"target": "LINE"
},
{
"source": "Mintlify",
"target": "Mintlify"

View File

@@ -35,6 +35,10 @@
"source": "Heartbeat",
"target": "Heartbeat"
},
{
"source": "LINE",
"target": "LINE"
},
{
"source": "Mintlify",
"target": "Mintlify"

View File

@@ -35,6 +35,10 @@
"source": "Heartbeat",
"target": "Heartbeat"
},
{
"source": "LINE",
"target": "LINE"
},
{
"source": "Mintlify",
"target": "Mintlify"

View File

@@ -39,6 +39,10 @@
"source": "Heartbeat",
"target": "Heartbeat"
},
{
"source": "LINE",
"target": "LINE"
},
{
"source": "local loopback",
"target": "local loopback"

View File

@@ -35,6 +35,10 @@
"source": "Heartbeat",
"target": "Heartbeat"
},
{
"source": "LINE",
"target": "LINE"
},
{
"source": "Mintlify",
"target": "Mintlify"

View File

@@ -35,6 +35,10 @@
"source": "Heartbeat",
"target": "Heartbeat"
},
{
"source": "LINE",
"target": "LINE"
},
{
"source": "Mintlify",
"target": "Mintlify"

View File

@@ -35,6 +35,10 @@
"source": "Heartbeat",
"target": "Heartbeat"
},
{
"source": "LINE",
"target": "LINE"
},
{
"source": "Mintlify",
"target": "Mintlify"

View File

@@ -35,6 +35,10 @@
"source": "Heartbeat",
"target": "Heartbeat"
},
{
"source": "LINE",
"target": "LINE"
},
{
"source": "Mintlify",
"target": "Mintlify"

View File

@@ -35,6 +35,10 @@
"source": "Heartbeat",
"target": "Heartbeat"
},
{
"source": "LINE",
"target": "LINE"
},
{
"source": "Mintlify",
"target": "Mintlify"

View File

@@ -35,6 +35,10 @@
"source": "Heartbeat",
"target": "Heartbeat"
},
{
"source": "LINE",
"target": "LINE"
},
{
"source": "Mintlify",
"target": "Mintlify"

View File

@@ -35,6 +35,10 @@
"source": "Heartbeat",
"target": "Heartbeat"
},
{
"source": "LINE",
"target": "LINE"
},
{
"source": "Mintlify",
"target": "Mintlify"

View File

@@ -35,6 +35,10 @@
"source": "Heartbeat",
"target": "Heartbeat"
},
{
"source": "LINE",
"target": "LINE"
},
{
"source": "Mintlify",
"target": "Mintlify"

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,10 @@
"source": "ClawHub",
"target": "ClawHub"
},
{
"source": "ClickClack",
"target": "ClickClack"
},
{
"source": "CLI",
"target": "命令列介面"
@@ -35,14 +39,38 @@
"source": "Heartbeat",
"target": "心跳偵測"
},
{
"source": "Feishu",
"target": "Feishu"
},
{
"source": "IRC",
"target": "IRC"
},
{
"source": "LINE",
"target": "LINE"
},
{
"source": "Mattermost",
"target": "Mattermost"
},
{
"source": "Mintlify",
"target": "Mintlify"
},
{
"source": "Nextcloud Talk",
"target": "Nextcloud Talk"
},
{
"source": "Node",
"target": "節點"
},
{
"source": "Nostr",
"target": "Nostr"
},
{
"source": "OpenClaw",
"target": "OpenClaw"
@@ -55,10 +83,30 @@
"source": "Plugin",
"target": "外掛"
},
{
"source": "QQ Bot",
"target": "QQ Bot"
},
{
"source": "QQBot",
"target": "QQ Bot"
},
{
"source": "QQ bot",
"target": "QQ Bot"
},
{
"source": "SMS",
"target": "SMS"
},
{
"source": "Skills",
"target": "Skills"
},
{
"source": "Synology Chat",
"target": "Synology Chat"
},
{
"source": "Tailscale",
"target": "Tailscale"
@@ -67,12 +115,48 @@
"source": "TaskFlow",
"target": "TaskFlow"
},
{
"source": "Tlon",
"target": "Tlon"
},
{
"source": "Twitch",
"target": "Twitch"
},
{
"source": "Twilio",
"target": "Twilio"
},
{
"source": "TUI",
"target": "終端介面"
},
{
"source": "WeChat",
"target": "微信"
},
{
"source": "Weixin",
"target": "微信"
},
{
"source": "Webhook",
"target": "網路鉤子"
},
{
"source": "Yuanbao",
"target": "騰訊元寶"
},
{
"source": "Zalo",
"target": "Zalo"
},
{
"source": "Zalo Personal",
"target": "Zalo Personal"
},
{
"source": "Zalo personal",
"target": "Zalo Personal"
}
]

View File

@@ -122,6 +122,33 @@ This fires ~56 times per month instead of 01 times per month. OpenClaw use
</Accordion>
</AccordionGroup>
### Command payloads
Use command payloads for deterministic scripts that should run inside the Gateway scheduler without starting a model-backed isolated agent turn. Command jobs execute on the Gateway host, capture stdout/stderr, record the run in cron history, and reuse the same `announce`, `webhook`, and `none` delivery modes as isolated jobs.
<Note>
Command cron is an operator-admin Gateway automation surface, not an agent
`tools.exec` call. Creating, updating, removing, or manually running cron jobs
requires `operator.admin`; scheduled command runs later execute inside the
Gateway process as that admin-authored automation. Agent exec policy such as
`tools.exec.mode`, approval prompts, and per-agent tool allowlists governs
model-visible exec tools, not command cron payloads.
</Note>
```bash
openclaw cron create "*/15 * * * *" \
--name "Queue depth probe" \
--command "scripts/check-queue.sh" \
--command-cwd "/srv/app" \
--announce \
--channel telegram \
--to "-1001234567890"
```
`--command <shell>` stores `argv: ["sh", "-lc", <shell>]`. Use `--command-argv '["node","scripts/report.mjs"]'` when you want exact argv execution without shell parsing. Optional `--command-env KEY=VALUE`, `--command-input`, `--timeout-seconds`, `--no-output-timeout-seconds`, and `--output-max-bytes` fields control the process environment, stdin, and output bounds.
If stdout is non-empty, that text is the delivered result. If stdout is empty and stderr is non-empty, stderr is delivered. If both streams are present, cron delivers a small `stdout:` / `stderr:` block. A zero exit code records the run as `ok`; non-zero exit, signal, timeout, or no-output timeout records `error` and can trigger failure alerts. A command that prints only `NO_REPLY` uses the normal cron silent-token suppression and posts nothing back to chat.
### Payload options for isolated jobs
<ParamField path="--message" type="string" required>
@@ -246,6 +273,17 @@ Failure notifications follow a separate destination path:
--webhook "https://example.invalid/openclaw/cron"
```
</Tab>
<Tab title="Command output">
```bash
openclaw cron create "*/15 * * * *" \
--name "Queue depth probe" \
--command "scripts/check-queue.sh" \
--command-cwd "/srv/app" \
--announce \
--channel telegram \
--to "-1001234567890"
```
</Tab>
</Tabs>
## Webhooks
@@ -432,6 +470,7 @@ Model override note:
- `openclaw cron add|edit --model ...` changes the job's selected model.
- If the model is allowed, that exact provider/model reaches the isolated agent run.
- If it is not allowed or cannot be resolved, cron fails the run with an explicit validation error.
- API `cron.update` payload patches can set `model: null` to clear a stored job model override.
- Configured fallback chains still apply because cron `--model` is a job primary, not a session `/model` override.
- Payload `fallbacks` replaces configured fallbacks for that job; `fallbacks: []` disables fallback and makes the run strict.
- A plain `--model` with no explicit or configured fallback list does not fall through to the agent primary as a silent extra retry target.

View File

@@ -1,5 +1,5 @@
---
summary: "Group chat behavior across surfaces (Discord/iMessage/Matrix/Microsoft Teams/Signal/Slack/Telegram/WhatsApp/Zalo)"
summary: "Group chat behavior across surfaces (Discord/iMessage/Matrix/Microsoft Teams/QQBot/Signal/Slack/Telegram/WhatsApp/Zalo)"
read_when:
- Changing group chat behavior or mention gating
- Scoping mentionPatterns to specific group conversations
@@ -7,7 +7,7 @@ title: "Groups"
sidebarTitle: "Groups"
---
OpenClaw treats group chats consistently across surfaces: Discord, iMessage, Matrix, Microsoft Teams, Signal, Slack, Telegram, WhatsApp, Zalo.
OpenClaw treats group chats consistently across surfaces: Discord, iMessage, Matrix, Microsoft Teams, QQBot, Signal, Slack, Telegram, WhatsApp, Zalo.
For always-on rooms that should provide quiet context unless the agent explicitly sends a visible message, see [Ambient room events](/channels/ambient-room-events).

View File

@@ -221,22 +221,22 @@ If the gateway logs `imessage: dropping group message from chat_id=<id>` or the
## Action parity at a glance
| Action | legacy BlueBubbles | bundled iMessage |
| ---------------------------------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| Send text / SMS fallback | ✅ | ✅ |
| Send media (photo, video, file, voice) | ✅ | ✅ |
| Threaded reply (`reply_to_guid`) | ✅ | ✅ (closes [#51892](https://github.com/openclaw/openclaw/issues/51892)) |
| Tapback (`react`) | ✅ | ✅ |
| Edit / unsend (macOS 13+ recipients) | ✅ | ✅ |
| Send with screen effect | ✅ | ✅ (closes part of [#9394](https://github.com/openclaw/openclaw/issues/9394)) |
| Rich text bold / italic / underline / strikethrough | ✅ | ✅ (typed-run formatting via attributedBody) |
| Rename group / set group icon | ✅ | ✅ |
| Add / remove participant, leave group | ✅ | ✅ |
| Read receipts and typing indicator | ✅ | ✅ (gated on private API probe) |
| Same-sender DM coalescing | ✅ | ✅ (DM-only; opt-in via `channels.imessage.coalesceSameSenderDms`) |
| Catchup of inbound messages received while gateway is down | ✅ (webhook replay + history fetch) | ✅ (opt-in via `channels.imessage.catchup.enabled`; closes [#78649](https://github.com/openclaw/openclaw/issues/78649)) |
| Action | legacy BlueBubbles | bundled iMessage |
| --------------------------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------- |
| Send text / SMS fallback | ✅ | ✅ |
| Send media (photo, video, file, voice) | ✅ | ✅ |
| Threaded reply (`reply_to_guid`) | ✅ | ✅ (closes [#51892](https://github.com/openclaw/openclaw/issues/51892)) |
| Tapback (`react`) | ✅ | ✅ |
| Edit / unsend (macOS 13+ recipients) | ✅ | ✅ |
| Send with screen effect | ✅ | ✅ (closes part of [#9394](https://github.com/openclaw/openclaw/issues/9394)) |
| Rich text bold / italic / underline / strikethrough | ✅ | ✅ (typed-run formatting via attributedBody) |
| Rename group / set group icon | ✅ | ✅ |
| Add / remove participant, leave group | ✅ | ✅ |
| Read receipts and typing indicator | ✅ | ✅ (gated on private API probe) |
| Same-sender DM coalescing | ✅ | ✅ (DM-only; opt-in via `channels.imessage.coalesceSameSenderDms`) |
| Inbound recovery after a restart | ✅ (webhook replay + history fetch) | ✅ (automatic: replay missed via since_rowid + dedupe; wider window on local) |
iMessage catchup is now available as an opt-in feature on the bundled plugin. On gateway startup, if `channels.imessage.catchup.enabled` is `true`, the gateway runs one `chats.list` + per-chat `messages.history` pass against the same JSON-RPC client used by `imsg watch`, replays each missed inbound row through the live dispatch path (allowlists, group policy, debouncer, echo cache), and persists a per-account cursor so subsequent startups pick up where they left off. See [Catching up after gateway downtime](/channels/imessage#catching-up-after-gateway-downtime) for tuning.
iMessage recovers messages missed while the gateway was down: on startup it replays from the last dispatched rowid via `imsg watch.subscribe` `since_rowid` and dedupes by GUID, while a stale-backlog age fence suppresses the Push-flush "backlog bomb". This runs over the `imsg` RPC connection, so it works for remote SSH `cliPath` setups too; local setups get a wider recovery window because they can read `chat.db`. See [Inbound recovery after a bridge or gateway restart](/channels/imessage#inbound-recovery-after-a-bridge-or-gateway-restart).
## Pairing, sessions, and ACP bindings

View File

@@ -9,7 +9,7 @@ title: "iMessage"
<Note>
For OpenClaw iMessage deployments, use `imsg` on a signed-in macOS Messages host. If your Gateway runs on Linux or Windows, point `channels.imessage.cliPath` at an SSH wrapper that runs `imsg` on the Mac.
**Gateway-downtime catchup is opt-in.** When enabled (`channels.imessage.catchup.enabled: true`), the gateway replays inbound messages that landed in `chat.db` while it was offline (crash, restart, Mac sleep) on next startup. Disabled by default — see [Catching up after gateway downtime](#catching-up-after-gateway-downtime). Closes [openclaw#78649](https://github.com/openclaw/openclaw/issues/78649).
**Inbound recovery is automatic.** After a bridge or gateway restart, iMessage replays the messages missed while it was down and suppresses the stale "backlog bomb" Apple can flush after a Push recovery, deduping so nothing is dispatched twice. There is no config to enable — see [Inbound recovery after a bridge or gateway restart](#inbound-recovery-after-a-bridge-or-gateway-restart).
</Note>
<Warning>
@@ -205,11 +205,24 @@ Treat this as a deliberate operational choice, not a default. If your threat mod
The `imsg status --json` output reports `bridge_version`, `rpc_methods`, and per-method `selectors` so you can see what the current build supports before you start.
2. **Disable System Integrity Protection.** This is macOS-version-specific because the underlying Apple requirement depends on the OS and hardware:
- **macOS 10.1310.15 (SierraCatalina):** disable Library Validation via Terminal, reboot to Recovery Mode, run `csrutil disable`, restart.
2. **Disable System Integrity Protection, and (on modern macOS) Library Validation.** Injecting a non-Apple helper dylib into the Apple-signed `Messages.app` needs SIP off **and** library validation relaxed. The Recovery-mode SIP step is macOS-version-specific:
- **macOS 10.13-10.15 (Sierra-Catalina):** disable Library Validation via Terminal, reboot to Recovery Mode, run `csrutil disable`, restart.
- **macOS 11+ (Big Sur and later), Intel:** Recovery Mode (or Internet Recovery), `csrutil disable`, restart.
- **macOS 11+, Apple Silicon:** power-button startup sequence to enter Recovery; on recent macOS versions hold the **Left Shift** key when you click Continue, then `csrutil disable`. Virtual-machine setups follow a separate flow take a VM snapshot first.
- **macOS 26 / Tahoe:** library-validation policies and `imagent` private-entitlement checks have tightened further; `imsg` may need an updated build to keep up. If `imsg launch` injection or specific `selectors` start returning false after a macOS major upgrade, check `imsg`'s release notes before assuming the SIP step succeeded.
- **macOS 11+, Apple Silicon:** power-button startup sequence to enter Recovery; on recent macOS versions hold the **Left Shift** key when you click Continue, then `csrutil disable`. Virtual-machine setups follow a separate flow, so take a VM snapshot first.
**On macOS 11 and later, `csrutil disable` alone is usually not enough.** Apple still enforces library validation against `Messages.app` as a platform binary, so an adhoc-signed helper is rejected (`Library Validation failed: ... platform binary, but mapped file is not`) even with SIP off. After disabling SIP, also disable library validation and reboot:
```bash
sudo defaults write /Library/Preferences/com.apple.security.libraryvalidation.plist DisableLibraryValidation -bool true
```
**macOS 26 (Tahoe), verified on 26.5.1:** SIP off **plus** the `DisableLibraryValidation` command above is sufficient to inject the helper across 26.0 through 26.5.x. **No boot-args are required.** The plist is the decisive factor and the most common missing step when injection fails on Tahoe:
- **With the plist:** `imsg launch` injects and `imsg status` reports `advanced_features: true`.
- **Without the plist (even with SIP off):** `imsg launch` fails with `Failed to launch: Timeout waiting for Messages.app to initialize`. AMFI rejects the adhoc helper at load, so the bridge never becomes ready and the launch times out. That timeout is the symptom most people hit on Tahoe, and the fix is the plist above, not anything more drastic.
This was confirmed with a controlled before/after on macOS 26.5.1 (Apple Silicon): with the plist, the dylib maps into `Messages.app` and the bridge comes up; remove the plist and reboot, and `imsg launch` produces the timeout failure above with the dylib not mapped.
If `imsg launch` injection or specific `selectors` start returning false after a macOS upgrade, this gate is the usual cause. Check your SIP and library-validation state before assuming the SIP step itself failed. If those settings are correct and the bridge still cannot inject, collect `imsg status --json` plus the `imsg launch` output and report it to the `imsg` project instead of weakening additional system-wide security controls.
Follow Apple's Recovery-mode flow for your Mac to disable SIP before running `imsg launch`.
@@ -641,14 +654,14 @@ When a user types a command and a URL together — e.g. `Dump https://example.co
The two rows arrive at OpenClaw ~0.8-2.0 s apart on most setups. Without coalescing, the agent receives the command alone on turn 1, replies (often "send me the URL"), and only sees the URL on turn 2 — at which point the command context is already lost. This is Apple's send pipeline, not anything OpenClaw or `imsg` introduces.
`channels.imessage.coalesceSameSenderDms` opts a DM into merging consecutive same-sender rows into a single agent turn. Group chats continue to dispatch per-message so multi-user turn structure is preserved.
`channels.imessage.coalesceSameSenderDms` opts a DM into buffering consecutive same-sender rows. When `imsg` exposes the structural URL-preview marker `balloon_bundle_id: "com.apple.messages.URLBalloonProvider"` on one of the source rows, OpenClaw merges only that real split-send and keeps any other buffered rows as separate turns. On older `imsg` builds that emit no balloon metadata at all, OpenClaw cannot tell a split-send from separate sends, so it falls back to merging the bucket. That preserves the pre-metadata behavior rather than regressing `Dump <url>` split-sends into two turns. Group chats continue to dispatch per-message so multi-user turn structure is preserved.
<Tabs>
<Tab title="When to enable">
Enable when:
- You ship skills that expect `command + payload` in one message (dump, paste, save, queue, etc.).
- Your users paste URLs, images, or long content alongside commands.
- Your users paste URLs alongside commands.
- You can accept the added DM turn latency (see below).
Leave disabled when:
@@ -689,7 +702,8 @@ The two rows arrive at OpenClaw ~0.8-2.0 s apart on most setups. Without coalesc
</Tab>
<Tab title="Trade-offs">
- **Added latency for DM messages.** With the flag on, every DM (including standalone control commands and single-text follow-ups) waits up to the debounce window before dispatching, in case a payload row is coming. Group-chat messages keep instant dispatch.
- **Precise merging needs current `imsg` payload metadata.** When the URL row includes `balloon_bundle_id`, only that real split-send merges and other buffered rows stay separate. On older `imsg` builds that expose no balloon metadata, OpenClaw falls back to merging the buffered bucket so `Dump <url>` split-sends are not regressed into two turns (interim back-compat, removed once `imsg` coalesces split-sends upstream).
- **Added latency for DM messages.** With the flag on, every DM (including standalone control commands and single-text follow-ups) waits up to the debounce window before dispatching, in case a URL-preview row is coming. Group-chat messages keep instant dispatch.
- **Merged output is bounded.** Merged text caps at 4000 chars with an explicit `…[truncated]` marker; attachments cap at 20; source entries cap at 10 (first-plus-latest retained beyond that). Every source GUID is tracked in `coalescedMessageGuids` for downstream telemetry.
- **DM-only.** Group chats fall through to per-message dispatch so the bot stays responsive when multiple people are typing.
- **Opt-in, per-channel.** Other channels (Telegram, WhatsApp, Slack, …) are unaffected. Legacy BlueBubbles configs that set `channels.bluebubbles.coalesceSameSenderDms` should migrate that value to `channels.imessage.coalesceSameSenderDms`.
@@ -699,77 +713,39 @@ The two rows arrive at OpenClaw ~0.8-2.0 s apart on most setups. Without coalesc
### Scenarios and what the agent sees
| User composes | `chat.db` produces | Flag off (default) | Flag on + 2500 ms window |
| ------------------------------------------------------------------ | --------------------- | --------------------------------------- | ----------------------------------------------------------------------- |
| `Dump https://example.com` (one send) | 2 rows ~1 s apart | Two agent turns: "Dump" alone, then URL | One turn: merged text `Dump https://example.com` |
| `Save this 📎image.jpg caption` (attachment + text) | 2 rows | Two turns (attachment dropped on merge) | One turn: text + image preserved |
| `/status` (standalone command) | 1 row | Instant dispatch | **Wait up to window, then dispatch** |
| URL pasted alone | 1 row | Instant dispatch | Instant dispatch (only one entry in bucket) |
| Text + URL sent as two deliberate separate messages, minutes apart | 2 rows outside window | Two turns | Two turns (window expires between them) |
| Rapid flood (>10 small DMs inside window) | N rows | N turns | One turn, bounded output (first + latest, text/attachment caps applied) |
| Two people typing in a group chat | N rows from M senders | M+ turns (one per sender bucket) | M+ turns — group chats are not coalesced |
The "Flag on" column shows behavior on an `imsg` build that emits `balloon_bundle_id`. On older `imsg` builds that emit no balloon metadata at all, the rows below marked "Two turns" / "N turns" instead fall back to a legacy merge (one turn): OpenClaw cannot structurally tell a split-send from separate sends, so it preserves the pre-metadata merge. Precise separation activates once the build emits balloon metadata.
## Catching up after gateway downtime
| User composes | `chat.db` produces | Flag off (default) | Flag on + window (imsg emits balloon metadata) |
| ------------------------------------------------------------------ | ----------------------------------- | --------------------------------------- | ------------------------------------------------ |
| `Dump https://example.com` (one send) | 2 rows ~1 s apart | Two agent turns: "Dump" alone, then URL | One turn: merged text `Dump https://example.com` |
| `Save this 📎image.jpg caption` (attachment + text) | 2 rows without URL balloon metadata | Two turns | Two turns (legacy merge on metadata-less builds) |
| `/status` (standalone command) | 1 row | Instant dispatch | **Wait up to window, then dispatch** |
| URL pasted alone | 1 row | Instant dispatch | Wait up to window, then dispatch |
| Text + URL sent as two deliberate separate messages, minutes apart | 2 rows outside window | Two turns | Two turns (window expires between them) |
| Rapid flood (>10 small DMs inside window) | N rows without URL balloon metadata | N turns | N turns (legacy merge on metadata-less builds) |
| Two people typing in a group chat | N rows from M senders | M+ turns (one per sender bucket) | M+ turns — group chats are not coalesced |
When the gateway is offline (crash, restart, Mac sleep, machine off), `imsg watch` resumes from the current `chat.db` state once the gateway comes back up — anything that arrived during the gap is, by default, never seen. Catchup replays those messages on the next startup so the agent does not silently miss inbound traffic.
## Inbound recovery after a bridge or gateway restart
Catchup is **disabled by default**. Enable it per channel:
iMessage recovers messages missed while the gateway was down, and at the same time suppresses the stale "backlog bomb" Apple can flush after a Push recovery. The default behavior is always on, built on the inbound dedupe.
```ts
channels: {
imessage: {
catchup: {
enabled: true, // master switch (default: false)
maxAgeMinutes: 120, // skip rows older than now - 2h (default: 120, clamp 1..720)
perRunLimit: 50, // max rows replayed per startup (default: 50, clamp 1..500)
firstRunLookbackMinutes: 30, // first run with no cursor: look back 30 min (default: 30)
maxFailureRetries: 10, // give up on a wedged guid after 10 dispatch failures (default: 10)
},
},
}
```
- **Replay dedupe.** Every dispatched inbound message is recorded by its Apple GUID in persistent plugin state (`imessage.inbound-dedupe`), claimed at ingestion and committed after handling (released on a transient failure so it can retry). Anything already handled is dropped instead of dispatched twice. This is what lets recovery replay aggressively without per-message bookkeeping.
- **Downtime recovery.** On startup the monitor remembers the last dispatched `chat.db` rowid (a persisted per-account cursor) and passes it to `imsg watch.subscribe` as `since_rowid`, so imsg replays the rows that landed while the gateway was down, then tails live. Replay is bounded to the most recent rows and to messages up to ~2 hours old, and the dedupe drops anything already handled.
- **Stale-backlog age fence.** Rows above the startup boundary are genuinely live; one whose send date is more than ~15 minutes older than its arrival is the Push-flush backlog and is suppressed. Replayed rows (at or below the boundary) use the wider recovery window instead, so a recently-missed message is delivered while ancient history is not.
### How it runs
Recovery works over both local and remote `cliPath` setups, because `since_rowid` replay runs over the same `imsg` RPC connection. The difference is the window: when the gateway can read `chat.db` (local), it anchors the startup rowid boundary, caps the replay span, and delivers missed messages up to a couple of hours old. Over a remote SSH `cliPath` it cannot read the database, so the replay is uncapped and every row uses the live age fence — it still recovers recently-missed messages and still suppresses old backlog, just with the narrower live window. Run the gateway on the Messages Mac for the wider recovery window.
One pass per `monitorIMessageProvider` startup, sequenced as `imsg launch` ready → `watch.subscribe` → `performIMessageCatchup` → live dispatch loop. Catchup itself uses `chats.list` + per-chat `messages.history` against the same JSON-RPC client used by `imsg watch`. Anything that arrives during the catchup pass flows through live dispatch normally; the existing inbound-dedupe cache absorbs any overlap with replayed rows.
### Operator-visible signal
Each replayed row is fed through the live dispatch path (`evaluateIMessageInbound` + `dispatchInboundMessage`), so allowlists, group policy, debouncer, echo cache, and read receipts behave identically on replayed and live messages.
### Cursor and retry semantics
Catchup keeps a per-account cursor in SQLite plugin state:
```json
{
"lastSeenMs": 1717900800000,
"lastSeenRowid": 482910,
"updatedAt": 1717900801234,
"failureRetries": { "<guid>": 1 }
}
```
- The cursor advances on each successful dispatch and is held when a row's dispatch throws — the next startup retries the same row from the held cursor.
- After the startup catchup query succeeds, later live-handled rows also advance the same cursor so a gateway restart does not replay messages that were already handled live. Live cursor writes do not jump past catchup failures that are still below `maxFailureRetries`.
- After `maxFailureRetries` consecutive throws against the same `guid`, catchup logs a `warn` and force-advances the cursor past the wedged message so subsequent startups can make progress.
- Already-given-up guids are skipped on sight (no dispatch attempt) on later runs and counted under `skippedGivenUp` in the run summary.
- `openclaw doctor --fix` imports legacy `<openclawStateDir>/imessage/catchup/*.json` cursor files into SQLite plugin state and archives the old files.
### Operator-visible signals
Suppressed backlog is logged at the default level, never silently dropped (the `recovery` flag shows which window applied):
```
imessage catchup: replayed=N skippedFromMe=… skippedGivenUp=… failed=… givenUp=… fetchedCount=…
imessage catchup: giving up on guid=<guid> after <N> failures; advancing cursor past it
imessage catchup: fetched <X> rows across chats, capped to perRunLimit=<Y>
imessage: suppressed stale inbound backlog account=<id> sent=<iso> recovery=<bool> (<N> suppressed since start)
```
A `WARN ... capped to perRunLimit` line means a single startup did not drain the full backlog. Raise `perRunLimit` (max 500) if your gaps regularly exceed the default 50-row pass.
### Migration
### When to leave it off
- Gateway runs continuously with watchdog auto-restart and gaps are always < a few seconds — the default of off is fine.
- DM volume is low and missed messages would not change agent behavior — the `firstRunLookbackMinutes` initial window can dispatch surprising old context on first enable.
When you turn catchup on, the first startup with no cursor only looks back `firstRunLookbackMinutes` (30 min default), not the full `maxAgeMinutes` window — this avoids replaying a long history of pre-enable messages.
`channels.imessage.catchup.*` is deprecated — downtime recovery is now automatic and needs no config for new setups. Existing configs with `catchup.enabled: true` remain honored as a compatibility profile for the recovery replay window. Disabled catchup blocks (`enabled: false` or no `enabled: true`) are retired; `openclaw doctor --fix` removes those.
## Troubleshooting

View File

@@ -152,7 +152,7 @@ to a group, then mention it or configure the group to run without a mention.
"*": {
requireMention: true,
historyLimit: 50,
toolPolicy: "restricted",
tools: { deny: ["exec", "read", "write"] },
},
GROUP_OPENID: {
name: "Release room",
@@ -174,10 +174,13 @@ settings include:
- `requireMention`: require an @mention before the bot replies. Default: `true`.
- `ignoreOtherMentions`: drop messages that mention someone else but not the bot.
- `historyLimit`: keep recent non-mention group messages as context for the next mentioned turn. Set `0` to disable.
- `toolPolicy`: `full`, `restricted`, or `none` for group-scoped tools.
- `tools`: allow/deny tools for the whole group.
- `toolsBySender`: per-sender group tool overrides; see [Groups](/channels/groups#groupchannel-tool-restrictions-optional).
- `name`: friendly label used in logs and group context.
- `prompt`: per-group behavior prompt appended to the agent context.
Old QQBot `toolPolicy` entries are retired. Run `openclaw doctor --fix` to migrate them to `tools`.
Activation modes are `mention` and `always`. `requireMention: true` maps to
`mention`; `requireMention: false` maps to `always`. A session-level activation
override, when present, wins over config.

View File

@@ -34,6 +34,27 @@ openclaw cron create "0 18 * * 1-5" \
--webhook "https://example.invalid/openclaw/cron"
```
Use `--command` for deterministic shell-style jobs that should run inside OpenClaw cron without starting an isolated agent/model run:
<Note>
Command cron jobs are admin-authored Gateway automation. Creating, editing,
removing, or manually running them requires `operator.admin`; the scheduled run
later executes in the Gateway process, not as an agent `tools.exec` tool call.
`tools.exec.*` and exec approvals still govern model-visible exec tools.
</Note>
```bash
openclaw cron create "*/15 * * * *" \
--name "Queue depth probe" \
--command "scripts/check-queue.sh" \
--command-cwd "/srv/app" \
--announce \
--channel telegram \
--to "-1001234567890"
```
`--command <shell>` stores `argv: ["sh", "-lc", <shell>]`. Use `--command-argv '["node","scripts/report.mjs"]'` for exact argv execution. Command jobs capture stdout/stderr, record normal cron history, and route output through the same `announce`, `webhook`, or `none` delivery modes as isolated jobs. A command that prints only `NO_REPLY` is suppressed.
## Sessions
`--session` accepts `main`, `isolated`, `current`, or `session:<id>`.
@@ -92,6 +113,10 @@ Note: isolated cron runs treat run-level agent failures as job errors even when
no reply payload is produced, so model/provider failures still increment error
counters and trigger failure notifications.
Command cron jobs do not start an isolated agent turn. A zero exit code records
`ok`; non-zero exit, signal, timeout, or no-output timeout records `error` and
can trigger the same failure notification path.
If an isolated run times out before the first model request, `openclaw cron show`
and `openclaw cron runs` include a phase-specific error such as
`setup timed out before runner start` or
@@ -252,6 +277,21 @@ openclaw cron create "0 7 * * *" \
`--light-context` applies to isolated agent-turn jobs only. For cron runs, lightweight mode keeps bootstrap context empty instead of injecting the full workspace bootstrap set.
Create a command job with exact argv, cwd, env, stdin, and output limits:
```bash
openclaw cron create "*/30 * * * *" \
--name "Position export" \
--command-argv '["node","scripts/export-position.mjs"]' \
--command-cwd "/srv/app" \
--command-env "NODE_ENV=production" \
--command-input '{"mode":"summary"}' \
--timeout-seconds 120 \
--no-output-timeout-seconds 30 \
--output-max-bytes 65536 \
--webhook "https://example.invalid/openclaw/cron"
```
## Common admin commands
Manual run and inspection:

View File

@@ -230,7 +230,7 @@ Notes:
- Doctor removes retired `plugins.entries.codex.config.codexDynamicToolsProfile`; Codex app-server always keeps Codex-native workspace tools native.
- Doctor warns when skills allowed for the default agent are unavailable in the current runtime environment because bins, env vars, config, or OS requirements are missing. `doctor --fix` can disable those unavailable skills with `skills.entries.<skill>.enabled=false`; install/configure the missing requirement instead when you want to keep the skill active.
- If sandbox mode is enabled but Docker is unavailable, doctor reports a high-signal warning with remediation (`install Docker` or `openclaw config set agents.defaults.sandbox.mode off`).
- If legacy sandbox registry files (`~/.openclaw/sandbox/containers.json` or `~/.openclaw/sandbox/browsers.json`) are present, doctor reports them; `openclaw doctor --fix` migrates valid entries into sharded registry directories and quarantines invalid legacy files.
- If legacy sandbox registry files or shard directories are present (`~/.openclaw/sandbox/containers.json`, `~/.openclaw/sandbox/browsers.json`, `~/.openclaw/sandbox/containers/`, or `~/.openclaw/sandbox/browsers/`), doctor reports them; `openclaw doctor --fix` migrates valid entries into SQLite and quarantines invalid legacy files.
- If `gateway.auth.token`/`gateway.auth.password` are SecretRef-managed and unavailable in the current command path, doctor reports a read-only warning and does not write plaintext fallback credentials. For exec-backed SecretRefs, doctor skips execution unless `--allow-exec` is present.
- If channel SecretRef inspection fails in a fix path, doctor continues and reports a warning instead of exiting early.
- After state-directory migrations, doctor warns when enabled default Telegram or Discord accounts depend on env fallback and `TELEGRAM_BOT_TOKEN` or `DISCORD_BOT_TOKEN` is unavailable to the doctor process.

View File

@@ -166,12 +166,12 @@ Prefer `openclaw sandbox recreate` over manual backend-specific cleanup. It uses
## Registry migration
OpenClaw stores sandbox runtime metadata as one JSON shard per container/browser entry under the sandbox state directory. Older installs may still have monolithic legacy files:
OpenClaw stores sandbox runtime metadata in the shared SQLite state database. Older installs may still have legacy sandbox registry files:
- `~/.openclaw/sandbox/containers.json`
- `~/.openclaw/sandbox/browsers.json`
Regular sandbox runtime reads do not rewrite those files. Run `openclaw doctor --fix` to migrate valid legacy entries into the sharded registry directories. Invalid legacy files are quarantined so one bad old registry cannot hide current runtime entries.
Some upgrades may also have one JSON shard per container/browser under `~/.openclaw/sandbox/containers/` or `~/.openclaw/sandbox/browsers/`. Regular sandbox runtime reads do not rewrite those legacy sources. Run `openclaw doctor --fix` to migrate valid legacy entries into SQLite. Invalid legacy files are quarantined so one bad old registry cannot hide current runtime entries.
## Configuration

View File

@@ -30,7 +30,7 @@ Treat them differently from normal config:
## Local model lean mode
`agents.defaults.experimental.localModelLean: true` is a pressure-release valve for weaker local-model setups. When it is on, OpenClaw drops three default tools — `browser`, `cron`, and `message` — from the agent's tool surface for every turn. Nothing else changes. Use `agents.list[].experimental.localModelLean` to enable or disable the same behavior for one configured agent.
`agents.defaults.experimental.localModelLean: true` is a pressure-release valve for weaker local-model setups. When it is on, OpenClaw drops three default tools — `browser`, `cron`, and `message` — from the agent's tool surface for every turn. It also defaults that run to structured Tool Search controls when `tools.toolSearch` is not explicitly configured, so larger plugin, MCP, or client tool catalogs stay behind `tool_search`, `tool_describe`, and `tool_call` instead of being dumped into the prompt. Runs that require direct `message` delivery keep that tool direct instead of enabling the lean-mode Tool Search default. Use `agents.list[].experimental.localModelLean` to enable or disable the same behavior for one configured agent.
### Why these three tools
@@ -40,7 +40,7 @@ These three tools have the largest descriptions and the most parameter shapes in
- The model picking the right tool vs. emitting malformed tool calls because there are too many similar-looking schemas.
- The Chat Completions adapter staying inside the server's structured-output limits vs. tripping a 400 on tool-call payload size.
Removing them does not silently rewire OpenClaw — it just makes the tool list shorter. The model still has `read`, `write`, `edit`, `exec`, `apply_patch`, web search/fetch (when configured), memory, and session/agent tools available.
Removing them does not silently rewire OpenClaw — it just makes the direct tool list shorter. The model still has `read`, `write`, `edit`, `exec`, `apply_patch`, web search/fetch (when configured), memory, and session/agent tools available. Extra catalogs remain callable through Tool Search unless you explicitly set `tools.toolSearch: false`.
### When to turn it on
@@ -56,6 +56,8 @@ If your backend handles the full default runtime cleanly, leave this off. Lean m
Lean mode also does not replace `tools.profile`, `tools.allow`/`tools.deny`, or the model `compat.supportsTools: false` escape hatch. If you need a permanent narrower tool surface for a specific agent, prefer those stable knobs over the experimental flag.
If you already tune Tool Search globally, OpenClaw leaves that operator config alone. Set `tools.toolSearch: false` to opt out of the lean-mode Tool Search default.
### Enable
```json5
@@ -94,7 +96,7 @@ Restart the Gateway after changing the flag, then confirm the trimmed tool list
openclaw status --deep
```
The deep status output lists the active agent tools; `browser`, `cron`, and `message` should be absent when lean mode is on.
The deep status output lists the active agent tools; `browser`, `cron`, and `message` should be absent when lean mode is on unless the current delivery mode forces direct `message` replies.
## Experimental does not mean hidden

View File

@@ -532,7 +532,7 @@ MiniMax is configured via `models.providers` because it uses custom endpoints:
See [/providers/minimax](/providers/minimax) for setup details, model options, and config snippets.
<Note>
On MiniMax's Anthropic-compatible streaming path, OpenClaw disables thinking by default unless you explicitly set it, and `/fast on` rewrites `MiniMax-M2.7` to `MiniMax-M2.7-highspeed`.
On MiniMax's Anthropic-compatible streaming path, OpenClaw disables thinking by default for the M2.x family unless you explicitly set it; MiniMax-M3 (and M3.x) stays on the provider's omitted/adaptive thinking path by default. `/fast on` rewrites `MiniMax-M2.7` to `MiniMax-M2.7-highspeed`.
</Note>
Plugin-owned capability split:

View File

@@ -48,6 +48,7 @@ script aliases; both forms are supported.
| `qa telegram` | Live transport lane against a real private Telegram group. |
| `qa discord` | Live transport lane against a real private Discord guild channel. |
| `qa slack` | Live transport lane against a real private Slack channel. |
| `qa whatsapp` | Live transport lane against real WhatsApp Web accounts. |
| `qa mantis` | Before and after verification runner for live transport bugs, with Discord status-reactions evidence, Crabbox desktop/browser smoke, and Slack-in-VNC smoke. See [Mantis](/concepts/mantis) and [Mantis Slack Desktop Runbook](/concepts/mantis-slack-desktop-runbook). |
## Operator flow
@@ -168,15 +169,16 @@ decision still comes from the Discord REST oracle.
CI uses the same command surface in `.github/workflows/qa-live-transports-convex.yml`. Scheduled and default manual runs execute the fast Matrix profile with live frontier credentials, `--fast`, and `OPENCLAW_QA_MATRIX_NO_REPLY_WINDOW_MS=3000`. Manual `matrix_profile=all` fans out into the five profile shards so the exhaustive catalog can run in parallel while keeping one artifact directory per shard.
For transport-real Telegram, Discord, and Slack smoke lanes:
For transport-real Telegram, Discord, Slack, and WhatsApp smoke lanes:
```bash
pnpm openclaw qa telegram
pnpm openclaw qa discord
pnpm openclaw qa slack
pnpm openclaw qa whatsapp
```
They target a pre-existing real channel with two bots (driver + SUT). Required env vars, scenario lists, output artifacts, and the Convex credential pool are documented in [Telegram, Discord, and Slack QA reference](#telegram-discord-and-slack-qa-reference) below.
They target a pre-existing real channel with two bots or accounts (driver + SUT). Required env vars, scenario lists, output artifacts, and the Convex credential pool are documented in [Telegram, Discord, Slack, and WhatsApp QA reference](#telegram-discord-slack-and-whatsapp-qa-reference) below.
For a full Slack desktop VM run with VNC rescue, run:
@@ -276,10 +278,10 @@ coverage helpers, and scenario-selection helper from
| Telegram | x | x | x | | | | | | | x | |
| Discord | x | x | x | | | | | | | | x |
| Slack | x | x | x | x | x | x | x | x | | | |
| WhatsApp | x | x | | x | x | x | | | x | x | |
This keeps `qa-channel` as the broad product-behavior suite while Matrix,
Telegram, and future live transports share one explicit transport-contract
checklist.
Telegram, and other live transports share one explicit transport-contract checklist.
For a disposable Linux VM lane without bringing Docker into the QA path, run:
@@ -308,25 +310,25 @@ guest: env-based provider keys, the QA live provider config path, and
`CODEX_HOME` when present. Keep `--output-dir` under the repo root so the guest
can write back through the mounted workspace.
## Telegram, Discord, and Slack QA reference
## Telegram, Discord, Slack, and WhatsApp QA reference
Matrix has a [dedicated page](/concepts/qa-matrix) because of its scenario count and Docker-backed homeserver provisioning. Telegram, Discord, and Slack are smaller - a handful of scenarios each, no profile system, against pre-existing real channels - so their reference lives here.
Matrix has a [dedicated page](/concepts/qa-matrix) because of its scenario count and Docker-backed homeserver provisioning. Telegram, Discord, Slack, and WhatsApp run against pre-existing real transports, so their reference lives here.
### Shared CLI flags
These lanes register through `extensions/qa-lab/src/live-transports/shared/live-transport-cli.ts` and accept the same flags:
| Flag | Default | Description |
| ------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `--scenario <id>` | - | Run only this scenario. Repeatable. |
| `--output-dir <path>` | `<repo>/.artifacts/qa-e2e/{telegram,discord,slack}-<timestamp>` | Where reports/summary/observed messages and the output log are written. Relative paths resolve against `--repo-root`. |
| `--repo-root <path>` | `process.cwd()` | Repository root when invoking from a neutral cwd. |
| `--sut-account <id>` | `sut` | Temporary account id inside the QA gateway config. |
| `--provider-mode <mode>` | `live-frontier` | `mock-openai` or `live-frontier` (legacy `live-openai` still works). |
| `--model <ref>` / `--alt-model <ref>` | provider default | Primary/alternate model refs. |
| `--fast` | off | Provider fast mode where supported. |
| `--credential-source <env\|convex>` | `env` | See [Convex credential pool](#convex-credential-pool). |
| `--credential-role <maintainer\|ci>` | `ci` in CI, `maintainer` otherwise | Role used when `--credential-source convex`. |
| Flag | Default | Description |
| ------------------------------------- | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `--scenario <id>` | - | Run only this scenario. Repeatable. |
| `--output-dir <path>` | `<repo>/.artifacts/qa-e2e/<transport>-<timestamp>` | Where reports/summary/observed messages and the output log are written. Relative paths resolve against `--repo-root`. |
| `--repo-root <path>` | `process.cwd()` | Repository root when invoking from a neutral cwd. |
| `--sut-account <id>` | `sut` | Temporary account id inside the QA gateway config. |
| `--provider-mode <mode>` | `live-frontier` | `mock-openai` or `live-frontier` (legacy `live-openai` still works). |
| `--model <ref>` / `--alt-model <ref>` | provider default | Primary/alternate model refs. |
| `--fast` | off | Provider fast mode where supported. |
| `--credential-source <env\|convex>` | `env` | See [Convex credential pool](#convex-credential-pool). |
| `--credential-role <maintainer\|ci>` | `ci` in CI, `maintainer` otherwise | Role used when `--credential-source convex`. |
Each lane exits non-zero on any failed scenario. `--allow-failures` writes artifacts without setting a failing exit code.
@@ -688,22 +690,52 @@ Required env when `--credential-source env`:
Optional:
- `OPENCLAW_QA_WHATSAPP_GROUP_JID` enables `whatsapp-mention-gating`.
- `OPENCLAW_QA_WHATSAPP_GROUP_JID` enables group scenarios such as
`whatsapp-mention-gating` and `whatsapp-group-allowlist-block`.
- `OPENCLAW_QA_WHATSAPP_CAPTURE_CONTENT=1` keeps message bodies in
observed-message artifacts.
Scenarios (`extensions/qa-lab/src/live-transports/whatsapp/whatsapp-live.runtime.ts`):
Scenario catalog (`extensions/qa-lab/src/live-transports/whatsapp/whatsapp-live.runtime.ts`):
- `whatsapp-canary`
- `whatsapp-pairing-block`
- `whatsapp-mention-gating`
- `whatsapp-approval-exec-native` - opt-in native WhatsApp exec approval
scenario. Requests an exec approval through the gateway, verifies the
WhatsApp message has native reaction approval affordances, resolves it, and
verifies the resolved WhatsApp follow-up.
- `whatsapp-approval-plugin-native` - opt-in native WhatsApp plugin approval
scenario. Enables exec and plugin approval forwarding together, then verifies
the same pending/resolved native WhatsApp path.
- Baseline and group gating: `whatsapp-canary`, `whatsapp-pairing-block`,
`whatsapp-mention-gating`, `whatsapp-top-level-reply-shape`,
`whatsapp-restart-resume`, `whatsapp-group-allowlist-block`.
- Native commands: `whatsapp-help-command`, `whatsapp-status-command`,
`whatsapp-commands-command`, `whatsapp-tools-compact-command`,
`whatsapp-whoami-command`, `whatsapp-context-command`,
`whatsapp-native-new-command`.
- Reply and final-output behavior: `whatsapp-tool-only-usage-footer`,
`whatsapp-reply-to-message`, `whatsapp-reply-context-isolation`,
`whatsapp-reply-delivery-shape`, `whatsapp-stream-final-message-accounting`.
- Inbound media and structured messages: `whatsapp-inbound-image-caption`,
`whatsapp-audio-preflight`, `whatsapp-inbound-structured-messages`,
`whatsapp-group-audio-gating`. These send real WhatsApp image, audio,
document, location, contact, and sticker events through the driver.
- Outbound Gateway and message action coverage:
`whatsapp-outbound-media-matrix`,
`whatsapp-outbound-document-preserves-filename`, `whatsapp-outbound-poll`,
`whatsapp-message-actions`.
- Access-control coverage: `whatsapp-access-control-dm-open`,
`whatsapp-access-control-dm-disabled`, `whatsapp-access-control-group-open`,
`whatsapp-access-control-group-disabled`, `whatsapp-group-allowlist-block`.
- Native approvals: `whatsapp-approval-exec-deny-native`,
`whatsapp-approval-exec-native`, `whatsapp-approval-exec-reaction-native`,
`whatsapp-approval-plugin-native`.
- Status reactions: `whatsapp-status-reactions`.
The catalog currently contains 35 scenarios. The `live-frontier` default lane is
kept small at 8 scenarios for fast smoke coverage. The `mock-openai` default
lane runs 29 deterministic scenarios through the real WhatsApp transport while
mocking only model output. Approval scenarios and a few heavier/blocking checks
remain explicit by scenario id.
The WhatsApp QA driver observes structured live events (`text`, `media`,
`location`, `reaction`, and `poll`) and can actively send media, polls,
contacts, locations, and stickers. QA Lab imports that driver through the
`@openclaw/whatsapp/api.js` package surface instead of reaching into private
WhatsApp runtime files. Message content is redacted by default. Outbound
poll and upload-file coverage run through deterministic gateway `poll` and
`message.action` calls instead of model-prompt-only tool invocation.
Output artifacts:

View File

@@ -255,10 +255,11 @@ See [Date & Time](/date-time) for full behavior details.
## Skills
When eligible skills exist, OpenClaw injects a compact **available skills list**
(`formatSkillsForPrompt`) that includes the **file path** for each skill. The
prompt instructs the model to use `read` to load the SKILL.md at the listed
location (workspace, managed, or bundled). If no skills are eligible, the
Skills section is omitted.
(`formatSkillsForPrompt`) that includes the **file path** and content-derived
`<version>` marker for each skill. The prompt instructs the model to use `read`
to load the SKILL.md at the listed location (workspace, managed, or bundled),
and to re-read a skill when its `<version>` differs from a previous turn. If no
skills are eligible, the Skills section is omitted.
Native Codex turns receive this list as turn-scoped collaboration developer
instructions instead of per-turn user input, except lightweight cron turns that
@@ -283,6 +284,7 @@ that guidance directly in every tool description.
<name>...</name>
<description>...</description>
<location>...</location>
<version>sha256:...</version>
</skill>
</available_skills>
```

View File

@@ -624,9 +624,6 @@ Before relying on an SSH wrapper for production sends, verify an outbound `imsg
sendWithEffect: true,
sendAttachment: true,
},
catchup: {
enabled: false,
},
},
},
}
@@ -642,7 +639,7 @@ Before relying on an SSH wrapper for production sends, verify an outbound `imsg
- `channels.imessage.configWrites`: allow or deny iMessage-initiated config writes.
- `channels.imessage.actions.*`: enable private API actions that are also gated by `imsg status` / `openclaw channels status --probe`.
- `channels.imessage.includeAttachments` is off by default; set it to `true` before expecting inbound media in agent turns.
- `channels.imessage.catchup.enabled`: opt in to replaying inbound messages that arrived while the Gateway was down.
- Inbound recovery after a bridge/gateway restart is automatic (GUID dedupe plus a stale-backlog age fence). Existing `channels.imessage.catchup.enabled: true` configs are still honored as a deprecated compatibility profile.
- `channels.imessage.groups`: group registry and per-group settings. With `groupPolicy: "allowlist"`, configure either explicit `chat_id` keys or a `"*"` wildcard entry so group messages can pass the registry gate.
- Top-level `bindings[]` entries with `type: "acp"` can bind iMessage conversations to persistent ACP sessions. Use a normalized handle or explicit chat target (`chat_id:*`, `chat_guid:*`, `chat_identifier:*`) in `match.peer.id`. Shared field semantics: [ACP Agents](/tools/acp-agents#persistent-channel-bindings).

View File

@@ -569,6 +569,7 @@ Configuring a custom/local provider `baseUrl` is also the narrow network trust d
- `models.providers.*.models.*.compat.requiresStringContent`: optional compatibility hint for string-only OpenAI-compatible chat endpoints. When `true`, OpenClaw flattens pure text `messages[].content` arrays into plain strings before sending the request.
- `models.providers.*.models.*.compat.strictMessageKeys`: optional compatibility hint for strict OpenAI-compatible chat endpoints. When `true`, OpenClaw strips outgoing Chat Completions message objects to `role` and `content` before sending the request.
- `models.providers.*.models.*.compat.thinkingFormat`: optional thinking payload hint. Use `"together"` for Together-style `reasoning.enabled`, `"qwen"` for top-level `enable_thinking`, or `"qwen-chat-template"` for `chat_template_kwargs.enable_thinking` on Qwen-family OpenAI-compatible servers that support request-level chat-template kwargs, such as vLLM. Configured vLLM Qwen models expose binary `/think` choices (`off`, `on`) for these formats.
- `models.providers.*.models.*.compat.requiresReasoningContentOnAssistantMessages`: optional compatibility hint for DeepSeek-style Chat Completions backends that require prior assistant messages to keep `reasoning_content` on replay. When `true`, OpenClaw preserves that field on outgoing assistant messages. Use this when wiring a custom DeepSeek-compatible proxy that rejects requests after stripped reasoning. Default `false`.
</Accordion>
<Accordion title="Amazon Bedrock discovery">
@@ -680,7 +681,7 @@ Interactive custom-provider onboarding infers image input for common vision mode
}
```
Set `MINIMAX_API_KEY`. Shortcuts: `openclaw onboard --auth-choice minimax-global-api` or `openclaw onboard --auth-choice minimax-cn-api`. The model catalog defaults to M3 and also includes the M2.7 variants. On the Anthropic-compatible streaming path, OpenClaw disables MiniMax thinking by default unless you explicitly set `thinking` yourself. `/fast on` or `params.fastMode: true` rewrites `MiniMax-M2.7` to `MiniMax-M2.7-highspeed`.
Set `MINIMAX_API_KEY`. Shortcuts: `openclaw onboard --auth-choice minimax-global-api` or `openclaw onboard --auth-choice minimax-cn-api`. The model catalog defaults to M3 and also includes the M2.7 variants. On the Anthropic-compatible streaming path, OpenClaw disables MiniMax M2.x thinking by default unless you explicitly set `thinking` yourself; MiniMax-M3 (and M3.x) stays on the provider's omitted/adaptive thinking path by default. `/fast on` or `params.fastMode: true` rewrites `MiniMax-M2.7` to `MiniMax-M2.7-highspeed`.
</Accordion>
<Accordion title="Moonshot AI (Kimi)">

View File

@@ -542,7 +542,7 @@ See [Inferred commitments](/concepts/commitments).
tools: {
// Additional /tools/invoke HTTP denies
deny: ["browser"],
// Remove tools from the default HTTP deny list
// Remove tools from the default HTTP deny list for owner/admin callers
allow: ["gateway"],
},
push: {
@@ -610,7 +610,10 @@ See [Inferred commitments](/concepts/commitments).
- `gateway.nodes.pairing.autoApproveCidrs`: optional CIDR/IP allowlist for auto-approving first-time node device pairing with no requested scopes. It is disabled when unset. This does not auto-approve operator/browser/Control UI/WebChat pairing, and it does not auto-approve role, scope, metadata, or public-key upgrades.
- `gateway.nodes.allowCommands` / `gateway.nodes.denyCommands`: global allow/deny shaping for declared node commands after pairing and platform allowlist evaluation. Use `allowCommands` to opt into dangerous node commands such as `camera.snap`, `camera.clip`, and `screen.record`; `denyCommands` removes a command even if a platform default or explicit allow would otherwise include it. After a node changes its declared command list, reject and re-approve that device pairing so the gateway stores the updated command snapshot.
- `gateway.tools.deny`: extra tool names blocked for HTTP `POST /tools/invoke` (extends default deny list).
- `gateway.tools.allow`: remove tool names from the default HTTP deny list.
- `gateway.tools.allow`: remove tool names from the default HTTP deny list for
owner/admin callers. This does not upgrade identity-bearing `operator.write`
callers into owner/admin access; `cron`, `gateway`, and `nodes` remain
unavailable to non-owner callers even when allowlisted.
</Accordion>

View File

@@ -315,7 +315,7 @@ If the model loads cleanly but full agent turns misbehave, work top-down — con
openclaw infer model run --gateway --model <provider/model> --prompt "Reply with exactly: pong" --json
```
3. **Try lean mode.** If both probes pass but real agent turns fail with malformed tool calls or oversized prompts, enable `agents.defaults.experimental.localModelLean: true`. It drops the three heaviest default tools (`browser`, `cron`, `message`) so the prompt shape is smaller and less brittle. See [Experimental Features → Local model lean mode](/concepts/experimental-features#local-model-lean-mode) for the full explanation, when to use it, and how to confirm it is on.
3. **Try lean mode.** If both probes pass but real agent turns fail with malformed tool calls or oversized prompts, enable `agents.defaults.experimental.localModelLean: true`. It drops the three heaviest default tools (`browser`, `cron`, `message`) and defaults larger tool catalogs behind structured Tool Search controls, except for runs that must keep direct `message` delivery semantics. See [Experimental Features → Local model lean mode](/concepts/experimental-features#local-model-lean-mode) for the full explanation, when to use it, and how to confirm it is on.
4. **Disable tools entirely as a last resort.** If lean mode is not enough, set `models.providers.<provider>.models[].compat.supportsTools: false` for that model entry. The agent will then operate without tool calls on that model.

View File

@@ -580,6 +580,9 @@ terminal summary, and sanitized error text.
`idempotencyKey` are optional.
- If both `sessionKey` and `agentId` are present, the resolved session agent must match
`agentId`.
- Owner-only core wrappers such as `cron`, `gateway`, and `nodes` require
owner/admin identity (`operator.admin`) even though the `tools.invoke`
method itself is `operator.write`.
- The response is an SDK-facing envelope with `ok`, `toolName`, optional `output`, and typed
`error` fields. Approval or policy refusals return `ok:false` in the payload rather than
bypassing the gateway tool policy pipeline.

View File

@@ -318,7 +318,7 @@ With the OpenShell backend:
Inbound media is copied into the active sandbox workspace (`media/inbound/*`).
<Note>
**Skills note:** the `read` tool is sandbox-rooted. With `workspaceAccess: "none"`, OpenClaw mirrors eligible skills into the sandbox workspace (`.../skills`) so they can be read. With `"rw"`, workspace skills are readable from `/workspace/skills`.
**Skills note:** the `read` tool is sandbox-rooted. With `workspaceAccess: "none"`, OpenClaw mirrors eligible skills into the sandbox workspace (`.../skills`) so they can be read. With `"rw"`, workspace skills are readable from `/workspace/skills`, and eligible managed, bundled, or plugin skills are materialized into the generated read-only path `/workspace/.openclaw/sandbox-skills/skills`.
</Note>
## Custom bind mounts

View File

@@ -39,7 +39,7 @@ exhaustive):
| `gateway.trusted_proxies_missing` | warn | Reverse-proxy headers are present but not trusted | `gateway.trustedProxies` | no |
| `gateway.http.no_auth` | warn/critical | Gateway HTTP APIs reachable with `auth.mode="none"` | `gateway.auth.mode`, `gateway.http.endpoints.*`, `plugins.entries.admin-http-rpc` | no |
| `gateway.http.session_key_override_enabled` | info | HTTP API callers can override `sessionKey` | `gateway.http.allowSessionKeyOverride` | no |
| `gateway.tools_invoke_http.dangerous_allow` | warn/critical | Re-enables dangerous tools over HTTP API | `gateway.tools.allow` | no |
| `gateway.tools_invoke_http.dangerous_allow` | warn/critical | Re-enables dangerous tools over HTTP API for owner/admin callers | `gateway.tools.allow` | no |
| `gateway.nodes.allow_commands_dangerous` | warn/critical | Enables high-impact node commands (camera/screen/contacts/calendar/SMS) | `gateway.nodes.allowCommands` | no |
| `gateway.nodes.deny_commands_ineffective` | warn | Pattern-like deny entries do not match shell text or groups | `gateway.nodes.denyCommands` | no |
| `gateway.tailscale_funnel` | critical | Public internet exposure | `gateway.tailscale.mode` | no |

View File

@@ -128,13 +128,19 @@ You can customize this deny list via `gateway.tools`:
tools: {
// Additional tools to block over HTTP /tools/invoke
deny: ["browser"],
// Remove tools from the default deny list
// Remove tools from the default deny list for owner/admin callers
allow: ["gateway"],
},
},
}
```
`gateway.tools.allow` is an exposure override, not a scope upgrade. In
identity-bearing HTTP modes, `cron`, `gateway`, and `nodes` remain unavailable
to callers that do not have owner/admin identity (`operator.admin`) even when
they are listed in `gateway.tools.allow`. Shared-secret bearer auth still follows
the full trusted-operator rule above.
To help group policies resolve context, you can optionally set:
- `x-openclaw-message-channel: <channel>` (example: `slack`, `telegram`)

View File

@@ -0,0 +1,376 @@
---
title: LTS category proposal
version: 1
---
# LTS category proposal
This proposal identifies a minimal set of maturity-scorecard categories that
should be eligible for the first enterprise-oriented LTS support promise.
Scores are shown as `Coverage/Quality` from the current
`inventory/<surface>/scores.yaml` files. They are useful context, but LTS
eligibility here is a human product-support decision and does not require the
current mechanical threshold of `coverage > 90` and `quality > 80`.
Coverage and Quality numbers are Codex-generated and still need human
verification before they are treated as authoritative.
Completeness is intentionally omitted until that score is ready for use.
Category names link to the corresponding per-category evidence note.
Legend:
- `Surface`: a top-level product or operating area in the taxonomy, such as `Gateway runtime`, `CLI`, `Slack`, or `Linux Gateway host`.
- `Category`: a scored capability area within one surface, used as the unit for maturity and LTS inclusion decisions.
- `✅`: category is included in the proposed initial LTS slice.
- `➡️`: category is deferred from the proposed initial LTS slice.
## Proposed initial LTS Surfaces
### Gateway runtime (12/13)
| Status | Category | Score (Coverage/Quality) |
| ------ | ----------------------------------------------------------------------------------------------------- | ------------------------ |
| ✅ | [Gateway Lifecycle](inventory/gateway-runtime/runtime-lifecycle-and-supervision.md) | `86/82` |
| ✅ | [WebSocket Connection](inventory/gateway-runtime/websocket-handshake-and-session-establishment.md) | `84/76` |
| ✅ | [Device Auth and Pairing](inventory/gateway-runtime/device-identity-auth-and-pairing.md) | `88/72` |
| ✅ | [Security Controls](inventory/gateway-runtime/security-and-hardening-posture.md) | `84/74` |
| ✅ | [Approvals and Remote Execution](inventory/gateway-runtime/approval-and-execution-safety.md) | `88/72` |
| ✅ | [Roles and Permissions](inventory/gateway-runtime/roles-scopes-and-operator-policy.md) | `85/62` |
| ✅ | [Health, Diagnostics, and Repair](inventory/gateway-runtime/observability-health-and-repair.md) | `68/62` |
| ✅ | [HTTP APIs](inventory/gateway-runtime/http-apis.md) | `88/74` |
| ✅ | [Hosted Web Surface](inventory/gateway-runtime/hosted-web-surface.md) | `88/74` |
| ✅ | [Gateway RPC APIs and Events](inventory/gateway-runtime/core-rpc-coverage.md) | `68/57` |
| ✅ | [Network Access and Discovery](inventory/gateway-runtime/network-exposure-and-transport-selection.md) | `68/62` |
| ➡️ | [Nodes and Remote Capabilities](inventory/gateway-runtime/node-transport-and-capability-relay.md) | `84/63` |
| ✅ | [Protocol Compatibility](inventory/gateway-runtime/protocol-typing-and-compatibility.md) | `72/70` |
### Security, auth, pairing, and secrets (5/6)
| Status | Category | Score (Coverage/Quality) |
| ------ | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
| ✅ | [Approval Policy and Tool Safeguards](inventory/security-auth-pairing-and-secrets/approval-policy-and-dangerous-tool-safeguards.md) | `86/72` |
| ✅ | [Gateway Auth and Remote Access](inventory/security-auth-pairing-and-secrets/gateway-auth-and-network-exposure.md) | `82/68` |
| ✅ | [Device and Node Pairing](inventory/security-auth-pairing-and-secrets/device-identity-and-operator-pairing.md) | `83/66` |
| ✅ | [Credential and Secret Hygiene](inventory/security-auth-pairing-and-secrets/secrets-storage-redaction-and-configuration-hygiene.md) | `78/62` |
| ✅ | [Channel Access Control](inventory/security-auth-pairing-and-secrets/channel-identity-allowlists-and-sender-pairing.md) | `78/66` |
| ➡️ | [Plugin Trust](inventory/security-auth-pairing-and-secrets/plugin-installation-trust-and-security-boundaries.md) | `76/70` |
### Agent Runtime (6/9)
| Status | Category | Score (Coverage/Quality) |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------ |
| ✅ | [Agent Turn Execution](inventory/agent-runtime-and-provider-execution/agent-turn-orchestration-and-runtime-lifecycle.md) | `82/74` |
| ✅ | [Model and Runtime Selection](inventory/agent-runtime-and-provider-execution/model-selection-provider-routing-and-runtime-policy.md) | `84/72` |
| ✅ | [Hosted Provider Execution](inventory/agent-runtime-and-provider-execution/hosted-provider-adapters-and-payload-compatibility.md) | `76/70` |
| ✅ | [Tool Execution Controls](inventory/agent-runtime-and-provider-execution/tool-execution-approvals-and-sandbox-policy.md) | `86/74` |
| ✅ | [Provider Auth](inventory/agent-runtime-and-provider-execution/provider-auth-profiles-and-credential-health.md) | `80/66` |
| ➡️ | [External Runtimes and Subagents](inventory/agent-runtime-and-provider-execution/cli-harnesses-external-runtimes-and-subagents.md) | `78/66` |
| ➡️ | [Local and Self-hosted Providers](inventory/agent-runtime-and-provider-execution/local-and-self-hosted-provider-execution.md) | `70/60` |
| ➡️ | [Streaming and Progress](inventory/agent-runtime-and-provider-execution/streaming-progress-and-preview-visibility.md) | `84/70` |
| ✅ | [Tool Calls and Response Handling](inventory/agent-runtime-and-provider-execution/streaming-tool-call-and-response-normalization.md) | `80/66` |
### Session, memory, and context engine (6/9)
| Status | Category | Score (Coverage/Quality) |
| ------ | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
| ✅ | [Session Routing](inventory/session-memory-and-context-engine/session-routing-and-conversation-binding.md) | `82/74` |
| ✅ | [CLI Session and Transcript Management](inventory/session-memory-and-context-engine/cli-session-and-transcript-management.md) | `74/68` |
| ✅ | [Context Engine](inventory/session-memory-and-context-engine/context-engine-and-runtime-assembly.md) | `72/80` |
| ✅ | [Transcript Persistence](inventory/session-memory-and-context-engine/transcript-persistence-and-durability.md) | `78/58` |
| ✅ | [Token Management](inventory/session-memory-and-context-engine/compaction-pruning-and-token-pressure.md) | `78/60` |
| ➡️ | [Cross-client History and Session Parity](inventory/session-memory-and-context-engine/cross-client-history-and-session-parity.md) | `76/62` |
| ➡️ | [Diagnostics, Maintenance, and Recovery](inventory/session-memory-and-context-engine/diagnostics-maintenance-and-recovery.md) | `72/68` |
| ✅ | [Core Prompts and Context](inventory/session-memory-and-context-engine/instruction-profile-and-context-visibility.md) | `68/70` |
| ➡️ | [Memory](inventory/session-memory-and-context-engine/memory-files-tools-and-active-memory.md) | `66/58` |
### CLI (6/7)
| Status | Category | Score (Coverage/Quality) |
| ------ | --------------------------------------------------------------------------------------------------------------------- | ------------------------ |
| ✅ | [CLI Setup](inventory/cli-install-update-onboard-doctor/package-install-and-cli-entrypoints.md) | `78/75` |
| ✅ | [Onboarding and Auth Setup](inventory/cli-install-update-onboard-doctor/first-run-onboarding-and-auth-selection.md) | `86/78` |
| ✅ | [Gateway Service Management](inventory/cli-install-update-onboard-doctor/gateway-service-install-and-lifecycle.md) | `88/66` |
| ✅ | [CLI Observability](inventory/cli-install-update-onboard-doctor/status-health-logs-and-diagnostics-support-path.md) | `84/74` |
| ✅ | [Doctor](inventory/cli-install-update-onboard-doctor/doctor-config-auth-plugin-and-lint.md) | `80/68` |
| ✅ | [Updates and Upgrades](inventory/cli-install-update-onboard-doctor/update-channel-and-core-upgrade-flow.md) | `82/68` |
| ➡️ | [Plugin and Channel Setup](inventory/cli-install-update-onboard-doctor/plugin-and-channel-setup-during-onboarding.md) | `82/72` |
### Linux Gateway host (4/5)
| Status | Category | Score (Coverage/Quality) |
| ------ | --------------------------------------------------------------------------------------------------------------------- | ------------------------ |
| ✅ | [Host Setup and Updates](inventory/linux-gateway-host/linux-cli-install-and-update-path.md) | `82/78` |
| ✅ | [Gateway Runtime and Service Control](inventory/linux-gateway-host/foreground-gateway-runtime-and-process-control.md) | `83/78` |
| ✅ | [Remote Access and Security](inventory/linux-gateway-host/remote-network-exposure-tls-and-tailscale.md) | `78/74` |
| ✅ | [Diagnostics and Repair](inventory/linux-gateway-host/diagnostics-logs-doctor-and-repair.md) | `82/78` |
| ➡️ | [Deployment Targets](inventory/linux-gateway-host/vps-container-and-cloud-deployment-guidance.md) | `76/72` |
### Windows via WSL2 (5/6)
| Status | Category | Score (Coverage/Quality) |
| ------ | ------------------------------------------------------------------------------------------------- | ------------------------ |
| ✅ | [WSL Setup](inventory/windows-via-wsl2/wsl2-install-and-runtime-prerequisites.md) | `76/70` |
| ✅ | [CLI](inventory/windows-via-wsl2/wsl2-cli.md) | `76/70` |
| ✅ | [Gateway Service Lifecycle](inventory/windows-via-wsl2/systemd-gateway-service-lifecycle.md) | `64/66` |
| ✅ | [Gateway Access and Exposure](inventory/windows-via-wsl2/auth-secrets-and-exposure-posture.md) | `70/65` |
| ✅ | [Diagnostics and Repair](inventory/windows-via-wsl2/diagnostics-doctor-logs-and-repair.md) | `74/72` |
| ➡️ | [Browser and Control UI](inventory/windows-via-wsl2/split-host-browser-and-control-ui-interop.md) | `72/70` |
### Native Windows (1/4)
| Status | Category | Score (Coverage/Quality) |
| ------ | ----------------------------------------------------------------------------------------------------------------------- | ------------------------ |
| ✅ | [CLI](inventory/native-windows-cli-and-gateway/native-powershell-install-and-cli-entrypoints.md) | `72/66` |
| ➡️ | [Gateway Management](inventory/native-windows-cli-and-gateway/native-gateway-foreground-runtime-and-process-control.md) | `68/62` |
| ➡️ | [Networking](inventory/native-windows-cli-and-gateway/windows-host-networking-portproxy-and-remote-access.md) | `58/56` |
| ➡️ | [Updates](inventory/native-windows-cli-and-gateway/windows-update-restart-handoff-and-package-locks.md) | `74/68` |
### Observability (3/5)
| Status | Category | Score (Coverage/Quality) |
| ------ | ------------------------------------------------------------------------------------------------------------------ | ------------------------ |
| ✅ | [Health and Repair](inventory/telemetry-diagnostics-and-observability/health-status-probes.md) | `80/76` |
| ✅ | [Logging](inventory/telemetry-diagnostics-and-observability/logging-log-tail-and-redaction.md) | `82/84` |
| ✅ | [Session Diagnostics](inventory/telemetry-diagnostics-and-observability/session-run-and-usage-diagnostics.md) | `82/78` |
| ➡️ | [Diagnostic Collection](inventory/telemetry-diagnostics-and-observability/diagnostics-export-support-bundles.md) | `76/74` |
| ➡️ | [Telemetry Export](inventory/telemetry-diagnostics-and-observability/diagnostic-events-hooks-and-trace-context.md) | `78/78` |
### Channel framework (5/8)
| Status | Category | Score (Coverage/Quality) |
| ------ | ----------------------------------------------------------------------------------------------------------------- | ------------------------ |
| ✅ | [Channel Setup](inventory/channel-framework/channel-setup.md) | `84/78` |
| ✅ | [Inbound Access and Identity Gates](inventory/channel-framework/inbound-access-and-identity-gates.md) | `80/76` |
| ✅ | [Conversation Routing and Delivery](inventory/channel-framework/conversation-routing-and-delivery.md) | `77/71` |
| ✅ | [Outbound Delivery and Reply Pipeline](inventory/channel-framework/outbound-delivery-and-reply-pipeline.md) | `82/75` |
| ✅ | [Status Health and Operator Controls](inventory/channel-framework/status-health-and-operator-controls.md) | `82/78` |
| ➡️ | [Channel Actions Commands and Approvals](inventory/channel-framework/channel-actions-commands-and-approvals.md) | `68/72` |
| ➡️ | [Group Thread and Ambient Room Behavior](inventory/channel-framework/group-thread-and-ambient-room-behavior.md) | `76/68` |
| ➡️ | [Media Attachments and Rich Channel Data](inventory/channel-framework/media-attachments-and-rich-channel-data.md) | `68/70` |
### Slack (5/5)
| Status | Category | Score (Coverage/Quality) |
| ------ | ---------------------------------------------------------------------------------------------------- | ------------------------ |
| ✅ | [Channel Setup and Operations](inventory/slack/app-install-auth-manifest-and-scopes.md) | `74/68` |
| ✅ | [Access and Identity](inventory/slack/dm-pairing-and-sender-authorization.md) | `74/70` |
| ✅ | [Conversation Routing and Delivery](inventory/slack/channel-thread-routing-and-session-isolation.md) | `64/66` |
| ✅ | [Media and Rich Content](inventory/slack/media-attachments-files-and-vision.md) | `64/66` |
| ✅ | [Native Controls and Approvals](inventory/slack/slash-commands-and-native-command-routing.md) | `72/70` |
### Discord (4/6)
| Status | Category | Score (Coverage/Quality) |
| ------ | ---------------------------------------------------------------------------------------------------------------- | ------------------------ |
| ✅ | [Channel Setup and Operations](inventory/discord/bot-setup-and-account-configuration.md) | `74/71` |
| ✅ | [Access and Identity](inventory/discord/dm-pairing-and-sender-authorization.md) | `74/72` |
| ✅ | [Conversation Routing and Delivery](inventory/discord/guild-channel-routing-and-session-isolation.md) | `74/72` |
| ✅ | [Media and Rich Content](inventory/discord/media-attachments-and-voice-message-handling.md) | `74/72` |
| ➡️ | [Native Controls and Approvals](inventory/discord/native-slash-commands-components-and-interactive-callbacks.md) | `58/72` |
| ➡️ | [Realtime Voice and Calls](inventory/discord/realtime-discord-voice-channels.md) | `74/66` |
### Telegram (5/5)
| Status | Category | Score (Coverage/Quality) |
| ------ | ------------------------------------------------------------------------------------------------ | ------------------------ |
| ✅ | [Channel Setup and Operations](inventory/telegram/bot-setup-and-account-configuration.md) | `76/70` |
| ✅ | [Access and Identity](inventory/telegram/dm-pairing-and-sender-authorization.md) | `76/68` |
| ✅ | [Conversation Routing and Delivery](inventory/telegram/group-forum-topic-and-session-routing.md) | `74/68` |
| ✅ | [Media and Rich Content](inventory/telegram/media-location-polls-and-rich-inputs.md) | `74/72` |
| ✅ | [Native Controls and Approvals](inventory/telegram/inline-buttons-approvals-and-actions.md) | `74/72` |
### OpenAI / Codex provider path (3/5)
| Status | Category | Score (Coverage/Quality) |
| ------ | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
| ✅ | [Model and Auth](inventory/openai-codex-provider-path/canonical-openai-model-routing-and-catalog.md) | `78/66` |
| ✅ | [Responses and Tool Compatibility](inventory/openai-codex-provider-path/codex-responses-transport-and-payload-compatibility.md) | `76/70` |
| ✅ | [Native Codex Harness](inventory/openai-codex-provider-path/native-codex-app-server-harness-and-thread-lifecycle.md) | `82/72` |
| ➡️ | [Image and Multimodal Input](inventory/openai-codex-provider-path/image-generation-editing-and-multimodal-input.md) | `80/72` |
| ➡️ | [Voice and Realtime Audio](inventory/openai-codex-provider-path/realtime-voice-transcription-and-speech.md) | `72/68` |
### Browser automation and exec/sandbox tools (2/3)
| Status | Category | Score (Coverage/Quality) |
| ------ | -------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
| ✅ | [Tool Invocation and Execution](inventory/browser-automation-and-exec-sandbox-tools/exec-routing-and-process-lifecycle.md) | `82/79` |
| ✅ | [Sandbox and Tool Policy](inventory/browser-automation-and-exec-sandbox-tools/sandbox-backends-and-workspace-isolation.md) | `76/72` |
| ➡️ | [Browser Automation](inventory/browser-automation-and-exec-sandbox-tools/browser-actions-snapshots-and-artifacts.md) | `78/74` |
### Plugins (7/9)
| Status | Category | Score (Coverage/Quality) |
| ------ | ----------------------------------------------------------------------------------------------------------------------- | ------------------------ |
| ✅ | [Installing and running plugins](inventory/plugin-sdk-and-bundled-plugin-architecture/runtime-loading-and-lifecycle.md) | `86/84` |
| ✅ | [Bundled plugins](inventory/plugin-sdk-and-bundled-plugin-architecture/bundled-plugin-discovery-and-inventory.md) | `86/84` |
| ➡️ | [Canvas plugin](inventory/plugin-sdk-and-bundled-plugin-architecture/canvas-plugin.md) | `76/66` |
| ✅ | [Plugin approvals](inventory/plugin-sdk-and-bundled-plugin-architecture/approval-and-security-boundaries.md) | `84/86` |
| ✅ | [Provider and tool plugins](inventory/plugin-sdk-and-bundled-plugin-architecture/provider-tool-plugin-architecture.md) | `84/82` |
| ✅ | [Channel plugins](inventory/plugin-sdk-and-bundled-plugin-architecture/channel-plugin-architecture.md) | `82/78` |
| ✅ | [Authoring and Packaging plugins](inventory/plugin-sdk-and-bundled-plugin-architecture/public-sdk-api-and-subpaths.md) | `77/74` |
| ✅ | [Publishing plugins](inventory/plugin-sdk-and-bundled-plugin-architecture/distribution-release-and-compatibility.md) | `79/82` |
| ➡️ | [Testing plugins](inventory/plugin-sdk-and-bundled-plugin-architecture/developer-testing-and-fixtures.md) | `84/81` |
## Prioritized non-LTS candidates
This section ranks the currently non-LTS surface/category pairs that should be
prioritized for future LTS eligibility. It is based on the current taxonomy,
`inventory/**/scores.yaml`, and sentiment from local `discrawl` and `gitcrawl`
archives.
Current scan basis:
- Initial LTS slice: `68` categories.
- Total taxonomy: `279` categories.
- Non-LTS scan scope: `211` categories.
- `gitcrawl` freshness: synced through 2026-05-28.
- `discrawl` freshness: synced through 2026-05-29.
### First Wave
#### Docker / Podman hosting
- [Container Setup](inventory/docker-podman-hosting/docker-install-compose-and-first-run-setup.md): `74/76`
- [Container Operations](inventory/docker-podman-hosting/runtime-configuration-state-volumes-and-secrets.md): `76/70`
- [Image Release and Validation](inventory/docker-podman-hosting/image-build-release-packaging-and-attestations.md): `84/78`
- [Agent Sandbox and Tooling](inventory/docker-podman-hosting/containerized-agents-sandbox-and-tooling-support.md): `75/68`
Why: this is the strongest enterprise deployment gap outside the initial LTS
slice. Discord support sentiment repeatedly clusters around VPS, Docker, WSL,
volume persistence, secrets, update, and rollback confusion. GitHub also has a
current Docker gateway restart-loop issue, `#86612`.
#### Microsoft Teams
- [Channel Setup and Operations](inventory/microsoft-teams/setup-app-registration-credentials-admin-install.md): `58/64`
- [Access and Identity](inventory/microsoft-teams/dm-pairing-sender-authorization-config-writes.md): `60/62`
- [Conversation Routing and Delivery](inventory/microsoft-teams/team-channel-routing-mention-gates-sessions-thread-context.md): `68/66`
- [Media and Rich Content](inventory/microsoft-teams/media-attachments-file-consent-graph-file-flows.md): `62/58`
- [Native Controls and Approvals](inventory/microsoft-teams/actions-reactions-polls-approvals-group-management.md): `64/66`
Why: Teams has low current scores, but it is the obvious second enterprise
workplace channel after Slack. GitHub has strong concrete signal for channel
session behavior, multiple-bot support, attachment handling, managed identity,
and setup/admin complexity: `#81084`, `#71058`, `#65329`, `#67177`, and
`#85149`.
#### Cross-provider auth
- Anthropic provider path / [Provider Auth and Recovery](inventory/anthropic-provider-path/auth-onboarding-and-credential-profile-health.md): `78/70`
- Google provider path / [Provider Setup and Credentials](inventory/google-provider-path/provider-auth-credentials-and-operator-setup.md): `72/60`
Why: provider auth is one of the highest recurring Discord support themes.
Users get stuck on missing auth, fallback routing, cooldowns, stale profiles,
plaintext secrets, provider mismatch, and unclear recovery commands. These
categories are prerequisites for making any multi-provider enterprise harness
reliable.
#### Gateway Web App
- [Browser Access and Trust](inventory/browser-control-ui-and-webchat/gateway-connection-auth-device-pairing-and-origins.md): `84/68`
- [Configuration](inventory/browser-control-ui-and-webchat/config-schema-editing-and-safe-writes.md): `82/78`
- [Browser UI](inventory/browser-control-ui-and-webchat/control-ui-static-shell-routing-and-pwa.md): `74/72`
- [WebChat Conversations](inventory/browser-control-ui-and-webchat/chat-composer-session-model-controls-and-rendering.md): `78/66`
- [Operator Console](inventory/browser-control-ui-and-webchat/diagnostics-logs-update-and-activity.md): `78/74`
Why: this is the operator and admin surface for an enterprise deployment.
GitHub has open UX and runtime issues around auth gates, transcript loss,
uploads, CJK input and streaming, and partial reloads: `#85750`, `#72500`,
`#83344`, `#81606`, `#86035`, `#60247`, and `#86435`.
#### Automation: cron, hooks, tasks, polling
- [Cron Jobs](inventory/automation-cron-hooks-tasks-polling/cron-job-lifecycle.md): `82/73`
- [Background Tasks and Flows](inventory/automation-cron-hooks-tasks-polling/background-task-ledger.md): `73/68`
- [Event Ingress](inventory/automation-cron-hooks-tasks-polling/channel-polling-webhooks.md): `65/58`
- [Automation Hooks](inventory/automation-cron-hooks-tasks-polling/internal-hooks.md): `78/72`
- [Heartbeat](inventory/automation-cron-hooks-tasks-polling/heartbeat-commitments.md): `82/72`
Why: enterprise agents need durable scheduled work, alerting, and recovery.
GitHub has current signal for startup races, duplicate names, silent data loss,
status visibility, elevated scoping, and owner-tool stripping: `#75889`,
`#76160`, `#83538`, `#51184`, `#41484`, and `#72954`.
#### TUI
- [Runtime Modes](inventory/tui-and-terminal-ux/launch-modes-and-cli-entrypoints.md): `78/72`
- [Input and Commands](inventory/tui-and-terminal-ux/composer-keybindings-and-input-editing.md): `76/70`
- [Session Management](inventory/tui-and-terminal-ux/session-lifecycle-history-and-resume.md): `80/68`
- [Local Shell Execution](inventory/tui-and-terminal-ux/local-shell-execution-and-approval-boundary.md): `70/76`
- [Rendering and Output Safety](inventory/tui-and-terminal-ux/streaming-message-rendering-and-tool-cards.md): `76/70`
Why: TUI is a real operator-facing surface with broad docs and decent baseline
coverage, but it is still less proven as a primary supported workflow than the
CLI and Gateway host paths in the initial slice. Promote it when launch modes,
command/input behavior, session resume, local shell boundaries, and streaming
rendering are treated as one terminal-native support promise.
### Second wave
#### macOS Gateway host
- [Gateway Service Lifecycle](inventory/macos-gateway-host/launchagent-service-lifecycle.md): `82/76`
- [Local Gateway Integration](inventory/macos-gateway-host/local-gateway-mode-host-configuration.md): `76/82`
- [Diagnostics and Observability](inventory/macos-gateway-host/diagnostics-logs-operator-observability.md): `80/83`
- [CLI Setup](inventory/macos-gateway-host/cli-install-runtime-prerequisites.md): `82/76`
- [Remote Gateway Mode](inventory/macos-gateway-host/remote-gateway-mode-transport.md): `72/82`
Why: Linux is the cleaner first LTS host, but macOS has heavy real-world support
volume and strong desktop-gateway relevance. Current issues include LaunchAgent
reporting, bind behavior, cert and update drift, external-volume failures,
Homebrew/runtime drift, unrecoverable upgrades, restart loops, and install
failures: `#81751`, `#65619`, `#86579`, `#87199`, `#75250`, `#85027`,
`#73673`, and `#60398`.
#### Browser automation and exec/sandbox tools
- [Browser Automation](inventory/browser-automation-and-exec-sandbox-tools/browser-actions-snapshots-and-artifacts.md): `78/74`
Why: the initial LTS slice already includes core tool invocation and sandbox
policy, but browser execution is part of a practical enterprise agent harness.
Open issues include sandbox/runtime mismatch, non-Docker backend support,
noVNC/CJK behavior, upload access, timeouts, and Control UI responsiveness.
#### Web search tools
- [Network Safety](inventory/web-search-tools/network-safety-ssrf-redirects-and-untrusted-content.md): `84/84`
- [Tool Availability and Fetch](inventory/web-search-tools/tool-exposure-policy-and-runtime-tool-wiring.md): `82/80`
- [Search Providers](inventory/web-search-tools/bundled-structured-search-providers.md): `76/72`
- [Setup and Diagnostics](inventory/web-search-tools/operator-setup-provider-selection-and-credential-repair.md): `74/70`
Why: web fetch and structured search are useful for enterprise research
workflows, but they are outside the minimal first support promise. Promote this
surface when network safety, runtime tool wiring, provider selection, timeout
behavior, and operator repair are accepted together. GitHub has search timeout,
provider-native tool, tool-drop, and provider option signal:
`#87505`, `#23353`, `#77826`, and `#84872`.
#### Gateway runtime
- [Nodes and Remote Capabilities](inventory/gateway-runtime/node-transport-and-capability-relay.md): `84/63`
Why: node pairing and remote node capability relay still harden the perimeter
around the existing Gateway LTS promise and need separate operational proof.
### Lower priority for LTS
Observability should add Diagnostic Collection and
Telemetry Export hardening after the runtime and channel priorities above.
Plugin SDK should add Testing plugins, Packaging plugins, then Publishing
plugins; this matters for ecosystem durability, but has weaker direct
enterprise sentiment than Docker, Teams, Slack, and provider auth.
Continue to defer mobile apps, voice, media generation, regional channels,
iMessage, Matrix, WhatsApp, and long-tail providers unless a specific customer
commitment changes the support boundary.
## Interpretation
This LTS slice is intentionally conservative. It promises enough for an
enterprise to run a usable agent harness with Gateway, auth and policy,
session/runtime execution, operational diagnostics, Linux hosting, Slack,
Discord, Telegram, the OpenAI/Codex provider path, and tool execution controls.
Categories outside this slice can keep shipping, but should not be part of the
initial LTS guarantee until their owner, support boundary, upgrade behavior,
and enterprise failure modes are explicitly accepted.

View File

@@ -0,0 +1,214 @@
---
title: Maturity scorecard process
version: 3
---
# Maturity scorecard process
This directory is an artifact root maintained by the local `claw-score` skill
defined in the external `claw-score` `SKILL.md`.
The skill owns scoring policy, scoring workflow, validation, artifact shape,
and renderer expectations. This README is the human-facing directory contract
and process overview.
The top-level scorecard layout is owned by the skill template
`.agents/skills/claw-score/references/maturity-scorecard-template.md`, then
rendered into [maturity-scorecard.md](maturity-scorecard.md).
Operationally, the skill separates three workflows: taxonomy maintenance, score
computation, and skill self-maintenance. The detailed agent instructions for
those live in the skill reference files, not in this README.
## Source files
- `taxonomy.yaml` is the source of truth for surfaces, maturity levels,
surface ids, category definitions, category `human_lts_override` values,
category `docs` reading lists, surface `completeness_instructions`, and
`last_score_run` provenance for the active in-repo surfaces.
- `/Users/kevinlin/tmp/maturity/taxonomy.yaml` stores the archived taxonomy for
the other surfaces that are temporarily out of the active in-repo scope.
- `<artifact-root>/<surface>/scores.yaml` is the per-surface score source for
Coverage, Quality, Completeness, and row identity (`name` and
`category_note`). The renderer joins taxonomy-owned category metadata from
`taxonomy.yaml`. Active artifact paths are derived by naming convention from
the taxonomy surface id: `inventory/<surface-id>/report.md`,
`inventory/<surface-id>/scores.yaml`, and `inventory/<surface-id>/<category-note>`.
Historical archived surfaces live at `/Users/kevinlin/tmp/maturity` and are
intentionally skipped by the normal `claw-score` render and sync workflows.
- [maturity-scorecard.md](maturity-scorecard.md), [taxonomy.md](taxonomy.md),
[taxonomy-outline.md](taxonomy-outline.md), and
`<artifact-root>/<surface>/report.md` are rendered Markdown artifacts. Do not
hand-edit their generated tables.
## Directory layout
```text
docs/kevinslin/maturity-scorecard/
├── README.md
├── taxonomy.md
├── taxonomy-outline.md
├── maturity-scorecard.md
└── inventory/
├── gateway-runtime/
│ ├── report.md
│ ├── <category>.md
│ └── scores.yaml
└── plugin-sdk-and-bundled-plugin-architecture/
├── report.md
├── <category>.md
└── scores.yaml
```
Interpret these files as follows:
- `README.md`: human-facing process overview and artifact contract.
- `taxonomy.md`: rendered taxonomy reference generated from the skill-owned
taxonomy YAML.
- `taxonomy-outline.md`: rendered surface outline grouped by family, generated
from the skill-owned taxonomy YAML.
- [maturity-scorecard.md](maturity-scorecard.md): rendered top-level scorecard generated from the
skill-owned taxonomy.
- `inventory/`: canonical artifact root for active maturity-scorecard work.
- `/Users/kevinlin/tmp/maturity`: archive location for historical artifact
trees and the archived taxonomy file. Treat it as out of scope unless
explicitly restoring archived work.
- `<artifact-root>/<surface>/scores.yaml`: per-surface score source generated or
refreshed by the skill.
- `<artifact-root>/<surface>/report.md`: rendered surface report.
- `<artifact-root>/<surface>/<category>.md`: per-category evidence note.
## Concepts
- `taxonomy`: the skill-owned YAML file that defines the top-level maturity
model, surface inventory, per-surface category metadata, and `last_score_run`
state.
- `scorecard`: the rendered top-level Markdown overview generated from the
taxonomy. Its generated table includes per-surface Coverage, Quality,
Completeness, and LTS status columns derived from `scores.yaml` plus
taxonomy `human_lts_override` metadata.
- `taxonomy doc`: the rendered Markdown reference view of the taxonomy,
including the surface inventory and per-surface categories.
- `taxonomy outline`: the rendered Markdown outline of active surfaces grouped
by family.
- `surface`: one scored product or platform area from the taxonomy.
- `surface slug`: the stable filesystem-friendly identifier used for a
surface's inventory directory and filenames.
- `artifact root`: the per-surface parent directory selected in taxonomy
naming convention. Active work currently uses `inventory/<surface-id>/`;
archived surfaces are marked in taxonomy with `archived: true`.
- `category`: a significant user-facing or operator-facing part of a surface
that gets its own evidence note and row in the per-surface score YAML. A
category should represent a capability area a user can actually utilize, not
an internal implementation bucket.
- `category note`: the per-category Markdown evidence artifact
`<artifact-root>/<surface>/<category>.md`. Notes include a taxonomy-derived
`## Features` section that mirrors the category feature list from
`taxonomy.yaml`.
- `scores.yaml`: the canonical per-surface score source
`<artifact-root>/<surface>/scores.yaml`; it stores Coverage, Quality,
Completeness, and row identity, while taxonomy owns features, docs, search
anchors, `human_lts_override`, and surface-level
`completeness_instructions`.
- `LTS.md`: hand-curated initial LTS slice. Its status rows must stay
synchronized with taxonomy `human_lts_override` values and rendered
per-surface report matrix LTS cells by running
`.agents/skills/claw-score/scripts/validate_lts_sync.py`.
- `completeness_instructions`: taxonomy-owned surface metadata pointing to a
skill-relative rubric file under `.agents/skills/claw-score/` that explains
how to score Completeness for that surface.
- `features`: taxonomy-owned category metadata stored as objects with `name`
and `description`. Keep `name` short and scannable; put the fuller
explanation in `description`. A feature should be a user-invokable
capability for that surface/category, not a handshake step or other
implementation-only detail.
- `docs`: taxonomy-owned category metadata listing repo-relative doc URLs that
best cover the category. Keep this as a short primary-reading list, not a
full evidence dump. During taxonomy maintenance, this list should be chosen
by scanning the OpenClaw docs corpus for the category and selecting the
canonical pages a reviewer should open first.
- `surface report`: the rendered per-surface Markdown report
`<artifact-root>/<surface>/report.md`.
Category display names should be short, operator-facing capability names.
Prefer fewer coarser categories, merge related concepts that share docs and
operator workflows, and keep old or implementation-heavy terminology in
`search_anchors`, feature descriptions, or evidence rather than in the display
name.
## Versioning
Markdown scorecard artifacts use frontmatter `version` for the scoring process
that produced that document.
During a real rescore, the surface report and category notes should have
frontmatter `version` equal to the active `scores.yaml process_version`.
YAML sources use:
- `version`: schema version for the file shape. This starts at `1`.
- `process_version`: scoring process version. Current scoring runs use `3`.
Do not bulk-update existing per-surface `last_score_run.process_version` or
`scores.yaml process_version` for render-only, taxonomy-only, or mechanical doc
changes. Update a surface's scoring provenance when that surface is actually
rescored with refreshed evidence.
## LTS
LTS is generated, not scored by category agents.
The renderer marks a category as LTS when either condition is true:
- `quality > 80 and coverage > 90`
- the matching taxonomy category sets `human_lts_override: true`
Keep `human_lts_override` in `taxonomy.yaml`. Do not write it into
`scores.yaml`.
## Regeneration
Use the skill scripts from the repository root:
```bash
python3 .agents/skills/claw-score/scripts/sync_taxonomy_categories.py \
--taxonomy .agents/skills/claw-score/taxonomy.yaml \
--scorecard-root docs/kevinslin/maturity-scorecard
python3 .agents/skills/claw-score/scripts/sync_scores_yaml.py \
--taxonomy .agents/skills/claw-score/taxonomy.yaml \
--scorecard-root docs/kevinslin/maturity-scorecard
python3 .agents/skills/claw-score/scripts/render_taxonomy_from_taxonomy.py \
--taxonomy .agents/skills/claw-score/taxonomy.yaml \
--taxonomy-doc docs/kevinslin/maturity-scorecard/taxonomy.md \
--taxonomy-outline-doc docs/kevinslin/maturity-scorecard/taxonomy-outline.md
python3 .agents/skills/claw-score/scripts/render_scorecard_from_taxonomy.py \
--taxonomy .agents/skills/claw-score/taxonomy.yaml \
--scorecard docs/kevinslin/maturity-scorecard/maturity-scorecard.md
```
Use each command's `--check` mode before handoff when verifying artifacts.
If the skill's renderers, sync scripts, or templates change, rerun the relevant
commands above and update this README in the same change when the artifact
contract or regeneration guidance changes.
## Editing rules
- For scoring, rescoring, audits, taxonomy changes, report regeneration, or
output-shape changes, use `claw-score`.
- When updating the `claw-score` skill itself, update the relevant source
files under `.agents/skills/claw-score/` and keep this README aligned with
any artifact-contract, terminology, or regeneration changes.
- Do not hand-edit generated tables or inventories in `taxonomy.md` or
`taxonomy-outline.md`; rerender them through the skill scripts.
- Do not hand-edit generated score tables in `maturity-scorecard.md` or
`<artifact-root>/<surface>/report.md`; rerender them through the skill
scripts. That includes the report's feature lists, which are rendered from
taxonomy.
- Do not hand-edit taxonomy-derived `## Features` sections in category notes;
update `taxonomy.yaml` and rerender the owning surface report instead.
- Keep agent instructions in the external `claw-score` `SKILL.md`, not in this
directory.

View File

@@ -0,0 +1,89 @@
---
title: "Agent Runtime - Agent Turn Execution Maturity Note"
version: 3
last_refreshed: 2026-05-30
last_refreshed_by: codex
---
# Agent Runtime - Agent Turn Execution Maturity Note
## Summary
Agent turns have a first-class runtime lifecycle: docs explain gateway/embedded starts, queueing, session locks, event streams, timeouts, and early termination; source centralizes turn execution in `runAgentTurnWithFallback`; tests exercise fallback orchestration, aborts, lifecycle backstops, event delivery, and runtime telemetry. Quality is Beta because archive evidence still shows recent empty/failed replies and timeout edge cases around long-running or restarted embedded turns.
## Category Scope
This category covers user/operator-visible turn execution: starting an agent turn, choosing gateway versus embedded runtime, establishing session/run ids, applying queue locks, bridging events, honoring aborts, timing provider/model work, and emitting terminal outcomes.
## Features
- Turn startup and runtime choice: Starting an agent turn and choosing gateway versus embedded runtime execution.
- Session and run coordination: Establishing session and run ids, queue locks, and related execution coordination.
- Abort and terminal outcomes: Honoring aborts, timing provider/model work, and emitting terminal outcomes.
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Stable (82%)`
Coverage is broad across concepts, CLI docs, source, and tests. The remaining coverage gap is direct scenario proof for every runtime restart/timeout path per provider release.
## Quality Score
- Score: `Beta (74%)`
The lifecycle has strong guardrails and diagnostics, but recent operational reports still show terminal-empty replies, restart recovery cases, and timeout-sensitive local/embedded runs that need clearer operator recovery behavior.
## Completeness Score
- Score: `Stable (82%)`
- Surface instructions: evaluated against `references/completeness/agent-runtime-and-provider-execution.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for Turn startup and runtime choice, Session and run coordination, Abort and terminal outcomes.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived Coverage score.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- Some runtime failure modes are documented through tests and archived issues rather than a single operator-facing troubleshooting guide.
- Long-running local or external runtime turns still appear sensitive to timeout configuration.
- Archive searches found little direct GitHub issue coverage for the narrow `agent.wait` and embedded fallback terms, so Discord evidence carries more of the field-signal burden.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/concepts/agent-loop.md` documents the agent RPC shape, `agentCommand`, `runEmbeddedAgent`, event bridge, `agent.wait`, queueing/session locks, streaming/tool/final payload behavior, event streams, timeouts, and early termination reasons.
- `/Users/kevinlin/code/openclaw/docs/cli/agent.md` documents running an agent turn via Gateway, model/thinking/local/deliver/timeout options, local preload behavior, gateway timeout fallback session/run ids, and SIGTERM/SIGINT `chat.abort`.
- `/Users/kevinlin/code/openclaw/docs/concepts/agent-runtimes.md` explains runtimes versus providers/model/channel, embedded harnesses, CLI backends, Codex surfaces, runtime ownership, runtime selection, and fail-closed explicit runtimes.
### Source
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.ts` imports `runEmbeddedAgent`, `runWithModelFallback`, runtime provider resolution, and outcome planning; it implements turn timing, context window resolution, `runAgentTurnWithFallback`, fallback candidate auth/profile setup, live model switches, run diagnostics, reply media, and compaction notices.
- `/Users/kevinlin/code/openclaw/src/agents/cli-runner.ts` finalizes CLI context engine turns, persists approved CLI transcripts, and runs CLI agent turns through the same hook path.
- `/Users/kevinlin/code/openclaw/packages/agent-core/src/agent-loop.test.ts` anchors EventStream failure handling in the lower-level agent loop package.
### Integration tests
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.test.ts` covers `runAgentTurnWithFallback`, abort signal propagation, queued fallback rechecks, fallback auth availability, CLI assistant event previews, lifecycle terminal backstops, gateway restart copy, external error formatting, live model switch restart/retry caps, and auth profile state on retries.
- `/Users/kevinlin/code/openclaw/src/agents/openclaw-tools.subagents.sessions-spawn.lifecycle.test.ts` covers spawned-session lifecycle cleanup, enough gateway request time, MCP cleanup, delete via `agent.wait`, timeout handling, account routing, and announce behavior.
### Unit tests
- `/Users/kevinlin/code/openclaw/packages/agent-core/src/agent-loop.test.ts` covers EventStream error paths.
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.test.ts` includes focused unit coverage for fallback orchestration, terminal result classification, empty result handling, and turn-level diagnostics.
### Gitcrawl queries
- `gitcrawl --json search issues -R openclaw/openclaw "agent loop gateway_timeout chat.abort embedded fallback"` returned no matching issues, suggesting the exact lifecycle query is not where field reports are clustered.
- `gitcrawl --json search issues -R openclaw/openclaw "runAgentTurnWithFallback agent runner timeout"` returned no matching issues.
- `gitcrawl --json search issues -R openclaw/openclaw "local model provider context timeout Ollama"` returned issues including #87642 on exposing `waitForRun` timeout for slow local LLMs, #86599 on local provider calls blocking the gateway event loop on Windows, and #74204 on memory embed timeout for local GGUF.
### Discrawl queries
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "runEmbeddedAgent agent.wait"` returned no matches.
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "agent.wait gateway_timeout embedded fallback"` returned no matches.
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "model fallback decision"` returned recent discussions around openai-codex timeouts, fallback decisions, No API key fallback decisions, OpenRouter timeout decisions, missing bearer logs, and repeated fallback errors in session repair loops.

View File

@@ -0,0 +1,94 @@
---
title: "Agent Runtime - External Runtimes and Subagents Maturity Note"
version: 3
last_refreshed: 2026-05-30
last_refreshed_by: codex
---
# Agent Runtime - External Runtimes and Subagents Maturity Note
## Summary
OpenClaw treats external runtimes as a first-class execution mode: docs separate providers from runtimes, explain Codex/OpenClaw/ACP/external harness ownership, document Claude CLI and Gemini CLI aliases, and describe subagent auth, delivery, cleanup, and recovery. Source bridges CLI transcripts and events through `runCliAgent`, and tests cover CLI previews, subagent sessions, lifecycle cleanup, and runtime override boundaries. Quality is Alpha because archives show recurring issues around `claude-cli`, ACP/subagent delivery, unsupported backend settings, trajectory artifacts, and auth propagation from main sessions.
## Category Scope
This category covers operator-visible execution outside the default embedded
provider path: choosing external harnesses, using CLI runtime aliases, running
subagent turns, and recovering from cleanup, timeout, or liveness issues in
those external runtimes.
## Features
- External harness selection: Choosing Codex app-server, ACP, and other external runtime harnesses.
- CLI runtime aliases: Runtime aliases and CLI-based execution paths such as Claude CLI and Gemini CLI.
- Subagent turns: Spawning, delivering, and announcing subagent work outside the default embedded path.
- Runtime recovery: Cleanup, timeout, and liveness behavior for external runtimes and subagents.
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Beta (78%)`
Coverage is good for Codex/CLI/subagent workflows, but external harnesses and ACP have less uniform proof than the embedded runtime.
## Quality Score
- Score: `Alpha (66%)`
CLI/subagent execution is functional but operationally fragile where backend-specific auth, tool permission boundaries, unsupported settings, and result delivery vary by runtime.
## Completeness Score
- Score: `Beta (78%)`
- Surface instructions: evaluated against `references/completeness/agent-runtime-and-provider-execution.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for External harness selection, CLI runtime aliases, Subagent turns, Runtime recovery.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived Coverage score.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- External runtime behavior shifts with upstream CLI tools and needs more release-by-release proof.
- Subagent UX and lifecycle semantics are well tested but still produce field reports around delivery, account routing, and parity.
- Some CLI artifacts and diagnostics, such as pure `claude-cli` trajectories, have active gap reports.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/concepts/agent-runtimes.md` documents runtimes versus providers/model/channel, embedded harnesses versus CLI backends, Codex surfaces, runtime decision tree, ownership split, runtime selection, fail-closed explicit runtimes, CLI backend aliases, Claude CLI, OpenAI default to Codex harness, and compatibility contract.
- `/Users/kevinlin/code/openclaw/docs/providers/anthropic.md` documents Claude CLI setup, same-host requirement, canonical Anthropic refs with `agentRuntime.id: "claude-cli"`, legacy refs, and thinking defaults.
- `/Users/kevinlin/code/openclaw/docs/providers/google.md` documents Gemini CLI OAuth setup, plugin capabilities, legacy aliases, and capability expectations.
- `/Users/kevinlin/code/openclaw/docs/tools/subagents.md` documents subagent auth, announce behavior, delivery routing, sessions_history sanitation, subagent tool policy, concurrency, liveness, and recovery.
### Source
- `/Users/kevinlin/code/openclaw/src/agents/cli-runner.ts` persists approved CLI user turn transcripts, finalizes CLI context engine turns, invokes `before_agent_reply`, and runs CLI agent turns.
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.ts` resolves CLI runtime execution providers, handles runtime overrides, bridges CLI assistant events into previews, forwards runtime plan/approval/command/patch events, and enforces live switch retry caps.
- `/Users/kevinlin/code/openclaw/src/agents/agent-tools.ts` applies subagent/inherited tool policy and configures tool execution boundaries used by spawned runtime sessions.
### Integration tests
- `/Users/kevinlin/code/openclaw/src/agents/openclaw-tools.subagents.sessions-spawn.lifecycle.test.ts` covers `sessions_spawn` lifecycle behavior, cleanup, enough gateway request time, MCP cleanup, delete via `agent.wait`, timeout handling, account routing, and announce behavior.
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.test.ts` covers forwarding static extra prompts to CLI backends, prepared CLI user turns at the persistence boundary, no CLI session reuse for room-event turns, CLI assistant event previews, reasoning previews, CLI runtime override boundaries, and Codex app-server telemetry.
### Unit tests
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.test.ts` includes focused tests for runtime override resolution, CLI preview bridging, external error formatting, gateway restart recovery copy, and live model switch retry caps.
- `/Users/kevinlin/code/openclaw/src/agents/agent-tools.message-provider-policy.test.ts` covers provider-policy behavior that affects external runtime tool surfaces.
### Gitcrawl queries
- `gitcrawl --json search issues -R openclaw/openclaw "claude-cli codex cli harness subagent sessions_spawn"` returned #73097 on PI harness ignoring `cliBackends` configuration and splitting subagent execution from chat path.
- `gitcrawl --json search issues -R openclaw/openclaw "openai-codex Anthropic Google provider tool call"` returned #80667 on `trajectory.jsonl` never being written for pure `claude-cli` sessions and #78196 on extension plugin loader behavior.
- `gitcrawl --json search issues -R openclaw/openclaw "local model provider context timeout Ollama"` returned #81214 on an OpenClaw subagent regression and #87642 on exposing subagent-control `waitForRun` timeout for slow local LLMs.
### Discrawl queries
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "sessions_spawn claude-cli"` returned Apr-May 2026 discussions about ACP runtime failures with Claude Opus settings, Claude CLI tool availability, tool permission boundaries/sandboxing, ACP/sub-agent relay UX, and subagent/ACP result delivery regressions.
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "tool call streaming"` returned Claude CLI/WebChat tool visibility concerns and app-server watchdog discussions that affect external runtime delivery.
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "No API key found provider openai-codex"` returned related Codex OAuth profile propagation and rebuild-recognition reports that affect external runtime/subagent sessions.

View File

@@ -0,0 +1,98 @@
---
title: "Agent Runtime - Hosted Provider Execution Maturity Note"
version: 3
last_refreshed: 2026-05-30
last_refreshed_by: codex
---
# Agent Runtime - Hosted Provider Execution Maturity Note
## Summary
Hosted provider adapter coverage is solid for OpenAI/Codex, Anthropic, Google, and OpenAI-compatible routes. Docs explain provider-specific setup, thinking controls, OAuth/API-key distinctions, CLI runtime alternatives, and capability expectations. Source includes provider-specific message/tool/thinking conversion, timeout handling, websocket/SSE behavior, prompt-cache affinity, and tool-call normalization. Quality is Beta because hosted provider payload semantics still change quickly, especially for Codex OAuth routing, Anthropic streaming JSON, Google tool-call ids, and OpenAI-compatible tool behavior.
## Category Scope
This category covers operator-visible hosted provider execution: running turns
against hosted providers, using provider-specific model options, exercising
hosted tool use, applying reasoning or cache controls, and receiving streamed
or final replies despite provider payload differences.
## Features
- Hosted provider turns: Running agent turns against hosted providers such as OpenAI, Anthropic, and Google.
- Provider-specific model options: Provider-specific model parameters and runtime request settings exposed to users or operators.
- Hosted tool use: Tool use behavior when the active runtime is a hosted provider.
- Reasoning and cache controls: Provider-specific reasoning, thinking, and cache-related controls during hosted execution.
- Hosted streaming and replies: Operator-visible streaming and reply behavior while hosted adapters normalize payload differences.
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Beta (76%)`
Coverage is good for the major providers, but OpenAI-compatible and fast-moving hosted provider variants still rely on scattered tests, docs, and archive evidence rather than a uniform compatibility table.
## Quality Score
- Score: `Beta (70%)`
Adapters include many compatibility guards, but provider payload drift and streaming/tool-call quirks remain visible in archived issues and Discord reports.
## Completeness Score
- Score: `Beta (76%)`
- Surface instructions: evaluated against `references/completeness/agent-runtime-and-provider-execution.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for Hosted provider turns, Provider-specific model options, Hosted tool use, Reasoning and cache controls, Hosted streaming and replies.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived Coverage score.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- Provider-specific tool-call and thinking semantics still need recurring live proof.
- OpenAI-compatible hosted providers and route aliases have less systematic evidence than first-party routes.
- Some adapter failures surface as generic fallback or missing-key errors, making operator diagnosis harder.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/providers/openai.md` documents OpenAI/Codex route distinctions, naming maps, capability tables, GPT-5.5/Codex app-server notes, Codex OAuth setup, and default agent routes.
- `/Users/kevinlin/code/openclaw/docs/providers/anthropic.md` documents Anthropic API key versus Claude CLI, canonical refs, legacy refs, thinking defaults, and prompt caching.
- `/Users/kevinlin/code/openclaw/docs/providers/google.md` documents Google plugin capabilities, Gemini CLI OAuth, model refs, capabilities, and thinking/reasoning controls.
- `/Users/kevinlin/code/openclaw/docs/concepts/models.md` documents model ref/runtime separation and fallback selection that provider adapters consume.
### Source
- `/Users/kevinlin/code/openclaw/src/llm/providers/openai-codex-responses.ts` implements Codex Responses transport setup, account id handling, body/header construction, timeout signals, websocket path, retryable error classification, and prompt-cache affinity.
- `/Users/kevinlin/code/openclaw/src/llm/providers/anthropic.ts` implements Anthropic stream setup, request parameter construction, event handling, OAuth system prompt handling, thinking modes, tool id normalization, message transforms, fine-grained tool streaming beta, and tool conversion.
- `/Users/kevinlin/code/openclaw/src/llm/providers/google-shared.ts` implements Google thinking-part semantics, thought signature retention, tool-call id requirements, assistant text/thinking/tool-call conversion, and tool-result conversion.
- `/Users/kevinlin/code/openclaw/src/agents/transport-stream-shared.ts` provides cross-provider transport stream sanitization, tool-call argument coercion, metadata merge, finalization, and error stream handling.
### Integration tests
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.test.ts` covers OpenAI session runtime overrides, Codex app-server telemetry, external error formatting, missing custom tool output guidance, and Bedrock tool mismatch reset hints.
- `/Users/kevinlin/code/openclaw/src/commands/models.list.e2e.test.ts` covers provider catalog rows, auth/local/provider behavior, and catalog responsiveness.
### Unit tests
- `/Users/kevinlin/code/openclaw/src/llm/providers/openai-codex-responses.test.ts` covers account id decoding, transport timeouts, websocket/SSE behavior, timeout behavior, and prompt-cache affinity.
- `/Users/kevinlin/code/openclaw/src/llm/providers/anthropic.test.ts` covers Anthropic provider auth and signed thinking replay.
- `/Users/kevinlin/code/openclaw/src/llm/providers/google-shared.test.ts` covers projecting text, thinking, tool calls, response ids, and usage.
- `/Users/kevinlin/code/openclaw/src/agents/transport-stream-shared.test.ts` covers sanitization, non-empty tool payload text, headers, success streams, and failure cleanup.
### Gitcrawl queries
- `gitcrawl --json search issues -R openclaw/openclaw "openai-codex Anthropic Google provider tool call"` returned #80667 on pure `claude-cli` sessions missing `trajectory.jsonl` and #78196 on extension plugin loader behavior.
- `gitcrawl --json search issues -R openclaw/openclaw "tool call streaming truncated tool_call provider"` returned #60593 on recurring Anthropic streaming JSON parse errors, #70033 on tool calls emitting empty `{}` arguments for large content, and #87711 on empty assistant delivery.
- `gitcrawl --json search prs -R openclaw/openclaw "provider error descriptors fallback rate limit"` returned PR #86642 adding structured provider error descriptors.
### Discrawl queries
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "tool call streaming"` returned discussions about native progress callbacks, provider streaming/tool-call wrapping, visible tool-call blocks, app-server idle watchdog behavior, Claude CLI/WebChat tool visibility, and Telegram progress modes.
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "openai-codex provider routing"` returned reports about OpenAI OAuth/Codex routing, direct OpenAI Responses path drift, stale persisted route state, and old config/runtime pins.
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "model fallback decision"` returned hosted-provider timeout and fallback discussions, including OpenRouter timeouts and missing bearer failures.

View File

@@ -0,0 +1,91 @@
---
title: "Agent Runtime - Local and Self-hosted Providers Maturity Note"
version: 3
last_refreshed: 2026-05-30
last_refreshed_by: codex
---
# Agent Runtime - Local and Self-hosted Providers Maturity Note
## Summary
Local and self-hosted provider execution is documented and implemented, with especially detailed Ollama guidance for native `/api/chat`, OpenAI-compatible `/v1`, local markers, auth profile format, tool-support flags, lean profiles, context windows, timeouts, and live smoke commands. Coverage is Beta because it is concentrated in Ollama/local-model docs and command behavior. Quality is Alpha because archive evidence shows local models still struggle with tool calling, cold-start timeouts, raw JSON/tool text, and event-loop blocking.
## Category Scope
This category covers local and self-hosted execution paths visible to users/operators: Ollama, OpenAI-compatible local servers, local model profile configuration, tool-capability flags, timeouts, context windows, local image/model smoke checks, and local provider failure handling.
## Features
- Local provider profiles: Local model profile configuration for Ollama and OpenAI-compatible local servers.
- Tool-capability flags: Local provider capability flags and behavior for tool use.
- Timeouts and context windows: Local provider timeout and context-window configuration.
- Local smoke checks: Local image and model smoke checks visible to operators.
- Local failure handling: Operator-facing failure handling for local and self-hosted providers.
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Beta (70%)`
Coverage is useful and operator-facing, but it is uneven across local backends and less uniformly tested than hosted providers.
## Quality Score
- Score: `Alpha (60%)`
Local execution is workable but still fragile in practice: model tool-calling quality, cold starts, context limits, local server blocking, and OpenAI-compatible mode quirks remain recurring issues.
## Completeness Score
- Score: `Beta (70%)`
- Surface instructions: evaluated against `references/completeness/agent-runtime-and-provider-execution.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for Local provider profiles, Tool-capability flags, Timeouts and context windows, Local smoke checks, Local failure handling.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived Coverage score.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- Local provider evidence is strongly Ollama-centered; other local/self-hosted runtimes need the same level of scenario proof.
- Tool-calling behavior depends heavily on model capability and provider mode.
- Timeout guidance exists, but operator defaults still produce reports for slow local LLMs.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/providers/ollama.md` documents native `/api/chat`, `/v1` warnings, raw tool JSON behavior, local auth rules, provider IDs, `models list`, exact `/model ollama` failure behavior, endpoint preflight, live test command, custom base URLs, `compat.supportsTools: false`, `localModelLean`, `timeoutSeconds`, OpenAI-compatible mode tool/streaming reliability warnings, context windows, streaming/tool-calling/thinking support, garbled output handling, and cold local model timeouts.
- `/Users/kevinlin/code/openclaw/docs/concepts/models.md` documents local/GGUF refs, model allowlists, and runtime-independent model refs.
- `/Users/kevinlin/code/openclaw/docs/cli/agent.md` documents `--local`, timeout options, and embedded fallback behavior for local agent runs.
### Source
- `/Users/kevinlin/code/openclaw/src/agents/agent-tools.ts` applies model-provider tool policy and suppresses tools such as web search for local lean profiles.
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/model-selection.ts` resolves model allowlists, local refs, thinking/reasoning settings, and context token limits used by local providers.
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.ts` handles context-window hints, local/embedded runtime fallback behavior, provider timeout copy, and local model retry/fallback interactions.
### Integration tests
- `/Users/kevinlin/code/openclaw/src/commands/models.list.e2e.test.ts` covers local/provider behavior in model catalog/status output.
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.test.ts` covers context-aware reserve token floors, overflow recovery text, local/runtime fallback interactions, model capacity copy, and timeout/fallback diagnostics relevant to local providers.
### Unit tests
- `/Users/kevinlin/code/openclaw/src/agents/agent-tools.message-provider-policy.test.ts` covers provider-based tool policy behavior.
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.test.ts` includes unit-style coverage for context windows, provider fallback state, and timeout/failure copy.
### Gitcrawl queries
- `gitcrawl --json search issues -R openclaw/openclaw "Ollama vLLM SGLang LM Studio tool calling"` returned no matches for the exact backend set.
- `gitcrawl --json search issues -R openclaw/openclaw "local model provider context timeout Ollama"` returned #87642 on exposing `waitForRun` timeout for slow local LLMs, #86599 on local model provider calls blocking the gateway event loop on Windows, #74204 on memory embed timeout for local GGUF, #81214 on subagent regression, and #65502 on resilient model fallback with retry and safe mode.
- `gitcrawl --json search prs -R openclaw/openclaw "Ollama native tool calling streaming"` returned no matching PRs.
### Discrawl queries
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "Ollama tool calling OpenClaw"` returned guidance that some local models are poor at tool calling, user questions about local model limitations and tool use, maintainer guidance that raw tools printed as text indicate model/tool-calling compatibility problems, and comments closing issues around local backend support and Ollama `/v1` misconfiguration.
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "models list provider routing fallback"` returned user-helping-user guidance on Ollama provider versus session/tool pressure and local/custom provider handling.
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "usage limit fallback openai-codex"` included adjacent operator discussions about fallback configuration, useful as contrast but not primary local-provider evidence.

View File

@@ -0,0 +1,92 @@
---
title: "Agent Runtime - Model and Runtime Selection Maturity Note"
version: 3
last_refreshed: 2026-05-30
last_refreshed_by: codex
---
# Agent Runtime - Model and Runtime Selection Maturity Note
## Summary
Model selection and provider routing are among the most mature parts of this surface. Docs explain model refs, configured defaults, user-selected strict refs, provider fallbacks, auth-profile fallbacks, `/model`, runtime overrides, and thinking/context policy. Source centralizes most state in `createModelSelectionState`, and tests cover model list/set behavior plus fallback/retry routing. Quality is Beta because archive evidence shows recent drift around Codex OAuth routes, stale `openai-codex` refs, and per-session/provider fallback state.
## Category Scope
This category covers selecting a model/provider/runtime for an agent turn, honoring user and config choices, resolving thinking/context settings, handling runtime provider overrides, and preserving or clearing invalid route state.
## Features
- Model reference selection: Selecting the model reference for an agent turn from user or configured defaults.
- Provider and runtime overrides: Handling provider selection and runtime overrides for a turn.
- Thinking and context settings: Resolving thinking and context settings as part of model selection.
- Invalid route recovery: Preserving or clearing invalid route state when selections drift or fail.
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Stable (84%)`
Coverage is strong across model docs, CLI command docs, provider docs, source, and e2e tests for model list/set and fallback normalization.
## Quality Score
- Score: `Beta (72%)`
Routing behavior is explicit and defensive, but quality is pulled down by recent operator-visible route repair, stale auth/provider refs, and fallback stickiness reports.
## Completeness Score
- Score: `Stable (84%)`
- Surface instructions: evaluated against `references/completeness/agent-runtime-and-provider-execution.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for Model reference selection, Provider and runtime overrides, Thinking and context settings, Invalid route recovery.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived Coverage score.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- Operator recovery for stale provider/runtime refs is spread across doctor behavior, provider docs, and error copy.
- Runtime policy is strong for common providers but field reports show route drift when Codex OAuth, custom provider IDs, and fallback profiles interact.
- The system needs recurring release-level scenario proof for route repair and fallback reset behavior.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/concepts/models.md` documents model refs versus runtime, selection order, provider/auth fallbacks, configured defaults, auto fallback selections, strict user session selections, model allowlists, local/GGUF refs, `/model` switching, live switching, and strict selected refs.
- `/Users/kevinlin/code/openclaw/docs/cli/models.md` documents `models list`, `models set`, status/probe options, catalog/auth columns, provider catalog responsiveness, ref parsing/fallback, auth profiles, login, paste-api-key, and OpenAI API versus ChatGPT/OAuth routing.
- `/Users/kevinlin/code/openclaw/docs/providers/openai.md` documents OpenAI/Codex route distinctions, naming map, GPT-5.5/Codex app-server repair notes, capability tables, and the default OpenAI agent route summary.
- `/Users/kevinlin/code/openclaw/docs/concepts/agent-runtimes.md` documents runtime selection, fail-closed explicit runtimes, CLI backend aliases, and OpenAI defaulting to the Codex harness.
### Source
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/model-selection.ts` implements provider/model initialization, allowlists, catalog visibility policy, direct stored override handling, stale legacy `openai-codex` override clearing, auth profile override validation, thinking/reasoning resolution, and context token resolution.
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.ts` applies fallback candidate auth profiles, live model switches, runtime config, runtime provider resolution, and retry state.
- `/Users/kevinlin/code/openclaw/src/agents/configured-provider-fallback.ts` defines configured fallback behavior for provider selection.
### Integration tests
- `/Users/kevinlin/code/openclaw/src/commands/models.set.e2e.test.ts` covers model setting and fallback normalization.
- `/Users/kevinlin/code/openclaw/src/commands/models.list.e2e.test.ts` covers model list/status, catalog/auth/local/provider behavior, and provider visibility.
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.test.ts` covers fallback rechecks, stale queued probe dropping after user model switches, preserving and re-persisting fallback origins, CLI runtime override boundaries, model capacity errors, live model switch restarts, and retry-loop caps.
### Unit tests
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.test.ts` includes focused tests for provider/model fallback retry state, auth profile preservation, dropping `authProfileId` when fallback switches providers, and same-provider auth profile fallback.
- `/Users/kevinlin/code/openclaw/src/commands/models.auth.provider-resolution.test.ts` covers auth-provider resolution behavior for model commands.
### Gitcrawl queries
- `gitcrawl --json search issues -R openclaw/openclaw "models list model selection fallback auth profile provider"` returned #59168 on using `provider/name` as the internal model key, #83954 on Pro-plan paths for `gpt-5.5-pro` and retired Spark via Codex CLI/app-server, and #70055 on disabling external CLI sync for auth profiles via config.
- `gitcrawl --json search issues -R openclaw/openclaw "No API key found provider openai-codex auth profile"` returned stale route and Codex OAuth issues including #86470 on doctor rewriting `openai-codex/*` to `openai/*`, #83223 on migrated routes still looking up `openai-codex` auth before fallback, and #86820 on compaction falling back to direct OpenAI API.
- `gitcrawl --json search issues -R openclaw/openclaw "rate limit fallback usage limit openai-codex"` returned #85103 on provider-wide quota fallback not triggering, #87467 on auto rate-limit fallback staying pinned after primary recovery, #79604 on rotating auth profiles before provider fallback, and #79611 on active-memory plugin failover.
### Discrawl queries
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "models list provider routing fallback"` returned a May 16 beta announcement emphasizing Codex app-server reliability, progress timeouts, compaction handling, tool policy enforcement, OAuth fallback, local/custom providers, and guidance on Ollama provider pressure, vision routing, CLI crashes, and per-turn model routing with `before_model_resolve`.
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "openai-codex provider routing"` returned maintainer notes around OpenAI OAuth/Codex routing, `openai-codex` being load-bearing in auth profile resolution, compaction routing, context config, auth order, and stale persisted route state.
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "model fallback decision"` returned recent discussions about openai-codex timeouts, fallback decisions, No API key fallback decisions, OpenRouter timeouts, and session repair loops.

View File

@@ -0,0 +1,113 @@
---
title: "Agent Runtime - Provider Auth Maturity Note"
version: 3
last_refreshed: 2026-05-30
last_refreshed_by: codex
---
# Agent Runtime - Provider Auth Maturity Note
## Summary
Provider auth is broad enough to cover setup, selection, health checks, fallback, and operator-facing diagnostics in one category. Docs explain API keys, OAuth, provider/auth-profile fallbacks, status and probe output, stale-route repair, and restart guidance. Source validates provider/profile compatibility, carries fallback candidate state, classifies structured provider failures, and formats missing-key, OAuth-refresh, capacity, and restart recovery guidance. Quality remains Alpha because archive evidence still shows repeated operator failures around Codex OAuth route repair, profile propagation, quota fallback semantics, sticky fallback state, and provider key discovery.
## Category Scope
This category covers provider credentials, auth profile health, and operator-visible provider recovery behavior: login and paste-key flows, provider auth profile selection, doctor and status repair, auth failover, provider fallback chains, quota and capacity recovery, missing-key and OAuth guidance, restart and stale-route hints, structured diagnostics, subagent credential propagation, and credential-related runtime errors.
## Features
- Login and API-key setup: Login, OAuth, and paste-key flows for provider access.
- Auth profile selection: Selecting and validating provider auth profiles.
- Credential health checks: Doctor, status, and related credential health checks and repair signals.
- Auth failover: Same-provider and cross-profile auth fallback behavior.
- Provider fallback recovery: Provider and auth-profile fallback behavior when execution fails.
- Rate-limit and capacity recovery: Recovery paths for quota, capacity, and rate-limit failures.
- Missing-key and OAuth guidance: Operator guidance for missing keys, expired OAuth state, and related auth failures.
- Restart and stale-route recovery: Recovery from stale route state, restart requirements, and related provider drift.
- Structured provider diagnostics: Structured provider errors and diagnostics delivered into logs or agent replies.
- Subagent credential propagation: Propagating provider credentials into subagent and delegated runtime flows.
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Stable (80%)`
Coverage is strong for OpenAI/Codex, Anthropic, Google, model commands, fallback state, and operator-facing recovery copy, but provider auth and diagnostics still span many flows and are not yet represented by a single end-to-end operator proof matrix.
## Quality Score
- Score: `Alpha (66%)`
Auth/profile behavior remains a frequent operational pain point, especially where Codex OAuth, direct OpenAI API routes, compaction, subagents, doctor repair, and quota fallback behavior overlap.
## Completeness Score
- Score: `Stable (80%)`
- Surface instructions: evaluated against `references/completeness/agent-runtime-and-provider-execution.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for Login and API-key setup, Auth profile selection, Credential health checks, Auth failover, Provider fallback recovery, Rate-limit and capacity recovery, Missing-key and OAuth guidance, Restart and stale-route recovery, Structured provider diagnostics, Subagent credential propagation.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived Coverage score.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- Codex OAuth route repair still produces recent open GitHub and Discord reports.
- Subagent and compaction flows can lose or reinterpret auth profile state.
- Quota-wide and account-specific provider failures need clearer fallback semantics.
- Recovery from stale `openai-codex` route state still depends on doctor repair and explicit guidance.
- Some missing-key and fallback diagnostics are strong in tests but still too hard for operators to map to root cause.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/cli/models.md` documents model status/auth overview, Codex OAuth troubleshooting, auth profile listing, login, paste-api-key, OpenAI API versus ChatGPT/OAuth, and Claude CLI notes.
- `/Users/kevinlin/code/openclaw/docs/concepts/models.md` documents primary model selection, fallbacks, provider auth failover, auto fallback selections, strict user selections, and live model switching.
- `/Users/kevinlin/code/openclaw/docs/cli/agent.md` documents gateway fallback behavior, embedded fallback metadata, gateway timeout fallback session/run id, and SIGTERM/SIGINT `chat.abort`.
- `/Users/kevinlin/code/openclaw/docs/providers/openai.md` documents `openai`, `openai-codex`, Codex plugin and `agentRuntime` naming, OpenAI/Codex route selection, Codex OAuth setup, and doctor repair behavior.
- `/Users/kevinlin/code/openclaw/docs/providers/anthropic.md` documents API key versus Claude CLI authentication and canonical Anthropic refs with `agentRuntime.id: "claude-cli"`.
- `/Users/kevinlin/code/openclaw/docs/providers/google.md` documents Google plugin auth, Gemini CLI OAuth setup, and warning/alias behavior.
- `/Users/kevinlin/code/openclaw/docs/tools/subagents.md` documents subagent auth resolution by agent id and fallback to main profiles.
### Source
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/model-selection.ts` validates auth profile overrides against accepted auth providers, clears invalid overrides, and handles stale legacy `openai-codex` state.
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.ts` carries fallback candidate auth profile state, applies live model switch auth changes, preserves same-provider auth fallback, and drops auth profile ids when switching providers.
- `/Users/kevinlin/code/openclaw/src/llm/providers/openai-codex-responses.ts` classifies retryable errors and configures timeout/retry behavior for Codex Responses transport.
- `/Users/kevinlin/code/openclaw/src/agents/transport-stream-shared.ts` builds structured failure streams with error details.
- `/Users/kevinlin/code/openclaw/src/commands/auth-choice.apply.api-providers.test.ts` maps API key/token provider choices for auth flows.
### Integration tests
- `/Users/kevinlin/code/openclaw/src/commands/models.list.e2e.test.ts` covers catalog auth/status presentation for providers.
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.test.ts` covers OAuth refresh failure guidance, missing API key guidance, stale `openai-codex` missing-key failures pointing at doctor repair, auth profile state on retries, provider-switch auth profile dropping, and same-provider auth fallback.
- `/Users/kevinlin/code/openclaw/src/commands/models.set.e2e.test.ts` covers fallback normalization in model command behavior.
### Unit tests
- `/Users/kevinlin/code/openclaw/src/commands/auth-choice.apply.api-providers.test.ts` covers auth choice mapping for API key/token providers.
- `/Users/kevinlin/code/openclaw/src/commands/models.auth.provider-resolution.test.ts` covers provider auth resolution for model commands.
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.test.ts` includes focused auth-profile regression coverage.
- `/Users/kevinlin/code/openclaw/src/llm/providers/openai-codex-responses.test.ts` covers transport timeouts and websocket/SSE behavior feeding retry decisions.
- `/Users/kevinlin/code/openclaw/src/agents/transport-stream-shared.test.ts` covers failure cleanup and non-empty failure streams.
### Gitcrawl queries
- `gitcrawl --json search issues -R openclaw/openclaw "No API key found provider openai-codex auth profile"` returned many active issues, including #84252 on doctor/status leaving `openai-codex` OAuth sidecar auth partially repaired, #87677 on memory embeddings through Codex OAuth runtime, #86470 on doctor rewriting `openai-codex/*` to `openai/*`, #85797 on image generation requiring an API key despite OAuth, #86820 on compaction falling back to direct OpenAI API, #87051 on OAuth profile not propagating to subagent sessions, #83223 on migrated routes still looking up `openai-codex` auth before fallback, and #80171 on runtime parity QA.
- `gitcrawl --json search issues -R openclaw/openclaw "openai-codex Anthropic Google provider tool call"` returned #80667 on missing `trajectory.jsonl` for pure `claude-cli` sessions and #78196 on extension plugin loader behavior.
- `gitcrawl --json search issues -R openclaw/openclaw "provider error guidance reauth fallback"` returned no direct matches.
- `gitcrawl --json search issues -R openclaw/openclaw "rate limit fallback usage limit openai-codex"` returned #85103 on model fallback chain not triggering for provider-wide quota exhaustion, #87467 on auto rate-limit fallback staying pinned to fallback after primary recovery, #79604 on rotating auth profiles within a candidate before next provider, and #79611 on active-memory plugin provider failover and timeout.
- `gitcrawl --json search prs -R openclaw/openclaw "provider error descriptors fallback rate limit"` returned #86642 adding structured provider error descriptors.
- `gitcrawl --json search prs -R openclaw/openclaw "agent runner fallback model switch"` returned PRs including #85235 on message-tool-only diagnostics, #80482 on cooldown inline API key billing failures, #62682 on terminal abort versus retryable failures, and #86089 on restart recovery replies.
### Discrawl queries
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "No API key found provider openai-codex"` returned May 2026 reports around OpenAI OAuth/Codex routing, plugin errors with `No API key found for provider "openai-codex"`, existing Codex auth no longer recognized after rebuild, direct API routing failures, and users seeing missing OpenAI keys despite Codex OAuth.
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "reauth provider auth profile"` returned Codex auth refresh/persistence reports, scope issues, stale auth order, token rotation failures, and older reauth command confusion.
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "openai-codex provider routing"` returned maintainer/user notes about auth profile resolution, compaction routing, context config, auth order, stale route state, and doctor repair guidance.
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "usage limit fallback openai-codex"` returned discussions about Claude CLI usage/billing fallback losing context, multi-account Codex OAuth failover, OpenAI rate limit auth/provider guidance, Codex backend challenge/limit paths, rate-limit/account-id errors, fallback configs, and model failover being blocked or sticky.
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "model fallback decision"` returned recent fallback decision logs for openai-codex timeouts, No API key cases, OpenRouter timeouts, missing bearer errors, Anthropic empty responses, and session repair loops.

View File

@@ -0,0 +1,328 @@
---
title: "Agent Runtime Maturity Report"
version: 3
last_refreshed: 2026-05-31
last_refreshed_by: codex
---
# Agent Runtime Maturity Report
## Top-level scores
These rollups are simple arithmetic means over the category-note numeric
scores in
`scores.yaml`. Percentages are rounded to the nearest whole number.
- Coverage: `Stable (80%)`
- Quality: `Alpha (69%)`
- Completeness: `Stable (80%)`
- LTS Features: `6/9`
## Summary
This report promotes the archived `agent-runtime-and-provider-execution` maturity evidence from `/Users/kevinlin/tmp/maturity/agent-runtime-and-provider-execution` into the current process-version-3 inventory contract.
The category Coverage and Quality scores come from the archived evidence-backed score rows. Completeness is initialized from the same archived evidence breadth and known-gap record, then joined with the surface-specific completeness rubric referenced by taxonomy.
## Matrix
| Category | LTS | Coverage | Quality | Completeness | Features to evaluate |
| ------------------------------------------------------------------------------------- | --- | -------------- | ------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [Agent Turn Execution](agent-turn-orchestration-and-runtime-lifecycle.md) | ✅ | `Stable (82%)` | `Beta (74%)` | `Stable (82%)` | Turn startup and runtime choice, Session and run coordination, Abort and terminal outcomes |
| [External Runtimes and Subagents](cli-harnesses-external-runtimes-and-subagents.md) | ❌ | `Beta (78%)` | `Alpha (66%)` | `Beta (78%)` | External harness selection, CLI runtime aliases, Subagent turns, Runtime recovery |
| [Hosted Provider Execution](hosted-provider-adapters-and-payload-compatibility.md) | ✅ | `Beta (76%)` | `Beta (70%)` | `Beta (76%)` | Hosted provider turns, Provider-specific model options, Hosted tool use, Reasoning and cache controls, Hosted streaming and replies |
| [Local and Self-hosted Providers](local-and-self-hosted-provider-execution.md) | ❌ | `Beta (70%)` | `Alpha (60%)` | `Beta (70%)` | Local provider profiles, Tool-capability flags, Timeouts and context windows, Local smoke checks, Local failure handling |
| [Model and Runtime Selection](model-selection-provider-routing-and-runtime-policy.md) | ✅ | `Stable (84%)` | `Beta (72%)` | `Stable (84%)` | Model reference selection, Provider and runtime overrides, Thinking and context settings, Invalid route recovery |
| [Provider Auth](provider-auth-profiles-and-credential-health.md) | ✅ | `Stable (80%)` | `Alpha (66%)` | `Stable (80%)` | Login and API-key setup, Auth profile selection, Credential health checks, Auth failover, Provider fallback recovery, Rate-limit and capacity recovery, Missing-key and OAuth guidance, Restart and stale-route recovery, Structured provider diagnostics, Subagent credential propagation |
| [Streaming and Progress](streaming-progress-and-preview-visibility.md) | ❌ | `Stable (84%)` | `Beta (70%)` | `Stable (84%)` | Streaming replies, Progress visibility |
| [Tool Calls and Response Handling](streaming-tool-call-and-response-normalization.md) | ✅ | `Stable (80%)` | `Alpha (66%)` | `Stable (80%)` | Tool-call handling, Usage and response reporting, Failure recovery |
| [Tool Execution Controls](tool-execution-approvals-and-sandbox-policy.md) | ✅ | `Stable (86%)` | `Beta (74%)` | `Stable (86%)` | Tool availability rules, Sandboxed exec behavior, Approval flow, Elevated execution, Tool safety controls, Delegated tool access |
## Scoring rubric
- Coverage:
maturity-label rating for integration, e2e, live, or server/runtime flow
evidence across the category. Unit tests can provide supporting context but never make a
feature covered by themselves.
- Quality:
maturity-label rating for implementation and operational robustness. Unit,
integration, e2e, live, and real runtime-flow test coverage are Coverage
inputs only; they do not raise or lower Quality.
- Completeness:
maturity-label rating for how fully the category delivers the intended
surface-specific capability set. Use the taxonomy-linked completeness
instructions for this surface.
- LTS:
calculated as `quality > 80 and coverage > 90`, or when the matching
taxonomy category sets `human_lts_override`.
- Shared score bands:
`Lovable = 95-100`, `Stable = 80-95`, `Beta = 70-80`,
`Alpha = 50-70`, and `Experimental = 0-50`. At shared boundaries, choose the
higher maturity label.
- Major quality/completeness gaps:
evidence text only, tracked in the detailed feature inventory rather than as a
separate scored dimension.
## Detailed feature inventory
### 1. Agent Turn Execution
Search anchors: agent RPC shape and event stream, runAgentTurnWithFallback, agent.wait timeout and terminal outcomes.
Category note: [Agent Turn Execution](agent-turn-orchestration-and-runtime-lifecycle.md)
Score decisions:
- Coverage: `Stable (82%)`
- Quality: `Beta (74%)`
- Completeness: `Stable (82%)`
- LTS: ✅
Features:
- Turn startup and runtime choice: Starting an agent turn and choosing gateway versus embedded runtime execution.
- Session and run coordination: Establishing session and run ids, queue locks, and related execution coordination.
- Abort and terminal outcomes: Honoring aborts, timing provider/model work, and emitting terminal outcomes.
Primary docs:
- `docs/concepts/agent-loop.md`
- `docs/cli/agent.md`
- `docs/concepts/agent-runtimes.md`
### 2. External Runtimes and Subagents
Search anchors: agent runtimes, subagent turns, CLI runtime aliases.
Category note: [External Runtimes and Subagents](cli-harnesses-external-runtimes-and-subagents.md)
Score decisions:
- Coverage: `Beta (78%)`
- Quality: `Alpha (66%)`
- Completeness: `Beta (78%)`
- LTS: ❌
Features:
- External harness selection: Choosing Codex app-server, ACP, and other external runtime harnesses.
- CLI runtime aliases: Runtime aliases and CLI-based execution paths such as Claude CLI and Gemini CLI.
- Subagent turns: Spawning, delivering, and announcing subagent work outside the default embedded path.
- Runtime recovery: Cleanup, timeout, and liveness behavior for external runtimes and subagents.
Primary docs:
- `docs/concepts/agent-runtimes.md`
- `docs/providers/anthropic.md`
- `docs/providers/google.md`
- `docs/tools/subagents.md`
### 3. Hosted Provider Execution
Search anchors: hosted provider turns, provider-specific model options, streaming reply normalization.
Category note: [Hosted Provider Execution](hosted-provider-adapters-and-payload-compatibility.md)
Score decisions:
- Coverage: `Beta (76%)`
- Quality: `Beta (70%)`
- Completeness: `Beta (76%)`
- LTS: ✅
Features:
- Hosted provider turns: Running agent turns against hosted providers such as OpenAI, Anthropic, and Google.
- Provider-specific model options: Provider-specific model parameters and runtime request settings exposed to users or operators.
- Hosted tool use: Tool use behavior when the active runtime is a hosted provider.
- Reasoning and cache controls: Provider-specific reasoning, thinking, and cache-related controls during hosted execution.
- Hosted streaming and replies: Operator-visible streaming and reply behavior while hosted adapters normalize payload differences.
Primary docs:
- `docs/providers/openai.md`
- `docs/providers/anthropic.md`
- `docs/providers/google.md`
- `docs/concepts/models.md`
### 4. Local and Self-hosted Providers
Search anchors: Ollama local provider profiles, OpenAI-compatible local servers, local smoke checks.
Category note: [Local and Self-hosted Providers](local-and-self-hosted-provider-execution.md)
Score decisions:
- Coverage: `Beta (70%)`
- Quality: `Alpha (60%)`
- Completeness: `Beta (70%)`
- LTS: ❌
Features:
- Local provider profiles: Local model profile configuration for Ollama and OpenAI-compatible local servers.
- Tool-capability flags: Local provider capability flags and behavior for tool use.
- Timeouts and context windows: Local provider timeout and context-window configuration.
- Local smoke checks: Local image and model smoke checks visible to operators.
- Local failure handling: Operator-facing failure handling for local and self-hosted providers.
Primary docs:
- `docs/providers/ollama.md`
- `docs/concepts/models.md`
- `docs/cli/agent.md`
### 5. Model and Runtime Selection
Search anchors: model reference selection, runtime overrides, thinking and context settings.
Category note: [Model and Runtime Selection](model-selection-provider-routing-and-runtime-policy.md)
Score decisions:
- Coverage: `Stable (84%)`
- Quality: `Beta (72%)`
- Completeness: `Stable (84%)`
- LTS: ✅
Features:
- Model reference selection: Selecting the model reference for an agent turn from user or configured defaults.
- Provider and runtime overrides: Handling provider selection and runtime overrides for a turn.
- Thinking and context settings: Resolving thinking and context settings as part of model selection.
- Invalid route recovery: Preserving or clearing invalid route state when selections drift or fail.
Primary docs:
- `docs/concepts/models.md`
- `docs/cli/models.md`
- `docs/providers/openai.md`
- `docs/concepts/agent-runtimes.md`
### 6. Provider Auth
Search anchors: login and API-key setup, auth profile selection, provider fallback recovery.
Category note: [Provider Auth](provider-auth-profiles-and-credential-health.md)
Score decisions:
- Coverage: `Stable (80%)`
- Quality: `Alpha (66%)`
- Completeness: `Stable (80%)`
- LTS: ✅
Features:
- Login and API-key setup: Login, OAuth, and paste-key flows for provider access.
- Auth profile selection: Selecting and validating provider auth profiles.
- Credential health checks: Doctor, status, and related credential health checks and repair signals.
- Auth failover: Same-provider and cross-profile auth fallback behavior.
- Provider fallback recovery: Provider and auth-profile fallback behavior when execution fails.
- Rate-limit and capacity recovery: Recovery paths for quota, capacity, and rate-limit failures.
- Missing-key and OAuth guidance: Operator guidance for missing keys, expired OAuth state, and related auth failures.
- Restart and stale-route recovery: Recovery from stale route state, restart requirements, and related provider drift.
- Structured provider diagnostics: Structured provider errors and diagnostics delivered into logs or agent replies.
- Subagent credential propagation: Propagating provider credentials into subagent and delegated runtime flows.
Primary docs:
- `docs/concepts/models.md`
- `docs/cli/agent.md`
- `docs/cli/models.md`
- `docs/providers/openai.md`
- `docs/providers/anthropic.md`
- `docs/providers/google.md`
- `docs/tools/subagents.md`
### 7. Streaming and Progress
Search anchors: streaming replies, progress visibility, event delivery.
Category note: [Streaming and Progress](streaming-progress-and-preview-visibility.md)
Score decisions:
- Coverage: `Stable (84%)`
- Quality: `Beta (70%)`
- Completeness: `Stable (84%)`
- LTS: ❌
Features:
- Streaming replies: Streaming block updates and partial assistant output before final delivery.
- Progress visibility: Progress preview events and item lifecycle updates surfaced during execution.
Primary docs:
- `docs/concepts/streaming.md`
- `docs/concepts/agent-loop.md`
### 8. Tool Calls and Response Handling
Search anchors: tool-call handling, usage reporting, failure recovery.
Category note: [Tool Calls and Response Handling](streaming-tool-call-and-response-normalization.md)
Score decisions:
- Coverage: `Stable (80%)`
- Quality: `Alpha (66%)`
- Completeness: `Stable (80%)`
- LTS: ✅
Features:
- Tool-call handling: Reliable tool-call behavior across providers, including malformed or provider-specific payload differences.
- Usage and response reporting: Response ids and usage accounting normalized into operator-visible runtime behavior.
- Failure recovery: Failure-stream finalization and cleanup when provider output is malformed or incomplete.
Primary docs:
- `docs/concepts/agent-loop.md`
- `docs/providers/ollama.md`
### 9. Tool Execution Controls
Search anchors: tool availability rules, sandboxed exec behavior, approval flow.
Category note: [Tool Execution Controls](tool-execution-approvals-and-sandbox-policy.md)
Score decisions:
- Coverage: `Stable (86%)`
- Quality: `Beta (74%)`
- Completeness: `Stable (86%)`
- LTS: ✅
Features:
- Tool availability rules: Which tools are available during a turn after policy resolution and provider-based suppression.
- Sandboxed exec behavior: Exec behavior, sandbox roots, and workspace constraints visible to operators.
- Approval flow: Operator approval gates for tool execution.
- Elevated execution: Elevated host execution rules and related controls.
- Tool safety controls: Before-tool-call hooks and related guardrails that shape operator-visible tool behavior.
- Delegated tool access: Inherited or narrowed tool policy for subagents and delegated execution.
Primary docs:
- `docs/gateway/sandbox-vs-tool-policy-vs-elevated.md`
- `docs/concepts/agent-loop.md`
- `docs/tools/subagents.md`
## Recommended scorecard interpretation
Use this migrated score as the current inventory baseline. Refresh individual categories with live category-agent research before treating a high score as an LTS promotion gate.
## Out of scope for this surface
- Redefining taxonomy category boundaries; taxonomy remains the source of truth for category identity, features, docs, and search anchors.
## Audit provenance
- Score source:
`docs/kevinslin/maturity-scorecard/inventory/agent-runtime-and-provider-execution/scores.yaml`.
- Taxonomy metadata source:
`.agents/skills/claw-score/taxonomy.yaml`.
- Archived evidence source:
`/Users/kevinlin/tmp/maturity/agent-runtime-and-provider-execution`.

View File

@@ -0,0 +1,48 @@
version: 1
process_version: 3
data:
- name: Agent Turn Execution
category_note: agent-turn-orchestration-and-runtime-lifecycle.md
coverage: 82
quality: 74
completeness: 82
- name: External Runtimes and Subagents
category_note: cli-harnesses-external-runtimes-and-subagents.md
coverage: 78
quality: 66
completeness: 78
- name: Hosted Provider Execution
category_note: hosted-provider-adapters-and-payload-compatibility.md
coverage: 76
quality: 70
completeness: 76
- name: Local and Self-hosted Providers
category_note: local-and-self-hosted-provider-execution.md
coverage: 70
quality: 60
completeness: 70
- name: Model and Runtime Selection
category_note: model-selection-provider-routing-and-runtime-policy.md
coverage: 84
quality: 72
completeness: 84
- name: Provider Auth
category_note: provider-auth-profiles-and-credential-health.md
coverage: 80
quality: 66
completeness: 80
- name: Streaming and Progress
category_note: streaming-progress-and-preview-visibility.md
coverage: 84
quality: 70
completeness: 84
- name: Tool Calls and Response Handling
category_note: streaming-tool-call-and-response-normalization.md
coverage: 80
quality: 66
completeness: 80
- name: Tool Execution Controls
category_note: tool-execution-approvals-and-sandbox-policy.md
coverage: 86
quality: 74
completeness: 86

View File

@@ -0,0 +1,84 @@
---
title: "Agent Runtime - Streaming and Progress Maturity Note"
version: 3
last_refreshed: 2026-05-31
last_refreshed_by: codex
---
# Agent Runtime - Streaming and Progress Maturity Note
## Summary
Streaming and progress visibility are well covered in docs and tests: OpenClaw separates provider/runtime streaming from channel delivery, documents block and preview streaming modes, and surfaces tool-progress and item lifecycle updates before final delivery. Coverage is Stable. Quality is Beta because runtime and channel differences still produce missing progress updates, suppressed previews, and occasional terminal-update confusion.
## Category Scope
This category covers operator-visible streaming and progress behavior before
final delivery: streaming replies, preview and block streaming modes, and
progress visibility through tool-progress or item lifecycle updates.
## Features
- Streaming replies: Streaming block updates and partial assistant output before final delivery.
- Progress visibility: Progress preview events and item lifecycle updates surfaced during execution.
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Stable (84%)`
Coverage is strong across streaming docs, agent-loop docs, event plumbing, and focused tests for preview updates, item lifecycle events, duplicate progress suppression, and terminal delivery behavior.
## Quality Score
- Score: `Beta (70%)`
Streaming and progress behavior is broadly solid, but field reports still show runtime- and channel-specific differences around progress callbacks, terminal updates, and preview behavior.
## Completeness Score
- Score: `Stable (84%)`
- Surface instructions: evaluated against `references/completeness/agent-runtime-and-provider-execution.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for Streaming replies, Progress visibility.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived combined category.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- Some progress visibility behavior still differs by runtime and channel.
- Native progress callbacks can still be suppressed or delayed in some flows.
- Terminal updates after long-running tool activity still need more consistent operator-facing behavior.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/concepts/streaming.md` documents the two streaming layers, block streaming, preview modes, channel mapping, runtime behavior, and tool-progress preview updates.
- `/Users/kevinlin/code/openclaw/docs/concepts/agent-loop.md` documents assistant and tool event streams, block streaming behavior, event-stream shapes, and session timeout semantics.
### Source
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.ts` bridges assistant and tool events into previews, tracks item lifecycle events, suppresses duplicate progress, and handles terminal streaming text before final delivery.
- `/Users/kevinlin/code/openclaw/src/llm/providers/anthropic.ts` emits streaming assistant and tool events, including fine-grained tool streaming behavior.
- `/Users/kevinlin/code/openclaw/src/llm/providers/google-shared.ts` emits assistant, thinking, and tool-call stream events that feed operator-visible progress and streaming behavior.
### Integration tests
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.test.ts` covers CLI assistant event previews, item lifecycle events, duplicate progress skipping, raw tool progress details, tool-start progress before slow typing, and Codex app-server telemetry.
### Unit tests
- `/Users/kevinlin/code/openclaw/src/llm/providers/google-shared.test.ts` covers streamed text, thinking, and tool event projection.
- `/Users/kevinlin/code/openclaw/src/llm/providers/anthropic.test.ts` covers streaming behavior that affects signed thinking replay and provider event handling.
### Gitcrawl queries
- `gitcrawl --json search issues -R openclaw/openclaw "tool call streaming truncated tool_call provider"` returned #60593 on recurring Anthropic streaming JSON parse errors, #70033 on tool calls emitting empty `{}` arguments for large content, and #87711 on empty assistant delivery.
### Discrawl queries
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "tool call streaming"` returned May 2026 discussions about `/verbose off` suppressing native progress callbacks, missing terminal updates, provider streaming/tool-call wrapping, and tool-call visibility modes.

View File

@@ -0,0 +1,93 @@
---
title: "Agent Runtime - Tool Calls and Response Handling Maturity Note"
version: 3
last_refreshed: 2026-05-31
last_refreshed_by: codex
---
# Agent Runtime - Tool Calls and Response Handling Maturity Note
## Summary
Tool-call handling and response normalization are well covered in docs and tests: OpenClaw normalizes malformed tool-call arguments, provider-specific payload differences, usage accounting, and terminal failure streams across adapters and shared transport code. Coverage is Stable. Quality is Alpha because recent archive evidence still shows empty tool arguments, raw tool JSON or visible tool-call blocks, and terminal-empty assistant delivery that still leaks surprising states to users.
## Category Scope
This category covers operator-visible tool-call and response-handling behavior:
reliable tool-call payload handling across providers, usage and response
reporting, and recovery when provider output is malformed, empty, or
incomplete.
## Features
- Tool-call handling: Reliable tool-call behavior across providers, including malformed or provider-specific payload differences.
- Usage and response reporting: Response ids and usage accounting normalized into operator-visible runtime behavior.
- Failure recovery: Failure-stream finalization and cleanup when provider output is malformed or incomplete.
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Stable (80%)`
Coverage is strong across provider adapters, shared transport code, and focused tests for tool payload coercion, response ids and usage, malformed provider output, and failure finalization.
## Quality Score
- Score: `Alpha (66%)`
Normalization is robust in source, but field reports show provider tool-call output and malformed responses still leak surprising states to users.
## Completeness Score
- Score: `Stable (80%)`
- Surface instructions: evaluated against `references/completeness/agent-runtime-and-provider-execution.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for Tool-call handling, Usage and response reporting, Failure recovery.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived Coverage score.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- Empty or malformed tool-call arguments still emerge in provider-specific edge cases.
- Raw tool JSON or visible tool-call blocks still appear in some local or compatibility modes.
- Terminal-empty and tool-only responses still need more consistent operator-facing explanation.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/concepts/agent-loop.md` documents tool-event streams, final payload behavior, and timeout behavior around the agent loop.
- `/Users/kevinlin/code/openclaw/docs/providers/ollama.md` documents native tool-calling, OpenAI-compatible mode reliability warnings, raw tool JSON as text, garbled output handling, and tool-calling compatibility caveats.
### Source
- `/Users/kevinlin/code/openclaw/src/agents/transport-stream-shared.ts` coerces transport tool-call arguments, merges headers and metadata, finalizes failure streams, and normalizes transport error details.
- `/Users/kevinlin/code/openclaw/src/llm/providers/anthropic.ts` normalizes streamed tool ids and partial JSON tool arguments for Anthropic responses.
- `/Users/kevinlin/code/openclaw/src/llm/providers/google-shared.ts` normalizes tool call ids, tool arguments, response ids, and usage accounting for Google-family providers.
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.ts` formats terminal empty/tool-only outcomes and bridges normalized tool results into operator-visible replies.
### Integration tests
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.test.ts` covers media-only tool results, plan-only terminal result fallback, terminal-empty result classification, stripping glued leading `NO_REPLY` tokens, streamed tool results delivery, and tool-only outcome handling.
- `/Users/kevinlin/code/openclaw/src/agents/agent-tools.before-tool-call.integration.e2e.test.ts` covers hook-driven tool parameter modification, blocking, deduplication, and context around tool-call execution.
### Unit tests
- `/Users/kevinlin/code/openclaw/src/agents/transport-stream-shared.test.ts` covers surrogate sanitization, non-empty tool payload text, header propagation, successful stream finalization, and failure cleanup.
- `/Users/kevinlin/code/openclaw/src/llm/providers/google-shared.test.ts` covers tool call projection, response ids, and usage.
- `/Users/kevinlin/code/openclaw/src/llm/providers/anthropic.test.ts` covers signed thinking replay and provider behavior that affects tool payload normalization.
### Gitcrawl queries
- `gitcrawl --json search issues -R openclaw/openclaw "tool call streaming truncated tool_call provider"` returned #60593 on recurring Anthropic streaming JSON parse errors, #70033 on tool calls emitting empty `{}` arguments for large content, and #87711 on empty assistant delivery.
- `gitcrawl --json search issues -R openclaw/openclaw "openai-codex Anthropic Google provider tool call"` returned adapter-adjacent issues for `claude-cli` session artifacts and extension plugin loading.
- `gitcrawl --json search prs -R openclaw/openclaw "provider error descriptors fallback rate limit"` returned #86642, which improves structured provider error descriptors feeding normalized runtime errors.
### Discrawl queries
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "tool call streaming"` returned May 2026 discussions about provider streaming/tool-call wrapping, visible tool-call blocks, Claude CLI/WebChat tool visibility, and missing terminal updates.
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "Ollama tool calling OpenClaw"` returned reports and guidance on raw tools printed as text and model/tool-calling compatibility.
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "model fallback decision"` returned fallback logs where missing or empty provider output contributed to operator-visible failure paths.

View File

@@ -0,0 +1,94 @@
---
title: "Agent Runtime - Tool Execution Controls Maturity Note"
version: 3
last_refreshed: 2026-05-30
last_refreshed_by: codex
---
# Agent Runtime - Tool Execution Controls Maturity Note
## Summary
Tool execution policy is the strongest component in this surface. Docs distinguish sandboxing, tool policy, and elevated approvals; source centralizes tool registration, workspace/sandbox roots, inherited/subagent policy, exec config, schema normalization, and before-tool-call hooks; tests cover approval gates, policy hooks, subagent tool restrictions, and progress behavior. Quality is Beta because archive evidence still shows edge cases around exec approval forwarding, per-agent deny rules, sandbox backend expectations, and plugin/service boundary assumptions.
## Category Scope
This category covers operator-visible control over tools during agent turns:
tool availability rules, sandboxed exec behavior, approval flow, elevated
execution, tool safety controls, and delegated tool access for subagents.
## Features
- Tool availability rules: Which tools are available during a turn after policy resolution and provider-based suppression.
- Sandboxed exec behavior: Exec behavior, sandbox roots, and workspace constraints visible to operators.
- Approval flow: Operator approval gates for tool execution.
- Elevated execution: Elevated host execution rules and related controls.
- Tool safety controls: Before-tool-call hooks and related guardrails that shape operator-visible tool behavior.
- Delegated tool access: Inherited or narrowed tool policy for subagents and delegated execution.
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Stable (86%)`
Coverage is strong across docs, source, e2e tests, unit tests, and archive evidence for policy and approval behavior.
## Quality Score
- Score: `Beta (74%)`
The design is mature, but policy semantics remain subtle for users and operators when CLI backends, subagents, plugin services, and elevated execution overlap.
## Completeness Score
- Score: `Stable (86%)`
- Surface instructions: evaluated against `references/completeness/agent-runtime-and-provider-execution.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for Tool availability rules, Sandboxed exec behavior, Approval flow, Elevated execution, Tool safety controls, Delegated tool access.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived Coverage score.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- Operator docs explain the policy layers, but field reports still show confusion about what sandboxing does and does not constrain.
- CLI backend approval forwarding is not as settled as the main embedded runtime path.
- Per-agent and inherited policy behavior needs continued regression proof as subagents expand.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/gateway/sandbox-vs-tool-policy-vs-elevated.md` distinguishes sandbox, tool policy, and elevated exec; documents tool policy layers/rules, tool groups, sandboxed MCP server allow gates, elevated exec-only gates, and sandbox jail fixes.
- `/Users/kevinlin/code/openclaw/docs/concepts/agent-loop.md` documents plugin hooks including `before_tool_call`, tool-call handling, and runtime event streams.
- `/Users/kevinlin/code/openclaw/docs/tools/subagents.md` documents subagent tool policy, tool restriction, auth resolution, announce behavior, delivery routing, concurrency, liveness, and recovery.
### Source
- `/Users/kevinlin/code/openclaw/src/agents/agent-tools.ts` implements model-provider tool policy, local model tool suppression, exec config merging, tool policy setup, group/sender/sandbox/subagent/inherited policy, workspace/sandbox roots, `apply_patch` restrictions, exec tool setup, the tool policy pipeline, schema normalization, and `before_tool_call` hook wrapping.
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.ts` forwards plan, approval, command output, and patch events through runtime delivery.
- `/Users/kevinlin/code/openclaw/src/agents/cli-runner.ts` persists approved CLI user turn transcripts and runs CLI hooks around backend execution.
### Integration tests
- `/Users/kevinlin/code/openclaw/src/agents/bash-tools.exec-gateway-approval.e2e.test.ts` covers gateway-hosted exec approvals on separate connections.
- `/Users/kevinlin/code/openclaw/src/agents/agent-tools.before-tool-call.integration.e2e.test.ts` covers normal `before_tool_call` behavior, parameter modification, blocking, deduplication, and context.
- `/Users/kevinlin/code/openclaw/src/agents/openclaw-tools.subagents.sessions-spawn.lifecycle.test.ts` covers subagent lifecycle, cleanup, timeout handling, account routing, announce behavior, and policy-adjacent session behavior.
### Unit tests
- `/Users/kevinlin/code/openclaw/src/agents/agent-tools.message-provider-policy.test.ts` covers provider-based message/tool policy behavior.
- `/Users/kevinlin/code/openclaw/src/auto-reply/reply/agent-runner-execution.test.ts` covers approval, command output, patch event forwarding, tool progress details, and streamed tool result delivery.
- `/Users/kevinlin/code/openclaw/src/agents/transport-stream-shared.test.ts` covers safe transport behavior for tool payloads.
### Gitcrawl queries
- `gitcrawl --json search issues -R openclaw/openclaw "exec approvals tool policy sandbox agent tool"` returned #44253 on per-agent `tools.selfDeny`, #69512 on forwarding `exec-approvals.json` allowlists to `claude-cli` backend sessions, #78965 on local user sandbox backend, #48532 on security by intent, #67440 on optional TOTP for exec approvals, #48503 on enriching `before_tool_call` events with action classification/input provenance, and #82548 on safety/quality observability events.
- `gitcrawl --json search issues -R openclaw/openclaw "claude-cli codex cli harness subagent sessions_spawn"` returned #73097 on PI harness ignoring `cliBackends` configuration and splitting subagent execution from chat path.
### Discrawl queries
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "exec approvals tool policy"` returned May 2026 release testing notes covering auth/profile, sandbox policy, and exec approvals; discussions of node file fetch policy; explanations that denying `exec` at agent tool policy level does not sandbox plugins/services; comments that sandbox/tool-policy/exec-approval controls are useful but not solved defaults; and issue-closing comments for related controls.
- `/Users/kevinlin/.local/bin/discrawl search --limit 10 "sessions_spawn claude-cli"` returned Claude CLI and ACP runtime discussions that affect tool permissions, sandbox boundaries, and subagent UX.

View File

@@ -0,0 +1,109 @@
---
title: "Android app - Background Service, Reconnect, and Presence Maturity Note"
version: 3
last_refreshed: 2026-05-30
last_refreshed_by: codex
---
# Android app - Background Service, Reconnect, and Presence Maturity Note
## Summary
Android background operation is implemented around a foreground service, persistent notification, reconnecting Gateway sessions, presence-alive beacons, notification-listener state, and foreground-service microphone type switching. Coverage is Alpha because docs and source cover the intended behavior but no live backgrounding scorecard was found. Quality is the weakest Android component: archive evidence includes a foreground-service crash issue and an active PR to avoid persistent `dataSync` foreground service use.
## Category Scope
- `NodeForegroundService`, persistent notification, background reconnect, node presence beacons, notification listener state, Gateway session reconnect, and reconnect after app backgrounding.
- Out of scope: individual node command handlers except where foreground/background state changes command availability.
## Features
- Background reconnect and presence: Foreground-service presence, reconnect, and node presence behavior.
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Alpha (62%)`
- Positive signals: Docs explicitly say Android keeps the Gateway connection alive through a foreground service, auto-reconnects after first pairing, and sends `node.presence.alive` when backgrounded while connected. Source implements foreground notification state, reconnect loop, presence beacon payload/skip logic, and reconnect tests.
- Negative signals: No live Android backgrounding scenario was found that proves app background, foreground-service notification, presence beacon handling, Gateway restart, network loss, and app relaunch together.
- Integration gaps: Need a real-device background/reconnect scorecard across Android 14/15 service restrictions, battery saver, network changes, Gateway restart, and Talk Mode microphone service promotion/demotion.
## Quality Score
- Score: `Alpha (55%)`
- Gitcrawl reports: `ForegroundServiceStartNotAllowedException Android` found issue #64903 for Android app crashes on `NodeForegroundService startForeground` with `ForegroundServiceStartNotAllowedException` and PR #80082 `fix(android): avoid dataSync FGS for persistent node`.
- Discrawl reports: `Android foreground service reconnect presence` returned no direct hits. Broader support context under node capability searches notes mobile node background state causes Canvas/camera/screen failures.
- Good qualities: The service uses a low-importance persistent notification, updates title/text from runtime state, adds a Disconnect action, switches service type for Talk Mode, and presence beacon responses require `handled: true` before counting durable last-seen updates.
- Bad qualities: Android foreground-service policy is a live crash risk, persistent node operation touches OS service quotas, and docs do not provide a current operator recipe for battery optimization, service denial, or reconnect triage.
- Excluded from quality: Test coverage and runtime-flow proof were not used to raise or lower Quality.
## Completeness Score
- Score: `Alpha (62%)`
- Surface instructions: evaluated against `references/completeness/android-app.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for Background reconnect and presence.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived Coverage score.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- Prove background reconnect and presence on current Android OS versions with real devices.
- Add operator guidance for foreground-service denial, battery saver, OEM background restrictions, and notification permission states.
- Clarify which commands intentionally fail while the app is backgrounded and how to recover.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/platforms/android.md` documents foreground service connection keepalive, auto-reconnect on launch, and presence alive beacons after authenticated node session connect and backgrounding.
- `/Users/kevinlin/code/openclaw/apps/android/README.md` lists authenticated background presence beacons and push notifications in the rebuild checklist.
- `/Users/kevinlin/code/openclaw/docs/nodes/troubleshooting.md` is linked as related Android node troubleshooting.
### Source
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/NodeForegroundService.kt` starts a foreground service, maintains the persistent notification, exposes Disconnect, and promotes service types for Talk Mode.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/node/ConnectionManager.kt` builds node connect options, user agent, advertised capabilities, and TLS policy used on reconnect.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/node/NodePresenceAliveBeacon.kt` builds and decodes `node.presence.alive`, throttles recent successes, and sanitizes failure reasons.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/gateway/GatewaySession.kt` owns connect, disconnect, reconnect, pause-after-auth-failure, and current connection closure.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/node/DeviceNotificationListenerService.kt` tracks notification-listener connection state and emits `notifications.changed` events.
### Integration tests
- No live background/reconnect Android scenario was found.
- `/Users/kevinlin/code/openclaw/src/gateway/android-node.capabilities.live.test.ts` requires the app to stay unlocked and foregrounded for capability execution, which highlights the missing background scenario.
### Unit tests
- `/Users/kevinlin/code/openclaw/apps/android/app/src/test/java/ai/openclaw/app/NodeForegroundServiceTest.kt` covers foreground-service notification/type behavior.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/test/java/ai/openclaw/app/gateway/GatewaySessionReconnectTest.kt` covers replacing active connections and reconnect pause behavior.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/test/java/ai/openclaw/app/node/NodePresenceAliveBeaconTest.kt` covers presence beacon payload/response helper behavior.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/test/java/ai/openclaw/app/node/ConnectionManagerTest.kt` covers connection manager behavior.
### Gitcrawl queries
Query:
`gitcrawl search openclaw/openclaw --query "ForegroundServiceStartNotAllowedException Android" --json`
Results:
- Issue #64903 `Android app crashes on NodeForegroundService startForeground with ForegroundServiceStartNotAllowedException`.
- PR #80082 `fix(android): avoid dataSync FGS for persistent node`.
### Discrawl queries
Query:
`/Users/kevinlin/.local/bin/discrawl search --mode fts --limit 5 "Android foreground service reconnect presence"`
Results:
- No direct hits.
Additional query context:
- `/Users/kevinlin/.local/bin/discrawl search --mode fts --limit 5 "Android node capabilities gateway commands"` found support guidance that mobile node Canvas/camera/screen commands fail when the app is backgrounded.

View File

@@ -0,0 +1,111 @@
---
title: "Android app - Media Capture Maturity Note"
version: 3
last_refreshed: 2026-05-30
last_refreshed_by: codex
---
# Android app - Media Capture Maturity Note
## Summary
Android media capture includes CameraX photo and clip capture, image payload resizing, camera HUD feedback, WebView Canvas/A2UI, photo-library access in the third-party flavor, and live capability checks for camera/canvas commands. Coverage is Alpha because the implementation is real but foreground-only and the Android camera docs are not fully aligned with the source command set. Quality is Alpha because permissions, payload limits, WebView readiness, Play flavor restrictions, and foreground state make the operator path fragile.
## Category Scope
Included in this category:
- Camera and media capture: Camera listing, capture, photo, screen, and media capture behavior.
## Features
- Camera and media capture: Camera listing, capture, photo, screen, and media capture behavior.
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Alpha (66%)`
- Positive signals: Android docs cover Canvas and camera foreground commands; source implements CameraX photo/clip capture, permission prompting, payload guards, A2UI/WebView actions, and photo-library access where the flavor permits it. The live capability suite profiles `camera.list`, `camera.snap`, `camera.clip`, `canvas.*`, and `canvas.a2ui.*`.
- Negative signals: Live capability tests are preconditioned on a paired, foregrounded, unlocked app and require the Screen tab for Canvas/A2UI. Docs for shared camera commands only list Android `camera.list` in the Android section even though source and platform docs expose snap/clip.
- Integration gaps: Need a real-device Android media scorecard that keeps the app foregrounded, grants camera/mic/photo permissions, invokes front/back photo, short clip with/without audio, Canvas navigate/eval/snapshot, and records background failure behavior.
## Quality Score
- Score: `Alpha (62%)`
- Gitcrawl reports: `camera.snap Android` found issue #87058 where Android node connected but advertised zero commands; the snippet notes `camera.snap`, `camera.clip`, and `canvas.*` are high-risk gated commands. `photos.latest Android` returned no direct hits.
- Discrawl reports: Search found January support messages describing Android node camera snap/clip, Canvas, voice wake, and screen recording as supported node abilities, and warning that nodes are often offline or foreground dependent.
- Good qualities: Camera commands request runtime permissions, clamp clip duration, cap payload size, recompress JPEGs under API limits, show camera HUD state, and separate Play flavor from third-party photo access.
- Bad qualities: Media commands are foreground-only, WebView/A2UI depends on Screen tab readiness and Gateway canvas host reachability, and Play flavor removes photo-library permissions. Source/docs alignment is imperfect for Android camera command details.
- Excluded from quality: Test coverage and runtime-flow proof were not used to raise or lower Quality.
## Completeness Score
- Score: `Alpha (66%)`
- Surface instructions: evaluated against `references/completeness/android-app.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for Camera and media capture.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived Coverage score.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- Align `/nodes/camera` Android command docs with source and platform docs for `camera.snap` and `camera.clip`.
- Add a foreground/background media failure-mode runbook with exact operator messages.
- Decide whether `photos.latest` is supported only for third-party builds or should have a Play-safe replacement.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/platforms/android.md` documents Canvas host, `canvas.eval`, `canvas.snapshot`, `canvas.navigate`, A2UI commands, and foreground-only camera commands `camera.snap` and `camera.clip`.
- `/Users/kevinlin/code/openclaw/docs/nodes/camera.md` documents Android camera settings, permissions, foreground requirement, and `camera.list`; its Android command list is narrower than the source and platform page.
- `/Users/kevinlin/code/openclaw/apps/android/README.md` documents Screen tab requirements for A2UI integration tests and says Play builds remove photo-library access.
### Source
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/node/CameraCaptureManager.kt` implements CameraX device listing, photo capture, clip recording, permission requests, EXIF rotation, JPEG scaling, and payload limits.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/node/CameraHandler.kt` handles `camera.list`, `camera.snap`, `camera.clip`, HUD state, debug logging, clip size limits, and base64 payloads.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/node/PhotosHandler.kt` implements `photos.latest` with permission checks, latest image query, resizing, and base64 budget caps.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/ui/CanvasScreen.kt` implements the WebView Canvas, safe browsing settings, WebMessage A2UI bridge, visibility lifecycle, and render-process handling.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/node/InvokeDispatcher.kt` enforces foreground requirement for camera and canvas commands.
### Integration tests
- `/Users/kevinlin/code/openclaw/src/gateway/android-node.capabilities.live.test.ts` includes profiles for `camera.list`, `camera.snap`, `camera.clip`, `canvas.present`, `canvas.navigate`, `canvas.eval`, `canvas.snapshot`, and A2UI push/reset commands.
- `/Users/kevinlin/code/openclaw/apps/android/scripts/perf-online-benchmark.sh` checks Screen tab WebView availability before running the screen benchmark.
### Unit tests
- `/Users/kevinlin/code/openclaw/apps/android/app/src/test/java/ai/openclaw/app/node/CameraHandlerTest.kt`, `JpegSizeLimiterTest.kt`, `PhotosHandlerTest.kt`, `CanvasControllerSnapshotParamsTest.kt`, `CanvasActionTrustTest.kt`, and `CanvasA2UIActionBridgeTest.kt` cover media and canvas helpers.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/test/java/ai/openclaw/app/node/InvokeDispatcherTest.kt` covers command dispatch behavior.
### Gitcrawl queries
Query:
`gitcrawl search openclaw/openclaw --query "camera.snap Android" --json`
Results:
- Issue #87058 `Android node connects but advertises zero commands ...`; snippet notes `camera.snap`, `camera.clip`, and `canvas.*` as correctly gated high-risk commands.
Query:
`gitcrawl search openclaw/openclaw --query "photos.latest Android" --json`
Results:
- No direct hits.
### Discrawl queries
Query:
`/Users/kevinlin/.local/bin/discrawl search --mode fts --limit 5 "Android camera snap screen canvas"`
Results:
- 2026-01-03 support messages describe Android nodes as paired companion devices that can expose camera snap/clip, Canvas, screen recording, and audio/TTS surfaces, while noting real availability depends on node connectivity.

View File

@@ -0,0 +1,103 @@
---
title: "Android app - Mobile Chat Maturity Note"
version: 3
last_refreshed: 2026-05-30
last_refreshed_by: codex
---
# Android app - Mobile Chat Maturity Note
## Summary
The Android app has a substantial mobile chat surface: session selection, history, optimistic sends, streaming assistant text, pending tool-call display, image attachments, thinking controls, markdown rendering, and online chat benchmark support. Coverage reaches Beta because the implementation spans Gateway chat RPCs and online UI proof, though no full Play-installed chat scenario was found. Quality stays Alpha because active archive evidence includes chat copy/reply review issues and the current source still depends on fast-moving mobile UI paths.
## Category Scope
Included in this category:
- Chat tab: Chat tab, session list/filtering, composer, image attachments, message parsing/rendering, model/provider status adjacent to chat, and Gateway chat RPC integration
## Features
- Chat tab: Chat tab, session list/filtering, composer, image attachments, message parsing/rendering, model/provider status adjacent to chat, and Gateway chat RPC integration
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Beta (70%)`
- Positive signals: Docs describe `chat.history`, `chat.send`, `chat.subscribe`, session selection, display normalization, and best-effort push updates. Source implements history bootstrap, session switching, optimistic messages, streaming assistant text, pending tool calls, abort/refresh, attachments, and markdown rendering. The online benchmark explicitly checks connected state and live chat composer.
- Negative signals: Evidence is stronger for source/unit/UI benchmark slices than for an end-to-end Play-installed chat path through install, pair, send, stream, background, reconnect, and resume.
- Integration gaps: Need recurring mobile chat QA that sends text and images, changes sessions, streams a tool-using answer, backgrounds/reopens the app, and verifies history parity with another client.
## Quality Score
- Score: `Alpha (66%)`
- Gitcrawl reports: `Android message copy text selection chat screen` found issue #57754 and PR #59603 for chat copy/text selection. The PR review record flagged reply quoting, attachment-only reply context, empty text actions, and reply send semantics.
- Discrawl reports: Search found GitHub mirror review comments on PR #59603 that identify user-visible reply/copy problems around multiline quoting, image-only messages, blank copy/share payloads, and local-only reply UI state.
- Good qualities: Chat logic tracks pending run IDs, normalizes sessions, strips noisy model-control/tool-call text from history, supports image attachments with size handling, and separates visible health/errors from composer send enablement.
- Bad qualities: Reply/copy behavior has had several subtle user-facing regressions, mobile chat UI is still being actively rebuilt, and session continuity across network changes/backgrounding lacks a published Android-specific runbook result.
- Excluded from quality: Test coverage and runtime-flow proof were not used to raise or lower Quality.
## Completeness Score
- Score: `Beta (70%)`
- Surface instructions: evaluated against `references/completeness/android-app.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for Chat tab.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived Coverage score.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- Add a mobile chat parity smoke against WebChat/TUI for history, streaming, sessions, attachments, and abort.
- Confirm reply/copy/text-selection behavior after the PR #59603 review findings.
- Make reconnect and session-resume state explicit in Android chat diagnostics.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/platforms/android.md` documents Chat tab history via `chat.history`, send via `chat.send`, best-effort `chat.subscribe`, session selection, and display normalization behavior.
- `/Users/kevinlin/code/openclaw/apps/android/README.md` says the rebuild includes restyled Chat UI, streaming support, and push notifications for gateway/chat status updates.
### Source
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/chat/ChatController.kt` implements session loading/switching, health, optimistic sends, pending runs, streaming assistant text, tool-call state, history, and Gateway `chat.send`.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatScreen.kt` composes chat header, notices, message list, attachments, voice shortcut, thinking level, refresh/abort, and send path.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatComposer.kt` implements composer controls, thinking selector, attachment strip, refresh, abort, and send enablement.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/ui/chat/ChatMarkdown.kt` renders markdown blocks, code, tables, task lists, links, images, and selection containers.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/ui/SessionsScreen.kt` renders recent/live session filters, sorting, and active-session rows.
### Integration tests
- `/Users/kevinlin/code/openclaw/apps/android/scripts/perf-online-benchmark.sh` verifies the app reaches a visible connected state and that the live chat composer is present, then runs chat session-switch or scroll benchmarks.
- No full Android chat e2e through a real Gateway answer and another client history parity check was found.
### Unit tests
- `/Users/kevinlin/code/openclaw/apps/android/app/src/test/java/ai/openclaw/app/chat/ChatControllerMessageIdentityTest.kt`, `ChatControllerSessionPolicyTest.kt`, and `ChatMessageContentParsingTest.kt` cover chat model/controller behavior.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/test/java/ai/openclaw/app/ui/chat/ChatComposerDraftTest.kt`, `ChatImageCodecTest.kt`, `ChatMarkdownTest.kt`, `ChatSheetContentTest.kt`, and `SessionFiltersTest.kt` cover chat UI helpers.
### Gitcrawl queries
Query:
`gitcrawl search openclaw/openclaw --query "Android message copy text selection chat screen" --json`
Results:
- Issue #57754 `Android: Add message copy and text selection to chat screen`.
- PR #59603 `feat(android): Add message copy and text selection to chat screen`.
### Discrawl queries
Query:
`/Users/kevinlin/.local/bin/discrawl search --mode fts --limit 5 "Android chat screen message copy"`
Results:
- 2026-04-03 GitHub mirror review comments on PR #59603 flagged multiline quote formatting, attachment-only reply context, empty copy/share actions for image-only messages, and missing reply target in outgoing send path.

View File

@@ -0,0 +1,108 @@
---
title: "Android app - Connection Setup Maturity Note"
version: 3
last_refreshed: 2026-05-30
last_refreshed_by: codex
---
# Android app - Connection Setup Maturity Note
## Summary
Android pairing and Gateway security have substantial implementation depth: setup-code/manual flows, mDNS and wide-area DNS-SD discovery, secure endpoint validation, device-token persistence, TLS fingerprint handling, node and operator roles, and reconnect policy. Coverage is Alpha near Beta because source and unit tests are strong but live Android pairing proof is preconditioned rather than turnkey. Quality remains Alpha because archive evidence shows repeated operator confusion around auth, LAN addressing, protocol/version skew, and manual `ws://` parsing.
## Category Scope
Included in this category:
- Gateway discovery: Gateway discovery, setup-code and manual endpoint parsing, WS/WSS connection setup, TLS trust decisions, device identity, stored device tokens, node/operator auth, and connection error handling
## Features
- Gateway discovery: Gateway discovery, setup-code and manual endpoint parsing, WS/WSS connection setup, TLS trust decisions, device identity, stored device tokens, node/operator auth, and connection error handling
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Alpha (68%)`
- Positive signals: Docs describe mDNS, setup-code/manual connection, secure remote endpoint rules, Tailscale Serve guidance, pairing approval commands, auto-reconnect, and node status checks. Unit tests cover endpoint parsing, auth payloads, token storage, TLS probe cleanup, reconnect, and setup flow logic.
- Negative signals: The main live Android capability suite assumes the app is already installed, reachable, paired, approved, and foregrounded. No clean first-run Android connect-to-approval live scenario was found.
- Integration gaps: Need a single live scenario that starts a fresh Gateway, connects Android by setup code and manual URL, exercises TLS trust/cleartext policy, approves pairing, verifies node/operator sessions, and records reconnection after auth failure.
## Quality Score
- Score: `Alpha (64%)`
- Gitcrawl reports: `Android pairing websocket TLS manual LAN setup protocol mismatch` found issue #87216 for manual LAN setup parsing `ws://` as host `ws`. Broader `Android app` search also surfaced #85966 for silent WebSocket close after node pairing and #78807 for private LAN pairing auth.
- Discrawl reports: Search found a March 7 GitHub mirror comment on #16638 where Android pairing with `gateway.auth.token` still hit `device signature invalid`, plus a February support thread walking a user through LAN IP, reachability, and auth/pairing diagnosis.
- Good qualities: The endpoint parser blocks insecure remote `ws://` while permitting loopback, emulator bridge, and private LAN hosts; stored device tokens are scoped by device and role; discovery TXT hints are not treated as authoritative TLS pins; node and operator roles are separated.
- Bad qualities: The user-facing failure surface is still easy to hit: wrong LAN host, token auth, stale protocol, TLS trust, and pairing retry states can all look like generic connection failure.
- Excluded from quality: Test coverage and runtime-flow proof were not used to raise or lower Quality.
## Completeness Score
- Score: `Alpha (68%)`
- Surface instructions: evaluated against `references/completeness/android-app.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for Gateway discovery.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived Coverage score.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- Add a scripted first-run pairing smoke for setup code, manual LAN, and remote WSS.
- Improve operator copy around pairing/auth versus wrong address versus TLS trust.
- Keep Play Store version skew visible in pairing errors so old clients fail with an actionable upgrade message.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/platforms/android.md` documents the Android to Gateway WebSocket path, device pairing role, secure endpoint rules, setup-code/manual modes, pairing approval, auto-reconnect, and status verification.
- `/Users/kevinlin/code/openclaw/apps/android/README.md` documents USB-only gateway testing with `adb reverse` and Connect/Pair steps.
- `/Users/kevinlin/code/openclaw/docs/gateway/bonjour.md` is linked from the Android runbook for discovery debugging.
### Source
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/gateway/GatewayDiscovery.kt` implements local NSD/mDNS plus optional wide-area DNS-SD discovery.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/ui/GatewayConfigResolver.kt` decodes setup codes, parses manual endpoints, and enforces secure remote URL rules.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/gateway/GatewaySession.kt` manages WebSocket connect, node/operator auth sources, RPCs, reconnect, and invoke handling.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/gateway/DeviceAuthStore.kt` persists device tokens and scopes by normalized device and role.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/node/ConnectionManager.kt` builds node/operator connect options and resolves TLS parameters.
### Integration tests
- `/Users/kevinlin/code/openclaw/src/gateway/android-node.capabilities.live.test.ts` connects a Gateway client, selects an Android node, verifies paired/connected state, and uses remote config for remote runs, but requires manual setup first.
- No clean Android first-run pairing e2e was found.
### Unit tests
- `/Users/kevinlin/code/openclaw/apps/android/app/src/test/java/ai/openclaw/app/gateway/GatewaySessionReconnectTest.kt` covers reconnect and pairing-required pause/retry behavior.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/test/java/ai/openclaw/app/gateway/GatewaySessionInvokeTest.kt`, `GatewaySessionInvokeTimeoutTest.kt`, `DeviceAuthPayloadTest.kt`, `DeviceAuthStoreTest.kt`, `GatewayBootstrapAuthTest.kt`, `GatewayConfigResolverTest.kt`, and `OnboardingFlowLogicTest.kt` cover core auth, invoke, parsing, and onboarding logic.
### Gitcrawl queries
Query:
`gitcrawl search openclaw/openclaw --query "Android pairing websocket TLS manual LAN setup protocol mismatch" --json`
Results:
- Issue #87216 `Android manual LAN setup parses ws:// as host ws and resolves http://ws:<port>`.
Additional query context:
- `gitcrawl search openclaw/openclaw --query "Android app" --json` found #85966 `Android UI/operator WebSocket closes silently ... after successful node pair` and #78807 `fix(mobile): allow private LAN pairing auth`.
### Discrawl queries
Query:
`/Users/kevinlin/.local/bin/discrawl search --mode fts --limit 5 "Android manual LAN ws host pairing"`
Results:
- 2026-03-07 GitHub mirror comment on #16638 reports Android node cannot pair when `gateway.auth.token` is configured and still hits `device signature invalid`.
- 2026-02-06 support thread explains LAN IP, reachability, firewall/client isolation, and auth/pairing as likely Android connection blockers.

View File

@@ -0,0 +1,117 @@
---
title: "Android app - Distribution Maturity Note"
version: 3
last_refreshed: 2026-05-30
last_refreshed_by: codex
---
# Android app - Distribution Maturity Note
## Summary
The Android app has a public Google Play install path, source build/run docs, Play and third-party product flavors, signed AAB release automation, version-code auto-bumping, and startup/performance scripts. Coverage remains Alpha because the app README still marks the rebuild as extremely alpha and leaves full end-to-end QA and release hardening unchecked. Quality is also Alpha: the Play policy split is a strong design choice, but archive evidence includes an outdated Play Store protocol mismatch and an open request for prebuilt APK release artifacts.
## Category Scope
Included in this category:
- Public Google Play install path: Public Google Play install path and source build/run entrypoints
- Manual install path: Manual install path and Google Play distribution behavior.
- Release smoke and startup performance: Release smoke and startup performance checks for Android app distribution.
## Features
- Public Google Play install path: Public Google Play install path and source build/run entrypoints
- Manual install path: Manual install path and Google Play distribution behavior.
- Release smoke and startup performance: Release smoke and startup performance checks for Android app distribution.
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Alpha (60%)`
- Positive signals: Public install is documented, source build/run commands exist, package scripts expose assemble/install/test/release tasks, release automation builds signed Play and third-party AABs, and Android benchmark scripts cover startup and online UI paths.
- Negative signals: The app README still labels the rebuild extremely alpha and leaves full end-to-end QA and release hardening incomplete. The release path is mostly documented and scripted, but no recurring public release smoke record was found.
- Integration gaps: Need a repeatable release checklist that installs the Play artifact, pairs it to a current Gateway, runs chat/voice/camera/background scenarios, verifies version compatibility, and records Play Console policy status.
## Quality Score
- Score: `Alpha (62%)`
- Gitcrawl reports: `Play Store Android app protocol mismatch` found issue #85971 for Play Store Android app v2026.4.5 protocol mismatch against Gateway >= v2026.5.12 and issue #87216 as a related manual LAN setup protocol-mismatch report. `Android APK releases` found issue #9443 requesting prebuilt Android APK releases.
- Discrawl reports: Search found a May 19 support message saying the Play Store app was outdated and had a protocol mismatch; the user built a newer app locally and then hit a connected/operator-offline state.
- Good qualities: Play and third-party flavors separate Google Play restricted permissions from sideload-only SMS, call-log, and photo surfaces. Release signing properties are kept local-only, release bundles are copied to a predictable output directory, and release AAB signatures are verified.
- Bad qualities: Distribution is still fragile for ordinary users because Play can lag Gateway protocol changes, APK artifacts are not fully productized, and docs explicitly say release hardening is unfinished.
- Excluded from quality: Test coverage and runtime-flow proof were not used to raise or lower Quality.
## Completeness Score
- Score: `Alpha (60%)`
- Surface instructions: evaluated against `references/completeness/android-app.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for Public Google Play install path, Manual install path, Release smoke and startup performance.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived Coverage score.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- Publish and record a current Play smoke path against the current Gateway protocol.
- Decide whether GitHub release APKs or third-party AAB/APK artifacts are part of the supported distribution promise.
- Add release hardening evidence for Play policy declarations, app signing, version skew, rollback, and fresh-install pairing.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/platforms/android.md` links the official Google Play app, describes Android as a companion node, and points to source under `apps/android`.
- `/Users/kevinlin/code/openclaw/apps/android/README.md` marks the rebuild as extremely alpha, lists rebuild checklist items, documents Play and third-party builds, and calls out full end-to-end QA and release hardening as unchecked.
- `/Users/kevinlin/code/openclaw/README.md` lists Android as an optional node with Connect, Chat, Voice, Canvas, Camera, Screen, and device command families.
### Source
- `/Users/kevinlin/code/openclaw/package.json` defines `android:assemble`, `android:install`, `android:bundle:release`, `android:test`, `android:test:integration`, lint, and third-party variants.
- `/Users/kevinlin/code/openclaw/apps/android/app/build.gradle.kts` sets `applicationId = "ai.openclaw.app"`, `minSdk = 31`, `targetSdk = 36`, Play and third-party flavors, release signing checks, R8/resource shrinking, lint warnings as errors, and version `2026.5.28`.
- `/Users/kevinlin/code/openclaw/apps/android/scripts/build-release-aab.ts` auto-bumps version name/code, builds Play and third-party release bundles, verifies signatures with `jarsigner`, and prints SHA-256 hashes.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/play/AndroidManifest.xml` removes restricted media permissions from the Play flavor; `/Users/kevinlin/code/openclaw/apps/android/app/src/thirdParty/AndroidManifest.xml` adds SMS and call-log permissions for the third-party flavor.
### Integration tests
- `/Users/kevinlin/code/openclaw/apps/android/benchmark/src/main/java/ai/openclaw/app/benchmark/StartupMacrobenchmark.kt` and `apps/android/scripts/perf-startup-benchmark.sh` cover startup measurement.
- `/Users/kevinlin/code/openclaw/apps/android/scripts/perf-online-benchmark.sh` measures launch-to-connected, Screen tab, and Chat tab paths on a connected device.
- No current Play Store install to paired Gateway release-smoke artifact was found.
### Unit tests
- `/Users/kevinlin/code/openclaw/apps/android/app/src/test/java/ai/openclaw/app/ui/OnboardingFlowLogicTest.kt` covers onboarding flow logic adjacent to install.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/test/java/ai/openclaw/app/SecurePrefsTest.kt` and `SecurePrefsNotificationForwardingTest.kt` cover stored app state used after install.
- Release AAB automation itself does not appear to have a dedicated unit test.
### Gitcrawl queries
Query:
`gitcrawl search openclaw/openclaw --query "Play Store Android app protocol mismatch" --json`
Results:
- Issue #85971 `[Bug] Play Store Android app v2026.4.5 protocol mismatch against Gateway >= v2026.5.12 - clawx user report`.
- Issue #87216 `Android manual LAN setup parses ws:// as host ws and resolves http://ws:<port>`.
Query:
`gitcrawl search openclaw/openclaw --query "Android APK releases" --json`
Results:
- Issue #9443 `Request: Prebuilt Android APK releases`.
### Discrawl queries
Query:
`/Users/kevinlin/.local/bin/discrawl search --mode fts --limit 5 "Android Play Store protocol mismatch"`
Results:
- 2026-05-19 support message: a user built the Android app locally because the Play Store app was outdated and had a protocol mismatch; the newer local build connected by Tailscale but reported operator offline.

View File

@@ -0,0 +1,124 @@
---
title: "Android app - Device Runtime Maturity Note"
version: 3
last_refreshed: 2026-05-30
last_refreshed_by: codex
---
# Android app - Device Runtime Maturity Note
## Summary
Android node capabilities are broad: device status/info/permissions/health, notifications, system notify, contacts, calendar, location, motion, camera, Canvas/A2UI, Talk PTT, and flavor-gated SMS/call-log/photos. Coverage reaches Beta because the Gateway live capability test executes the advertised non-interactive command surface against a paired Android node. Quality remains Alpha because archive evidence includes zero-command advertisement failures, notification forwarding cross-session risk, and multiple open requests for additional native Android capability families.
## Category Scope
Included in this category:
- Background reconnect and presence: Foreground-service presence, reconnect, and node presence behavior.
- Device command availability: Android device command availability and capability advertisement.
## Features
- Background reconnect and presence: Foreground-service presence, reconnect, and node presence behavior.
- Device command availability: Android device command availability and capability advertisement.
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Beta (70%)`
- Positive signals: Docs enumerate Android command families and flavor-dependent availability. Source has a central registry for advertised capabilities and commands, dispatcher gates for foreground and permission-sensitive commands, and handlers across the major device surfaces. The live capability test reads `node.describe`, applies the effective Gateway allowlist, invokes advertised non-interactive commands, and fails on unmapped command profiles.
- Negative signals: The live suite is preconditioned and excludes interactive screen-recording consent. It validates command contracts but does not prove every user-facing permission grant or long-lived device-state workflow.
- Integration gaps: Need a full Android node command scorecard that records command availability across Play and third-party builds, denied/granted permissions, foreground/background state, and Gateway allowlist/denylist policy.
## Quality Score
- Score: `Alpha (63%)`
- Gitcrawl reports: `notifications.list Android node` found issue #48516 for notification forwarding causing cross-session replies and issue #87058 for Android node connecting but advertising zero commands. `Android Health Connect read-only node commands` found #78611, and `Google Home API bridge Android app native smart-home` found #78476 as future capability requests.
- Discrawl reports: Search found a GitHub mirror review note that the live Android suite now filters declared commands by effective policy allowlist, a review note asking to add `callLog.search` to live capability profiles, and support guidance that Canvas/camera/screen commands fail when a mobile node is backgrounded or does not advertise the capability.
- Good qualities: Capability advertisement is data-driven, sensitive surfaces are gated by build flavor and runtime availability, command dispatch returns structured errors, and Gateway policy is applied before live command execution.
- Bad qualities: Capability shape is large and permission-dependent; notification events can affect chat/session routing; Play flavor removes several high-value device commands; and future native Android capability asks are already accumulating.
- Excluded from quality: Test coverage and runtime-flow proof were not used to raise or lower Quality.
## Completeness Score
- Score: `Beta (70%)`
- Surface instructions: evaluated against `references/completeness/android-app.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for Background reconnect and presence, Device command availability.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived Coverage score.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- Record command availability matrices for Play versus third-party flavor.
- Add release-smoke evidence for notification forwarding session routing and policy filters.
- Keep live capability profiles in lockstep with every newly advertised Android command.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/platforms/android.md` lists command families: device status/info/permissions/health, notifications, photos, contacts, calendar, call log, SMS, motion, camera, Canvas, and Talk.
- `/Users/kevinlin/code/openclaw/apps/android/README.md` documents Google Play restricted permissions and the Play versus third-party flavor split.
- `/Users/kevinlin/code/openclaw/docs/plugins/sdk-runtime.md` documents paired node invocation from Gateway-loaded plugins and CLI commands.
### Source
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/NodeRuntime.kt` wires handlers, capability flags, node/operator sessions, sensitive feature config, and command dispatch.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/node/InvokeCommandRegistry.kt` defines advertised capabilities and commands plus availability gates.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/node/InvokeDispatcher.kt` routes commands and enforces foreground, debug, permission, flavor, and availability errors.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/node/DeviceHandler.kt`, `NotificationsHandler.kt`, `ContactsHandler.kt`, `CalendarHandler.kt`, `LocationHandler.kt`, `MotionHandler.kt`, `SystemHandler.kt`, `SmsHandler.kt`, and `CallLogHandler.kt` implement the command families.
- `/Users/kevinlin/code/openclaw/src/gateway/android-node.capabilities.live.test.ts` is the Gateway-side live capability harness.
### Integration tests
- `/Users/kevinlin/code/openclaw/src/gateway/android-node.capabilities.live.test.ts` connects to a Gateway, selects a paired Android node, reads `node.describe`, resolves allowlist policy, invokes every mapped advertised non-interactive command, and verifies payload contracts or expected deterministic errors.
- The suite explicitly skips interactive screen-recording consent.
### Unit tests
- `/Users/kevinlin/code/openclaw/apps/android/app/src/test/java/ai/openclaw/app/node/InvokeCommandRegistryTest.kt`, `InvokeDispatcherTest.kt`, `DeviceHandlerTest.kt`, `NotificationsHandlerTest.kt`, `DeviceNotificationListenerServiceTest.kt`, `ContactsHandlerTest.kt`, `CalendarHandlerTest.kt`, `LocationHandlerTest.kt`, `MotionHandlerTest.kt`, and `SystemHandlerTest.kt` cover core command behavior.
- `/Users/kevinlin/code/openclaw/src/gateway/android-node.capabilities.policy-config.test.ts` and `android-node.capabilities.policy-source.test.ts` cover live-suite policy config behavior.
### Gitcrawl queries
Query:
`gitcrawl search openclaw/openclaw --query "notifications.list Android node" --json`
Results:
- Issue #48516 `Android node notification forwarding causes cross-session replies (WhatsApp duplicate sends to wrong group)`.
- Issue #87058 `Android node connects but advertises zero commands ...`.
Query:
`gitcrawl search openclaw/openclaw --query "Android Health Connect read-only node commands" --json`
Results:
- Issue #78611 `[Feature]: Android Health Connect read-only node commands`.
Query:
`gitcrawl search openclaw/openclaw --query "Google Home API bridge Android app native smart-home" --json`
Results:
- Issue #78476 `Feature: Google Home API bridge in Android app for native smart-home control`.
### Discrawl queries
Query:
`/Users/kevinlin/.local/bin/discrawl search --mode fts --limit 5 "Android node capabilities gateway commands"`
Results:
- 2026-03-19 GitHub mirror review note says the live suite now treats policy allowlist as part of runnable preconditions.
- 2026-03-13 GitHub mirror review note asks to add a `callLog.search` profile to Android live capability checks.
- 2026-03-13 support thread explains Canvas/camera/screen capability failures when no paired node is connected, the mobile app is backgrounded, or capabilities are not advertised.

View File

@@ -0,0 +1,237 @@
---
title: "Android app Maturity Report"
version: 3
last_refreshed: 2026-05-30
last_refreshed_by: codex
---
# Android app Maturity Report
## Top-level scores
These rollups are simple arithmetic means over the category-note numeric
scores in
`scores.yaml`. Percentages are rounded to the nearest whole number.
- Coverage: `Alpha (65%)`
- Quality: `Alpha (62%)`
- Completeness: `Alpha (65%)`
- LTS Features: `0/7`
## Summary
This report promotes the archived `android-app` maturity evidence from `/Users/kevinlin/tmp/maturity/android-app` into the current process-version-3 inventory contract.
The category Coverage and Quality scores come from the archived evidence-backed score rows. Completeness is initialized from the same archived evidence breadth and known-gap record, then joined with the surface-specific completeness rubric referenced by taxonomy.
## Matrix
| Category | LTS | Coverage | Quality | Completeness | Features to evaluate |
| ----------------------------------------------- | --- | ------------- | ------------- | ------------- | ------------------------------------------------------------------------------------------- |
| [Media Capture](camera-media-capture.md) | ❌ | `Alpha (66%)` | `Alpha (62%)` | `Alpha (66%)` | Camera and media capture |
| [Mobile Chat](chat-sessions-ui.md) | ❌ | `Beta (70%)` | `Alpha (66%)` | `Beta (70%)` | Chat tab |
| [Connection Setup](gateway-pairing-security.md) | ❌ | `Alpha (68%)` | `Alpha (64%)` | `Alpha (68%)` | Gateway discovery |
| [Distribution](install-release-distribution.md) | ❌ | `Alpha (60%)` | `Alpha (62%)` | `Alpha (60%)` | Public Google Play install path, Manual install path, Release smoke and startup performance |
| [Settings](settings-permissions-diagnostics.md) | ❌ | `Alpha (64%)` | `Alpha (66%)` | `Alpha (64%)` | Settings sheet |
| [Voice](voice-talk-wake.md) | ❌ | `Alpha (66%)` | `Alpha (60%)` | `Alpha (66%)` | Voice tab |
| [Device Runtime](node-device-capabilities.md) | ❌ | `Alpha (62%)` | `Alpha (55%)` | `Alpha (62%)` | Background reconnect and presence, Device command availability |
## Scoring rubric
- Coverage:
maturity-label rating for integration, e2e, live, or server/runtime flow
evidence across the category. Unit tests can provide supporting context but never make a
feature covered by themselves.
- Quality:
maturity-label rating for implementation and operational robustness. Unit,
integration, e2e, live, and real runtime-flow test coverage are Coverage
inputs only; they do not raise or lower Quality.
- Completeness:
maturity-label rating for how fully the category delivers the intended
surface-specific capability set. Use the taxonomy-linked completeness
instructions for this surface.
- LTS:
calculated as `quality > 80 and coverage > 90`, or when the matching
taxonomy category sets `human_lts_override`.
- Shared score bands:
`Lovable = 95-100`, `Stable = 80-95`, `Beta = 70-80`,
`Alpha = 50-70`, and `Experimental = 0-50`. At shared boundaries, choose the
higher maturity label.
- Major quality/completeness gaps:
evidence text only, tracked in the detailed feature inventory rather than as a
separate scored dimension.
## Detailed feature inventory
### 1. Media Capture
Search anchors: camera.list, camera.capture, screen capture.
Category note: [Media Capture](camera-media-capture.md)
Score decisions:
- Coverage: `Alpha (66%)`
- Quality: `Alpha (62%)`
- Completeness: `Alpha (66%)`
- LTS: ❌
Features:
- Camera and media capture: Camera listing, capture, photo, screen, and media capture behavior.
Primary docs:
- `docs/platforms/android.md`
- `docs/nodes/camera.md`
### 2. Mobile Chat
Search anchors: Chat tab, chat.history, mobile UI.
Category note: [Mobile Chat](chat-sessions-ui.md)
Score decisions:
- Coverage: `Beta (70%)`
- Quality: `Alpha (66%)`
- Completeness: `Beta (70%)`
- LTS: ❌
Features:
- Chat tab: Chat tab, session list/filtering, composer, image attachments, message parsing/rendering, model/provider status adjacent to chat, and Gateway chat RPC integration
Primary docs:
- `docs/platforms/android.md`
### 3. Connection Setup
Search anchors: Setup Code, Manual, Bonjour.
Category note: [Connection Setup](gateway-pairing-security.md)
Score decisions:
- Coverage: `Alpha (68%)`
- Quality: `Alpha (64%)`
- Completeness: `Alpha (68%)`
- LTS: ❌
Features:
- Gateway discovery: Gateway discovery, setup-code and manual endpoint parsing, WS/WSS connection setup, TLS trust decisions, device identity, stored device tokens, node/operator auth, and connection error handling
Primary docs:
- `docs/platforms/android.md`
- `docs/gateway/bonjour.md`
- `docs/gateway/pairing.md`
### 4. Distribution
Search anchors: Google Play, Manual, Startup macrobenchmark.
Category note: [Distribution](install-release-distribution.md)
Score decisions:
- Coverage: `Alpha (60%)`
- Quality: `Alpha (62%)`
- Completeness: `Alpha (60%)`
- LTS: ❌
Features:
- Public Google Play install path: Public Google Play install path and source build/run entrypoints
- Manual install path: Manual install path and Google Play distribution behavior.
- Release smoke and startup performance: Release smoke and startup performance checks for Android app distribution.
Primary docs:
- `docs/platforms/android.md`
### 5. Settings
Search anchors: Settings sheet, Notification forwarding, diagnostics.
Category note: [Settings](settings-permissions-diagnostics.md)
Score decisions:
- Coverage: `Alpha (64%)`
- Quality: `Alpha (66%)`
- Completeness: `Alpha (64%)`
- LTS: ❌
Features:
- Settings sheet: Settings sheet and settings detail screens, permission request UX, notification forwarding controls, Nodes & Devices status, provider/model diagnostics, secure preferences, and copyable Gateway diagnostic report
Primary docs:
- `docs/platforms/android.md`
### 6. Voice
Search anchors: Talk Mode, Voice tab, wake.
Category note: [Voice](voice-talk-wake.md)
Score decisions:
- Coverage: `Alpha (66%)`
- Quality: `Alpha (60%)`
- Completeness: `Alpha (66%)`
- LTS: ❌
Features:
- Voice tab: Voice tab, manual mic capture, Talk Mode listen/think/speak loop, Gateway Talk config, talk.speak, realtime relay mode, voice capture service type, and voice e2e receiver/script
Primary docs:
- `docs/platforms/android.md`
- `docs/nodes/talk.md`
### 7. Device Runtime
Search anchors: foreground service, node.presence.alive, background reconnect, Additional Android command families, node capabilities, command handling.
Category note: [Device Runtime](node-device-capabilities.md)
Score decisions:
- Coverage: `Alpha (62%)`
- Quality: `Alpha (55%)`
- Completeness: `Alpha (62%)`
- LTS: ❌
Features:
- Background reconnect and presence: Foreground-service presence, reconnect, and node presence behavior.
- Device command availability: Android device command availability and capability advertisement.
Primary docs:
- `docs/platforms/android.md`
- `docs/nodes/troubleshooting.md`
- `docs/gateway/protocol.md`
## Recommended scorecard interpretation
Use this migrated score as the current inventory baseline. Refresh individual categories with live category-agent research before treating a high score as an LTS promotion gate.
## Out of scope for this surface
- Redefining taxonomy category boundaries; taxonomy remains the source of truth for category identity, features, docs, and search anchors.
## Audit provenance
- Score source:
`docs/kevinslin/maturity-scorecard/inventory/android-app/scores.yaml`.
- Taxonomy metadata source:
`.agents/skills/claw-score/taxonomy.yaml`.
- Archived evidence source:
`/Users/kevinlin/tmp/maturity/android-app`.

View File

@@ -0,0 +1,38 @@
version: 1
process_version: 3
data:
- name: Media Capture
category_note: camera-media-capture.md
coverage: 66
quality: 62
completeness: 66
- name: Mobile Chat
category_note: chat-sessions-ui.md
coverage: 70
quality: 66
completeness: 70
- name: Connection Setup
category_note: gateway-pairing-security.md
coverage: 68
quality: 64
completeness: 68
- name: Distribution
category_note: install-release-distribution.md
coverage: 60
quality: 62
completeness: 60
- name: Settings
category_note: settings-permissions-diagnostics.md
coverage: 64
quality: 66
completeness: 64
- name: Voice
category_note: voice-talk-wake.md
coverage: 66
quality: 60
completeness: 66
- name: Device Runtime
category_note: node-device-capabilities.md
coverage: 62
quality: 55
completeness: 62

View File

@@ -0,0 +1,112 @@
---
title: "Android app - Settings Maturity Note"
version: 3
last_refreshed: 2026-05-30
last_refreshed_by: codex
---
# Android app - Settings Maturity Note
## Summary
Android settings and diagnostics cover a large operator surface: profile, Gateway settings, camera/location/mic/photos/motion/SMS/call-log/notification permissions, notification forwarding policy, nodes/devices view, provider/model state, and copyable Gateway diagnostics. Coverage is Alpha because the source and unit coverage are broad but no integrated operator recovery scenario was found. Quality is Alpha but stronger than the background service because the app has clear safety controls, policy filters, and copyable diagnostic text.
## Category Scope
Included in this category:
- Settings sheet: Settings sheet and settings detail screens, permission request UX, notification forwarding controls, Nodes & Devices status, provider/model diagnostics, secure preferences, and copyable Gateway diagnostic report
## Features
- Settings sheet: Settings sheet and settings detail screens, permission request UX, notification forwarding controls, Nodes & Devices status, provider/model diagnostics, secure preferences, and copyable Gateway diagnostic report
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Alpha (64%)`
- Positive signals: Docs describe permission prerequisites and notification forwarding controls. Source implements permission launchers, rationale/settings dialogs, notification allowlist/blocklist/quiet-hours/rate-limit/session controls, node/device status panels, and diagnostic report copy. Unit tests cover several settings and policy helpers.
- Negative signals: No integrated Android operator recovery flow was found for "Gateway offline", "pairing/auth failure", "missing permission", "notification listener disabled", and "node capability unavailable" from one UI path.
- Integration gaps: Need a settings/diagnostics scenario that starts from common failures, copies diagnostics, changes permissions/policy, reconnects Gateway, and verifies the corresponding command/capability state changes.
## Quality Score
- Score: `Alpha (66%)`
- Gitcrawl reports: `Android light mode theme toggle` found issue #87688 requesting a light mode/theme toggle. More capability-specific searches found future Health Connect and Google Home requests, which imply settings will need more capability management as Android expands.
- Discrawl reports: `Android settings permissions diagnostics notification forwarding` returned no direct hits.
- Good qualities: Permission prompts are centralized and can show rationale/settings dialogs; notification forwarding has allowlist/blocklist, quiet hours, rate limiting, session key, safer self-package handling, and app-picker UI; Gateway diagnostics text tells users what commands and facts to provide.
- Bad qualities: Operator recovery is spread across several screens, theme/accessibility customization is incomplete, and there is no recorded live flow tying settings changes to Gateway/node capability changes.
- Excluded from quality: Test coverage and runtime-flow proof were not used to raise or lower Quality.
## Completeness Score
- Score: `Alpha (64%)`
- Surface instructions: evaluated against `references/completeness/android-app.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for Settings sheet.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived Coverage score.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- Add a compact Android diagnostics runbook for connection, permissions, notification forwarding, and node command availability.
- Add live proof that settings toggles update advertised capabilities without stale Gateway state.
- Decide whether theme/accessibility options are part of the Android app support promise before promotion.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/platforms/android.md` documents Android permissions, notification forwarding controls, connection diagnostics, and related troubleshooting links.
- `/Users/kevinlin/code/openclaw/apps/android/README.md` documents rebuild items for settings restyle, permission requests in onboarding/settings, push notifications, security hardening, and Play restricted permissions.
### Source
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/ui/SettingsSheet.kt` implements broad settings and permission controls, notification forwarding UI, assistant role state, camera/location/mic/photos/motion/SMS/call-log availability, and installed-app picker state.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/ui/NodesDevicesSettingsScreen.kt` shows live nodes, paired devices, pending device requests, status badges, and refresh/error states.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/PermissionRequester.kt` centralizes missing-permission requests, rationale dialogs, timeouts, and settings redirects.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/NotificationForwardingPolicy.kt` implements package allow/block filtering, quiet-hours evaluation, and burst limiting.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/ui/GatewayDiagnostics.kt` builds a copyable diagnostic prompt with screen, app version, device, Android SDK, gateway address, and status/error.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/SecurePrefs.kt` persists app, Gateway, notification, and device settings.
### Integration tests
- No integrated Android settings/operator recovery scenario was found.
- `/Users/kevinlin/code/openclaw/src/gateway/android-node.capabilities.live.test.ts` indirectly depends on settings-controlled command availability and policy allowlists.
### Unit tests
- `/Users/kevinlin/code/openclaw/apps/android/app/src/test/java/ai/openclaw/app/PermissionRequesterTest.kt`, `NotificationForwardingPolicyTest.kt`, `SecurePrefsTest.kt`, and `SecurePrefsNotificationForwardingTest.kt` cover permission and settings helpers.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/test/java/ai/openclaw/app/ui/SettingsSheetNotificationAppsTest.kt`, `ProviderModelStatusTest.kt`, and `GatewayConfigResolverTest.kt` cover settings UI helpers.
### Gitcrawl queries
Query:
`gitcrawl search openclaw/openclaw --query "Android light mode theme toggle" --json`
Results:
- Issue #87688 `Android app: Add light mode / theme toggle`.
- Issue #28300 `Theme Customization System - Preset Themes + Custom Theme Studio` as adjacent theme work.
Query:
`gitcrawl search openclaw/openclaw --query "Android app settings permissions diagnostics" --json`
Results:
- No direct hits.
### Discrawl queries
Query:
`/Users/kevinlin/.local/bin/discrawl search --mode fts --limit 5 "Android settings permissions diagnostics notification forwarding"`
Results:
- No direct hits.

View File

@@ -0,0 +1,107 @@
---
title: "Android app - Voice Maturity Note"
version: 3
last_refreshed: 2026-05-30
last_refreshed_by: codex
---
# Android app - Voice Maturity Note
## Summary
Android voice has moved beyond a placeholder: the app exposes manual mic dictation and Talk Mode UI, Gateway `talk.speak` synthesis with local fallback, realtime relay plumbing, foreground-service microphone type switching, voice e2e scripts, and focused unit coverage. Coverage is Alpha because the strongest e2e artifact is a debug-script path rather than a repeated user install flow. Quality is Alpha because the archive records voice churn, a prior mic-thrashing loop, and unresolved requests around agent/session switching and per-agent TTS voice.
## Category Scope
Included in this category:
- Voice tab: Voice tab, manual mic capture, Talk Mode listen/think/speak loop, Gateway Talk config, talk.speak, realtime relay mode, voice capture service type, and voice e2e receiver/script
## Features
- Voice tab: Voice tab, manual mic capture, Talk Mode listen/think/speak loop, Gateway Talk config, talk.speak, realtime relay mode, voice capture service type, and voice e2e receiver/script
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Alpha (66%)`
- Positive signals: Docs describe manual mic and Talk capture modes, Android 14+ foreground-service microphone requirements, Gateway `talk.speak`, local TTS fallback, realtime Gateway relay conditions, and Voice Wake being disabled in UX/runtime. The debug voice e2e script can run normal and realtime voice paths through an installed debug app.
- Negative signals: Voice proof depends on debug receiver/script setup and synthetic transcripts; no recurring real-device audio latency, microphone permission, speech-recognizer failure, provider fallback, and background/foreground scenario was found.
- Integration gaps: Need a signed-app voice scorecard that grants microphone permission, runs manual mic and Talk Mode, exercises `talk.speak` fallback, verifies realtime relay when configured, backgrounds/reopens the app, and records failure classifications.
## Quality Score
- Score: `Alpha (60%)`
- Gitcrawl reports: `Android Talk Mode` found issue #56613 requesting Voice/Talk tab agent switching and per-agent TTS voice, plus PR #80082 adjusting Android foreground-service use for Talk Mode. `Android app` search also surfaces the broader app rebuild/release context.
- Discrawl reports: Search found a landed PR comment for #66179 exposing Talk Mode in UI and foreground microphone permission; a comment closing #47883 after replacing a mic thrashing loop with the new manual mic path; and a support message noting older Android partial voice behavior and voice-wake/talk-mode churn.
- Good qualities: The current source separates manual mic from Talk Mode, checks microphone permission, handles speech recognizer availability, tracks listening/speaking state, pauses capture during TTS, and falls back from Gateway `talk.speak` when eligible.
- Bad qualities: Voice behavior is constrained by Android speech recognizer availability, audio focus, foreground-service policy, provider configuration, and session routing. User-facing requests for Talk tab agent/session switching remain open.
- Excluded from quality: Test coverage and runtime-flow proof were not used to raise or lower Quality.
## Completeness Score
- Score: `Alpha (66%)`
- Surface instructions: evaluated against `references/completeness/android-app.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for Voice tab.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived Coverage score.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- Add real-device audio and provider failure scorecards for manual mic and Talk Mode.
- Add Voice tab session/agent selection if Android is expected to match Chat session controls.
- Keep docs aligned with actual Voice Wake state; current docs correctly say Android Voice Wake remains disabled.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/platforms/android.md` documents manual Mic, continuous Talk Mode, foreground-service microphone behavior, Gateway `talk.speak`, local TTS fallback, realtime relay conditions, and disabled Voice Wake.
- `/Users/kevinlin/code/openclaw/apps/android/README.md` lists Voice tab full functionality in the rebuild checklist and documents the `voice-e2e.sh` script.
- `/Users/kevinlin/code/openclaw/docs/nodes/talk.md` is the shared Talk behavior reference.
### Source
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/ui/VoiceScreen.kt` exposes manual dictation and Talk UI, permission prompts, speaker toggle, status, and transcript rendering.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/voice/MicCaptureManager.kt` implements manual mic transcription, queueing, Gateway send, TTS pause/resume, and pending run timeout.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/voice/TalkModeManager.kt` implements Talk Mode listening, speech recognizer lifecycle, chat finalization, realtime relay, audio playback, and interruption controls.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/voice/TalkSpeakClient.kt` calls Gateway `talk.speak` and classifies local fallback.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/main/java/ai/openclaw/app/NodeForegroundService.kt` promotes Talk Mode to `dataSync|microphone`.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/debug/java/ai/openclaw/app/VoiceE2eReceiver.kt` supports debug voice e2e orchestration.
### Integration tests
- `/Users/kevinlin/code/openclaw/apps/android/scripts/voice-e2e.sh` installs the debug app, grants `RECORD_AUDIO`, uses `adb reverse`, drives normal and realtime voice modes through `VoiceE2eReceiver`, captures screenshots, and saves filtered logcat.
- No repeated signed Play build voice scenario was found.
### Unit tests
- `/Users/kevinlin/code/openclaw/apps/android/app/src/test/java/ai/openclaw/app/voice/MicCaptureManagerTest.kt`, `TalkModeManagerTest.kt`, `TalkSpeakClientTest.kt`, `TalkAudioPlayerTest.kt`, `TalkDirectiveParserTest.kt`, `TalkModeConfigParsingTest.kt`, `VoiceWakeCommandExtractorTest.kt`, `VoiceWakeManagerTest.kt`, and `ChatEventTextTest.kt` cover the main voice helpers.
- `/Users/kevinlin/code/openclaw/apps/android/app/src/test/java/ai/openclaw/app/NodeForegroundServiceTest.kt` covers foreground-service type behavior.
### Gitcrawl queries
Query:
`gitcrawl search openclaw/openclaw --query "Android Talk Mode" --json`
Results:
- Issue #56613 `[Feature]: Talk/Voice tab - agent/session switching + per-agent TTS voice`.
- PR #80082 `fix(android): avoid dataSync FGS for persistent node`.
### Discrawl queries
Query:
`/Users/kevinlin/.local/bin/discrawl search --mode fts --limit 5 "Android Talk Mode Voice tab"`
Results:
- 2026-04-25 GitHub mirror comment on #66179 says Android Talk Mode UI and foreground microphone permission landed.
- 2026-04-25 GitHub mirror comment on #47883 says the prior mic thrashing loop was replaced by manual Voice tab backed by `MicCaptureManager`.
- 2026-03-28 GitHub mirror issue #56613 requests Voice/Talk tab agent switching and per-agent TTS voice.

View File

@@ -0,0 +1,147 @@
---
title: "Anthropic provider path - Provider Auth and Recovery Maturity Note"
version: 3
last_refreshed: 2026-05-30
last_refreshed_by: codex
---
# Anthropic provider path - Provider Auth and Recovery Maturity Note
## Summary
Anthropic auth has first-class docs and source paths for API keys, Claude CLI
credential reuse, setup-token profiles, auth profile ordering, and doctor hints.
Coverage is Stable because the direct API-key and Claude CLI paths are present
in docs, plugin registration, provider auth choices, config defaults, and
focused tests. Quality is Beta because GitHub and Discord archive evidence still
shows users hitting orphaned profiles, gateway-host credential mismatch, stale
setup-token or OAuth behavior, and "No API key found" confusion.
## Category Scope
Included in this category:
- API-key onboarding: Covers API-key onboarding across Anthropic credential surface before a model request is made: onboarding choices, API-key storage, Claude CLI credential migration, setup-token validation, and related credential setup and health behavior.
- Claude CLI credential reuse: Covers Claude CLI credential reuse across Anthropic credential surface before a model request is made: onboarding choices, API-key storage, Claude CLI credential migration, setup-token validation, and related credential setup and health behavior.
- Setup-token auth: Covers Setup-token auth across Anthropic credential surface before a model request is made: onboarding choices, API-key storage, Claude CLI credential migration, setup-token validation, and related credential setup and health behavior.
- Auth profile health: Covers Auth profile health across Anthropic credential surface before a model request is made: onboarding choices, API-key storage, Claude CLI credential migration, setup-token validation, and related credential setup and health behavior.
- Model status: Covers Model status across operator diagnostics and recovery for Anthropic provider failures: status output, usage windows, auth profile source reporting, cooldown and disabled profile reporting, and related diagnostics and recovery behavior.
- Usage windows: Covers Usage windows across operator diagnostics and recovery for Anthropic provider failures: status output, usage windows, auth profile source reporting, cooldown and disabled profile reporting, and related diagnostics and recovery behavior.
- Cooldown/profile reporting: Covers Cooldown/profile reporting across operator diagnostics and recovery for Anthropic provider failures: status output, usage windows, auth profile source reporting, cooldown and disabled profile reporting, and related diagnostics and recovery behavior.
- Long-context recovery: Covers Long-context recovery across operator diagnostics and recovery for Anthropic provider failures: status output, usage windows, auth profile source reporting, cooldown and disabled profile reporting, and related diagnostics and recovery behavior.
- Fallback guidance: Covers Fallback guidance across operator diagnostics and recovery for Anthropic provider failures: status output, usage windows, auth profile source reporting, cooldown and disabled profile reporting, and related diagnostics and recovery behavior.
## Features
- API-key onboarding: Covers API-key onboarding across Anthropic credential surface before a model request is made: onboarding choices, API-key storage, Claude CLI credential migration, setup-token validation, and related credential setup and health behavior.
- Claude CLI credential reuse: Covers Claude CLI credential reuse across Anthropic credential surface before a model request is made: onboarding choices, API-key storage, Claude CLI credential migration, setup-token validation, and related credential setup and health behavior.
- Setup-token auth: Covers Setup-token auth across Anthropic credential surface before a model request is made: onboarding choices, API-key storage, Claude CLI credential migration, setup-token validation, and related credential setup and health behavior.
- Auth profile health: Covers Auth profile health across Anthropic credential surface before a model request is made: onboarding choices, API-key storage, Claude CLI credential migration, setup-token validation, and related credential setup and health behavior.
- Model status: Covers Model status across operator diagnostics and recovery for Anthropic provider failures: status output, usage windows, auth profile source reporting, cooldown and disabled profile reporting, and related diagnostics and recovery behavior.
- Usage windows: Covers Usage windows across operator diagnostics and recovery for Anthropic provider failures: status output, usage windows, auth profile source reporting, cooldown and disabled profile reporting, and related diagnostics and recovery behavior.
- Cooldown/profile reporting: Covers Cooldown/profile reporting across operator diagnostics and recovery for Anthropic provider failures: status output, usage windows, auth profile source reporting, cooldown and disabled profile reporting, and related diagnostics and recovery behavior.
- Long-context recovery: Covers Long-context recovery across operator diagnostics and recovery for Anthropic provider failures: status output, usage windows, auth profile source reporting, cooldown and disabled profile reporting, and related diagnostics and recovery behavior.
- Fallback guidance: Covers Fallback guidance across operator diagnostics and recovery for Anthropic provider failures: status output, usage windows, auth profile source reporting, cooldown and disabled profile reporting, and related diagnostics and recovery behavior.
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Stable (84%)`
- Positive signals: Anthropic docs describe API-key, Claude CLI, and setup-token style paths; `extensions/anthropic/openclaw.plugin.json` publishes auth choices and setup env vars; `extensions/anthropic/register.runtime.ts` implements API-key auth, setup-token auth, Claude CLI migration, synthetic auth, and doctor hints.
- Negative signals: Setup-token live proof is env-gated, and profile health depends on per-agent auth stores and gateway-host runtime state.
- Integration gaps: The audit found strong focused tests and one live setup-token lane, but not a repeated release artifact proving API-key, setup-token, and Claude CLI migration across fresh hosts every release.
## Quality Score
- Score: `Beta (74%)`
- Gitcrawl reports: #83268 reports Anthropic API keys being registered under an orphan `claude` provider and silently falling back to OAuth; #72255 reports orphaned per-agent credentials after config declarations are removed; #80514 reports a Claude Pro Max cap warning being classified as billing failure.
- Discrawl reports: Discord archive results include "No API key found for provider anthropic" cases tied to shell versus daemon runtime mismatch, stale/broken auth stores, setup-token policy failures, and profile order confusion.
- Good qualities: The provider owns explicit auth methods, validates setup-token shape, writes auth profiles with locks, resolves Claude CLI native auth only for the synthetic `claude-cli` provider, and emits doctor guidance for legacy profile repair.
- Bad qualities: Users still need to understand several credential planes: gateway token, Anthropic API key, Anthropic setup-token, Claude CLI native auth, per-agent auth store, profile order, cooldown, and daemon environment.
- Excluded from quality: Unit, integration, e2e, live, and real runtime-flow test presence or absence; those are Coverage inputs only.
## Completeness Score
- Score: `Stable (84%)`
- Surface instructions: evaluated against `references/completeness/anthropic-provider-path.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for API-key onboarding, Claude CLI credential reuse, Setup-token auth, Auth profile health, Model status, Usage windows, Cooldown/profile reporting, Long-context recovery, Fallback guidance.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived Coverage score.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- Setup-token behavior can be upstream-policy dependent and less predictable
than API-key auth.
- Per-agent auth store routing and daemon environment mismatch remain frequent
support themes.
- Anthropic API-key and Claude CLI routes share the provider label but have
materially different billing and operational behavior.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/providers/anthropic.md` documents API-key setup, Claude CLI setup, setup-token-style troubleshooting, `openclaw models list --provider anthropic`, and states that API keys are the clearest production path for long-lived gateways.
- `/Users/kevinlin/code/openclaw/docs/gateway/doctor.md` documents OAuth expiry and stale Anthropic profile repair guidance.
- `/Users/kevinlin/code/openclaw/docs/gateway/configuration-examples.md` includes Anthropic API-key profile examples and model/fallback config.
### Source
- `/Users/kevinlin/code/openclaw/extensions/anthropic/openclaw.plugin.json` declares `providers: ["anthropic"]`, setup env vars `ANTHROPIC_OAUTH_TOKEN` and `ANTHROPIC_API_KEY`, and provider auth choices for Claude CLI, setup-token, and API key.
- `/Users/kevinlin/code/openclaw/extensions/anthropic/register.runtime.ts` implements `runAnthropicSetupTokenAuth`, `runAnthropicSetupTokenNonInteractive`, `runAnthropicCliMigration`, `runAnthropicCliMigrationNonInteractive`, `resolveClaudeCliSyntheticAuth`, `createProviderApiKeyAuthMethod`, and `buildAnthropicAuthDoctorHint`.
- `/Users/kevinlin/code/openclaw/extensions/anthropic/config-defaults.ts` resolves Anthropic default auth mode from profile order, API-key profiles, OAuth/token profiles, and env vars before seeding cache/heartbeat/default model behavior.
- `/Users/kevinlin/code/openclaw/src/commands/doctor-claude-cli.ts` inspects Claude CLI command, credential readability, workspace/project directory health, and selected `claude-cli` runtime agents.
### Integration tests
- `/Users/kevinlin/code/openclaw/src/agents/anthropic.setup-token.live.test.ts` env-gates a live setup-token profile smoke that resolves Anthropic models, extracts the profile API key, and completes a simple prompt.
- `/Users/kevinlin/code/openclaw/test/scripts/package-acceptance-workflow.test.ts` verifies package-acceptance workflow wiring for Anthropic credentials and live Anthropic profiles.
- `/Users/kevinlin/code/openclaw/src/commands/models.list.e2e.test.ts` covers provider catalog/auth rows that include configured provider behavior.
### Unit tests
- `/Users/kevinlin/code/openclaw/extensions/anthropic/index.test.ts` covers Claude CLI auth profile migration, synthetic OAuth/token auth, API defaulting, Anthropic config defaults, and doctor-profile hooks.
- `/Users/kevinlin/code/openclaw/extensions/anthropic/provider-policy-api.test.ts` covers provider policy normalization, API-key defaults, Claude CLI config normalization, and thinking profile exposure.
- `/Users/kevinlin/code/openclaw/src/commands/doctor-claude-cli.test.ts` covers Claude CLI doctor behavior.
- `/Users/kevinlin/code/openclaw/src/llm/utils/oauth/anthropic.test.ts` covers Anthropic OAuth login and refresh utilities.
### Gitcrawl queries
Query: `gitcrawl --json search issues -R openclaw/openclaw "Anthropic API key auth profile setup-token claude-cli"`
Results:
- #83268 `[Bug]: Anthropic API key pasted via wizard is registered under provider claude (orphan), silently falling back to OAuth`.
- #72255 `[Bug]: Pi runtime silently uses orphaned credentials from per-agent auth-profiles.json after their declarations are removed from openclaw.json`.
Query: `gitcrawl --json search issues -R openclaw/openclaw "anthropic claude-cli auth login setup token"`
Results:
- #70279 `claude-cli backend silently skipped on systemd-managed root gateway, never spawns subprocess`.
- #72255 also appeared as an auth-store leak issue.
Query: `gitcrawl --json search issues -R openclaw/openclaw "Anthropic usage status API key Claude"`
Results:
- #83268 repeated the orphaned provider registration issue.
- #80514 reported Claude Pro Max cap warning classification causing a false billing cooldown.
### Discrawl queries
Query: `discrawl search --limit 10 "Anthropic API key no credentials profile"`
Results:
- Returned support threads for "No API key found for provider anthropic" where shell status and daemon runtime disagreed, setup-token paths were confused with API keys, auth profiles won over env vars, and profile order/cooldown affected fallback behavior.
Query: `discrawl search --limit 10 "Anthropic usage status Claude API key"`
Results:
- Returned discussions where users confused Claude account usage, Anthropic API-key billing, extra-usage errors, and active profile source in `openclaw models status`.

View File

@@ -0,0 +1,136 @@
---
title: "Anthropic provider path - Claude CLI Backend Maturity Note"
version: 3
last_refreshed: 2026-05-30
last_refreshed_by: codex
---
# Anthropic provider path - Claude CLI Backend Maturity Note
## Summary
The Claude CLI runtime is a supported bundled backend with docs, plugin
registration, MCP bridge config, live stdio session defaults, permission-mode
normalization, session resume, and `/think` effort mapping. Coverage is Stable
because the main runtime contract is documented and implemented. Quality is
Alpha because archive evidence shows active user-visible failures around
backend registration, systemd/root gateway execution, permissions, stream
buffering, and session resume.
## Category Scope
This category covers OpenClaw's host-local Claude CLI path after auth is
available: the `claude-cli` backend, its command/args/env defaults, MCP tool
bridge, native tool mode, live stdio JSONL sessions, permission-mode mapping,
thinking effort args, session id persistence, transcript validation, and
fallback prelude behavior.
## Features
- Runtime selection: Covers Runtime selection across OpenClaw's host-local Claude CLI path after auth is available: the `claude-cli` backend, its command/args/env defaults, MCP tool bridge, native tool mode, and related claude cli backend behavior.
- Session continuity: Covers Session continuity across OpenClaw's host-local Claude CLI path after auth is available: the `claude-cli` backend, its command/args/env defaults, MCP tool bridge, native tool mode, and related claude cli backend behavior.
- MCP/tool bridge: Covers MCP/tool bridge across OpenClaw's host-local Claude CLI path after auth is available: the `claude-cli` backend, its command/args/env defaults, MCP tool bridge, native tool mode, and related claude cli backend behavior.
- Permission-mode mapping: Covers Permission-mode mapping across OpenClaw's host-local Claude CLI path after auth is available: the `claude-cli` backend, its command/args/env defaults, MCP tool bridge, native tool mode, and related claude cli backend behavior.
- Fallback prelude: Covers Fallback prelude across OpenClaw's host-local Claude CLI path after auth is available: the `claude-cli` backend, its command/args/env defaults, MCP tool bridge, native tool mode, and related claude cli backend behavior.
## Archive Freshness
- gitcrawl: `gitcrawl doctor --json` succeeded with `version=0.2.1`, `last_sync_at=2026-05-28T19:09:52.784704Z`, `repository_count=2`, `thread_count=29810`, `open_thread_count=11181`, `cluster_count=18594`, `db_path=/Users/kevinlin/.config/gitcrawl/stores/gitcrawl-store/data/openclaw__openclaw.sync.db`, `api_supported=false`, `github_token_present=false`, and `openai_key_present=true`.
- discrawl: `discrawl status --json` succeeded with `state=current`, `generated_at=2026-05-30T14:10:20Z`, `last_sync_at=2026-05-29T19:27:40Z`, `messages=1487536`, `channels=25831`, `threads=25603`, `embedding_backlog=0`, `database_path=/Users/kevinlin/Library/Application Support/discrawl/discrawl.db`, `database_bytes=8035926016`, `share.remote=git@github.com-personal:openclaw/discord-store.git`, and `share.needs_update=true`.
## Coverage Score
- Score: `Stable (82%)`
- Positive signals: Docs cover Claude CLI setup, config, sessions, permissions, thinking effort, and fallback prelude; source registers a full backend with live stdio defaults and MCP bridge; tests cover backend registration and config normalization.
- Negative signals: Live Claude CLI coverage is largely indirect through package-acceptance workflow definitions and plugin/unit tests rather than a single direct live runtime test in this audit.
- Integration gaps: Channel-session and daemon/root gateway paths have archived failures that are not obviously covered by the focused backend tests.
## Quality Score
- Score: `Alpha (68%)`
- Gitcrawl reports: #70279 reports the backend being skipped on a systemd-managed root gateway; #85408 reports hardcoded MCP flags blocking user-scope MCPs; #85601 reports a bundled MCP config tempDir race; #86050 reports Gateway buffering Claude CLI stream events; #78828 reports root gateway permission-mode stalls.
- Discrawl reports: Discord archive results include `MissingAgentHarnessError: claude-cli is not registered` in Discord group chats while DMs worked, plus guidance showing config path divergence across session routing.
- Good qualities: The backend has conservative default args, clears inherited Claude/Anthropic env that could steer child processes, serializes runs, validates project transcript resume, and maps OpenClaw exec policy into Claude permission mode.
- Bad qualities: The path depends on external CLI installation, local login, host PATH, local project transcript files, channel/session runtime lookup, and provider-owned CLI behavior.
- Excluded from quality: Unit, integration, e2e, live, and real runtime-flow test presence or absence; those are Coverage inputs only.
## Completeness Score
- Score: `Stable (82%)`
- Surface instructions: evaluated against `references/completeness/anthropic-provider-path.md`.
- Positive signals: archived docs, source, test, Gitcrawl, and Discrawl evidence cover the taxonomy scope for Runtime selection, Session continuity, MCP/tool bridge, Permission-mode mapping, Fallback prelude.
- Negative signals: the archived note predated process-version-3 Completeness scoring, so this score is initialized from the same evidence breadth and known-gap record used for the archived Coverage score.
- Missing capability branches: see `## Known Gaps` and `## Evidence` below for the recorded missing branches and operator-visible caveats.
## Known Gaps
- The Claude CLI path is operationally sensitive to host setup and session
routing.
- Group/channel session paths have shown runtime lookup divergence from DM/main
session paths.
- Some fixes appear as active or recent PRs/issues, so the lived support record
is still noisy.
## Evidence
### Docs
- `/Users/kevinlin/code/openclaw/docs/gateway/cli-backends.md` documents the `claude-cli` backend, MCP bridge behavior, session support, native permission mapping, thinking effort mapping, login prerequisites, session resume, and fallback prelude.
- `/Users/kevinlin/code/openclaw/docs/providers/anthropic.md` documents Claude CLI as the host-local credential reuse path and warns about same-host expectations.
- `/Users/kevinlin/code/openclaw/docs/gateway/config-agents.md` recommends canonical `anthropic/*` model refs plus model-scoped `agentRuntime.id: "claude-cli"`.
### Source
- `/Users/kevinlin/code/openclaw/extensions/anthropic/cli-backend.ts` registers `claude-cli` with `bundleMcp`, Claude config-file bridge, native tool mode, stream-json args, live stdio sessions, workspace-scoped image args, session ids, raw transcript reseed, watchdog defaults, and serialization.
- `/Users/kevinlin/code/openclaw/extensions/anthropic/cli-shared.ts` clears inherited Anthropic/Claude env vars, normalizes `--setting-sources`, maps OpenClaw exec policy to Claude permission mode, and maps OpenClaw thinking levels to `--effort`.
- `/Users/kevinlin/code/openclaw/extensions/anthropic/config-defaults.ts` backfills `agentRuntime.id: "claude-cli"` for selected canonical Anthropic refs when Claude CLI auth is selected.
- `/Users/kevinlin/code/openclaw/src/commands/doctor-claude-cli.ts` checks command resolution, credentials, workspace/project directory health, and active Claude CLI runtime agents.
### Integration tests
- `/Users/kevinlin/code/openclaw/test/scripts/package-acceptance-workflow.test.ts` verifies live Anthropic and Claude CLI workflow wiring, including `OPENCLAW_LIVE_CLI_BACKEND_MODEL=claude-cli/claude-sonnet-4-6` and package install of `@anthropic-ai/claude-code`.
- `/Users/kevinlin/code/openclaw/scripts/e2e/mcp-channels-docker.sh` and `/Users/kevinlin/code/openclaw/scripts/e2e/mcp-channels-docker-client.ts` cover MCP channel notification/permission framing adjacent to Claude channel mode.
### Unit tests
- `/Users/kevinlin/code/openclaw/extensions/anthropic/index.test.ts` verifies `claude-cli` backend registration, config defaults, auth migration, and synthetic auth.
- `/Users/kevinlin/code/openclaw/extensions/anthropic/cli-shared.test.ts` verifies permission args, safe setting sources, effort mapping, config normalization, and transcript reseed config.
- `/Users/kevinlin/code/openclaw/src/commands/doctor-claude-cli.test.ts` covers doctor diagnostics for the Claude CLI path.
- `/Users/kevinlin/code/openclaw/src/plugins/bundle-claude-inspect.test.ts` covers bundled Claude inspection behavior.
### Gitcrawl queries
Query: `gitcrawl --json search issues -R openclaw/openclaw "claude-cli live session resume transcript missing permission mode"`
Results:
- Returned no direct results for that exact combined query.
Query: `gitcrawl --json search issues -R openclaw/openclaw "Claude CLI OpenClaw MCP allowedTools permission-mode"`
Results:
- #85408 `openclaw agent CLI spawn hardcodes --strict-mcp-config + --allowedTools mcp__openclaw__*, blocking user-scope MCPs`.
- #85601 `[regression] Bundled MCP config tempDir race still present`.
- #86050 `[Bug]: Gateway buffers claude-cli stream events; surfaces only see the final assembled message`.
- #78828 `Claude CLI on root gateway: inferred bypassPermissions breaks, acceptEdits partly works, blocked turns can stall until timeout`.
Query: `gitcrawl --json search prs -R openclaw/openclaw "claude-cli"`
Results:
- Returned active/recent PRs including #73122 backend registration guardrails, #74990 subscription path in onboard wizard, #85505 host-only CLI auth epoch mode, #87702 env-var scrubbing when spawning Claude, #77148 session fork-on-resume, #86649 partial-message streaming deltas, and #86568 auth cooldown skip for CLI providers.
### Discrawl queries
Query: `discrawl search --limit 10 "Claude CLI OpenClaw auth login claude-cli"`
Results:
- Returned a May 26, 2026 support thread where Discord DMs worked but group chats failed with `MissingAgentHarnessError: Requested agent harness "claude-cli" is not registered`, plus older archive entries closing Claude CLI persistence issues and noting implemented CLI delegation.
Query: `discrawl search --limit 10 "Anthropic usage status Claude API key"`
Results:
- Returned April 2026 guidance recommending the CLI subprocess path for Claude subscription usage and warning about direct API-key/API billing configuration.

Some files were not shown because too many files have changed in this diff Show More