Compare commits

...

551 Commits

Author SHA1 Message Date
Peter Steinberger
68601c55dd chore(release): bump 2026.5.28 beta 3 2026-05-29 17:13:29 +01:00
Peter Steinberger
e1de8ff83f ci(release): make plugin publish retries idempotent 2026-05-29 15:17:45 +01:00
Peter Steinberger
47271214ab test(release): widen live session control timeout 2026-05-29 12:33:11 +01:00
Peter Steinberger
d30a8d30d3 test(release): align live provider timeouts 2026-05-29 12:09:25 +01:00
Peter Steinberger
0897a525bb ci(release): require all plugins for core publish 2026-05-29 12:06:47 +01:00
Peter Steinberger
5d16843c1b test(release): size explicit live fallback models 2026-05-29 11:28:26 +01:00
Peter Steinberger
12188a602f test(release): typecheck live gate hardening 2026-05-29 11:18:19 +01:00
Peter Steinberger
a93c1fa6fe test(release): harden live provider gates 2026-05-29 11:08:57 +01:00
Peter Steinberger
c0444b5838 test(release): refresh plugin sdk api baseline 2026-05-29 10:19:52 +01:00
Peter Steinberger
92f720835b test(release): satisfy doctor migration lint 2026-05-29 10:04:24 +01:00
Peter Steinberger
1f5a34d5a6 docs(changelog): add missing release refs 2026-05-29 09:55:23 +01:00
Peter Steinberger
36513fc204 test(release): fix beta live release checks 2026-05-29 09:53:42 +01:00
Peter Steinberger
d64c73abed docs(changelog): refresh 2026.5.28 notes 2026-05-29 09:30:33 +01:00
Peter Steinberger
7a1d428aa2 chore(release): prepare 2026.5.28-beta.2 2026-05-29 09:28:55 +01:00
Peter Steinberger
75d069e58e chore(release): prepare 2026.5.28-beta.1 2026-05-29 09:28:55 +01:00
Vincent Koc
628104662b refactor: share browser client request helpers 2026-05-29 10:26:44 +02:00
Peter Steinberger
d4a17477b0 fix(schema): reject noncanonical array refs 2026-05-29 04:25:10 -04:00
Peter Steinberger
b78ebacb18 refactor: centralize plugin model discovery 2026-05-29 09:24:08 +01:00
Jayesh Betala
189a7962b2 fix(cli): reject empty config path segments
Reject malformed dot-notation config paths before `openclaw config get/set/unset` reads or mutates config. Empty, leading, trailing, whitespace-only, and dot-before-bracket segments now fail closed instead of normalizing to a different key, while valid bracket paths and escaped dots continue to work.

Thanks @jbetala7 for the fix.

Verification:
- `git diff --check`
- `node scripts/run-vitest.mjs run src/cli/config-cli.test.ts` (111 passed)
- GitHub exact-head checks on `116254ba414bff6a0c3881e34fad30baca95ef0a`: 76 success, 23 skipped, 1 neutral, 0 failures

Fixes #87564

Co-authored-by: Jayesh Betala <jayesh.betala7@gmail.com>
2026-05-29 09:23:32 +01:00
UB
d18ee1881c fix(discord): remove optional runtime error shims
Remove unreachable optional chaining from four Discord message-handler-family runtime error calls.

This aligns the code with the required RuntimeEnv.error contract while leaving production behavior unchanged for valid runtimes. Maintainer-updated PR proof clarifies that shared queue reporter hooks still treat malformed runtime reporter failures as best-effort.
2026-05-29 09:22:44 +01:00
Peter Steinberger
5ff0c75da7 fix(config): preserve large numeric schema keys 2026-05-29 04:20:42 -04:00
Peter Steinberger
f6d293a1ee fix(telegram): reject unsafe callback pages 2026-05-29 04:15:13 -04:00
Ninty
ee6eab8143 fix(agents): clean up exec abort listener after completion (#83022)
Clean up completed exec tool-call abort listeners so normal foreground completion and background-yield no longer retain the exec run/session context through AbortSignal listener state.

The listener cleanup now lives beside the exec listener registration and runs when the foreground process settles, rejects, or the tool returns a background running result. Existing abort/timeout/background behavior remains owned by the process supervisor and process registry.

Verification:
- gh pr checks 83022
- gh api repos/openclaw/openclaw/commits/fe86528ecb2043b6febef5c2eec53f9124be5543/check-runs
- git merge-tree --write-tree origin/main refs/remotes/pr/83022
- git diff --check origin/main...refs/remotes/pr/83022
- node AbortSignal add/remove listener probe

Thanks @c19354837.

Co-authored-by: Ninty <c19354837@hotmail.com>
2026-05-29 09:15:07 +01:00
Syu
843577f69a test(tasks): cover legacy flow run migration edge cases
Add regression coverage for legacy and hybrid flow_runs SQLite migrations, including post-rebuild managed writes and canonical owner_key schema assertions.\n\nVerification:\n- node scripts/run-vitest.mjs src/tasks/task-flow-registry.store.test.ts\n- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
2026-05-29 09:15:01 +01:00
Vincent Koc
a4bb9b1438 refactor: share browser debug route responses 2026-05-29 10:14:18 +02:00
Peter Steinberger
895d1a90f3 fix(google-meet): reject invalid auth timeouts 2026-05-29 04:11:02 -04:00
Vincent Koc
456cade93c fix(together): bound generated video downloads 2026-05-29 10:10:39 +02:00
Jayesh Betala
2990c00cb5 fix(skills): tolerate BOM-prefixed frontmatter
Fixes #66479.

Workspace skills whose SKILL.md starts with a UTF-8 BOM now keep their shared markdown frontmatter metadata, so they remain discoverable through skills list. The fix strips one leading BOM at the parser boundary and adds parser plus workspace discovery regression coverage.

Thanks @jbetala7 for the fix.

Co-authored-by: Jayesh Betala <jayesh.betala7@gmail.com>
2026-05-29 09:10:21 +01:00
Peter Steinberger
35cdd40182 fix(cli): reject unsafe duration values 2026-05-29 04:06:56 -04:00
Pavan Kumar Gondhi
9497629c1e fix(msteams): pin attachment fetch DNS
Route Microsoft Teams attachment downloads through the shared SSRF guarded fetch path so DNS validation is pinned into the dispatcher used for the actual request.

Keep Teams auth fallback and allowlisted HTTPS Authorization redirect behavior while failing closed for custom fetch hooks that cannot accept dispatcher injection.

Verification:
- CI=1 OPENCLAW_VITEST_MAX_WORKERS=1 timeout 300 node scripts/run-vitest.mjs run extensions/msteams/src/attachments/shared.test.ts extensions/msteams/src/attachments/bot-framework.test.ts src/infra/net/fetch-guard.ssrf.test.ts
- gh pr checks 87567 --repo openclaw/openclaw --watch=false

PR: #87567
2026-05-29 09:03:50 +01:00
Peter Steinberger
e5063f51cb fix(phone-control): reject invalid arm durations 2026-05-29 04:03:45 -04:00
Vincent Koc
3e050d05e8 refactor: share session tab registry helpers 2026-05-29 10:02:18 +02:00
Peter Steinberger
8363d6596c ci: retry transient checkout fetch timeouts 2026-05-29 09:00:45 +01:00
Peter Steinberger
6fab00acaa fix(docs): preserve plugin reference manual sections 2026-05-29 09:00:28 +01:00
Peter Steinberger
24614ac100 refactor(browser): centralize route numeric readers 2026-05-29 03:59:19 -04:00
Vincent Koc
6c309b9883 refactor: share browser route navigation policy 2026-05-29 09:52:12 +02:00
Peter Steinberger
2ea8d88d63 fix(browser): validate cookie expiry values 2026-05-29 03:50:19 -04:00
Vincent Koc
0fbd975fe8 test(infra): avoid host-specific exec path fixtures 2026-05-29 09:49:49 +02:00
Peter Steinberger
ac52499aca fix(browser): validate screenshot timeout 2026-05-29 03:46:53 -04:00
Vincent Koc
4ad875308f fix(memory): bound remote JSON responses 2026-05-29 09:45:39 +02:00
Peter Steinberger
c48a4a3188 fix(browser): validate geolocation options 2026-05-29 03:43:06 -04:00
Abdel Gomez-Perez
9de6abd8d7 fix(agents): bridge CLI tool progress events 2026-05-29 13:04:31 +05:30
Peter Steinberger
c7f50738c0 fix(browser): validate permission grant timeout 2026-05-29 03:34:06 -04:00
Peter Steinberger
dca86d47e0 fix(browser): validate hook download timeouts 2026-05-29 03:30:46 -04:00
Peter Steinberger
854cb9292d fix(browser): validate response body numeric options 2026-05-29 03:27:34 -04:00
Vincent Koc
fce7470495 refactor: share file transfer node host path handling 2026-05-29 09:26:24 +02:00
Peter Steinberger
0b24f47465 fix(browser): tighten act numeric parsing 2026-05-29 03:23:42 -04:00
Peter Steinberger
4fae13e29e fix(browser): centralize snapshot numeric parsing 2026-05-29 03:15:56 -04:00
Peter Steinberger
0bc591a7d7 fix(browser): reject invalid tab indexes 2026-05-29 03:07:15 -04:00
Vincent Koc
0a14f593c3 refactor: share file transfer node invoke handling 2026-05-29 09:05:36 +02:00
Vincent Koc
c9a939ad2d fix(release): bound ClawHub owner metadata 2026-05-29 09:03:22 +02:00
Peter Steinberger
286883cc54 fix(browser): cap route timer delays 2026-05-29 03:03:07 -04:00
Peter Steinberger
b0730944eb fix(browser): cap cli request timeouts 2026-05-29 02:50:51 -04:00
Vincent Koc
d78b0814d5 fix(gateway): avoid cold-loading providers for MCP inventory 2026-05-29 08:48:35 +02:00
Vincent Koc
2879f76301 refactor: share xai code execution tool config 2026-05-29 08:48:27 +02:00
Peter Steinberger
13ac8a0758 fix(google-meet): validate api page size 2026-05-29 02:46:50 -04:00
Peter Steinberger
31f3914082 fix(voice-call): bound cli numeric options 2026-05-29 02:42:44 -04:00
Peter Steinberger
8e56c024df fix(gateway): cap handshake timer delays 2026-05-29 02:38:45 -04:00
Vincent Koc
70230f4235 refactor: share brave web search metadata 2026-05-29 08:34:37 +02:00
Vincent Koc
44adda3195 fix(release): bound ClawHub verifier responses 2026-05-29 08:33:50 +02:00
Peter Steinberger
4829d30cf0 fix(mattermost): bound slash callback env port 2026-05-29 02:31:53 -04:00
ZC
7a381b807e fix(cron): preflight model fallbacks before skip (#82887)
Fix cron local-model preflight fallback handling so scheduled runs try configured fallback candidates before skipping when the local primary is unavailable.

Verification:
- GitHub CI on PR head fe884dab90: passing required CI checks.
- Local focused cron/model fallback tests passed earlier for the touched surface.
- Local merge-wrapper build and check passed on the prepared candidate.
- Local full pnpm test reported unrelated failures outside this PR's touched files; touched files are limited to cron docs, src/agents/model-fallback.ts, and src/cron/isolated-agent/*.

Co-authored-by: chen-zhang-cs-code <chenzhangcode@163.com>
Co-authored-by: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
2026-05-29 14:29:26 +08:00
Ayaan Zaidi
c559776c51 fix(channels): preserve room event progress suppression 2026-05-29 11:56:58 +05:30
Ayaan Zaidi
f9b1132bbb test(channels): cover suppressed group progress callbacks 2026-05-29 11:56:58 +05:30
Peter Lindsey
85b6f91bd7 fix(dispatch): forward channel-owned progress callbacks in all chat types when verbose is off
Remove the chatType === 'direct' guard from
shouldAllowQuietChannelOwnedProgressCallbacks so that channel-owned native
progress callbacks (onToolStart, onItemEvent, onPlanUpdate,
onApprovalEvent, onCommandOutput, onPatchSummary, onCompactionStart/End)
are forwarded in group and group-channel sessions when verbose is off.

Previously the guard required chatType === 'direct', which meant that
/verbose off would suppress all progress callbacks in group sessions
while direct sessions continued to relay them. Message-level tool
summary suppression is handled separately; native channel relay hooks
should not be gated on chat type.

Closes #87612
2026-05-29 11:56:58 +05:30
Peter Steinberger
0f0c744517 fix(config): bound gateway env ports 2026-05-29 02:26:01 -04:00
Vincent Koc
91a78477d0 refactor: share xai web search metadata 2026-05-29 08:23:32 +02:00
Peter Steinberger
625b793635 fix(daemon): centralize tcp port bounds 2026-05-29 02:22:03 -04:00
YEEE
aa53823981 fix(whatsapp): resolve auth dir from active profile (#82492)
Merged via squash.

Prepared head SHA: 82b1404905
Co-authored-by: lidge-jun <243035832+lidge-jun@users.noreply.github.com>
Co-authored-by: mcaxtr <7562095+mcaxtr@users.noreply.github.com>
Reviewed-by: @mcaxtr
2026-05-29 03:20:26 -03:00
Rob Pierson
b474f429ee feat(plugin-sdk): add reply payload sending hook (#82823)
* feat(plugin-sdk): add reply payload sending hook

* fix(dispatch): compose caller beforeDeliver with plugin hooks instead of nullish-coalescing

ClawSweeper review identified that Telegram's identity beforeDeliver
would skip reply_payload_sending and message_sending hooks entirely.
Now we always compose caller-provided hooks with global plugin hooks
so plugins get a chance to run even when the caller already supplies
a beforeDeliver hook.

Also adds regression test for composition case.

* test(dispatch): align beforeDeliver hook assertion with current context

* fix(plugin-sdk): remove leftover merge markers from hook types

* feat(plugin-sdk): add reply payload sending hook

* fix(plugin-sdk): protect reply payload media trust

* fix(auto-reply): honor suppressed routed ACP blocks

* fix(auto-reply): avoid double message sending hooks

* fix(auto-reply): require routed reply kind

* test(auto-reply): type routed suppression mock

* fix(auto-reply): honor reply payload hooks in followups

* fix(auto-reply): suppress empty hooked dispatcher replies

* fix(auto-reply): wire reply payload hooks at dispatcher boundary

* fix(plugins): preserve reply payload metadata in hooks

* fix(auto-reply): defer reply hook availability checks

* fix(auto-reply): preserve message hook order for routed payloads

* fix(auto-reply): persist routed payload hook decisions

* fix(auto-reply): run routed payload hooks inside delivery

* fix(auto-reply): enforce message hooks after payload edits

* fix(auto-reply): gate source reply mirrors on delivery

* fix(auto-reply): scope hook-mutated media delivery

* chore(plugin-sdk): refresh reply hook api baseline

* fix(auto-reply): mirror delivered source replies

---------

Co-authored-by: Rob via OpenClaw <noreply@openclaw.local>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-05-29 07:15:10 +01:00
Peter Steinberger
8d63d466b8 fix(infra): preserve inline option values 2026-05-29 02:12:59 -04:00
Vincent Koc
05ff7d374f refactor: share tavily web search helpers 2026-05-29 08:12:48 +02:00
Gio Della-Libera
08beb6b0e8 Policy: add policy file comparison command (#86768)
Merged via squash.

Prepared head SHA: 2023e8cba1
Co-authored-by: giodl73-repo <235387111+giodl73-repo@users.noreply.github.com>
Reviewed-by: @giodl73-repo
2026-05-28 23:10:27 -07:00
Galin Iliev
8124fb4aa4 fix(gateway): cache single session row child indexes
Cache single-row gateway session child indexes without hiding live subagent registry changes.

Summary:
- Reuses store-derived child-session candidates for repeated single-row session loads.
- Keeps runtime subagent registry reads live per row so moved child sessions do not stay attached to stale parents.
- Versions the session-store cache and includes that version in the single-row cache key so same-object store rewrites cannot reuse stale child candidates.
- Adds focused regression coverage for cache reuse, live registry refresh, and same-object session-store writes.

Verification:
- git diff --check
- pnpm tsgo:prod
- pnpm test src/gateway/session-utils.single-row-cache.test.ts src/gateway/session-utils.subagent.test.ts -- --reporter=verbose
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- GitHub CI run 26620265206: passing
- Azure Crabbox cbx_a58389e50f49: single-row-loads 13.622240 ms before vs 1.869456 ms after, 7.29x speedup, 86.3% reduction
2026-05-28 23:09:10 -07:00
Peter Steinberger
f212176e91 fix(azure): preserve equals in deployment maps 2026-05-29 02:03:03 -04:00
Vincent Koc
611adb2ee0 test(browser): align loopback auth mock types 2026-05-29 08:01:21 +02:00
Vincent Koc
667c03f87e refactor: share fal provider http auth 2026-05-29 08:01:21 +02:00
Peter Steinberger
fa9901c78f fix(discord): escape component custom id delimiters 2026-05-29 01:58:31 -04:00
Dallin Romney
ed36f423da fix(ci): bound manual git fetches (#87839)
* fix(ci): bound manual git fetches

* fix(ci): cover platform fetch guards

* fix(ci): fail timed out target fetches

* fix(ci): repair typecheck regressions

* fix(ci): refresh CI expectations

* fix(ci): preserve main cron coverage
2026-05-28 22:56:54 -07:00
Vincent Koc
2e042fbca8 fix(browser): reject excessive viewport resizes 2026-05-29 07:51:27 +02:00
Dallin Romney
cdeafd1895 chore: revert dependency guard backfill machinery (#87867)
* Revert "ci: isolate dependency guard backfill label (#87882)"

This reverts commit 21b33bd04d.

* Revert "ci: add dependency guard backfill label trigger (#87866)"

This reverts commit 5a6472718d.

* ci: preserve clawsweeper bot label filter
2026-05-28 22:50:59 -07:00
Peter Steinberger
621db8f0b1 fix(browser): reject explicit zero cdp ports 2026-05-29 01:43:05 -04:00
Vincent Koc
f5e1fe9755 refactor: share firecrawl web search metadata 2026-05-29 07:41:27 +02:00
Peter Steinberger
e9d49299d6 fix(canvas): default malformed host base paths 2026-05-29 01:34:30 -04:00
Peter Steinberger
00e4d54e1f fix(diffs): normalize render presentation numbers 2026-05-29 01:31:35 -04:00
Dallin Romney
31627d0808 fix(gateway): drop unused transcript option binding (#87899) 2026-05-28 22:29:57 -07:00
Peter Steinberger
6bf2fdf739 fix(channels): normalize direct dm guard numeric overrides 2026-05-29 01:28:40 -04:00
Peter Steinberger
fa1d5f6584 fix(markdown): normalize non-finite render chunk limits 2026-05-29 01:25:26 -04:00
Peter Steinberger
1e5ccd1ce8 fix(matrix): centralize initial sync limit coercion 2026-05-29 01:22:24 -04:00
Peter Steinberger
b43910b590 fix(gateway): default non-finite http media caps 2026-05-29 01:17:41 -04:00
Vincent Koc
6fdf6b0680 refactor: share acp dispatch text helpers 2026-05-29 07:15:41 +02:00
Peter Steinberger
13cb9f8277 docs: update Anthropic Claude CLI billing guidance 2026-05-29 06:14:30 +01:00
Peter Steinberger
8eb5ff08c8 fix(agents): bound media duplicate guard age 2026-05-29 01:12:45 -04:00
Vincent Koc
309fdd95da fix(scripts): silence diffs viewer side-effect warning 2026-05-29 07:11:46 +02:00
Peter Steinberger
1188aa3b81 feat: add Claude Opus 4.8 support (#87890)
* feat: add Claude Opus 4.8 support

* fix: omit Vertex Opus sampling overrides

* fix: preserve Opus adaptive thinking levels

* fix: clamp Anthropic max effort support

* fix: use sha256 for QA mock call ids

* fix: type Anthropic transport test model metadata

* test: update PDF model default for Opus 4.8
2026-05-29 06:10:42 +01:00
Peter Steinberger
98611e6272 fix(agents): normalize subagent capability depth 2026-05-29 01:09:04 -04:00
Gio Della-Libera
5fb83af3e3 Policy: add ingress channel conformance checks (#85744)
Policy: add ingress channel conformance checks (#85744)

Merged via squash.

Prepared head SHA: bd63c8d153
Co-authored-by: giodl73-repo <235387111+giodl73-repo@users.noreply.github.com>
Reviewed-by: @giodl73-repo
2026-05-28 22:07:49 -07:00
Peter Steinberger
0e86ca1352 fix(gateway): default non-finite recent transcript limits 2026-05-29 01:05:04 -04:00
clawsweeper[bot]
22e8cd2a1d fix(gateway): clear completed session active runs (#87810)
Summary:
- This PR adds an internal gateway active-run projection flag, clears it during terminal lifecycle handling be ... ons.list on that flag, adds gateway regression coverage, and tightens memory-wiki confidence normalization.
- PR surface: Source +29, Tests +131. Total +160 across 7 files.
- Reproducibility: yes. Source inspection shows current main can broadcast terminal sessions.changed before ch ...  the abort-controller entry, and the before/after recording supports the visible stuck In progress symptom.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(gateway): preserve chat retry guard after terminal state
- PR branch already contained follow-up commit before automerge: fix(gateway): clear completed session active runs

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

Prepared head SHA: 9b132bdc2b
Review: https://github.com/openclaw/openclaw/pull/87810#issuecomment-4569094800

Co-authored-by: scotthuang <scotthuang@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: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
2026-05-29 05:03:10 +00:00
Peter Steinberger
7979639cd8 fix(gateway): cap non-finite preauth limits 2026-05-29 01:01:20 -04:00
Peter Steinberger
8ada0f4ae2 fix(gateway): default non-finite auth guard limits 2026-05-29 00:58:19 -04:00
Vincent Koc
1d11178d02 refactor: reuse subagent target resolver 2026-05-29 06:56:03 +02:00
Galin Iliev
935f84b8e9 fix(agents): reuse cached subagent registry reads
Reduce repeated subagent registry clone work on hot read paths while preserving cloned snapshot behavior for default callers.

Verification:
- pnpm tsgo:prod
- node scripts/run-vitest.mjs src/agents/subagent-registry.persistence.test.ts --reporter=verbose
- git diff --check
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- PR CI green at 51bd608d09
2026-05-28 21:55:44 -07:00
Peter Steinberger
141b0b3afb fix(discord): normalize scheduler numeric options 2026-05-29 00:55:27 -04:00
Peter Steinberger
3c5f5efc8c fix(discord): default non-finite chunk limits 2026-05-29 00:51:17 -04:00
Peter Steinberger
59cec74d89 fix(browser): clamp non-finite viewport dimensions 2026-05-29 00:46:07 -04:00
Vincent Koc
0f72a042d6 fix(scripts): harden shared flag parsing 2026-05-29 06:45:21 +02:00
Peter Steinberger
1e48ca4e32 fix(browser): default non-finite chrome mcp click delays 2026-05-29 00:42:37 -04:00
Vincent Koc
4b147f2c2e refactor: share embedding provider runtime lookup 2026-05-29 06:41:16 +02:00
Ted Li
8a60f39221 fix(agents): enforce subagent run timeouts
Fix explicit subagent runTimeoutSeconds enforcement so wait, lifecycle, session-store reconciliation, sweeper recovery, pending delivery retry, and in-flight cleanup paths preserve the configured deadline as the terminal contract.

Adds regression coverage for late competing terminal sources, observed child/session starts, restored successful waits without startedAt, and cron schedule identity stagger normalization.

Co-authored-by: Ted Li <tl2493@columbia.edu>
2026-05-29 05:39:41 +01:00
Peter Steinberger
4638f58615 fix(browser): default non-finite keypress delays 2026-05-29 00:38:45 -04:00
Peter Steinberger
c7144a8689 fix(browser): default non-finite DOM text budgets 2026-05-29 00:35:43 -04:00
Peter Steinberger
4dd3ba149c fix(browser): default non-finite snapshot limits 2026-05-29 00:32:35 -04:00
Peter Steinberger
30c24bba97 fix(core): centralize non-finite integer options 2026-05-29 00:28:32 -04:00
Peter Steinberger
27cd18748f fix(memory): default non-finite lancedb text limits 2026-05-29 00:23:52 -04:00
Dallin Romney
21b33bd04d ci: isolate dependency guard backfill label (#87882) 2026-05-28 21:21:13 -07:00
Vincent Koc
2fef80aee5 refactor: share provider catalog projection 2026-05-29 06:21:05 +02:00
Peter Steinberger
25a5cb3270 fix(memory): default non-finite qmd read windows 2026-05-29 00:18:27 -04:00
Ramrajprabu
f3cfd752d3 feat(copilot): add GitHub Copilot agent runtime
Adds the opt-in bundled GitHub Copilot agent runtime, pinned SDK install path, docs/inventory, SDK/tool/sandbox/auth wiring, and replay/tool-safety fixes.

Verification:
- Local: git diff --check; fnm exec --using 24.15.0 pnpm tsgo:extensions; fnm exec --using 24.15.0 pnpm check:test-types; fnm exec --using 24.15.0 pnpm build.
- Autoreview local: clean for the replay-safety fix; branch autoreview engine returned empty output twice, so local autoreview plus local/Crabbox/CI proof was used.
- Crabbox focused Copilot: run_2c0db9f48a4a, 19 files / 485 tests passed.
- Crabbox additional boundary shard: run_26a246a1aa24, prompt snapshots and plugin SDK boundary/export checks passed.
- Crabbox live Copilot: run_d128e4048b4e, real gpt-4.1 turn with live_echo phase-1-green and clean session-file check.
- GitHub checks: green on head 7cc8657e0d, including Dependency Guard after exact-head approval.

Co-authored-by: Ramraj Balasubramanian <ramrajba@microsoft.com>
2026-05-29 05:15:22 +01:00
Peter Steinberger
15772c527a fix(memory-wiki): default non-finite search limits 2026-05-29 00:14:26 -04:00
Peter Steinberger
846ca1e5bd fix(memory-wiki): default non-finite page line options 2026-05-29 00:11:30 -04:00
Vincent Koc
dc0d833efc fix(scripts): reject loose changed bench workers 2026-05-29 06:10:03 +02:00
Peter Steinberger
9596b7bd7a fix(memory): default non-finite read window options 2026-05-29 00:07:49 -04:00
Peter Steinberger
adabff1bf0 fix(browser): centralize non-finite tool timeouts 2026-05-29 00:04:04 -04:00
Peter Steinberger
0bacc93208 fix(qa-lab): keep package telegram harness off private sdk 2026-05-29 05:03:10 +01:00
Peter Steinberger
dac13d9a69 fix(browser): default non-finite navigation timeouts 2026-05-29 00:00:44 -04:00
Gio Della-Libera
af64a824a1 Policy: add sandbox posture conformance checks (#85572)
Policy: add sandbox posture conformance checks (#85572)

Merged via squash.

Prepared head SHA: 1cf1953d8c
Co-authored-by: giodl73-repo <235387111+giodl73-repo@users.noreply.github.com>
Co-authored-by: giodl73-repo <235387111+giodl73-repo@users.noreply.github.com>
Reviewed-by: @giodl73-repo
2026-05-28 21:00:24 -07:00
Vincent Koc
c037ab5c74 fix(doctor): report failed MCP tool schema loading 2026-05-29 05:57:24 +02:00
Vincent Koc
58149e41dc fix(scripts): reject loose startup bench budgets 2026-05-29 05:56:59 +02:00
Peter Steinberger
00c9f81171 fix: retry transient recurring cron failures 2026-05-29 04:54:42 +01:00
Peter Steinberger
3c8ad8cbaa fix(browser): default non-finite fetch timeouts 2026-05-28 23:52:40 -04:00
Peter Steinberger
b2bdad5bee fix(browser): default non-finite snapshot timeouts 2026-05-28 23:48:33 -04:00
Vincent Koc
27b15a19e8 refactor(voice): catalog voice models through providers (#87794)
* refactor(providers): catalog voice models

* feat(tts): route speech through voice models

* refactor(tts): rename speaker selection fields

* refactor(tts): mark default speech models

* test(tts): type migrated speaker config assertions

* refactor(providers): avoid catalog merge map spread

* fix(tts): honor voice model fallbacks

* refactor(tts): move speech core into package

* chore(tts): register speech core knip workspace

* fix(tts): show migrated speaker voice in status

* fix(tts): satisfy speech core lint

* fix(tts): preserve explicit model aliases

* test(tts): narrow provider config assertion

* test(doctor): allow slow commitments repair check

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-05-29 04:46:45 +01:00
Vincent Koc
398b98dcbe refactor: share acpx lazy runtime proxy 2026-05-29 05:45:20 +02:00
Vincent Koc
9ec4e94c48 fix(scripts): reject loose test perf budgets 2026-05-29 05:44:56 +02:00
Peter Steinberger
18ef59bb33 fix(browser): default non-finite dialog arm timeouts 2026-05-28 23:44:42 -04:00
Peter Steinberger
e7fb8cabb6 fix(discord): default non-finite identify concurrency 2026-05-28 23:40:43 -04:00
Peter Steinberger
6f9d5e1b95 fix(channels): default non-finite typing options 2026-05-28 23:37:28 -04:00
Peter Steinberger
2209faef40 feat: improve cron create delivery ergonomics
Summary:
- Add Hermes-style schedule-first cron create parsing while preserving flagged create options.
- Support webhook create/edit delivery and clear stale webhook/chat delivery fields across mode changes.
- Update cron docs and schedule identity normalization tests.

Verification:
- pnpm test src/cron/schedule-identity.test.ts src/cli/cron-cli.test.ts src/cron/service.jobs.test.ts -- --reporter=verbose
- pnpm test src/cli/cron-cli.test.ts src/cron/service.jobs.test.ts -- --reporter=verbose
- pnpm check:test-types
- pnpm check:import-cycles
- pnpm check:docs
- pnpm check:changed via Crabbox run_8c44bcb158da, exit 0
- autoreview branch diff clean
2026-05-29 04:34:50 +01:00
Vincent Koc
4b18234fc1 fix(scripts): enforce plugin sdk surface budgets 2026-05-29 05:32:12 +02:00
Vincent Koc
bf30361bc8 refactor: dedupe voice stream frame adapter 2026-05-29 05:31:50 +02:00
Peter Steinberger
cb085ec5f1 fix(discord): default non-finite REST numeric options 2026-05-28 23:30:47 -04:00
Dallin Romney
5a6472718d ci: add dependency guard backfill label trigger (#87866) 2026-05-28 20:26:32 -07:00
Peter Steinberger
fd643139b1 fix(memory): validate non-finite lancedb numeric config 2026-05-28 23:22:32 -04:00
Peter Steinberger
d8f2437cf4 test(doctor): bound config flow schema warnings 2026-05-29 04:22:23 +01:00
Vincent Koc
ffd4a80145 refactor: share live transport QA CLI helpers 2026-05-29 05:21:23 +02:00
clawsweeper[bot]
4df1fcf7b3 feat(discord): show commentary in progress drafts (#85200)
Adds opt-in Discord progress-draft commentary for assistant preambles while keeping commentary hidden by default and final delivery unchanged.
Keeps commentary config Discord-specific, strips directive tags/NO_REPLY, and clears stale commentary rows without stopping the active draft stream.
Thanks @bryanpearson.

Co-authored-by: bryanpearson <bryanmpearson@gmail.com>
2026-05-29 04:21:06 +01:00
Forrest 0x59
5c7f960125 fix(test): resolve temp dir outside Windows mock to prevent dirty folders on Linux (#85677)
Merged via squash.

Prepared head SHA: ddd6291bde
Co-authored-by: forrest0x59 <250948165+forrest0x59@users.noreply.github.com>
Co-authored-by: mcaxtr <7562095+mcaxtr@users.noreply.github.com>
Reviewed-by: @mcaxtr
2026-05-29 00:18:43 -03:00
Peter Steinberger
5a84869c06 test: remove duplicate catalog write expectation 2026-05-29 04:15:27 +01:00
Peter Steinberger
bca6a91fc4 fix: harden smart-quoted argument repair (#86611) 2026-05-29 04:15:27 +01:00
Fermin Quant
059bed7731 fix(agents): repair smart-quoted edit arrays 2026-05-29 04:15:27 +01:00
Fermin Quant
d4543ac8e4 fix(agents): satisfy smart quote lint 2026-05-29 04:15:27 +01:00
Fermin Quant
fae58591cd fix(agents): decode smart-quoted arg escapes 2026-05-29 04:15:27 +01:00
Fermin Quant
d560588e1e fix(agents): handle exact smart-quoted args 2026-05-29 04:15:27 +01:00
Fermin Quant
1c0b8f6a6b fix(agents): repair smart-quoted tool args 2026-05-29 04:15:27 +01:00
Peter Steinberger
5f301e09ea fix(sandbox): default non-finite novnc token ttl 2026-05-28 23:08:57 -04:00
Peter Steinberger
f2dfb67f2c fix(agents): default non-finite run wait timeouts 2026-05-28 23:05:26 -04:00
Peter Steinberger
01d9963e4e fix(models): default non-finite catalog browse timeout 2026-05-28 23:01:45 -04:00
samzong
c237de552a [Fix] Prefer external session delivery context (#87476)
* fix(sessions): prefer external delivery context

Signed-off-by: samzong <samzong.lu@gmail.com>

* fix: route Feishu session announces from delivery context

* fix: accept normalized cron schedule inputs

---------

Signed-off-by: samzong <samzong.lu@gmail.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-05-29 03:59:54 +01:00
Peter Steinberger
3cf9877d0c fix(agents): alias typebox format for extensions 2026-05-29 03:58:59 +01:00
Peter Steinberger
d503ec52d8 feat: add Fal Krea image model schemas (#87845)
* feat: add fal krea image model schemas

* fix: support fal model-specific aspect ratios

* fix: preserve fal native auto aspect ratio

* fix: honor image model-specific geometry
2026-05-29 03:58:46 +01:00
Peter Steinberger
c91cbf3f71 fix(codex): default non-finite app-server timeouts 2026-05-28 22:58:14 -04:00
Vincent Koc
b012ae46aa refactor: dedupe migrate selection helpers 2026-05-29 04:55:42 +02:00
Peter Steinberger
ee3efc0152 test(plugins): alias gateway workspace packages in plugin loader 2026-05-29 03:54:10 +01:00
Peter Steinberger
45892a6595 fix(heartbeat): default non-finite schedule inputs 2026-05-28 22:53:53 -04:00
Peter Steinberger
9d84a13bb8 fix(slack): default non-finite thread cache options 2026-05-28 22:51:16 -04:00
Peter Steinberger
9dd3bce549 feat: add codex supervisor extension
* feat: add codex supervisor plugin

* fix: restore merged branch checks

* fix: unblock supervisor extension CI

* fix: restore merged agent checks
2026-05-29 03:49:43 +01:00
Peter Steinberger
c8cc010e09 fix(infra): centralize non-finite numeric option bounds 2026-05-28 22:48:19 -04:00
Peter Steinberger
6e25112aad fix(collection): preserve maps for non-finite upper bounds 2026-05-28 22:42:43 -04:00
Peter Steinberger
a4ff3e19ea test: repair gateway client boundary snapshots 2026-05-29 03:40:08 +01:00
Vincent Koc
9ca791288c fix(scripts): parse startup bench gateway ports 2026-05-29 04:39:37 +02:00
Peter Steinberger
564ccf1faa fix(dedupe): bound non-finite retention options 2026-05-28 22:39:09 -04:00
Vincent Koc
47e86bc1ac refactor: share task sqlite store helpers 2026-05-29 04:35:45 +02:00
Peter Steinberger
7f6579e416 fix(shared): default non-finite string sample limits 2026-05-28 22:35:00 -04:00
Peter Steinberger
19d9e71b84 fix(shared): bound non-finite expiring cache options 2026-05-28 22:33:10 -04:00
Peter Steinberger
dbf711c2ea fix(acp): default non-finite session rate limits 2026-05-28 22:31:17 -04:00
Peter Steinberger
c7a1e909a3 fix(plugin-sdk): default non-finite webhook guard limits 2026-05-28 22:29:04 -04:00
Peter Steinberger
fce00ccb6e fix(acp): ignore non-finite retention options 2026-05-28 22:26:20 -04:00
Peter Steinberger
2f8b1a8c0e fix(gateway): default non-finite readiness waits 2026-05-28 22:24:00 -04:00
Peter Steinberger
51b5f75b92 refactor: move plugin model catalogs into plugin state 2026-05-29 03:23:57 +01:00
Peter Steinberger
94db48d028 fix(sandbox): skip non-finite docker resource limits 2026-05-28 22:20:49 -04:00
Peter Steinberger
2dcca3ec8a test(vitest): alias gateway client package to source 2026-05-29 03:17:06 +01:00
Vincent Koc
91df558e69 fix(qa): reject loose otel size limits 2026-05-29 04:16:15 +02:00
Peter Steinberger
6f2add2cc6 fix(gateway): centralize safe timeout delays 2026-05-28 22:15:15 -04:00
Peter Steinberger
bb2254520d test: fix cron schedule identity legacy fixture types 2026-05-29 03:12:56 +01:00
Peter Steinberger
d5bbf3033c perf: avoid full session snapshots for entry reads 2026-05-29 03:12:56 +01:00
Peter Steinberger
c36ba9ea7a fix(memory): keep qmd numeric overrides positive 2026-05-28 22:11:58 -04:00
Peter Steinberger
185e62a9ae fix: show reasoning previews in Slack 2026-05-29 03:08:48 +01:00
Peter Steinberger
66bf324256 fix: default non-finite matrix timeouts 2026-05-28 22:07:49 -04:00
Peter Steinberger
0d189102f5 fix: clamp web provider subsecond timeouts 2026-05-28 22:04:22 -04:00
Vincent Koc
60392a1136 fix(scripts): reject loose memory fd limits 2026-05-29 03:59:04 +02:00
Peter Steinberger
d7aa368776 fix: reject negative cron timeouts 2026-05-28 21:58:00 -04:00
Peter Steinberger
025e6ac31d refactor: tighten gateway client test boundary 2026-05-29 02:56:51 +01:00
Peter Steinberger
f5cb6177e4 fix: align message numeric schemas 2026-05-28 21:54:23 -04:00
Vincent Koc
c3e629cbf4 refactor: share non-interactive onboard config writes 2026-05-29 03:51:51 +02:00
Peter Steinberger
edda0608ac fix: advertise telegram poll duration integer 2026-05-28 21:51:28 -04:00
Peter Steinberger
b425438a58 fix(memory-wiki): narrow synthesis confidence normalization 2026-05-29 02:49:06 +01:00
Peter Steinberger
c0094a232d fix: validate feishu bitable page size 2026-05-28 21:48:06 -04:00
Peter Steinberger
d6c76eb5bf perf: prefer bundled plugin dist entries 2026-05-29 02:47:30 +01:00
Peter Steinberger
d33c2eefce fix: validate feishu chat page size 2026-05-28 21:45:20 -04:00
Peter Steinberger
d2fbc8c0e7 fix: validate message poll duration hours 2026-05-28 21:43:04 -04:00
Vincent Koc
4835a7ecd9 fix(e2e): reject loose parallels limits 2026-05-29 03:41:25 +02:00
Peter Steinberger
b779bdb5a0 fix: centralize cron schedule number coercion 2026-05-28 21:39:06 -04:00
Vincent Koc
a087dbd9e9 fix(doctor): validate tool schemas with model context 2026-05-29 03:32:34 +02:00
Peter Steinberger
6bdaada782 fix: normalize memory wiki confidence 2026-05-28 21:32:10 -04:00
Vincent Koc
417b6e72c4 fix(context-engine): expose fallback metadata after quarantine 2026-05-29 02:32:06 +01:00
Vincent Koc
14ce8733fe fix(context-engine): quarantine broken plugin engines 2026-05-29 02:32:06 +01:00
Vincent Koc
9813ff2f0a refactor: share channel setup promotion keys 2026-05-29 03:29:32 +02:00
Peter Steinberger
9b692f0a5b test(plugins): expect openclaw npm metadata lookup 2026-05-29 02:29:07 +01:00
Peter Steinberger
4ac6bb1964 fix: validate memory search min score 2026-05-28 21:28:02 -04:00
Dallin Romney
e0aa820257 ci: rename dependency guard workflow (#87842) 2026-05-28 18:26:49 -07:00
Peter Steinberger
fe76bae1ed fix: validate lancedb memory importance 2026-05-28 21:25:05 -04:00
Peter Steinberger
b1117d9862 refactor: extract gateway client package (#87797)
* refactor: extract gateway client package

* chore: drop generated gateway package artifacts

* refactor: move gateway protocol package

* refactor: remove old gateway protocol tree

* test: keep auth compat split in run mode

* test: expose gateway wrapper options for internals

* fix: watch moved gateway package sources

* test: normalize slash command import guard

* chore: teach knip gateway package entries

* ci: route gateway client package checks

* fix: reuse ipaddr for gateway client hosts

* fix: sync gateway protocol usage schema
2026-05-29 02:23:42 +01:00
Peter Steinberger
fd8353012f fix: parse diffs numeric options 2026-05-28 21:22:05 -04:00
Peter Steinberger
c0d525c8a0 fix: validate whatsapp login timeout 2026-05-28 21:18:29 -04:00
Vincent Koc
c66c404d58 fix(e2e): reject loose pty env limits 2026-05-29 03:17:12 +02:00
Peter Steinberger
10a3417bd3 fix: validate browser act numeric params 2026-05-28 21:16:12 -04:00
Dallin Romney
c8f2bbf76d ci: guard dependency graph PR changes (#87791) 2026-05-28 18:13:54 -07:00
Peter Steinberger
efc93bf282 fix: validate google meet numeric params 2026-05-28 21:11:53 -04:00
Josh Avant
92051f6746 fix: probe stale rate-limit cooldown primaries (#87833) 2026-05-28 18:11:14 -07:00
Shakker
73cf516def fix: preserve embedded base system prompts
Preserve OpenClaw-owned embedded system prompts after active tool selection in both normal embedded attempts and compaction. Adds an exact base prompt path on AgentSession that keeps active tool prompt metadata current for extension hooks.

Fixes #87807.

Verification:
- mise exec node@24.16.0 -- node scripts/run-vitest.mjs src/agents/sessions/sdk.test.ts src/agents/embedded-agent-runner/system-prompt.test.ts src/agents/embedded-agent-runner/run/attempt.spawn-workspace.context-engine.test.ts src/agents/embedded-agent-runner/compact.hooks.test.ts --reporter=dot
- mise exec node@24.16.0 -- pnpm tsgo:core
- git diff --check
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main

Thanks @shakkernerd.
2026-05-29 02:09:15 +01:00
Dallin Romney
93c30de17b ci: restore timing summary artifact (#87832)
* ci: restore timing summary artifact

* ci: report pnpm warmup fanout timing

* ci: run timing summary from trusted base
2026-05-28 18:08:12 -07:00
Merlin
00067563a6 fix(doctor): handle gateway SecretRefs in auth checks
Handle exec-backed Gateway SecretRefs in doctor, lint, and health probing without executing providers by default.

- Add `openclaw doctor --allow-exec` for explicit SecretRef execution during lint/doctor checks.
- Skip only the active exec-backed gateway probe path and avoid local service diagnostics for remote-only skipped health.
- Keep env-winning and dormant fallback credentials probeable, stabilize related tests, and remove a stale live-shard fixture left by the moving base.

Verification:
- `node scripts/run-vitest.mjs src/commands/doctor-gateway-auth-token.test.ts src/commands/doctor.warns-state-directory-is-missing.e2e.test.ts src/gateway/credentials.test.ts src/gateway/probe-auth.test.ts src/commands/doctor-gateway-daemon-flow.test.ts test/scripts/test-live-shard.test.ts --reporter=verbose`
- `mise x node@24.13.0 -- pnpm prompt:snapshots:check`
- `pnpm tsgo:prod`
- `pnpm build`
- `.agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main`
- Crabbox AWS live config proof: `run_f44a4d9dae4e`
- GitHub CI: green on final head `88d24abdbf9529a59d75d1d5e04eac74bbbbc267` after rerunning a stale in-progress Security High workflow.

Co-authored-by: Merlin <258679497+funmerlin@users.noreply.github.com>
2026-05-29 02:07:50 +01:00
Peter Steinberger
3aae25358e fix: validate lobster numeric options 2026-05-28 21:06:23 -04:00
Peter Steinberger
5d8cf28578 fix: validate file transfer numeric params 2026-05-28 21:03:39 -04:00
Alix-007
99bd275359 fix(ui): scope usage by agent filter
Fixes #87132.

Default Usage now requests all configured agents with `agentScope: "all"`, while selecting a specific agent sends `agentId` consistently to both session usage and cost usage calls. The gateway now supports explicit all-agent session usage, aggregates all-agent cost summaries across configured agents, and keeps scoped cache entries separate. Legacy gateway fallbacks remain for older `agentId` / `agentScope` support, with protocol docs/schema and Swift generated models updated.

Verification:
- `node scripts/run-vitest.mjs ui/src/ui/controllers/usage.node.test.ts ui/src/ui/app-render-usage-tab.test.ts ui/src/ui/views/usage.test.ts --reporter=dot`
- `node scripts/run-vitest.mjs run --config test/vitest/vitest.gateway-methods.config.ts src/gateway/server-methods/usage.test.ts src/gateway/server-methods/usage.cost-usage-cache.test.ts src/gateway/server-methods/usage.sessions-usage.test.ts --reporter=dot`
- `pnpm check:test-types`
- `pnpm protocol:check`
- targeted `node scripts/run-oxlint.mjs ...`
- `git diff --check`
- autoreview clean after Swift compatibility fix
- PR CI green at head `d67156a3c552c4f9c8b6edf8516b6242bf5cdd26`

Co-authored-by: Alix-007 <267018309+Alix-007@users.noreply.github.com>
2026-05-29 02:03:33 +01:00
Vincent Koc
d4021d1d54 fix(e2e): reject loose bundled sweep limits 2026-05-29 03:02:21 +02:00
Peter Steinberger
f927e532da perf: cache installed package paths 2026-05-29 02:00:21 +01:00
Peter Steinberger
0ae1ac17ea fix: validate web guarded fetch timeouts 2026-05-28 20:59:02 -04:00
David
37c5003ed9 fix(auth): harden Codex auth probes (#87559)
* fix(auth): harden Codex auth probes

* fix: preserve Codex probe auth overlay (#87559)

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-05-29 01:58:48 +01:00
Vincent Koc
ca41fa293f refactor: share live transport scenario helpers 2026-05-29 02:58:05 +02:00
Peter Steinberger
7bf100b028 fix: validate embedded chat history limits 2026-05-28 20:56:45 -04:00
Peter Steinberger
f7b0b5429e fix: validate llm task numeric options 2026-05-28 20:54:18 -04:00
Vincent Koc
8bd4736f03 fix(ui): replay pending cron filter reloads 2026-05-29 02:50:58 +02:00
Peter Steinberger
27d1c08c51 fix: normalize web provider numeric params 2026-05-28 20:49:19 -04:00
Sebastien Tardif
b998a921c7 fix(codex): preserve reasoning stream snapshots
Keep Codex reasoning updates as accumulated snapshots and mark the stream payload so channel consumers can distinguish snapshots from deltas.

This prevents Discord and Teams progress previews from duplicating accumulated reasoning text while preserving delta-style reasoning for legacy producers.

Refs #86708
Thanks @SebTardif.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-05-29 01:48:51 +01:00
Peter Steinberger
61cf005437 fix: normalize canvas numeric params 2026-05-28 20:45:27 -04:00
Peter Steinberger
61c538e2fc fix: validate memory recall limits 2026-05-28 20:42:42 -04:00
Peter Steinberger
f09b69a78f test: drop removed gateway live shard fixture 2026-05-28 20:41:11 -04:00
Peter Steinberger
091e15139b refactor: centralize numeric tool schemas 2026-05-28 20:39:51 -04:00
Vincent Koc
c903b271cf fix(e2e): reject loose mock config limits 2026-05-29 02:38:29 +02:00
Dallin Romney
5cccfe1c17 docs: correct ci timing summary guidance (#87813) 2026-05-28 17:36:54 -07:00
Peter Steinberger
7e04680e23 fix: parse browser top-level timeouts 2026-05-28 20:36:34 -04:00
Peter Steinberger
3a20a0cd4f fix: remove unused voice agent helper 2026-05-28 20:35:22 -04:00
Peter Steinberger
1901b832eb perf: cache installed package metadata 2026-05-29 01:32:11 +01:00
Peter Steinberger
5869131eea test: refresh numeric tool prompt snapshots 2026-05-28 20:29:06 -04:00
Peter Steinberger
101cb70844 test: type message gateway options 2026-05-28 20:25:18 -04:00
Peter Steinberger
913241ebf9 fix: parse browser snapshot numeric params 2026-05-28 20:25:18 -04:00
Peter Steinberger
e12a6d6a67 refactor(agents): own system prompt assembly 2026-05-29 01:22:09 +01:00
Peter Steinberger
c3ff31e770 fix: validate slack read limits 2026-05-28 20:20:51 -04:00
Peter Steinberger
7bcef07297 test: update numeric tool schema snapshots 2026-05-28 20:18:49 -04:00
Peter Steinberger
ecbb5cd9b6 fix: preserve cron gateway timeout parsing 2026-05-28 20:18:49 -04:00
Vincent Koc
ffd517b513 test: dedupe schtasks startup fallback helpers 2026-05-29 02:17:18 +02:00
Vincent Koc
5685238656 fix(e2e): reject loose telegram rtt limits 2026-05-29 02:16:54 +02:00
Peter Steinberger
f8d63f4b24 fix: centralize gateway timeout schema 2026-05-28 20:15:36 -04:00
Peter Steinberger
592277cd77 ci(release): bound cross-os baseline packing 2026-05-29 01:13:10 +01:00
Peter Steinberger
fc8b57e0cf fix: validate gateway rpc timeouts 2026-05-28 20:10:06 -04:00
Peter Steinberger
92a405b536 chore: remove unused plugin source loader 2026-05-28 20:08:42 -04:00
Peter Steinberger
c4e4d122e9 fix: validate gateway restart delays 2026-05-28 20:06:50 -04:00
Peter Steinberger
9119e8d99c fix: validate image caps 2026-05-28 20:02:51 -04:00
Vincent Koc
f9f4c4959b fix(e2e): reject loose helper env limits 2026-05-29 02:02:23 +02:00
Vincent Koc
d264119c75 test: dedupe gateway context fixture 2026-05-29 02:01:14 +02:00
Peter Steinberger
a92eb02ec3 fix: validate pdf byte cap 2026-05-28 19:59:42 -04:00
Peter Steinberger
f77a2687b6 test: refresh codex dynamic tool snapshots 2026-05-28 19:59:13 -04:00
Peter Steinberger
661a9ba559 fix: validate node command timeouts 2026-05-28 19:56:23 -04:00
Peter Steinberger
068e02684b fix: validate node media numeric params 2026-05-28 19:53:58 -04:00
Peter Steinberger
3cb4f33d3c fix: validate node photo limit 2026-05-28 19:50:10 -04:00
Peter Steinberger
0296f0a779 perf: load provider discovery entries natively 2026-05-29 00:49:20 +01:00
Peter Steinberger
49f36ab58d fix: validate node media duration 2026-05-28 19:46:06 -04:00
Peter Steinberger
28597d2790 fix: validate cron context count 2026-05-28 19:43:27 -04:00
Vincent Koc
72de534a93 test: dedupe realtime WebRTC helpers 2026-05-29 01:43:07 +02:00
Vincent Koc
7c16af4933 fix(e2e): reject loose fixture config limits 2026-05-29 01:42:49 +02:00
Peter Steinberger
0e40408375 perf: speed up launcher version output 2026-05-29 00:41:40 +01:00
Peter Steinberger
9a4aa438bb fix: validate session spawn timeout 2026-05-28 19:41:11 -04:00
Peter Steinberger
f2843d3d79 fix: validate session send timeout 2026-05-28 19:38:35 -04:00
Peter Steinberger
d7fca5794d fix: validate image numeric options 2026-05-28 19:35:50 -04:00
Peter Steinberger
4c49ca75d9 fix: validate session tool numeric params 2026-05-28 19:32:00 -04:00
Gio Della-Libera
82cb02a4fd fix(cli): preserve Discord voice outbound helper (#85529)
Merged via squash.

Prepared head SHA: e5f5e3d23f
Co-authored-by: giodl73-repo <giodl73-repo@users.noreply.github.com>
Co-authored-by: giodl73-repo <235387111+giodl73-repo@users.noreply.github.com>
Reviewed-by: @giodl73-repo
2026-05-28 16:29:08 -07:00
Peter Steinberger
30de7874cf fix: validate memory wiki numeric params 2026-05-28 19:27:51 -04:00
Dallin Romney
2ba725ef48 fix: stabilize code mode timeout and prompt snapshots (#87809)
* fix: normalize code mode timeout interrupts

* test: refresh firecrawl prompt snapshots
2026-05-28 16:26:12 -07:00
Peter Steinberger
36de671cad fix: validate perplexity token budgets 2026-05-28 19:25:20 -04:00
Dallin Romney
aeeccdf27f fix(ci): bound ClawHub docs checkout (#87811) 2026-05-28 16:25:09 -07:00
Peter Steinberger
46546e6817 test(slack): serialize shared-global media tests 2026-05-29 00:23:48 +01:00
Peter Steinberger
6a65cc5e9c fix(e2e): fail fast when gateway exits before readiness 2026-05-29 00:23:48 +01:00
Peter Steinberger
b4f03c2e64 fix: validate subagents recent minutes 2026-05-28 19:23:08 -04:00
Vincent Koc
38fd443677 fix(e2e): reject loose gateway network timeouts 2026-05-29 01:21:50 +02:00
Peter Steinberger
3c907250b9 fix: validate firecrawl numeric options 2026-05-28 19:20:18 -04:00
Peter Steinberger
1211123fe6 fix(agents): pass agent id to bootstrap preload 2026-05-29 00:18:18 +01:00
Peter Steinberger
e9cca2d1ef fix: validate memory search result counts 2026-05-28 19:17:42 -04:00
Jason (Json)
1610b4983f fix: scope jiti transform cache by OpenClaw install
Scope jiti filesystem transform caches for OpenClaw plugin loaders by package version and package.json install metadata so stale transforms cannot survive upgrades or package reinstalls.

Covers the central plugin module loader and the plugin SDK root alias CJS loader, while preserving jiti filesystem-cache env opt-outs and the TMPDIR cwd guard.

Verification: CI run 26601117143 passed; Real behavior proof run 26601445285 passed; CodeQL selected checks passed in run 26601117126; CodeQL Critical Quality plugin-boundary and plugin-sdk-package-contract passed in run 26601117074; OpenGrep PR diff passed in run 26601117137.

Refs: https://github.com/openclaw/openclaw/pull/87745
Thanks @fuller-stack-dev.
2026-05-29 00:17:04 +01:00
Peter Steinberger
13c1aa7fb9 test(ui): cover cron table filter e2e 2026-05-29 00:15:17 +01:00
Peter Steinberger
8a8767dd1e fix: validate imessage action integers 2026-05-28 19:14:45 -04:00
Vincent Koc
9dd8ffd767 refactor: dedupe session storage indexing 2026-05-29 01:14:34 +02:00
Peter Steinberger
46a67eea4c fix: throttle voice wake meter preview 2026-05-29 00:14:05 +01:00
Peter Steinberger
361753908e fix: validate memory get ranges 2026-05-28 19:11:37 -04:00
Peter Steinberger
56a5d7e865 fix(codex): defer report-mode plugin approvals
Route Codex app-server report-mode PreToolUse plugin approval requirements through the matching app-server approval request instead of failing closed. Shares duplicate in-flight approvals, preserves block/rewrite fail-closed behavior, and keeps generic plugin allow-always scoped to one Codex request. Supersedes #86978; thanks @clawSean for the original docs clarification.
2026-05-29 00:09:23 +01:00
Peter Steinberger
44dc29f397 fix: validate web fetch max chars 2026-05-28 19:06:39 -04:00
AMARA
3029326a56 fix(memory): compact short-term promotion entries
Compact promoted short-term memory snippets before writing them into MEMORY.md, while keeping the full rehydrated snippet in recall state for ranking/provenance. Adds the deep-dreaming config surface and docs, with the default promoted snippet cap set to 160 estimated tokens.

Verification:
- git diff --check
- fnm exec --using v24.13.0 node scripts/run-vitest.mjs run extensions/memory-core/src/short-term-promotion.test.ts extensions/memory-core/src/dreaming.test.ts src/memory-host-sdk/dreaming.test.ts
- GitHub CI run 26605272497
- CodeQL security run 26605272404

Co-authored-by: AMARA <amara@eyeinthesky.pl>
2026-05-29 00:05:54 +01:00
Peter Steinberger
5990524c5f fix: validate google chat reaction limits 2026-05-28 19:04:31 -04:00
Peter Steinberger
b240ce2085 fix: validate discord action integers 2026-05-28 19:02:24 -04:00
Vincent Koc
e32a59bc79 fix(e2e): reject loose lifecycle metric limits 2026-05-29 01:01:18 +02:00
Dallin Romney
ac8c56cc70 test: refresh codex prompt snapshots (#87803) 2026-05-28 15:59:05 -07:00
Peter Steinberger
201fe25dad ci(release): let Telegram QA wait on credential leases 2026-05-28 23:58:52 +01:00
Peter Steinberger
74d5aeae1a fix: validate matrix action integers 2026-05-28 18:53:51 -04:00
Peter Steinberger
7932a4aa74 fix: validate slack action limits 2026-05-28 18:49:51 -04:00
Vincent Koc
6d90e00fa3 refactor: dedupe channel approval forwarding 2026-05-29 00:48:40 +02:00
Peter Steinberger
b0e9569ebd fix: validate telegram action integers 2026-05-28 18:46:26 -04:00
Vincent Koc
444dd19a28 fix(e2e): reject loose codex media limits 2026-05-29 00:46:22 +02:00
Peter Steinberger
59d4327698 fix: validate web search count integers 2026-05-28 18:38:39 -04:00
Peter Steinberger
9a7014ac38 fix: validate tavily integer options 2026-05-28 18:34:43 -04:00
Vincent Koc
7b8ec95108 fix(ci): stabilize agentic drift checks (#87786)
* fix(ci): stabilize agentic drift checks

* fix(ci): refresh opengrep scanner pin

* fix(ci): avoid full-depth opengrep checkout
2026-05-28 23:33:47 +01:00
Vincent Koc
8176bc8a76 fix(e2e): reject loose live plugin timeouts 2026-05-29 00:33:32 +02:00
Peter Steinberger
66d71238a8 fix: validate tool timeout integers 2026-05-28 18:32:17 -04:00
Peter Steinberger
b21b105752 fix: validate video duration option 2026-05-28 18:29:55 -04:00
Peter Steinberger
b877fc58a5 refactor: centralize numeric coercion helpers 2026-05-28 18:27:36 -04:00
Kevin Lin
359c31b7e7 Add WhatsApp approval QA scenarios (#87782)
* test(qa): add WhatsApp approval scenarios

* fix(qa): keep WhatsApp approval scenarios explicit
2026-05-28 15:27:20 -07:00
Peter Steinberger
86d7beab99 fix: keep plugin registry memo fresh for installs 2026-05-28 23:25:12 +01:00
Peter Steinberger
365f551f9d fix: validate music duration option 2026-05-28 18:24:27 -04:00
Vincent Koc
278d04aa4b fix(e2e): reject loose chat tools limits 2026-05-29 00:23:11 +02:00
Peter Steinberger
9184b096bf fix: validate image generation numeric options 2026-05-28 18:21:59 -04:00
Peter Steinberger
4491232874 fix: resolve compatible npm plugin versions
* fix: resolve compatible npm plugin versions

* fix: satisfy plugin install lint

* fix: refresh plugin install tests on latest main
2026-05-28 23:20:32 +01:00
Peter Steinberger
11ef608685 fix: validate firecrawl numeric options 2026-05-28 18:18:33 -04:00
Alix-007
ff21b4e731 fix(cron): complete jobs filters
Server-side cron job list filtering now applies schedule-kind and last-run-status filters before pagination, and the UI only sends table filters for the cron table view.

Fixes #9455.

Co-authored-by: Alix-007 <267018309+Alix-007@users.noreply.github.com>
2026-05-28 23:18:31 +01:00
Peter Steinberger
2df9b2e8ea fix: validate perplexity token budgets 2026-05-28 18:14:36 -04:00
Vincent Koc
7ebd600297 test: dedupe tui pty helpers 2026-05-29 00:14:04 +02:00
Peter Steinberger
bd77ebc761 ci(release): fix release smoke timeouts 2026-05-28 23:13:33 +01:00
Vincent Koc
7be08d0376 fix(cli): keep agents delete local fallback 2026-05-29 00:12:29 +02:00
Peter Steinberger
f5c7d77fb0 fix: validate openrouter stt temperature 2026-05-28 18:12:11 -04:00
Vincent Koc
3ca1135515 fix(e2e): reject loose runtime smoke limits 2026-05-29 00:11:17 +02:00
Peter Steinberger
80f7e36ddc fix: validate lmstudio discovered context lengths 2026-05-28 18:10:55 -04:00
Peter Steinberger
8e806e9125 fix: validate lmstudio preload context length 2026-05-28 18:09:13 -04:00
Peter Steinberger
f77e09f78e fix: validate llm task numeric options 2026-05-28 18:07:37 -04:00
Peter Steinberger
4287cd2e6e fix: validate lmstudio configured token metadata 2026-05-28 18:05:31 -04:00
Peter Steinberger
ac05545dba fix: validate copilot model token limits 2026-05-28 18:03:50 -04:00
Peter Steinberger
aa09f44b47 fix: validate kilocode model token metadata 2026-05-28 18:02:34 -04:00
Peter Steinberger
ef7ad6f744 fix: validate chutes model token metadata 2026-05-28 18:01:07 -04:00
Peter Steinberger
39db00f896 fix: validate vercel gateway model token metadata 2026-05-28 17:59:42 -04:00
Peter Steinberger
423531df50 fix: validate deepinfra model metadata numbers 2026-05-28 17:58:06 -04:00
Peter Steinberger
cb790f77da docs: polish release performance report 2026-05-28 22:57:10 +01:00
Peter Steinberger
938b2a84dd fix: validate byteplus video duration metadata 2026-05-28 17:54:45 -04:00
Aamir Jawaid
04c2982535 fix(msteams): rebase TeamsSDK patterns to simplify Teams Integration (#76262)
* fix(msteams): rebase SDK migration onto current main

Reapply the msteams SDK migration (originally on feat/msteams-sdk-migration)
on top of upstream/main, resolving conflicts with parallel msteams work that
landed upstream during our session.

What got applied vs decisions made:

CLEANLY APPLIED (3-way patch):
- monitor.ts, monitor-handler.ts, polls.ts, reply-stream-controller.ts/.test.ts,
  reply-dispatcher.ts, attachments/download.ts, monitor.lifecycle.test.ts,
  monitor-handler/message-handler.ts, monitor-handler.types.ts, etc.
- streaming-message.ts + .test.ts deletions

WHOLESALE TAKE FROM ORIGINAL BRANCH (partial 3-way left broken cross-refs):
- sdk.ts, sdk.test.ts, messenger.ts, feedback-reflection.ts,
  send-context.ts, send.test.ts

KEPT UPSTREAM (deferred for separate cleanup):
- extensions/msteams/package.json (still has jsonwebtoken/jwks-rsa per
  Peter's b3bc60ae25 incremental approach)
- src/plugins/contracts/package-manifest.contract.test.ts (consistent with
  package.json)
- pnpm-lock.yaml (avoids lockfile churn; pnpm install --frozen-lockfile clean)

ADAPTED:
- Dockerfile matrix-sdk-crypto check now wraps upstream's new retry-loop in
  the if-matrix-bundled gate

KNOWN TEST FAILURES (need eyes — see PR comment):
- attachments.test.ts: 1 fail (pre-existing — warn meta arg shape changed in
  our migration but test wasn't updated)
- reply-dispatcher.test.ts: 6 fails (pre-existing — tests mock old
  TeamsHttpStream, not updated for our ctx.stream rewrite)
- send.test.ts: 4 fails (NEW from merge — upstream's send.ts changed media
  loading; our mocks need updating or take upstream's send.test.ts wholesale)

UPSTREAM COMMITS POTENTIALLY MISSED (in wholesale-take files):
- 08c4af0ddf fix(msteams): accept conversation id allowlists
- e1840b8581 fix(msteams): bind global audience tokens to app id
- Channels turn-kernel refactor (ffe67e9cdc / 1ead1b2d18 / 9a9cd0c0ab) —
  may be partially preserved in cleanly-patched files

Static checks pass: pnpm check:changed is green (typecheck, lint, contract
tests, import cycles, etc.). Manual testing required before merge.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(msteams): preserve thread routing for channel and group-chat replies

- monitor.ts: adaptSdkContext now uses ctx.reply() for channel and groupChat
  conversations (so the SDK threads outbound activities to the inbound's
  replyToId/serviceUrl) and ctx.send() only for personal DMs (where
  reply()'s blockquote-prepend is ugly).
- messenger.ts: sendProactively passes resolvedThreadId on the non-thread
  fallback path so channel @mentions that fall through outbound.ts -> send.ts
  still land in the original thread instead of top-level.

Live-validated: channel @mention -> bot replies in thread, threaded reply
-> bot replies in same thread, no top-level leakage.

* fix(msteams): tag outbound SDK calls with OpenClaw User-Agent

- user-agent.ts: add buildOpenClawUserAgentFragment() that returns just
  'OpenClaw/<version>'. The SDK's Client.clone merges this with its own
  'teams.ts[apps]/<sdk-version>' identifier — passing the full buildUserAgent()
  here would double-print the SDK token.
- sdk.ts: pass the fragment via AppOptions.client.headers['User-Agent'] so
  the Teams backend can identify OpenClaw traffic for usage telemetry.

Final UA looks like 'OpenClaw/<openclaw-version> teams.ts[apps]/<sdk-version>'.

* fix(msteams): handle StreamCancelledError when user presses Stop mid-stream

The new SDK throws StreamCancelledError synchronously from stream.emit/update
when the user pressed Stop in Teams: Teams replies 403 to the next chunk
update, the SDK flips _canceled, and any subsequent emit() throws. The old
custom TeamsHttpStream either swallowed cancel or didn't expose this exception
type, so the migration inherited an SDK behavior the original code didn't have
to handle.

Symptom on 2026-05-05: pressing Stop during a streaming reply caused an
unhandled promise rejection that crashed the Node 24 process. Docker restarted
the gateway about two minutes after each Stop click. Two related bugs surfaced
once the crash was caught: the would-be block fallback re-delivered the full
text as a second message (duplicate after Stop), and the typing-keepalive kept
pulsing in Teams for the rest of the agent run because nothing told it to
stop.

reply-stream-controller.ts:
- Wrap stream.update / stream.emit / stream.close in try/catch that swallows
  StreamCancelledError (matched by .name to dodge tsgo's SDK re-export
  resolution quirk). Latch a wasCanceled flag so subsequent calls
  short-circuit even if stream.canceled is stale.
- preparePayload() returns undefined when the stream was canceled — the
  streamed prefix is already visible to the user, so dropping the payload
  prevents a duplicate block message from overriding the cancel intent.

reply-dispatcher.ts:
- Typing-keepalive gate now also checks streamController.wasCanceled() so
  typing pulses stop firing once Stop is observed. Otherwise the bot keeps
  pulsing for the rest of the (uncancellable) agent run.

reply-stream-controller.test.ts:
- 6 new regression tests cover: cancel-during-emit (the crash scenario),
  cancel-during-update, cancel-during-finalize, non-cancel error propagation,
  post-cancel inactivity, and dropped-payload-on-cancel.

Live-validated: long streaming reply + Stop mid-stream -> stream freezes,
no duplicate message, no zombie typing, container stays healthy.

* fix(msteams): allow Bearer-token retry on Skype CDN attachment downloads

Teams puts inline DM images and clipboard-pasted images on
*.asm.skype.com URLs (e.g. us-api.asm.skype.com/v1/objects/<id>/views/imgo).
The download path in attachments/download.ts already does a plain GET first
and falls back to a Bearer-token retry on 401/403 — but the retry was gated
on the URL being in DEFAULT_MEDIA_AUTH_HOST_ALLOWLIST. asm.skype.com hosts
were in DEFAULT_MEDIA_HOST_ALLOWLIST (download permitted) but not in the
auth-host list, so a 401 plain-GET response skipped the retry and surfaced
as a missing image to the agent.

Add asm.skype.com and ams.skype.com to the auth allowlist so openclaw
attempts the Bearer-token retry consistently, matching how it treats the
other CDN/Bot-Framework hosts already in the list.

Note: this does not unblock all clipboard-pasted DM images — for at least
some tenants asm.skype.com rejects the Bot Framework token (returns 401
even with auth). Routing those URLs through <serviceUrl>/v3/attachments/...
the way #62219 already handles HTML-wrapped attachments is a separate
follow-up. The +button 'Upload from this device' path works today because
Teams generates an attachment with an HTML wrapper that triggers the
existing BF v3 attachments fallback in monitor-handler/inbound-media.ts.

* fix(msteams): align docker-compose msteams port default with plugin default

The plugin defaults webhook.port to 3978 (the Bot Framework standard used in
Microsoft samples) and listens on whatever the operator sets there. The
docker-compose.yml port mapping was exposing ${OPENCLAW_MSTEAMS_PORT:-3000}:3000
which only works for operators who explicitly set webhook.port to 3000.
Default-config users would have the plugin listening on 3978 inside the
container while compose forwarded 3000, causing connection refused.

Realign to ${OPENCLAW_MSTEAMS_PORT:-3978}:3978 so a default-config docker
compose up Just Works with Teams. Operators wanting a custom port override
both webhook.port in openclaw.json and OPENCLAW_MSTEAMS_PORT env var.

* fix(msteams): post-rebase reconciliation with main

Three follow-ups after rebasing the SDK migration onto current main:

- reply-dispatcher.ts: rename createChannelReplyPipeline to its post-rebase
  identifier createChannelMessageReplyPipeline (the plugin-sdk barrel renamed
  it during the 1454-commit rebase window).
- reply-dispatcher.ts: tighten the typing-keepalive onStartError signature to
  (err: unknown) to satisfy upstream's stricter type checks.
- messenger.ts: drop the unconditional thread suffix on the bottom proactive
  fallback. The previous behavior threaded all top-level proactive sends when
  the stored ref had a threadId, which contradicts replyStyle='top-level'
  semantics (and breaks the new upstream test). Threading on the proactive
  path is preserved where it matters — the onRevoked branch within
  replyStyle==='thread' still passes resolvedThreadId, which is the original
  #55198 fix path.
- attachments.test.ts: update the warn-call assertion to match the migration's
  inline message format (host=... error=...) — the structured meta object was
  being dropped by the logger formatter pre-migration.

* feat(msteams): port streaming preview/progress features to ctx.stream

While the SDK migration was open, upstream landed preview/progress/draft
streaming features built on the OLD custom TeamsHttpStream class (which the
migration deletes). This commit ports the user-visible parts of those
features onto the new ctx.stream substrate so the migration doesn't lose
ground:

- pickInformativeStatusText: reads custom labels from
  msteams.streaming.progressDraft config via resolveChannelProgressDraftLabel.
  Falls back to the plugin-sdk default rotation. Pre-rebase used a hardcoded
  4-string array.
- streamMode resolution: "partial" (default, per-token streaming),
  "progress" (no tokens; preview card carries informative label that updates
  as tools run), or "block" (no native streaming). Mode is read from
  cfg.channels.msteams.streaming.preview.
- progress-draft gate: createChannelProgressDraftGate gates informative
  updates so the rotating label only starts firing once meaningful work has
  begun (avoids flicker before the first tool call).
- noteProgressWork() / pushProgressLine(): public methods on the controller
  for callers (typing keepalive ticks, tool-event callbacks) to signal work.
  pushProgressLine appends tool names as bullets above the rotating label
  when streaming.previewToolProgress is enabled. Wiring these into actual
  tool events is a separate follow-up.
- preparePayload progress-mode path: when stream is active but no tokens
  streamed (progress mode) and a final text payload arrives, emit the text
  into the stream so the preview card transitions in place to the final
  reply on close().

reply-dispatcher: pass log + msteamsConfig + a stable progressSeed
(${accountId}:${conversation.id}) to createTeamsReplyStreamController so the
informative-label rotation is consistent across reconnects.

What's NOT ported and why:
- Live-edit-via-replaceInformativeWithFinal: the SDK's HttpStream natively
  accumulates emitted text + entities + channelData and flushes ONE final
  activity at close() using the same activity id as the preview. So the
  separate "replace informative with final" call from upstream is
  unnecessary — we get live-finalization for free via the SDK's design.
- pushProgressLine triggers from tool events: needs reply-pipeline-side
  callbacks the new SDK migration didn't surface yet. Follow-up.

Tests: existing 22 reply-stream-controller tests still pass (the new
behaviors are additive).

* feat(msteams): wire pipeline tool events to streaming progress + fix test debt

Two follow-ups from yesterday's stopping point:

1. Wire pipeline events into the stream controller's progress-draft surface.
   reply-dispatcher's replyOptions now exposes onReasoningStream, onToolStart,
   onItemEvent, onPlanUpdate, onApprovalEvent, onCommandOutput callbacks that
   format each event via the channel-streaming helpers and route through
   streamController.pushProgressLine(). Mirrors the discord adapter's wiring.
   Also:
   - resolveChannelStreamingPreviewToolProgress + ...SuppressDefaultTool... so
     the dispatcher exposes suppressDefaultToolProgressMessages on its
     replyOptions when progress mode is on.
   - Switch disableBlockStreaming resolution to the channel-streaming helpers
     (resolveChannelPreviewStreamMode + resolveChannelStreamingBlockEnabled)
     so streaming.mode='block' and streaming.block.enabled=true are honored
     alongside the legacy blockStreaming boolean.

2. Fix the test debt that the rebase exposed:
   - reply-dispatcher.test.ts: drop the streamInstances + TeamsHttpStream
     mock pattern (file deleted by migration); replace with a streamMock
     provided via context.stream that mirrors the SDK's IStreamer shape
     (update/emit/close/canceled). Update assertions on sendInformativeUpdate
     -> stream.update, stream.update -> stream.emit. Drop the
     resumes-typing-between-segments test (no equivalent in the new
     ctx.stream model — the SDK's HttpStream doesn't have a 'between
     segments' notion; close ends the stream).
   - send.test.ts: fix two stale mock targets — loadOutboundMediaFromUrl
     comes from openclaw/plugin-sdk/outbound-media (not /msteams), and
     resolveMarkdownTableMode comes from openclaw/plugin-sdk/markdown-table-runtime
     (not /config-runtime). The previous mock paths were no-ops post-migration.

All 854 msteams tests now pass (was 17 failing in 4 files yesterday).

* fix(msteams): SDK streaming delta + use app.reply for proactive thread sends

Two narrow regressions exposed by the @microsoft/teams.apps migration:

- The SDK's HttpStream.emit appends each chunk to its internal buffer
  (`this.text += activity.text`), but the channel reply pipeline emits
  cumulative text on each chunk. Forwarding cumulative text into an
  appending sink produced "chunk1 + chunk1chunk2 + chunk1chunk2chunk3..."
  duplication for streamed (DM) replies. Track the emitted prefix length
  in the stream controller and only forward the new tail.
- Replace the manual `${convId};messageid=${msgId}` URL construction in
  the proactive thread fallback with `app.reply()`, which builds the
  threaded conversation id via the SDK's own toThreadedConversationId
  helper. Mechanically equivalent today; removes coupling to Teams' URL
  format and tracks any future SDK changes.

Also adds the `reply` method to the structural MSTeamsApp type so the
refactor typechecks without casts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(msteams): bump @microsoft/teams.api and teams.apps to 2.0.10

2.0.10 adds support for the AAD v1 token issuer that the Bot Framework
JWT validator needs. The minor version bump pulls teams.cards / common /
graph along to 2.0.10 too.

Add `@microsoft/teams.*` to `minimumReleaseAgeExclude` in
pnpm-workspace.yaml because 2.0.10 was published <48h ago and the default
`minimumReleaseAge: 2880` (~2 days) would otherwise reject it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* revert(msteams): remove asm.skype.com auth-host allowlist additions

These hosts were added in dfc169d31d for inline DM image auth-retry, but
the commit's own footnote acknowledges it doesn't actually unblock
clipboard-pasted images (asm.skype.com rejects Bot Framework tokens in
at least some tenants). The change is unrelated to the SDK migration and
the user-visible bug it claimed to fix isn't fixed; lifting it out keeps
this PR focused on the migration. Will land as a separate PR if the
auth-allowlist consistency improvement is wanted on its own.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(msteams): typed ExpressAdapter helper, drop unknown-cast pyramid

The monitor's SDK bootstrap had an awkward chain:

  httpServerAdapter: new (
    (await import("@microsoft/teams.apps")) as unknown as {
      ExpressAdapter: new (app: unknown) => unknown;
    }
  ).ExpressAdapter(expressApp) as never,

Three casts (`unknown`, structural shape literal, `never`) were a
defensive workaround from when the SDK's hashed d.ts files tripped up
tsgo. With the SDK's exports now resolving cleanly, the same import can
be done with full types.

- Extend the lazy `loadSdkModules()` cache to include `ExpressAdapter`
  alongside `App` so the dynamic import is shared.
- Add `createMSTeamsExpressAdapter(serverOrApp)` helper in `sdk.ts` that
  encapsulates the lazy import and returns a properly-typed adapter
  instance.
- Replace `httpServerAdapter`'s structural shape on `CreateMSTeamsAppOptions`
  with the SDK's own `IHttpServerAdapter` interface (re-exported from
  `@microsoft/teams.apps`).

The call site in `monitor.ts` becomes a single typed call with no `any`,
no `unknown`, no `as never`. The lazy-load behavior is preserved: nothing
imports `@microsoft/teams.apps` at module load time.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(msteams): unbreak tsgo:extensions on the ExpressAdapter helper

CI's check-prod-types failed because the previous commit's typed helper
used `typeof import("@microsoft/teams.apps").ExpressAdapter`, which
tsc/tsgo's NodeNext resolution can't follow through the SDK's chained
`export *` barrel:

    @microsoft/teams.apps/dist/index.d.ts:
        export * from "./http";          // folder with index.d.ts
        export * from "./app";           // single .d.ts file

The folder re-export drops `ExpressAdapter` and `IHttpServerAdapter` from
the namespace shape under `tsconfig.extensions.json` (passes under the
per-extension `tsconfig.json` because of inherited `paths`). Same root
cause as why we already model `MSTeamsApp` structurally (line 47 comment).

Switch the ExpressAdapter side to the same structural-shape pattern:
- Define `MSTeamsHttpServerAdapter` and `MSTeamsExpressAdapterCtor` locally.
- Cast `m.ExpressAdapter` once inside `loadSdkModules` (the runtime export
  is fine; only the type surface is hidden).
- `httpServerAdapter` on `CreateMSTeamsAppOptions` and the return type of
  `createMSTeamsExpressAdapter` use the local structural type.

Net result: the call site in `monitor.ts` stays the cast-free single line
the previous commit landed; the one remaining cast is confined to the
SDK-loading helper with an explanatory comment.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(msteams): drop unused jsonwebtoken/jwks-rsa deps

The SDK migration removed all `import "jsonwebtoken"` / `import "jwks-rsa"`
from source code (the SDK does JWT validation internally now), but the
package.json entries and the matching `package-manifest.contract.test.ts`
expectation were left orphaned. Drop both:

- `extensions/msteams/package.json`: remove `jsonwebtoken` (^9), `jwks-rsa`
  (^4) from `dependencies` and `@types/jsonwebtoken` from `devDependencies`.
- `src/plugins/contracts/package-manifest.contract.test.ts`: remove the
  two entries from msteams's `pluginLocalRuntimeDeps` expectation.
- `monitor.lifecycle.test.ts`: extend the `./sdk.js` mock with the
  `createMSTeamsExpressAdapter` export added in the typed-helper cleanup,
  so the lifecycle suite still mounts after the deps drop.

Lockfile regenerates accordingly. All msteams tests (865) pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(msteams): drop unused @microsoft/teams.api direct dep

CI's deadcode:dependencies (knip) flagged @microsoft/teams.api as
unused in extensions/msteams. The plugin source uses structural type
aliases (MSTeamsActivityParams, MSTeamsActivityLike, etc.) to dodge
tsgo resolution bugs with teams.api's hashed d.ts files, so it never
imports teams.api directly. The package is brought in transitively
via @microsoft/teams.apps; the only other reference is
probe.test.ts's vi.mock("@microsoft/teams.api"), which works on the
import-path string and doesn't require a direct dep declaration.

Lockfile regenerates accordingly. tsgo:extensions, knip, and all
865 msteams tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(msteams): clear three CI gate failures (lint, contract, deprecated config API)

Three CI checks flagged on the latest run; all three are msteams-local
and unrelated to one another:

- **check-lint** / **check-additional-extension-bundled**:
  `oxlint` flagged a redundant `as string[]` assertion in
  `reply-dispatcher.ts:431`. The preceding `every((s: unknown) => typeof
  s === "string")` already narrows the array type, so the cast does
  nothing. Drop it.

- **checks-fast-contracts-plugins-c**: the
  `package-manifest.contract.test.ts` `pluginLocalRuntimeDeps` for
  msteams still expected `@microsoft/teams.api`, but the deadcode
  cleanup commit (8f4050f51a) dropped it from
  `extensions/msteams/package.json`. Remove it from the contract test
  too — `teams.api` is only present transitively via `teams.apps`,
  which is the reason knip flagged it.

- **check-additional-runtime-topology-architecture**: the deprecated
  internal config API guard caught `messenger.ts:223` calling
  `getMSTeamsRuntime().config.loadConfig()`. Switch to
  `config.current()` to match the pattern used by phone-control,
  synology-chat, and matrix.

Pre-existing failures on this run that are NOT msteams-related and not
caused by this PR: `check-test-types` (errors in
`src/agents/openai-transport-stream.test.ts` and
`pi-embedded-runner/openai-stream-wrappers.test.ts`) and `macos-swift`
(`hoistAwait` in `MacNodeRuntime.swift`). Leaving those for upstream.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(msteams): cast config.current() return to OpenClawConfig

The previous commit switched `messenger.ts:223` from the deprecated
`config.loadConfig()` to `config.current()` to satisfy the architecture
guard, but `config.current()` returns a deeply-readonly type that's not
assignable to the `Partial<OpenClawConfig>` parameter
`resolveMarkdownTableMode` expects (a mutable type from the SDK
contract). Phone-control, synology-chat, and matrix all cast at this
seam — adopt the same pattern.

Verified locally: tsgo:core, tsgo:extensions, check:architecture, and
test:extensions:package-boundary:compile all pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(msteams): address PR review — pre-auth body limit, allowlist log level, /api/messages forwarder, narrow release-age exclude

Four narrow fixes from the PR review (BradGroux + clawsweeper bot +
galiniliev's plan), each its own concern:

- **pre-auth-body-limit** (monitor.ts) — install
  `express.json({ limit: DEFAULT_WEBHOOK_MAX_BODY_BYTES })` before the
  bearer-presence gate and SDK route. Express memoizes the parsed body
  on the request, so the SDK's later `json()` becomes a no-op and our
  limit applies before any handler parses bodies. Closes the gap where
  a `Bearer garbage`-shaped attacker could force unbounded JSON parsing
  before token validation.

- **allowlist-error-logging** (monitor.ts) — restore main's `runtime.error`
  level for the `msteams resolve failed` catch (was downgraded to
  `runtime.log` mid-merge). Graph allowlist resolution failures are
  security-relevant; they need to surface to operators.

- **legacy-messages-route** (monitor.ts) — when `webhook.path` is set
  to a custom value, also accept POSTs on the legacy `/api/messages`
  path with a one-time deprecation warning, then re-enter the Express
  middleware chain on the configured path. Keeps existing Azure Bot
  registrations working through the transition. Cast-free
  (`expressApp(req, res, next)` works because `Application extends
  IRouter extends RequestHandler`).

- **release-age-scope** (pnpm-workspace.yaml) — narrow
  `@microsoft/teams.*` glob to the single direct dep
  `@microsoft/teams.apps`. Future scoped packages no longer get a
  freshness-guard pass.

Tests + checks: msteams suite (867), tsgo:core, tsgo:extensions,
tsgo:test, lint:extensions, check:architecture, knip --dependencies,
package-manifest contract, all green.

Still pending from the review (separate commits):
- auth-coverage-tests (Brad #1 + comment) — tests proving the SDK accepts
  `aud=<bot app id>` and rejects `aud=api.botframework.com`.
- invoke-response-handling (Brad #2, codex P2) — file-consent invoke ack
  must return through the SDK invoke handler, not `ctx.sendActivity`.
- stream-failure-fallback (codex P2, galin F5) — `streamFailed` latch so
  partial streams fall back to block delivery on non-cancel errors.
- serviceurl-routing (Brad #4, codex P2) — proposed rebuttal pending
  empirical confirmation that `smba.trafficmanager.net/teams` routes to
  non-default-region conversations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(msteams): lock SDK auth contract — aud + v1/v2 issuer coverage

Adds extensions/msteams/src/auth-coverage.test.ts driving ServiceTokenValidator
and createEntraTokenValidator directly with jose-minted RS256 tokens against an
in-memory JWKS (via JwksClient.prototype patch). Locks in the three contract
cases @BradGroux flagged on #76262: aud=<bot app id> accepted, aud=api.botframework.com
rejected even when appid/azp match, and v1/v2 issuers accepted for allowed tenant
(disallowed tenant rejected).

Drops a stale ambient module declaration in src/types/microsoft-teams-sdk.d.ts
that was shadowing the SDK's real jwt-validator types with a long-renamed
createServiceTokenValidator surface.

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

* fix(msteams): route file-consent invokes through typed app.on, drop broken invokeResponse send

Brad #2 / codex #4 on PR #76262 — `ctx.sendActivity({ type: "invokeResponse", ... })`
no longer reaches Teams as an HTTP InvokeResponse on the new SDK; it becomes
an outbound Bot Framework activity instead. Move file-consent accept/decline
to typed `app.on("file.consent.accept|decline", ...)` handlers. The SDK's
typed-route layer wraps a void return into `{ status: 200 }`
(`app.process.js:130`), so the manual ack disappears.

While in here, type `MSTeamsApp.on` properly. Borrowing the SDK's `App.on`
directly fails because that function carries a `this: App<TPlugin>`
constraint our structural alias can't satisfy, so we model an equivalent
generic over `IRoutes` with route-specific overloads (`card.action`,
`file.consent.*`, `activity`). The overloads work around a tsgo bug — the
`@microsoft/teams.api` `Activity` discriminated union collapses to `any`,
turning `ActivityRoutes` into a `[string]: RouteHandler<X, void>` index
signature that swallows every typed `Out` not already void-compatible
(card.action returns `AdaptiveCardActionResponse`; the others happen to
include `void`). Real tsc resolves cleanly. Linked upstream:
https://github.com/microsoft/typescript-go/issues/1057.

Other cleanups:
- Cast-free call sites for `adaptSdkContext` (now returns
  `MSTeamsTurnContext` instead of `unknown`).
- card.action error responses include `innerHttpError` per the SDK's
  `HttpError` shape requirement.
- Activity catch-all also skips `fileConsent/invoke` now that it's
  typed-routed (parallel to the existing `adaptiveCard/action` skip).

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

* fix(msteams): route SSO sign-in invokes through typed app.on, drop broken invokeResponse send

Brad #2 / codex #4 on PR #76262, SSO half. Continue the typed-route migration:
`signin/tokenExchange` and `signin/verifyState` now register via
`app.on("signin.token-exchange" | "signin.verify-state", ...)`. Per the
SDK's router, registering a user route with the same name as a system
route removes the system default — so the SDK's built-in handlers (which
would call `api.users.token.exchange` themselves and emit a `signin` event
nobody currently subscribes to) are silenced, and only ours runs. The SDK
wraps a void return into the HTTP 200 InvokeResponse, so the legacy
`ctx.sendActivity({ type: "invokeResponse", ... })` ack — broken on the new
SDK because it becomes an outbound BF activity instead of the HTTP
response — is gone.

The handler body is extracted from the activity-catch-all dispatch in
`monitor-handler.ts` to a new `signin-invoke.ts`, parallel to
`file-consent-invoke.ts`. `isSigninInvokeAuthorized` is now exported from
`monitor-handler.ts` so the new handler can reuse it. The activity
catch-all skips the SSO invoke names alongside the existing skips for
`adaptiveCard/action` and `fileConsent/invoke`.

`MSTeamsAppOn` overloads now cover the two SSO routes with their typed
ctx (`ISignInTokenExchangeInvokeActivity` / `ISignInVerifyStateInvokeActivity`).
Tests in `monitor-handler.sso.test.ts` were rewritten to call the
extracted handler directly — the `registered.run(ctx)` shape no longer
covers SSO, and the `expect(ctx.sendActivity).toHaveBeenCalledWith({ type:
"invokeResponse" })` assertions were dropped to match the new contract
(the SDK ack happens via the typed-route return value).

Note on overlap with #77784 (Stefan Stüben, Microsoft): that PR is doing
a much bigger SSO rework (sign-in card / sign-in-link / six-digit-code
fallbacks plus a `ctx.auth` plumbed to plugin tools). This change is
the small migration-correctness fix and is structured so #77784's SSO
body changes drop into the typed-route registrations cleanly on rebase.

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

* fix(msteams): route message-submit (feedback) invokes through typed app.on

Last invoke off the activity catch-all dispatch. `message/submitAction`
(thumbs up/down on AI-generated messages) now registers via
`app.on("message.submit", ...)`. Same shape as file-consent and SSO:
handler body extracted to a new `feedback-invoke.ts`, the SDK wraps a
void return into the HTTP 200 InvokeResponse, the broken
`ctx.sendActivity({ type: "invokeResponse", ... })` line is gone, and
the activity catch-all skips this invoke name alongside the others.

`isFeedbackInvokeAuthorized` is exported from `monitor-handler.ts` so
`feedback-invoke.ts` can reuse it. Tests in
`monitor-handler.feedback-authz.test.ts` were rewritten to call the
extracted handler directly — the old `handler.run(ctx)` shape no longer
intercepts feedback, and `originalRun` was removed because the typed
route is the dispatch point now.

`MSTeamsAppOn` overload added with the typed
`IMessageSubmitActionInvokeActivity` ctx, slotted between the SSO
overloads and the `activity` catch-all so `activity` stays last.

This leaves only `message`, `conversationUpdate`, and `messageReaction`
flowing through `app.on("activity", ...)` → `handler.run`. Promoting
those is the path to deleting the catch-all entirely.

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

* fix(msteams): fall back to block delivery when partial-mode stream fails mid-flight

codex #5 / Galin F5 on PR #76262. `reply-stream-controller.ts` previously
re-threw any non-cancel error from `stream.emit` during partial streaming
and from `stream.emit`/`stream.close` during finalize. Combined with
`preparePayload` suppressing block delivery once `tokensEmitted` was
true, that meant a network blip or API error mid-stream produced a
truncated reply with no recovery — the user saw the prefix that made it
through and nothing else.

Add a `streamFailed` latch parallel to `canceledLocally` / `tokensEmitted`:

- `onPartialReply`: catch non-cancel errors, set `streamFailed = true`,
  log a warn, don't propagate (the pipeline must keep running so
  `preparePayload` can decide).
- `preparePayload`: when `tokensEmitted && streamFailed`, fall through to
  block delivery instead of suppressing. The user may see a duplicate
  (streamed prefix + full block reply); intentional — matches the
  pre-migration `TeamsHttpStream.hasContent` recovery and is better than
  truncated-only.
- `finalize`: same latch + warn on non-cancel close failure, swallow
  rather than throw. The streamed content already reached the user; the
  closing activity (AI-Generated marker, feedback channelData) is the
  only loss, not worth blowing up the dispatcher.
- `isStreamActive` returns false once the stream has failed.

New tests cover crash-mid-stream after tokens were emitted (assert block
delivery payload is returned), happy-path no-duplicate behavior (assert
`preparePayload` still suppresses when nothing failed), and finalize
close-failure (assert no throw). The pre-existing "re-throws non-cancel"
test was inverted to assert non-throwing latch behavior.

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

* fix(msteams): declare @microsoft/teams.api as a runtime dependency

Type-only `import("@microsoft/teams.api/dist/...").TypeName` references
in `sdk.ts` (added when typed `MSTeamsApp.on` overloads were introduced)
are picked up by the `extension-runtime-dependencies` contract test as
genuine runtime imports. Declaring `@microsoft/teams.api` as a direct
dep makes the contract pass; the package was already coming in
transitively via `@microsoft/teams.apps`.

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

* fix(msteams): keep SSO on SDK signin routes

* test(msteams): avoid redundant signin handler assertion

* docs(msteams): clarify Teams cloud support

* fix(msteams): use current SDK string helper

* fix(msteams): gate SDK invoke side effects

* test(msteams): avoid implicit any in lifecycle tests

* fix(msteams): preserve SDK user agent and matrix check

* fix(msteams): expose SDK common dependency

* fix(msteams): use SDK user agent merge

* fix(msteams): fall back when stream close no-ops

* chore(msteams): drop unrelated merge artifacts

* chore(msteams): restore unrelated main files

* chore(msteams): restore unrelated main files

* chore(msteams): restore unrelated main files

* test(msteams): type stream close mock result

* fix(msteams): configure Teams cloud service URL

* chore(msteams): refresh shrinkwrap

* chore(deps): refresh shrinkwrap locks

* chore(ci): rerun guards after main sync

* chore(deps): refresh shrinkwrap for node 24

* chore(config): refresh docs baseline

* fix(msteams): preserve Teams SDK proactive references

* fix(msteams): harden SDK proactive sends

* fix(msteams): align service url contract

* test: fix bonjour beacon type narrowing

* fix(msteams): ignore ambient service url

* fix(msteams): fall through submit invokes

* test: align shrinkwrap override policy with Teams SDK deps

* fix(msteams): ack invoke routes promptly

* fix(msteams): support china cloud boundaries

* test: sync PR with current CI gates

* test: isolate channel setup registry metadata

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-05-28 22:54:34 +01:00
Peter Steinberger
912e9dd0a6 fix: validate pixverse video response ids 2026-05-28 17:52:04 -04:00
Peter Steinberger
1fd73947d1 fix: validate pixverse video seed metadata 2026-05-28 17:49:58 -04:00
Peter Steinberger
4dbe2a3d2b fix: validate deepinfra video seed metadata 2026-05-28 17:48:09 -04:00
Peter Steinberger
1e913580d4 fix: validate fal seedance durations 2026-05-28 17:46:16 -04:00
Vincent Koc
dcecda5596 fix(e2e): reject loose journey limits 2026-05-28 23:45:26 +02:00
Vincent Koc
b8311ad6ea refactor: dedupe script validation helpers 2026-05-28 23:44:34 +02:00
Peter Steinberger
bab9a8dc37 fix: validate together video durations 2026-05-28 17:44:06 -04:00
Peter Steinberger
6d39b94a7b ci(release): serialize Telegram CI bot consumers 2026-05-28 22:42:06 +01:00
Paul Frederiksen
e69855e68c fix(codex): recover raw missing-thread compaction failures (#87738)
Recover Codex compaction paths when a stale app-server thread binding returns an unstructured `thread not found` failure. The raw missing-thread response now shares the same recovery behavior as structured missing/stale binding failures for preflight, queued compaction, and CLI fallback.

Fixes #87736.

Co-authored-by: Paul Frederiksen <paul@paulfrederiksen.com>
2026-05-28 22:41:44 +01:00
Peter Steinberger
2bc3c7ad5a fix: validate byteplus video durations 2026-05-28 17:40:33 -04:00
Jason (Json)
0dbdaf98ea fix: release session lock before runtime teardown (#87747)
Summary:
- The PR reorders embedded attempt cleanup to release the session write lock before session/MCP/LSP teardown, treats sessions_yield cleanup as abort-like for flush timing, and adds focused regression tests.
- PR surface: Source +14, Tests +71. Total +85 across 3 files.
- Reproducibility: yes. Source inspection shows current main releases the cleanup lock only after runtime tear ... R body’s terminal proof exercises the same ordering with production cleanup and filesystem lock primitives.

Automerge notes:
- PR branch already contained follow-up commit before automerge: Merge branch 'main' into fix/session-lock-release-before-teardown

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

Prepared head SHA: 178192fa0e
Review: https://github.com/openclaw/openclaw/pull/87747#issuecomment-4566994280

Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: Jason (Json) <263060202+fuller-stack-dev@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-05-28 21:40:06 +00:00
Peter Steinberger
59997d8689 perf: avoid no-op session store rewrites 2026-05-28 22:37:56 +01:00
Peter Steinberger
a2d386638c fix: validate byteplus video seeds 2026-05-28 17:37:52 -04:00
Dallin Romney
563ad77d13 ci: sparse checkout CodeQL actions shard (#87775) 2026-05-28 14:36:31 -07:00
Vincent Koc
b05aefa3cf fix(release): bound beta smoke waits 2026-05-28 23:35:06 +02:00
Peter Steinberger
fc6fd9aa36 fix: validate inworld speech temperature 2026-05-28 17:34:49 -04:00
Peter Steinberger
769de93f9c fix: validate xai speech speed 2026-05-28 17:32:39 -04:00
Peter Steinberger
e04158a028 fix: validate volcengine speech speed ratio 2026-05-28 17:30:33 -04:00
Jason (Json)
8a007c987d fix(agents): fallback when generated media handoff locks
Generated-media completions now use the existing idempotent direct-media fallback when active requester wake has already failed and the requester-agent handoff hits a session write-lock-shaped no-response error. Generic requester-agent handoff errors still fail visibly instead of direct-sending after an unknown side effect.

Release-note context: fixes a message-delivery loss path for generated images, music, and video where the artifact had been created but the final handoff could be reported as failed after a session write lock.

Verification:
- GitHub CI run 26601111985 passed at b0be994332.
- Blacksmith Testbox through Crabbox tbx_01ksr2jtt3fnz0zqvwmqq513h7 covered the exact lock fallback and qa-channel generated-media smoke.
- git diff --check origin/main...refs/remotes/pull/87741/head passed before merge.

Co-authored-by: Jason (Json) <263060202+fuller-stack-dev@users.noreply.github.com>
2026-05-28 22:30:13 +01:00
Peter Steinberger
51b69497a3 fix: cover explicit live model discovery 2026-05-28 22:27:35 +01:00
Peter Steinberger
c84d53ccfe fix: validate minimax speech voice settings 2026-05-28 17:26:29 -04:00
Peter Steinberger
d9452e6acb fix: validate openrouter video seeds 2026-05-28 17:16:51 -04:00
Peter Steinberger
ee6f26406f fix(release): keep mock release lanes offline
(cherry picked from commit 6ef61e4daf2fda31b84c3652feafb04677415371)
2026-05-28 22:15:42 +01:00
Peter Steinberger
4512363e85 fix: validate pixverse video seeds 2026-05-28 17:12:22 -04:00
Peter Steinberger
a5241782ca fix(nostr): close relay pool after subscription shutdown 2026-05-28 22:10:43 +01:00
IWhatsskill
8a76cc3470 fix(nostr): keep dm subscriptions alive until abort 2026-05-28 22:10:43 +01:00
Peter Steinberger
22515eea44 fix: validate google live thinking budget 2026-05-28 17:09:36 -04:00
Peter Steinberger
017e241162 fix: validate google live vad timing 2026-05-28 17:07:08 -04:00
Peter Steinberger
09c3768cdd fix: validate elevenlabs latency tiers 2026-05-28 17:04:07 -04:00
Vincent Koc
f0207d3ea0 fix(security): bound prod audit registry responses 2026-05-28 23:02:24 +02:00
Peter Steinberger
b5202f975b fix: validate elevenlabs speech seeds 2026-05-28 17:01:59 -04:00
Peter Steinberger
516be11db9 docs: add shrinkwrap and release performance report 2026-05-28 22:00:55 +01:00
Vincent Koc
3807a01542 test: dedupe gateway benchmark teardown tests 2026-05-28 23:00:29 +02:00
Peter Steinberger
1d965d9a6f fix: validate elevenlabs voice settings 2026-05-28 16:59:08 -04:00
Peter Steinberger
ec4a00beae fix: validate openai speech speeds 2026-05-28 16:56:01 -04:00
Peter Steinberger
3533297cd9 fix: validate openai realtime voice numeric config 2026-05-28 16:49:10 -04:00
Vincent Koc
db66004b31 fix(agents): stream assistant deltas incrementally (#87671) 2026-05-28 21:48:33 +01:00
Peter Steinberger
2b69cfe030 fix: validate openai realtime transcription vad 2026-05-28 16:46:15 -04:00
Super Zheng
0c716d7717 perf(plugins): reuse facade manifest snapshots
Reuses the current plugin metadata snapshot in facade activation checks when the resolved boundary config matches, avoiding repeated manifest registry loads on the facade path.

Falls back to manifest registry loading when the current snapshot is missing or belongs to a different config/environment. Adds regression coverage for snapshot mismatch, snapshot reuse, and Windows path normalization.

Co-authored-by: 郑苏波 (Super Zheng) <superzheng@tencent.com>
2026-05-28 21:45:17 +01:00
Peter Steinberger
9a21e4e6c2 perf: cache plugin registry snapshots 2026-05-28 21:45:09 +01:00
Peter Steinberger
b5d90ae4ec fix: validate voice-call legacy streaming numbers 2026-05-28 16:43:31 -04:00
Vincent Koc
b3fbe5325e refactor: dedupe approval and benchmark helpers 2026-05-28 22:41:26 +02:00
Peter Steinberger
607e6c206f fix: validate elevenlabs realtime numeric config 2026-05-28 16:39:25 -04:00
Peter Steinberger
48291462ef fix: validate runway video durations 2026-05-28 16:35:09 -04:00
Peter Steinberger
ccf3476a4a fix: validate deepinfra video seeds 2026-05-28 16:32:58 -04:00
Peter Steinberger
6966c202b9 fix: validate media size dimensions 2026-05-28 16:30:23 -04:00
Fermin Quant
205d6b730f fix(agents): attribute embedded tool logs to channels
Fixes #50565.
2026-05-28 21:29:26 +01:00
Peter Steinberger
a661506b0f fix(release): satisfy lint for beta preflight 2026-05-28 21:28:50 +01:00
Dallin Romney
2be9eb1e97 ci: consolidate Blacksmith runner defaults (#87503) 2026-05-28 13:28:22 -07:00
Peter Steinberger
714ff554fd fix: validate provider retry attempts 2026-05-28 16:28:07 -04:00
Peter Steinberger
b1c95a82a0 fix: refresh live Together and Vydra coverage 2026-05-28 21:27:50 +01:00
Vincent Koc
9268f9fe8a fix(e2e): drop unused codex media event buffer 2026-05-28 22:27:39 +02:00
Peter Steinberger
90c2ac3b6a fix: validate memory retry attempts 2026-05-28 16:26:08 -04:00
ooiuuii
f49a3e4c26 fix: bound aggregate prompt tool results
Bound aggregate tool-result history at the provider prompt boundary without rewriting persisted session entries.

Provider-visible prompt history now trims older aggregate tool results before newer evidence, while canonical session history, slash/extension command handlers, and context-engine afterTurn snapshots stay unmodified.

Co-authored-by: luyifan <al3060388206@gmail.com>
2026-05-28 21:25:02 +01:00
Peter Steinberger
4cbce8458d fix: validate acp prompt timeout metadata 2026-05-28 16:23:59 -04:00
Peter Steinberger
80c50c2370 test(whatsapp): fix remote media header mocks 2026-05-28 21:22:17 +01:00
Peter Steinberger
898f74c27e test(release): align runtime alias expectation 2026-05-28 21:22:17 +01:00
Peter Steinberger
a8dec44f56 fix(release): accept openclaw qa runtime alias 2026-05-28 21:22:17 +01:00
Peter Steinberger
2267ddc3a0 fix(release): accept openclaw runtime alias 2026-05-28 21:22:17 +01:00
Tideclaw
05202c1f8a test: tighten imessage notification callback type 2026-05-28 21:22:16 +01:00
Vincent Koc
9803261f71 fix(media): cancel ignored input fetch bodies 2026-05-28 21:22:16 +01:00
Vincent Koc
5e68d2f811 fix(media): cancel oversized fetch responses 2026-05-28 21:22:16 +01:00
Peter Steinberger
6e3f38d033 fix: validate telegram throttle ids 2026-05-28 16:21:35 -04:00
Peter Steinberger
e85231d63d fix: validate google oauth token expiry 2026-05-28 16:19:34 -04:00
Vincent Koc
686751f639 test(agents): add small model live profile (#87638) 2026-05-28 21:17:40 +01:00
Peter Steinberger
f7507fd921 fix: validate msteams error status codes 2026-05-28 16:16:43 -04:00
Peter Steinberger
ea682182d0 fix: isolate npm plugin installs per package (#87647)
* fix: isolate npm plugin installs per package

* test: assert isolated npm plugin projects in upgrade survivor

* test: assert plugin lifecycle npm project roots

* test: resolve npm project deps in live assertions

* fix: resolve codex bins from isolated npm projects

* docs: document isolated npm plugin projects

* ci: configure testbox workflow for crabbox

* fix: stabilize npm project fingerprint

* fix: keep fetch runtime import side-effect free

* test: keep dynamic live model unit hermetic

* ci: handle empty node toolcache roots

* test: make nounset toolcache probe deterministic
2026-05-28 21:16:07 +01:00
Vincent Koc
2b587be44d fix(e2e): bound tool search fetch bodies 2026-05-28 22:14:46 +02:00
Vincent Koc
50e6bd307d test(agents): narrow Codex bootstrap assertions 2026-05-28 22:14:38 +02:00
Peter Steinberger
7bc871139d fix: validate pricing content length headers 2026-05-28 16:14:25 -04:00
Dallin Romney
c629270f23 ci: filter release workflow matrices (#87508) 2026-05-28 13:12:54 -07:00
Dallin Romney
3dee915b3b ci: warm pnpm store before node fanout (#87518)
* ci: warm pnpm store before node fanout

* test: update pnpm cache acceptance key

* ci: clarify pnpm cache save controls
2026-05-28 13:11:20 -07:00
Peter Steinberger
09c5b2dd37 fix: validate discord component numeric limits 2026-05-28 16:10:07 -04:00
Vincent Koc
59205bd63c fix(e2e): bound Telegram Bot API helper bodies 2026-05-28 22:01:36 +02:00
Agustin Rivera
6fd4aa8a27 fix(nvidia): load featured model catalog (#80775)
* fix(nvidia): load featured model catalog

Co-authored-by: CaptainTimon <CaptainTimon@users.noreply.github.com>

* fix(nvidia): widen catalog fetch timeout

* fix(nvidia): cover catalog registration

* fix(picker): include provider catalog loader

* fix(nvidia): guard featured catalog fetch

* fix(nvidia): sync bundled catalog with live API

Replace minimaxai/minimax-m2.5 (MiniMax M2.5) with minimaxai/minimax-m2.7 (Minimax M2.7) and z-ai/glm5 (GLM-5) with z-ai/glm-5.1 (GLM 5.1) in the bundled fallback catalog to match NVIDIA's public featured-models endpoint.

Update docs table and all extension test expectations.

* fix(nvidia): retain shipped catalog refs

* fix(picker): keep alias catalog rows

* fix(nvidia): restore live catalog priority

---------

Co-authored-by: CaptainTimon <CaptainTimon@users.noreply.github.com>
2026-05-28 12:59:55 -07:00
Peter Steinberger
409356fc66 fix: validate synology rate limits 2026-05-28 15:57:51 -04:00
Peter Steinberger
c0946e6e58 chore: remove stale dependency ownership records 2026-05-28 20:56:51 +01:00
Peter Steinberger
fcbc254d0d fix: validate feishu action count params 2026-05-28 15:55:19 -04:00
Vincent Koc
9cb4e48018 test(infra): use bonjour beacon type in discovery test
Replace the loose bonjour discovery test record shape with the exported GatewayBonjourBeacon contract so invalid-port regression coverage keeps passing the core test typecheck gate.

Verification:
- node scripts/run-vitest.mjs run src/infra/bonjour-discovery.test.ts --reporter=verbose
- node scripts/run-oxlint.mjs src/infra/bonjour-discovery.test.ts
- ./node_modules/oxfmt/bin/oxfmt --check --threads=1 src/infra/bonjour-discovery.test.ts
- git diff --check
- .agents/skills/autoreview/scripts/autoreview --mode local
- AWS Crabbox corepack pnpm check:changed: run_1af313cdd0bb, cbx_f0ef52388e7c, provider aws, exit 0

PR: https://github.com/openclaw/openclaw/pull/87765
2026-05-28 20:53:02 +01:00
Peter Steinberger
46f023d097 fix: validate nextcloud talk numeric flags 2026-05-28 15:52:39 -04:00
Peter Steinberger
8b180fe829 fix: reject malformed tlon sse event ids 2026-05-28 15:50:40 -04:00
Vincent Koc
b84078a975 fix(e2e): bound Telegram RTT driver API bodies 2026-05-28 21:48:45 +02:00
Peter Steinberger
1dcb677985 fix: reject partial qq reminder durations 2026-05-28 15:48:30 -04:00
Peter Steinberger
c42664f9b2 fix: require integer qmd line metadata 2026-05-28 15:46:23 -04:00
Peter Steinberger
04a6fd7fde fix: validate debug proxy connect ports 2026-05-28 15:44:19 -04:00
Peter Steinberger
483b06fb86 fix(ci): serialize oxlint on constrained runners 2026-05-28 20:42:37 +01:00
Peter Steinberger
d487c58c6f fix: validate browser profile ports 2026-05-28 15:40:16 -04:00
Peter Steinberger
1e67387475 fix: validate browser responsebody limits 2026-05-28 15:37:59 -04:00
Peter Steinberger
8ed9330a30 perf: defer Slack full startup (#87760) 2026-05-28 20:37:09 +01:00
Vincent Koc
605e2976ed fix(e2e): bound release fixture response bodies 2026-05-28 21:36:25 +02:00
Peter Steinberger
8fbdfc0a76 fix: validate browser geolocation numbers 2026-05-28 15:35:14 -04:00
Peter Steinberger
503d8d5542 fix: validate browser snapshot numbers 2026-05-28 15:32:25 -04:00
Peter Steinberger
f99259d25c fix(perf): preserve gateway health benchmark auth 2026-05-28 20:29:24 +01:00
Peter Steinberger
ec8ff27803 fix: validate browser viewport dimensions 2026-05-28 15:29:12 -04:00
Peter Steinberger
afb56ea972 fix: reject invalid browser tab indexes 2026-05-28 15:26:33 -04:00
clawsweeper[bot]
3617247c65 fix(tui): force repaint final chat events (#87423)
Summary:
- The PR changes three TUI final chat-event early returns to call `tui.requestRender(true)` and adds focused event-handler assertions for those branches.
- PR surface: Source 0, Tests +25. Total +25 across 2 files.
- Reproducibility: yes. Current main and the latest release still have the three unforced final-event repaint calls, and the linked source PR includes PTY terminal proof showing the changed behavior after the patch.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(tui): force repaint final chat events

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

Prepared head SHA: 570dc3af86
Review: https://github.com/openclaw/openclaw/pull/87423#issuecomment-4558845936

Co-authored-by: Ted Li <tl2493@columbia.edu>
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-05-28 19:26:05 +00:00
Rohit
376b03f8ea fix(plugins): reject incompatible package plugin API installs (#87477)
* fix(plugins): enforce package plugin API compatibility

* fix(plugins): preserve plugin API prerelease floors

* fix(plugins): gate persisted plugin api compatibility

* fix(plugins): skip incompatible package discovery

* fix(plugins): check api compatibility before package shape

* fix(plugins): gate bundle package api compatibility

* docs(plugins): clarify plugin API release sync

* test(agents): keep dynamic live model unit test runtime-free

* fix(plugins): normalize correction plugin api floors

* test(agents): align dynamic normalizer expectation

* fix(plugins): reject malformed plugin api metadata

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-05-28 20:25:30 +01:00
Vincent Koc
4d5b317ace fix(e2e): bound ClawHub preflight response bodies 2026-05-28 21:24:18 +02:00
Vincent Koc
396a8ef6f8 fix(agents): loosen abort settle env typing
Narrow the abort-settle timeout helper to the env keys it reads and keep the dynamic live-model hook unit test from loading provider normalization/runtime plugins.\n\nProof: focused Vitest for live-model-dynamic-candidates, oxfmt/oxlint/diff checks, autoreview clean, AWS Crabbox run_8a485e593c2e corepack pnpm check:changed exit 0, and PR CI green.
2026-05-28 20:24:06 +01:00
Peter Steinberger
5eed10fd6e fix: reject invalid discovery ports 2026-05-28 15:23:47 -04:00
Peter Steinberger
76130fd988 fix: parse tar verbose sizes strictly 2026-05-28 15:19:53 -04:00
Peter Steinberger
73168d37ac feat: support encrypted PDF extraction (#87751) 2026-05-28 20:19:49 +01:00
Peter Steinberger
41366d3f51 fix: ignore unsafe timestamp values 2026-05-28 15:17:13 -04:00
Peter Steinberger
bd773d2f61 fix: parse subagent depth strings strictly 2026-05-28 15:15:06 -04:00
Peter Steinberger
2a5a9fd720 fix: parse usage query numbers strictly 2026-05-28 15:13:27 -04:00
Vincent Koc
4fb904ca63 fix(e2e): bound Parallels host server stderr 2026-05-28 21:11:44 +02:00
clawsweeper[bot]
dfe9774387 fix(minimax): stream music generation responses (#84764)
Summary:
- The PR updates the bundled MiniMax music provider to request streaming hex responses, decode SSE/audio bodie ... while preserving JSON/url fallbacks, and adds provider tests for streaming, fallback, and timeout behavior.
- PR surface: Source +148, Tests +152. Total +300 across 2 files.
- Reproducibility: yes. by source inspection and live proof, though I did not run a fresh live reproduction. C ... s provider fallback, and the source PR reports a 130s live MiniMax provider run succeeding after the patch.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(minimax): stream music generation responses
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8456…

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

Prepared head SHA: 806b0b40f2
Review: https://github.com/openclaw/openclaw/pull/84764#issuecomment-4504175527

Co-authored-by: Neerav Makwana <261249544+neeravmakwana@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-05-28 19:11:37 +00:00
Peter Steinberger
663cf97bea fix: parse sessions filters strictly 2026-05-28 15:11:08 -04:00
Peter Steinberger
7c4601ec73 feat(slack): render progress as native task cards
Render Slack progress-mode updates as native task-card progress blocks, with bounded Slack chunk text and stable fallback behavior.

Also deep-merge Slack account streaming objects over top-level defaults while preserving legacy scalar account overrides, and keep the plugin SDK fetch runtime import path from evaluating guarded-fetch dispatcher code.

Verification:
- pnpm test extensions/slack/src/progress-blocks.test.ts extensions/slack/src/accounts.test.ts src/plugin-sdk/fetch-runtime.test.ts
- pnpm lint --threads=8
- git diff --check
- .agents/skills/autoreview/scripts/autoreview --mode local
- GitHub PR checks green on #87748 at 4803e98820

Refs #82258

Co-authored-by: Simon van Laak <32648751+simonvanlaak@users.noreply.github.com>
2026-05-28 20:08:51 +01:00
Peter Steinberger
588078224b fix: parse session lock env timeouts strictly 2026-05-28 15:06:57 -04:00
Peter Steinberger
a0fcb91670 fix(release): keep private test helpers out of npm pack 2026-05-28 20:05:50 +01:00
Dallin Romney
2d8cebba5c test: surface broad local Vitest runs (#87757) 2026-05-28 12:05:19 -07:00
Peter Steinberger
1ac8c71cf5 fix: reject unsafe memory duration values 2026-05-28 15:04:27 -04:00
Dallin Romney
9f0fccd3a5 test(auto-reply): narrow directive model test dependencies (#87519)
* test(auto-reply): narrow directive model test dependencies

* test(auto-reply): stabilize directive model test mocks
2026-05-28 12:03:08 -07:00
Peter Steinberger
490c226202 fix: parse matrix no-reply window strictly 2026-05-28 15:02:14 -04:00
Peter Steinberger
a2595f16d4 fix: reject unsafe bash env integers 2026-05-28 15:00:08 -04:00
Vincent Koc
1a926d19b0 fix(e2e): bound RTT credential broker bodies 2026-05-28 20:58:01 +02:00
Peter Steinberger
d23e4111b0 fix: parse matrix qa env timeouts strictly 2026-05-28 14:57:52 -04:00
Peter Steinberger
a691d52329 fix: parse slack cache ttl strictly 2026-05-28 14:54:02 -04:00
Vincent Koc
f9834a3f95 ci: pin macOS runner labels 2026-05-28 20:52:52 +02:00
Peter Steinberger
43e243f436 fix: support grouped skill folders
Support grouped skill folders while keeping skill invocation flat via frontmatter names.

Includes bounded nested SKILL.md discovery, refresh/watch coverage for grouped folders, plugin symlink containment, and docs for grouped skill organization.

Verification:
- Node 24 targeted skill discovery and refresh tests passed locally.
- Docs checks passed locally and in CI.
- Autoreview clean.
- Crabbox live OpenAI proof showed nested foo/bar skills listed and visible in the agent system prompt.
- CI run 26595118581 passed.
2026-05-28 19:52:27 +01:00
Peter Steinberger
4b8c260444 fix: parse browser action timeouts strictly 2026-05-28 14:51:23 -04:00
alkor2000
b3db1dba85 fix(anthropic): stop migrating current claude-haiku-4-5 to sonnet (#87719)
Summary:
- The branch preserves current Claude Haiku 4.5 refs in the Anthropic resolver and doctor migration, repoints the bare `haiku` family alias to `claude-haiku-4-5`, and updates regression tests.
- PR surface: Source +5, Tests +21. Total +26 across 4 files.
- Reproducibility: yes. Current main source maps the bare `haiku` alias and explicit Haiku 4.5 migration path  ... de-sonnet-4-6`; the PR body also supplies before/after terminal proof for the resolver and migration tests.

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

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

Prepared head SHA: 64429e23b3
Review: https://github.com/openclaw/openclaw/pull/87719#issuecomment-4566419633

Co-authored-by: alkor2000 <200923177@qq.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-05-28 18:50:36 +00:00
Peter Steinberger
0786f586af fix: parse memory wiki cli numbers strictly 2026-05-28 14:49:02 -04:00
Peter Steinberger
f0bfa650dc fix: parse browser cli numbers strictly 2026-05-28 14:46:34 -04:00
NVIDIAN
6fbdae1c51 fix(memory-core): cap Dreaming short-term recall growth
Cap Dreaming short-term recall stores so repeated recall recording, repair, and promotion application cannot grow the JSON artifact without bound.

The fix keeps full normalized snippets for recall identity and contamination checks before truncating persisted snippets, exposes the new overflow audit code through the SDK facade, and adds regression coverage for recording, repair, promotion rehydration, and deterministic retention ties.

Fixes #87095.

Verification:
- OPENCLAW_VITEST_MAX_WORKERS=1 node scripts/run-vitest.mjs extensions/memory-core/src/short-term-promotion.test.ts src/commands/doctor-memory-search.test.ts src/plugin-sdk/memory-core-engine-runtime.test.ts
- pnpm tsgo:prod
- pnpm check:test-types
- pnpm lint --threads=8
- git diff --check
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- PR CI run 26594527697: unrelated current-main failures only in checks-node-agentic-plugin-sdk and checks-node-agentic-agents; same failures reproduced on main run 26594198639.

Co-authored-by: ai-hpc <mail.speedy.hpc@hotmail.com>
2026-05-28 19:44:58 +01:00
Peter Steinberger
e8f29087ae fix: validate configure gateway ports strictly 2026-05-28 14:43:19 -04:00
Peter Steinberger
b2fdbc53e8 fix: parse qa parent pid strictly 2026-05-28 14:41:02 -04:00
Peter Steinberger
528371e7a4 docs(changelog): refresh 2026.5.28 notes 2026-05-28 19:39:42 +01:00
Peter Steinberger
5e33d7dff9 fix: parse discord gateway timeouts strictly 2026-05-28 14:39:05 -04:00
Peter Steinberger
cd80b4efca fix: parse qa cli integers strictly 2026-05-28 14:36:47 -04:00
Peter Steinberger
68ff0b9881 fix: parse memory cli numbers strictly 2026-05-28 14:33:18 -04:00
Peter Steinberger
8ec4a72f64 fix: parse voice call cli integers strictly 2026-05-28 14:31:24 -04:00
Vincent Koc
8338986a59 fix(e2e): bound telegram credential broker bodies 2026-05-28 20:29:16 +02:00
Peter Steinberger
d23e4aea6f fix: parse google meet cli numbers strictly 2026-05-28 14:28:49 -04:00
Peter Steinberger
a82dfb8e58 fix: parse google meet env numbers strictly 2026-05-28 14:25:58 -04:00
Peter Steinberger
2afff85ca4 fix: parse signal archive length strictly 2026-05-28 14:23:26 -04:00
Peter Steinberger
b87510957f docs: clarify Codex native hook relay recovery 2026-05-28 19:21:50 +01:00
Peter Steinberger
4ad9f0bdbb refactor: route node proxy agents through proxyline 2026-05-28 19:21:50 +01:00
Peter Steinberger
2305bca782 fix: parse discord rate limit headers strictly 2026-05-28 14:21:29 -04:00
Peter Steinberger
bcf354eac1 fix: parse codex retry headers strictly 2026-05-28 14:19:47 -04:00
Vincent Koc
21e69fdd4f fix(ollama): promote plain text tool calls
Wrap Ollama native streams with the shared plain-text tool-call compatibility wrapper so local/plain-text tool requests are delivered as structured toolCall events when matching tools are available.

Verified with live local Ollama proof, focused Testbox Vitest, Testbox check:changed, and autoreview.
2026-05-28 19:18:41 +01:00
Peter Steinberger
7859ee396e fix: parse provider retry dates strictly 2026-05-28 14:17:36 -04:00
Peter Steinberger
5eee488d93 fix: parse discord api retry headers strictly 2026-05-28 14:12:33 -04:00
Peter Steinberger
1d28dd87a5 fix: parse discord retry delays strictly 2026-05-28 14:08:04 -04:00
Peter Steinberger
a8991e02d8 fix: parse feishu startup timeout env strictly 2026-05-28 14:05:23 -04:00
Peter Steinberger
99f70284bf fix: parse feishu timeout env strictly 2026-05-28 14:03:06 -04:00
Peter Steinberger
21db3ff11c fix: parse telegram qa timeout env strictly 2026-05-28 14:00:56 -04:00
Peter Steinberger
19d1c217dc fix: parse qa credential integer env strictly 2026-05-28 13:57:33 -04:00
Lior Balmas
492105db5a fix(media): compact whatsapp terminal qr (#87581) 2026-05-28 10:57:03 -07:00
Peter Steinberger
d3b5413a01 fix: parse qa worker stagger env strictly 2026-05-28 13:55:13 -04:00
Peter Steinberger
2e8b3445fb fix: parse qa transport timeout env strictly 2026-05-28 13:53:18 -04:00
Peter Steinberger
339a74a342 fix: parse qa process metrics strictly 2026-05-28 13:51:33 -04:00
Peter Steinberger
5b79ab0901 fix: parse codex computer use timeout env strictly 2026-05-28 13:49:38 -04:00
Peter Steinberger
929b3a4f16 fix: parse codex migration timeout env strictly 2026-05-28 13:45:05 -04:00
Peter Steinberger
2cde331772 fix: parse qa suite concurrency env strictly 2026-05-28 13:41:28 -04:00
Dallin Romney
5f9d71f8af fix(ci): raise plugin sdk strict smoke heap (#87729) 2026-05-28 10:39:32 -07:00
Peter Steinberger
df4475d232 fix: parse embedded abort settle timeout strictly 2026-05-28 13:38:19 -04:00
Peter Steinberger
f90e266416 fix: parse sdk retry wait env strictly 2026-05-28 13:36:34 -04:00
Vincent Koc
bbc9a7d3fa fix(e2e): bound OpenWebUI probe response bodies 2026-05-28 19:35:12 +02:00
Peter Steinberger
d47eee4407 fix: parse queue caps strictly 2026-05-28 13:34:47 -04:00
Peter Steinberger
2122dccb91 fix: parse gateway usage days strictly 2026-05-28 13:31:45 -04:00
Peter Steinberger
d08bcb427e fix: parse http idle timeout strings strictly 2026-05-28 13:29:15 -04:00
Peter Steinberger
42688f5aae fix: parse cleanup timeout env strictly 2026-05-28 13:27:20 -04:00
Peter Steinberger
d6c8e05de9 fix: parse handshake timeout env strictly 2026-05-28 13:25:12 -04:00
Peter Steinberger
ca87241289 fix: parse cron stagger strings strictly 2026-05-28 13:22:54 -04:00
Peter Steinberger
ed9299a216 fix: reject invalid cron epoch timestamps 2026-05-28 13:20:09 -04:00
Peter Steinberger
3bf86877c2 fix: parse cron task run ids strictly 2026-05-28 13:17:57 -04:00
clawsweeper[bot]
202ccf4cf7 fix(native-hook-relay): prune stale bridge files on registration (#87706)
Summary:
- The PR adds registration-time pruning of expired or ESRCH-dead native-hook relay bridge JSON files and regression tests for dead, expired, live, and unknown-liveness foreign records.
- PR surface: Source +59, Tests +148. Total +207 across 2 files.
- Reproducibility: yes. The linked source PR includes a concrete live WSL2/systemd reproduction with stale bri ...  hook failures, and current source shows the native hook CLI fails closed when the relay cannot be reached.

Automerge notes:
- PR branch already contained follow-up commit before automerge: test(native-hook-relay): cover stale bridge pruning
- PR branch already contained follow-up commit before automerge: ci: raise plugin sdk strict smoke heap
- PR branch already contained follow-up commit before automerge: test(native-hook-relay): satisfy process kill mock types
- PR branch already contained follow-up commit before automerge: fix(native-hook-relay): prune stale bridge files on registration

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

Prepared head SHA: 65c17cdf6e
Review: https://github.com/openclaw/openclaw/pull/87706#issuecomment-4566131519

Co-authored-by: Applied-AI-Solutions-hub <Applied-AI-Solutions-hub@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-05-28 17:17:28 +00:00
Peter Steinberger
27dfb9149f test: cover dynamic live model refs 2026-05-28 18:16:45 +01:00
Peter Steinberger
b2fc8af1b1 fix: reject malformed media content length 2026-05-28 13:15:25 -04:00
Peter Steinberger
f3a23f8f5d fix: parse ffprobe sample rates strictly 2026-05-28 13:12:42 -04:00
Peter Steinberger
c2c29588f4 chore: update dependency pins 2026-05-28 18:10:39 +01:00
Peter Steinberger
0311171350 fix: parse ps cpu time formats 2026-05-28 13:10:28 -04:00
Peter Steinberger
5393240441 fix: clamp read tool line limits 2026-05-28 13:06:21 -04:00
Peter Steinberger
5ebf3b0396 fix: parse sandbox stat fields strictly 2026-05-28 13:03:14 -04:00
Vincent Koc
ea0b6bcb1f fix(scripts): give boundary root shims macos headroom 2026-05-28 19:00:45 +02:00
Vincent Koc
5fc5aa8f81 fix(e2e): bound kitchen sink rpc probe bodies 2026-05-28 19:00:45 +02:00
Peter Steinberger
a23a668d91 fix: honor bare ipv6 no_proxy entries 2026-05-28 12:54:13 -04:00
Peter Steinberger
e205888fa7 fix: honor ipv6 no_proxy entries 2026-05-28 12:50:59 -04:00
Nachiket Torwekar
53475c21b8 perf: reduce latency across async I/O hot paths
Improves gateway/device-auth/session discovery latency by caching unchanged device-auth reads, deduping session root realpaths, cleaning temp dirs in parallel, and bulk-loading APNs registrations for iOS exec approval delivery.

The maintainer fixup replaces per-device APNs registration reads with a single canonical store snapshot, preserving empty-target skip behavior and requested target ordering while avoiding delayed read failures from the bounded queue path.

Verification:
- node scripts/run-vitest.mjs src/gateway/exec-approval-ios-push.test.ts src/infra/push-apns.store.test.ts src/infra/device-auth-store.test.ts src/config/sessions/targets.test.ts src/test-utils/tracked-temp-dirs.test.ts src/utils/run-with-concurrency.test.ts
- env -u OPENCLAW_TESTBOX pnpm check:changed
- env -u OPENCLAW_TESTBOX pnpm test:changed
- pnpm exec oxfmt --check --threads=1 on touched files
- autoreview clean: no accepted/actionable findings
- before/after 500-device APNs discovery benchmark: p50 189.89 ms -> 2.03 ms
- GitHub Actions CI 26588266247 green after rerun; Real behavior proof 26588276271 green

Co-authored-by: Nachiket Torwekar <nachiket.torwekar@gmail.com>
2026-05-28 17:47:26 +01:00
Peter Steinberger
9e1faf81ab fix: count qmd output caps by code point 2026-05-28 12:47:08 -04:00
Peter Steinberger
f4f059ef94 fix: cap chrome mcp stderr by utf8 bytes 2026-05-28 12:42:18 -04:00
Peter Steinberger
75c3b53038 [codex] Use clawpdf for PDF extraction (#87670)
* feat: use clawpdf for PDF extraction

* fix: align approval action prompt typing

* chore: use clawpdf 0.2.0

* fix: lazily load clawpdf backend
2026-05-28 17:35:39 +01:00
Peter Steinberger
478e0ec3f8 fix: keep stderr tail within utf8 byte cap 2026-05-28 12:35:00 -04:00
clawsweeper[bot]
51e240123b fix(images): skip CLI image cache refs (#87523)
Summary:
- The branch filters OpenClaw CLI image-cache paths out of prompt image-reference detection and adds parser/helper regression tests.
- PR surface: Source +17, Tests +65. Total +82 across 3 files.
- Reproducibility: yes. source-level reproduction is high confidence: current main still scans replayed prompt ... ectImageReferences and has no cache-path exclusion before loadPromptRefImages can reload stale image paths.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(images): skip CLI image cache refs
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8750…

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

Prepared head SHA: dfe0408df8
Review: https://github.com/openclaw/openclaw/pull/87523#issuecomment-4560945125

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: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
2026-05-28 16:34:54 +00:00
clawsweeper[bot]
e9655b9fdc fix(ui): preserve session picker on empty search blur (#87682)
Summary:
- The PR changes the Control UI chat session picker blur handler to skip empty-query search application and adds a regression test that picker options remain clickable after an empty search blur.
- PR surface: Source +4, Tests +52. Total +56 across 2 files.
- Reproducibility: yes. The issue steps, before recording, and current-main source path all point to the same  ... r clearing picker state before click delivery; I did not rerun a live browser repro in this read-only pass.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(ui): preserve session picker on empty search blur

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

Prepared head SHA: bb14687756
Review: https://github.com/openclaw/openclaw/pull/87682#issuecomment-4565441074

Co-authored-by: Ryan Weng <14496969+ryan4559@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.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-05-28 16:34:50 +00:00
Peter Steinberger
0d66710539 fix: parse lsp content length by byte 2026-05-28 12:29:45 -04:00
rain
2df8021cda fix(agents): surface MCP structured content in tool results
Surface inbound bundle-MCP structuredContent as the model-visible result when present so agents can read Codex MCP threadId values and continue with codex-reply. Preserve non-structured content behavior, preserve the empty-result fallback, and keep details.structuredContent for internal consumers.

Also remove an unused secrets path helper that was breaking the latest prod-type gate on main.

Fixes #87511.

Verification:
- node scripts/run-vitest.mjs src/agents/agent-bundle-mcp-tools.materialize.test.ts
- pnpm exec oxfmt --check src/secrets/path-utils.ts src/agents/agent-bundle-mcp-materialize.ts src/agents/agent-bundle-mcp-tools.materialize.test.ts
- pnpm tsgo:prod
- local check-guards shard commands
- live Codex MCP smoke with codex__codex and codex__codex-reply same-thread continuation
- autoreview clean
- CI run 26587222874 green

Co-authored-by: Pluviobyte <Pluviobyte@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 17:29:29 +01:00
clawsweeper[bot]
cd0b692b61 fix(voice-call): make webhook replays token-safe
Fix replay handling for voice-call webhooks so duplicate signed requests do not mint or expose realtime stream tokens.

- Return token-free Twilio replay TwiML before realtime setup shortcuts.
- Cache bounded non-Twilio first responses for idempotent replay XML while skipping duplicate side effects.
- Cover Twilio realtime replay and Plivo replay behavior with regression tests.
- Remove an unused secrets path helper that was tripping latest-main prod type CI.

Fixes #87497.

Co-authored-by: Coy Geek <65363919+coygeek@users.noreply.github.com>
2026-05-28 17:29:15 +01:00
Vincent Koc
716fd67e03 fix(scripts): bound Z.AI fallback repro output 2026-05-28 18:19:34 +02:00
Peter Steinberger
a85ff92c05 perf: cache bundled channel entry resolution 2026-05-28 17:18:51 +01:00
Val Alexander
96635c7c27 fix(webchat): preserve sends through reconnect (#87531)
* fix(webchat): preserve sends through reconnect

* fix(webchat): scope queued sends by session

* fix(webchat): localize queue retry labels

* fix(secrets): remove unused path helper

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-05-28 17:18:24 +01:00
Peter Steinberger
c00ac952a8 fix: reject malformed inspected tcp ports 2026-05-28 12:15:23 -04:00
Peter Steinberger
982e2cf0ef fix: reject malformed marketplace content length 2026-05-28 12:11:50 -04:00
Peter Steinberger
03e6181f9f fix: reject exponent provider integer options 2026-05-28 12:08:09 -04:00
Andy Ye
5f88932806 fix(sessions): recover empty preflight compaction
Fixes #87016.

Empty preflight compaction recovery now resets stale token snapshots immediately, preserves valid legacy transcript rows during cleanup, and avoids re-persisting stale context-budget or compaction metadata after a successful retry.

Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
2026-05-28 17:06:38 +01:00
Peter Steinberger
fb80d3a491 perf: reduce gateway startup sidecar overhead 2026-05-28 17:05:19 +01:00
Peter Steinberger
e67ff0c43e fix: canonicalize secret target array indexes 2026-05-28 12:02:21 -04:00
Peter Steinberger
c9c53e3153 fix: harden config array index parsing 2026-05-28 11:58:40 -04:00
David
7a36bb37af feat(gateway): show warm MCP tools in effective inventory
Add read-only MCP visibility to `tools.effective` by projecting MCP tools only after a session catalog has already been warmed by an agent turn. Keep the gateway additive: no `tools.effective.refresh`, no forced MCP startup, and no behavior change for MCP loading.

Verification:
- `git diff --check origin/main..HEAD`
- `node scripts/run-vitest.mjs run --config test/vitest/vitest.agents.config.ts --reporter=verbose src/agents/tools-effective-inventory.test.ts`
- GitHub checks green on `a8a7f8442adb216f60da24d50118374a15c62e06`, including `Real behavior proof`, `check-guards`, `check-prod-types`, `check-test-types`, `build-artifacts`, `Critical Quality (gateway-runtime-boundary)`, and `Critical Quality (network-runtime-boundary)`.

Co-authored-by: David Huang <nxmxbbd@gmail.com>
2026-05-28 16:52:53 +01:00
Vincent Koc
b261e9e6dd fix(approvals): restore reaction command prompt lines 2026-05-28 17:32:58 +02:00
Vincent Koc
e707b452c0 fix(scripts): bound control UI i18n process output 2026-05-28 17:32:58 +02:00
Peter Steinberger
79e733cc34 docs: remove public GHSA fix mechanism details 2026-05-28 16:30:39 +01:00
Peter Steinberger
f8c8c0d41e fix(agents): handle seeded Anthropic signatures 2026-05-28 16:28:36 +01:00
Jerry Xin
8dc9cfe734 fix(agents): concatenate signature_delta chunks in transport stream
The anthropic-transport-stream was overwriting thinkingSignature on each
signature_delta event instead of appending. Since Anthropic sends the
thinking block signature across multiple streaming chunks, only the last
chunk survived. The truncated signature was persisted to session JSONL,
causing all subsequent replay attempts to fail with HTTP 400:

  thinking or redacted_thinking blocks in the latest assistant message
  cannot be modified

This permanently bricked sessions with no user recovery path.

Fix: accumulate signature_delta values by concatenating instead of
overwriting, matching the correct implementation in the LLM provider
layer (src/llm/providers/anthropic.ts:629-634).

Includes real-scenario proof against live Anthropic API validating that
correct signatures replay successfully while truncated signatures are
rejected.

Fixes #87574
Refs #80625, #85781, #87475
2026-05-28 16:28:36 +01:00
Peter Steinberger
e5adde9fe3 fix(auto-reply): respect provider for directive persistence (#87683) 2026-05-28 16:27:19 +01:00
rain
ad3e3cb7d2 fix(agents): preserve reasoning_content replay across DeepSeek tier suffixes (#87593)
* fix(agents): preserve reasoning_content replay across DeepSeek tier suffixes

OpenCode Zen exposes DeepSeek V4 as `deepseek-v4-flash-free`, which keeps the upstream DeepSeek thinking-mode contract that requires `reasoning_content` to be passed back on follow-up requests. The existing replay allowlist only matched the bare ids (`deepseek-v4-flash`, `kimi-k2-thinking`, ...), so the tier-suffixed id missed every candidate and the sanitizer stripped `reasoning_content` from the assistant turn. DeepSeek then rejected the second API call with HTTP 400 and the session deadlocked.

Strip the well-known tier suffixes (`-free`, `-paid`, `-trial`) when generating allowlist candidates so the base model id matches and the reasoning replay survives. Existing matching for prefixed / colon-suffixed routes is unchanged.

Fixes #87575

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

* fix(agents): avoid spread-rebuild when iterating allowlist candidates

oxlint flagged the [...candidates] spread as an unnecessary array copy. Use an explicit baseCount loop bound instead so we still iterate the original entries while pushing tier-stripped variants onto the same array.

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

* test(opencode): add live DeepSeek replay probe

* test(opencode): avoid forced tool choice in live replay

---------

Co-authored-by: Pluviobyte <Pluviobyte@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-05-28 16:25:54 +01:00
clawsweeper[bot]
5216841a9e docs: treat CLI setup flows as API contracts (#87685)
Co-authored-by: ClawSweeper <clawsweeper@users.noreply.github.com>
2026-05-28 16:17:42 +01:00
Peter Steinberger
b601550c97 docs: harden GHSA wording guidance 2026-05-28 16:16:10 +01:00
2265 changed files with 106321 additions and 23340 deletions

View File

@@ -1,6 +1,6 @@
---
name: openclaw-ghsa-maintainer
description: Inspect, patch, validate, publish, or confirm OpenClaw GHSA security advisories and private-fork state.
description: "Inspect, patch, validate, publish, or confirm OpenClaw GHSA security advisories and private-fork state."
---
# OpenClaw GHSA Maintainer
@@ -85,3 +85,4 @@ jq -r .description < /tmp/ghsa.refetch.json | rg '\\\\n'
- Publishing fails with HTTP 422 if required fields are missing or the private fork still has open PRs.
- A payload that looks correct in shell can still be wrong if Markdown was assembled with escaped newline strings.
- Advisory PATCH sequencing matters; separate field updates when GHSA API constraints require it.
- Public hardening/no-publish comments and draft text should avoid raw commit hashes, PR titles/numbers, and fix-mechanism summaries. Prefer patched-version fields or release-only wording; keep SHAs, PRs, and implementation notes in internal evidence.

View File

@@ -1,6 +1,6 @@
---
name: security-triage
description: Triage OpenClaw security advisories, drafts, and GHSA reports with shipped-tag and trust-model proof.
description: "Triage OpenClaw security advisories, drafts, and GHSA reports with shipped-tag and trust-model proof."
---
# Security Triage
@@ -87,11 +87,19 @@ When preparing a maintainer-ready close reply:
- exact reason for close
- exact code refs
- exact shipped tag / release facts
- exact fix commit or canonical duplicate GHSA when applicable
- fix provenance or canonical duplicate GHSA when applicable
- optional hardening note only if worthwhile and functionality-preserving
Keep tone firm, specific, non-defensive.
## Public Wording Hygiene
- Keep raw commit hashes, PR titles/numbers, and fix-mechanism summaries out of public advisory text. Use the patched release/version field only.
- Keep exact commit SHAs, PRs, and implementation notes in internal notes and verification files.
- For hardening/no-publish outcomes, do not add exploit-heavy details, "Fixed by" text, or a "Fix Commit(s)" section. Thank reporters, preserve credit, state the `SECURITY.md` boundary, and say clearly that the GHSA will close without publication.
- For published CVE/GHSA text, prefer `### Patched Versions` with the fixed release. Do not explain how the patch works unless Peter explicitly asks for that public detail.
- Keep GHSA ids out of changelog and release-note wording unless Peter explicitly asks.
## Discussion Mode
When Peter is manually posting GHSA comments, use this flow:

View File

@@ -29,6 +29,11 @@ actions:
- openclaw
runnerVersion: latest
ephemeral: true
blacksmith:
org: openclaw
workflow: .github/workflows/ci-check-testbox.yml
job: check
ref: main
aws:
region: eu-west-1
rootGB: 400

8
.github/CODEOWNERS vendored
View File

@@ -11,8 +11,10 @@
/.github/workflows/codeql.yml @openclaw/openclaw-secops
/.github/workflows/codeql-android-critical-security.yml @openclaw/openclaw-secops
/.github/workflows/codeql-critical-quality.yml @openclaw/openclaw-secops
/.github/workflows/dependency-change-awareness.yml @openclaw/openclaw-secops
/test/scripts/dependency-change-awareness-workflow.test.ts @openclaw/openclaw-secops
/.github/workflows/dependency-guard.yml @openclaw/openclaw-secops
/test/scripts/dependency-guard-workflow.test.ts @openclaw/openclaw-secops
/test/scripts/dependency-guard-script.test.ts @openclaw/openclaw-secops
/scripts/github/dependency-guard.mjs @openclaw/openclaw-secops
/package-lock.json @openclaw/openclaw-secops
/npm-shrinkwrap.json @openclaw/openclaw-secops
/extensions/*/package-lock.json @openclaw/openclaw-secops
@@ -29,7 +31,7 @@
/src/gateway/**/*secret*.ts @openclaw/openclaw-secops
/src/gateway/security-path*.ts @openclaw/openclaw-secops
/src/gateway/resolve-configured-secret-input-string*.ts @openclaw/openclaw-secops
/src/gateway/protocol/**/*secret*.ts @openclaw/openclaw-secops
/packages/gateway-protocol/src/**/*secret*.ts @openclaw/openclaw-secops
/src/gateway/server-methods/secrets*.ts @openclaw/openclaw-secops
/src/agents/*auth*.ts @openclaw/openclaw-secops
/src/agents/**/*auth*.ts @openclaw/openclaw-secops

View File

@@ -14,6 +14,10 @@ self-hosted-runner:
- blacksmith-16vcpu-ubuntu-2404-arm
- blacksmith-6vcpu-macos-latest
- blacksmith-12vcpu-macos-latest
- blacksmith-6vcpu-macos-15
- blacksmith-12vcpu-macos-15
- blacksmith-6vcpu-macos-26
- blacksmith-12vcpu-macos-26
# Ignore patterns for known issues
paths:

View File

@@ -35,17 +35,29 @@ runs:
exit 0
fi
# Check if any changed file is a doc
DOCS=$(echo "$CHANGED" | grep -E '^docs/|\.md$|\.mdx$' || true)
if [ -n "$DOCS" ]; then
docs_changed=false
non_docs=false
while IFS= read -r changed_path; do
case "$changed_path" in
test/fixtures/*)
non_docs=true
;;
docs/* | *.md | *.mdx)
docs_changed=true
;;
*)
non_docs=true
;;
esac
done <<< "$CHANGED"
if [ "$docs_changed" = "true" ]; then
echo "docs_changed=true" >> "$GITHUB_OUTPUT"
else
echo "docs_changed=false" >> "$GITHUB_OUTPUT"
fi
# Check if all changed files are docs or markdown
NON_DOCS=$(echo "$CHANGED" | grep -vE '^docs/|\.md$|\.mdx$' || true)
if [ -z "$NON_DOCS" ]; then
if [ "$non_docs" = "false" ]; then
echo "docs_only=true" >> "$GITHUB_OUTPUT"
echo "Docs-only change detected — skipping heavy jobs"
else

View File

@@ -38,9 +38,15 @@ runs:
exit 0
fi
fetch_base_ref() {
timeout --signal=TERM --kill-after=10s 30s git \
-c protocol.version=2 \
fetch "$@"
}
for deepen_by in 25 100 300; do
echo "Base commit missing; deepening $FETCH_REF by $deepen_by."
if ! git fetch --no-tags --deepen="$deepen_by" origin -- "$FETCH_REF"; then
if ! fetch_base_ref --no-tags --deepen="$deepen_by" origin -- "$FETCH_REF"; then
echo "::warning title=ensure-base-commit fetch failed::Failed to deepen $FETCH_REF by $deepen_by while looking for $BASE_SHA"
fi
if git rev-parse --verify "$BASE_SHA^{commit}" >/dev/null 2>&1; then
@@ -50,7 +56,7 @@ runs:
done
echo "Base commit still missing; fetching full history for $FETCH_REF."
if ! git fetch --no-tags origin -- "$FETCH_REF"; then
if ! fetch_base_ref --no-tags origin -- "$FETCH_REF"; then
echo "::warning title=ensure-base-commit fetch failed::Failed to fetch full history for $FETCH_REF while looking for $BASE_SHA"
fi
if git rev-parse --verify "$BASE_SHA^{commit}" >/dev/null 2>&1; then

View File

@@ -20,9 +20,13 @@ inputs:
required: false
default: "true"
use-actions-cache:
description: Whether to restore and save the pnpm store with actions/cache.
description: Whether to restore the pnpm store with actions/cache.
required: false
default: "true"
save-actions-cache:
description: Whether to save the pnpm store with actions/cache after install when no exact cache restored.
required: false
default: "false"
runs:
using: composite
steps:
@@ -45,6 +49,7 @@ runs:
openclaw_ensure_node "$REQUESTED_NODE_VERSION"
- name: Setup pnpm
id: setup-pnpm
uses: ./.github/actions/setup-pnpm-store-cache
with:
node-version: ${{ inputs.node-version }}
@@ -130,3 +135,10 @@ runs:
ln -sfn "$PNPM_CONFIG_MODULES_DIR" node_modules
ln -sfn . "$PNPM_CONFIG_MODULES_DIR/node_modules"
fi
- name: Save pnpm store cache
if: ${{ inputs.install-deps == 'true' && inputs.use-actions-cache == 'true' && inputs.save-actions-cache == 'true' && runner.os != 'Windows' && steps.setup-pnpm.outputs.store-cache-hit != 'true' }}
uses: actions/cache/save@v5
with:
path: ${{ steps.setup-pnpm.outputs.store-path }}
key: ${{ steps.setup-pnpm.outputs.store-cache-primary-key }}

View File

@@ -14,7 +14,7 @@ inputs:
required: false
default: ""
use-actions-cache:
description: Whether actions/cache should cache the pnpm store.
description: Whether actions/cache should restore the pnpm store.
required: false
default: "true"
outputs:
@@ -24,6 +24,15 @@ outputs:
project-dir:
description: Directory containing the packageManager file used for pnpm resolution.
value: ${{ steps.setup-pnpm.outputs.project-dir }}
store-cache-hit:
description: Whether the pnpm store cache restored an exact key.
value: ${{ steps.pnpm-store-cache.outputs.cache-hit }}
store-cache-primary-key:
description: Exact pnpm store cache key used for restore/save.
value: ${{ steps.pnpm-store-cache.outputs.cache-primary-key }}
store-path:
description: Resolved pnpm store path.
value: ${{ steps.pnpm-store.outputs.path }}
runs:
using: composite
steps:
@@ -81,14 +90,15 @@ runs:
echo "path=$store_path" >> "$GITHUB_OUTPUT"
- name: Restore pnpm store cache
id: pnpm-store-cache
if: ${{ inputs.use-actions-cache == 'true' && runner.os != 'Windows' }}
uses: actions/cache@v5
uses: actions/cache/restore@v5
with:
path: ${{ steps.pnpm-store.outputs.path }}
key: pnpm-store-${{ runner.os }}-${{ inputs.node-version }}-${{ hashFiles(inputs.lockfile-path) }}
key: pnpm-store-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}-${{ hashFiles(inputs.package-manager-file) }}-${{ hashFiles(inputs.lockfile-path) }}
restore-keys: |
pnpm-store-${{ runner.os }}-${{ inputs.node-version }}-
pnpm-store-${{ runner.os }}-
pnpm-store-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}-${{ hashFiles(inputs.package-manager-file) }}-
pnpm-store-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}-
- name: Record pnpm version
id: pnpm-version

View File

@@ -95,7 +95,7 @@ openclaw_find_toolcache_node() {
done
local node_root candidate candidate_version
for node_root in "${roots[@]}"; do
for node_root in ${roots[@]+"${roots[@]}"}; do
while IFS= read -r candidate; do
candidate_version="$("$candidate" -p 'process.versions.node' 2>/dev/null || true)"
if openclaw_node_version_matches "$candidate_version" "$requested_node"; then

View File

@@ -19,7 +19,7 @@ paths:
- src/config/types.channel*.ts
- src/gateway/server-channel*.ts
- src/gateway/server-methods/channels.ts
- src/gateway/protocol/schema/channels.ts
- packages/gateway-protocol/src/schema/channels.ts
- src/infra/channel-*.ts
- src/infra/exec-approval-channel-runtime.ts
- src/infra/outbound/channel-*.ts

View File

@@ -30,7 +30,7 @@ paths:
- src/gateway/**/*auth*.ts
- src/gateway/*secret*.ts
- src/gateway/**/*secret*.ts
- src/gateway/protocol/**/*secret*.ts
- packages/gateway-protocol/src/**/*secret*.ts
- src/gateway/resolve-configured-secret-input-string*.ts
- src/gateway/security-path*.ts
- src/gateway/server-methods/secrets*.ts

View File

@@ -30,7 +30,7 @@ paths:
- src/gateway/**/*auth*.ts
- src/gateway/*secret*.ts
- src/gateway/**/*secret*.ts
- src/gateway/protocol/**/*secret*.ts
- packages/gateway-protocol/src/**/*secret*.ts
- src/gateway/resolve-configured-secret-input-string*.ts
- src/gateway/security-path*.ts
- src/gateway/server-methods/secrets*.ts

View File

@@ -15,7 +15,7 @@ query-filters:
paths:
- src/gateway/method-scopes.ts
- src/gateway/protocol
- packages/gateway-protocol/src
- src/gateway/server-methods
- src/gateway/server-methods.ts
- src/gateway/server-methods-list.ts

View File

@@ -76,6 +76,8 @@ predicate allowedRawSocketClientCall(Expr call) {
or
allowedOwnerScope(call, "src/proxy-capture/proxy-server.ts", "startDebugProxyServer")
or
allowedOwnerScope(call, "extensions/codex-supervisor/src/json-rpc-client.ts", "connectCodexSupervisorUnixSocket")
or
allowedOwnerScope(call, "extensions/irc/src/client.ts", "connectIrcClient")
or
allowedOwnerScope(call, "extensions/qa-lab/src/lab-server-capture.ts", "probeTcpReachability")

14
.github/labeler.yml vendored
View File

@@ -188,7 +188,7 @@
- "ui/**"
- "src/gateway/control-ui.ts"
- "src/gateway/control-ui-shared.ts"
- "src/gateway/protocol/**"
- "packages/gateway-protocol/src/**"
- "src/gateway/server-methods/chat.ts"
- "src/infra/control-ui-assets.ts"
@@ -196,6 +196,7 @@
- changed-files:
- any-glob-to-any-file:
- "src/gateway/**"
- "packages/gateway-protocol/src/**"
- "src/daemon/**"
- "docs/gateway/**"
@@ -398,6 +399,17 @@
- changed-files:
- any-glob-to-any-file:
- "extensions/codex/**"
"extensions: codex-supervisor":
- changed-files:
- any-glob-to-any-file:
- "extensions/codex-supervisor/**"
- "docs/plugins/reference/codex-supervisor.md"
- "docs/specs/claw-supervisor.md"
"extensions: copilot":
- changed-files:
- any-glob-to-any-file:
- "extensions/copilot/**"
- "docs/plugins/copilot.md"
"extensions: kimi-coding":
- changed-files:
- any-glob-to-any-file:

View File

@@ -188,7 +188,10 @@ jobs:
run: |
set -euo pipefail
git fetch --no-tags --depth=50 origin "+refs/heads/main:refs/remotes/origin/main"
timeout --signal=TERM --kill-after=10s 30s git \
-c protocol.version=2 \
fetch --no-tags --prune --no-recurse-submodules --depth=50 origin \
"+refs/heads/main:refs/remotes/origin/main"
node_bin="$(dirname "$(node -p 'process.execPath')")"
sudo ln -sf "$node_bin/node" /usr/local/bin/node

View File

@@ -89,7 +89,10 @@ jobs:
run: |
set -euo pipefail
git fetch --no-tags --depth=50 origin "+refs/heads/main:refs/remotes/origin/main"
timeout --signal=TERM --kill-after=10s 30s git \
-c protocol.version=2 \
fetch --no-tags --prune --no-recurse-submodules --depth=50 origin \
"+refs/heads/main:refs/remotes/origin/main"
node_bin="$(dirname "$(node -p 'process.execPath')")"
sudo ln -sf "$node_bin/node" /usr/local/bin/node

View File

@@ -86,12 +86,38 @@ jobs:
git init "$GITHUB_WORKSPACE"
git -C "$GITHUB_WORKSPACE" config gc.auto 0
git -C "$GITHUB_WORKSPACE" remote add origin "https://github.com/${CHECKOUT_REPO}.git"
if ! git -C "$GITHUB_WORKSPACE" fetch --no-tags --depth=1 origin "+${CHECKOUT_REF}:refs/remotes/origin/checkout"; then
fetch_checkout_ref() {
local ref="$1"
local fetch_status
for attempt in 1 2 3; do
timeout --signal=TERM --kill-after=10s 30s git -C "$GITHUB_WORKSPACE" \
-c protocol.version=2 \
fetch --no-tags --prune --no-recurse-submodules --depth=1 origin \
"+${ref}:refs/remotes/origin/checkout" && return 0
fetch_status="$?"
if [ "$fetch_status" != "124" ] && [ "$fetch_status" != "137" ]; then
return "$fetch_status"
fi
if [ "$attempt" = "3" ]; then
return "$fetch_status"
fi
echo "::warning::checkout fetch for '$ref' timed out on attempt $attempt; retrying"
sleep 5
done
}
if fetch_checkout_ref "$CHECKOUT_REF"; then
:
else
fetch_status="$?"
if [ "$fetch_status" = "124" ] || [ "$fetch_status" = "137" ]; then
echo "::error::checkout fetch for '$CHECKOUT_REF' timed out"
exit "$fetch_status"
fi
if [ "$GITHUB_EVENT_NAME" != "workflow_dispatch" ] || [ "$CHECKOUT_REF" = "$CHECKOUT_FALLBACK_REF" ]; then
exit 1
exit "$fetch_status"
fi
echo "::warning::workflow_dispatch target_ref '$CHECKOUT_REF' is unavailable; falling back to head SHA '$CHECKOUT_FALLBACK_REF'"
git -C "$GITHUB_WORKSPACE" fetch --no-tags --depth=1 origin "+${CHECKOUT_FALLBACK_REF}:refs/remotes/origin/checkout"
fetch_checkout_ref "$CHECKOUT_FALLBACK_REF"
fi
git -C "$GITHUB_WORKSPACE" checkout --detach refs/remotes/origin/checkout
@@ -321,12 +347,38 @@ jobs:
git init "$GITHUB_WORKSPACE"
git -C "$GITHUB_WORKSPACE" config gc.auto 0
git -C "$GITHUB_WORKSPACE" remote add origin "https://github.com/${CHECKOUT_REPO}.git"
if ! git -C "$GITHUB_WORKSPACE" fetch --no-tags --depth=1 origin "+${CHECKOUT_REF}:refs/remotes/origin/checkout"; then
fetch_checkout_ref() {
local ref="$1"
local fetch_status
for attempt in 1 2 3; do
timeout --signal=TERM --kill-after=10s 30s git -C "$GITHUB_WORKSPACE" \
-c protocol.version=2 \
fetch --no-tags --prune --no-recurse-submodules --depth=1 origin \
"+${ref}:refs/remotes/origin/checkout" && return 0
fetch_status="$?"
if [ "$fetch_status" != "124" ] && [ "$fetch_status" != "137" ]; then
return "$fetch_status"
fi
if [ "$attempt" = "3" ]; then
return "$fetch_status"
fi
echo "::warning::checkout fetch for '$ref' timed out on attempt $attempt; retrying"
sleep 5
done
}
if fetch_checkout_ref "$CHECKOUT_REF"; then
:
else
fetch_status="$?"
if [ "$fetch_status" = "124" ] || [ "$fetch_status" = "137" ]; then
echo "::error::checkout fetch for '$CHECKOUT_REF' timed out"
exit "$fetch_status"
fi
if [ "$GITHUB_EVENT_NAME" != "workflow_dispatch" ] || [ "$CHECKOUT_REF" = "$CHECKOUT_FALLBACK_REF" ]; then
exit 1
exit "$fetch_status"
fi
echo "::warning::workflow_dispatch target_ref '$CHECKOUT_REF' is unavailable; falling back to head SHA '$CHECKOUT_FALLBACK_REF'"
git -C "$GITHUB_WORKSPACE" fetch --no-tags --depth=1 origin "+${CHECKOUT_FALLBACK_REF}:refs/remotes/origin/checkout"
fetch_checkout_ref "$CHECKOUT_FALLBACK_REF"
fi
git -C "$GITHUB_WORKSPACE" checkout --detach refs/remotes/origin/checkout
@@ -414,13 +466,73 @@ jobs:
- name: Audit production dependencies
run: node scripts/pre-commit/pnpm-audit-prod.mjs --audit-level=high
# Warm the lockfile- and pnpm-pinned store once before Linux Node shards fan out.
# On a cold key this job owns the save, so later shards restore the exact key.
pnpm-store-warmup:
permissions:
contents: read
needs: [preflight]
if: needs.preflight.outputs.run_node == 'true' || needs.preflight.outputs.run_check_docs == 'true'
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-24.04') }}
timeout-minutes: 20
steps:
- name: Checkout
shell: bash
env:
CHECKOUT_REPO: ${{ github.repository }}
CHECKOUT_SHA: ${{ needs.preflight.outputs.checkout_revision }}
run: |
set -euo pipefail
workdir="$GITHUB_WORKSPACE"
reset_checkout_dir() {
mkdir -p "$workdir"
find "$workdir" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
}
checkout_attempt() {
local attempt="$1"
reset_checkout_dir
git init "$workdir" >/dev/null
git config --global --add safe.directory "$workdir"
git -C "$workdir" remote add origin "https://github.com/${CHECKOUT_REPO}.git"
git -C "$workdir" config gc.auto 0
timeout --signal=TERM --kill-after=10s 30s git -C "$workdir" \
-c protocol.version=2 \
fetch --no-tags --prune --no-recurse-submodules --depth=1 origin \
"+${CHECKOUT_SHA}:refs/remotes/origin/ci-target" || return 1
git -C "$workdir" checkout --force --detach "$CHECKOUT_SHA" || return 1
test -f "$workdir/.github/actions/setup-node-env/action.yml" || return 1
echo "checkout attempt ${attempt}/5 succeeded"
}
for attempt in 1 2 3 4 5; do
if checkout_attempt "$attempt"; then
exit 0
fi
echo "checkout attempt ${attempt}/5 failed"
sleep $((attempt * 5))
done
echo "checkout failed after 5 attempts" >&2
exit 1
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
save-actions-cache: "true"
# Build dist once for Node-relevant changes and share it with downstream jobs.
# Keep this overlapping with the fast correctness lanes so green PRs get heavy
# test/build feedback sooner instead of waiting behind a full `check` pass.
build-artifacts:
permissions:
contents: read
needs: [preflight]
needs: [preflight, pnpm-store-warmup]
if: needs.preflight.outputs.run_build_artifacts == 'true'
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && 'blacksmith-16vcpu-ubuntu-2404' || 'ubuntu-24.04') }}
timeout-minutes: 20
@@ -652,7 +764,7 @@ jobs:
permissions:
contents: read
name: ${{ matrix.check_name }}
needs: [preflight]
needs: [preflight, pnpm-store-warmup]
if: needs.preflight.outputs.run_checks_fast_core == 'true'
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-24.04') }}
timeout-minutes: 60
@@ -741,7 +853,7 @@ jobs:
permissions:
contents: read
name: ${{ matrix.checkName }}
needs: [preflight]
needs: [preflight, pnpm-store-warmup]
if: needs.preflight.outputs.run_plugin_contracts_shards == 'true'
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-24.04') }}
timeout-minutes: 60
@@ -821,7 +933,7 @@ jobs:
permissions:
contents: read
name: ${{ matrix.checkName }}
needs: [preflight]
needs: [preflight, pnpm-store-warmup]
if: needs.preflight.outputs.run_checks_fast == 'true'
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-24.04') }}
timeout-minutes: 60
@@ -973,9 +1085,9 @@ jobs:
permissions:
contents: read
name: ${{ matrix.check_name }}
needs: [preflight]
needs: [preflight, pnpm-store-warmup]
if: needs.preflight.outputs.run_checks_node_core_nondist == 'true'
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && (matrix.runner || 'ubuntu-24.04') || 'ubuntu-24.04') }}
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && (matrix.runner || 'blacksmith-8vcpu-ubuntu-2404') || 'ubuntu-24.04') }}
timeout-minutes: 60
strategy:
fail-fast: false
@@ -1079,9 +1191,9 @@ jobs:
permissions:
contents: read
name: ${{ matrix.check_name }}
needs: [preflight]
if: ${{ !cancelled() && always() && needs.preflight.outputs.run_check == 'true' }}
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && matrix.runner || 'ubuntu-24.04') }}
needs: [preflight, pnpm-store-warmup]
if: ${{ !cancelled() && always() && needs.preflight.outputs.run_check == 'true' && needs.pnpm-store-warmup.result == 'success' }}
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && (matrix.runner || 'blacksmith-4vcpu-ubuntu-2404') || 'ubuntu-24.04') }}
timeout-minutes: 20
strategy:
fail-fast: false
@@ -1172,7 +1284,7 @@ jobs:
pnpm lint:auth:pairing-account-scope
pnpm check:import-cycles
# build-artifacts already runs the tsdown/runtime build for the same Node-relevant changes.
pnpm build:plugin-sdk:strict-smoke
NODE_OPTIONS=--max-old-space-size=8192 pnpm build:plugin-sdk:strict-smoke
;;
prod-types)
pnpm tsgo:prod
@@ -1210,8 +1322,8 @@ jobs:
permissions:
contents: read
name: ${{ matrix.check_name }}
needs: [preflight]
if: ${{ !cancelled() && always() && needs.preflight.outputs.run_check_additional == 'true' }}
needs: [preflight, pnpm-store-warmup]
if: ${{ !cancelled() && always() && needs.preflight.outputs.run_check_additional == 'true' && needs.pnpm-store-warmup.result == 'success' }}
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && 'blacksmith-8vcpu-ubuntu-2404' || 'ubuntu-24.04') }}
timeout-minutes: 20
strategy:
@@ -1377,7 +1489,7 @@ jobs:
check-docs:
permissions:
contents: read
needs: [preflight]
needs: [preflight, pnpm-store-warmup]
if: needs.preflight.outputs.run_check_docs == 'true'
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-24.04') }}
timeout-minutes: 20
@@ -1434,11 +1546,44 @@ jobs:
- name: Checkout ClawHub docs source
run: |
set -euo pipefail
git init clawhub-source
git -C clawhub-source config gc.auto 0
git -C clawhub-source remote add origin "https://github.com/openclaw/clawhub.git"
git -C clawhub-source fetch --no-tags --depth=1 origin "+HEAD:refs/remotes/origin/checkout"
git -C clawhub-source checkout --detach refs/remotes/origin/checkout
workdir="$GITHUB_WORKSPACE/clawhub-source"
started_at="$(date +%s)"
reset_checkout_dir() {
mkdir -p "$workdir"
find "$workdir" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
}
checkout_attempt() {
local attempt="$1"
reset_checkout_dir
git init "$workdir" >/dev/null
git -C "$workdir" config gc.auto 0
git -C "$workdir" remote add origin "https://github.com/openclaw/clawhub.git"
timeout --signal=TERM --kill-after=10s 30s git -C "$workdir" \
-c protocol.version=2 \
fetch --no-tags --prune --no-recurse-submodules --depth=1 origin \
"+refs/heads/main:refs/remotes/origin/checkout" || return 1
git -C "$workdir" checkout --force --detach refs/remotes/origin/checkout || return 1
echo "ClawHub checkout attempt ${attempt}/5 succeeded"
}
for attempt in 1 2 3 4 5; do
if checkout_attempt "$attempt"; then
elapsed="$(( $(date +%s) - started_at ))"
echo "ClawHub checkout completed in ${elapsed}s"
exit 0
fi
echo "ClawHub checkout attempt ${attempt}/5 failed"
sleep $((attempt * 5))
done
echo "ClawHub checkout failed after 5 attempts" >&2
exit 1
- name: Check docs
env:
@@ -1462,7 +1607,25 @@ jobs:
git init "$GITHUB_WORKSPACE"
git -C "$GITHUB_WORKSPACE" config gc.auto 0
git -C "$GITHUB_WORKSPACE" remote add origin "https://github.com/${CHECKOUT_REPO}.git"
git -C "$GITHUB_WORKSPACE" fetch --no-tags --depth=1 origin "+${CHECKOUT_SHA}:refs/remotes/origin/checkout"
fetch_checkout_ref() {
local fetch_status
for attempt in 1 2 3; do
timeout --signal=TERM --kill-after=10s 30s git -C "$GITHUB_WORKSPACE" \
-c protocol.version=2 \
fetch --no-tags --prune --no-recurse-submodules --depth=1 origin \
"+${CHECKOUT_SHA}:refs/remotes/origin/checkout" && return 0
fetch_status="$?"
if [ "$fetch_status" != "124" ] && [ "$fetch_status" != "137" ]; then
return "$fetch_status"
fi
if [ "$attempt" = "3" ]; then
return "$fetch_status"
fi
echo "::warning::checkout fetch for '$CHECKOUT_SHA' timed out on attempt $attempt; retrying"
sleep 5
done
}
fetch_checkout_ref
git -C "$GITHUB_WORKSPACE" checkout --detach refs/remotes/origin/checkout
- name: Setup Python
@@ -1510,7 +1673,27 @@ jobs:
git init "$GITHUB_WORKSPACE"
git -C "$GITHUB_WORKSPACE" config gc.auto 0
git -C "$GITHUB_WORKSPACE" remote add origin "https://github.com/${CHECKOUT_REPO}.git"
git -C "$GITHUB_WORKSPACE" fetch --no-tags --depth=1 origin "+${CHECKOUT_SHA}:refs/remotes/origin/checkout"
fetch_checkout_ref() {
git -C "$GITHUB_WORKSPACE" \
-c protocol.version=2 \
fetch --no-tags --prune --no-recurse-submodules --depth=1 origin \
"+${CHECKOUT_SHA}:refs/remotes/origin/checkout" &
local fetch_pid="$!"
local elapsed=0
while kill -0 "$fetch_pid" 2>/dev/null; do
if [ "$elapsed" -ge 30 ]; then
kill -TERM "$fetch_pid" 2>/dev/null || true
sleep 10
kill -KILL "$fetch_pid" 2>/dev/null || true
wait "$fetch_pid" || true
return 124
fi
sleep 1
elapsed=$((elapsed + 1))
done
wait "$fetch_pid"
}
fetch_checkout_ref
git -C "$GITHUB_WORKSPACE" checkout --detach refs/remotes/origin/checkout
- name: Try to exclude workspace from Windows Defender (best-effort)
@@ -1595,7 +1778,7 @@ jobs:
name: ${{ matrix.check_name }}
needs: [preflight]
if: ${{ !cancelled() && always() && needs.preflight.outputs.run_macos_node == 'true' }}
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'macos-latest' || (github.repository == 'openclaw/openclaw' && 'blacksmith-6vcpu-macos-latest' || 'macos-latest') }}
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'macos-15' || (github.repository == 'openclaw/openclaw' && 'blacksmith-6vcpu-macos-15' || 'macos-15') }}
timeout-minutes: 20
strategy:
fail-fast: false
@@ -1610,7 +1793,27 @@ jobs:
git init "$GITHUB_WORKSPACE"
git -C "$GITHUB_WORKSPACE" config gc.auto 0
git -C "$GITHUB_WORKSPACE" remote add origin "https://github.com/${CHECKOUT_REPO}.git"
git -C "$GITHUB_WORKSPACE" fetch --no-tags --depth=1 origin "+${CHECKOUT_SHA}:refs/remotes/origin/checkout"
fetch_checkout_ref() {
git -C "$GITHUB_WORKSPACE" \
-c protocol.version=2 \
fetch --no-tags --prune --no-recurse-submodules --depth=1 origin \
"+${CHECKOUT_SHA}:refs/remotes/origin/checkout" &
local fetch_pid="$!"
local elapsed=0
while kill -0 "$fetch_pid" 2>/dev/null; do
if [ "$elapsed" -ge 30 ]; then
kill -TERM "$fetch_pid" 2>/dev/null || true
sleep 10
kill -KILL "$fetch_pid" 2>/dev/null || true
wait "$fetch_pid" || true
return 124
fi
sleep 1
elapsed=$((elapsed + 1))
done
wait "$fetch_pid"
}
fetch_checkout_ref
git -C "$GITHUB_WORKSPACE" checkout --detach refs/remotes/origin/checkout
- name: Setup Node environment
@@ -1644,7 +1847,7 @@ jobs:
name: "macos-swift"
needs: [preflight]
if: needs.preflight.outputs.run_macos_swift == 'true'
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'macos-26' || (github.repository == 'openclaw/openclaw' && 'blacksmith-12vcpu-macos-latest' || 'macos-26') }}
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'macos-26' || (github.repository == 'openclaw/openclaw' && 'blacksmith-12vcpu-macos-26' || 'macos-26') }}
timeout-minutes: 20
steps:
- name: Checkout
@@ -1656,7 +1859,27 @@ jobs:
git init "$GITHUB_WORKSPACE"
git -C "$GITHUB_WORKSPACE" config gc.auto 0
git -C "$GITHUB_WORKSPACE" remote add origin "https://github.com/${CHECKOUT_REPO}.git"
git -C "$GITHUB_WORKSPACE" fetch --no-tags --depth=1 origin "+${CHECKOUT_SHA}:refs/remotes/origin/checkout"
fetch_checkout_ref() {
git -C "$GITHUB_WORKSPACE" \
-c protocol.version=2 \
fetch --no-tags --prune --no-recurse-submodules --depth=1 origin \
"+${CHECKOUT_SHA}:refs/remotes/origin/checkout" &
local fetch_pid="$!"
local elapsed=0
while kill -0 "$fetch_pid" 2>/dev/null; do
if [ "$elapsed" -ge 30 ]; then
kill -TERM "$fetch_pid" 2>/dev/null || true
sleep 10
kill -KILL "$fetch_pid" 2>/dev/null || true
wait "$fetch_pid" || true
return 124
fi
sleep 1
elapsed=$((elapsed + 1))
done
wait "$fetch_pid"
}
fetch_checkout_ref
git -C "$GITHUB_WORKSPACE" checkout --detach refs/remotes/origin/checkout
- name: Install XcodeGen / SwiftLint / SwiftFormat
@@ -1867,3 +2090,53 @@ jobs:
exit 1
;;
esac
ci-timings-summary:
permissions:
actions: read
contents: read
name: ci-timings-summary
needs:
- preflight
- security-fast
- pnpm-store-warmup
- build-artifacts
- checks-fast-core
- checks-fast-plugin-contracts-shard
- checks-fast-channel-contracts-shard
- checks-node-compat
- checks-node-core-test-nondist-shard
- check-shard
- check-additional-shard
- check-docs
- skills-python
- checks-windows
- macos-node
- macos-swift
- android
if: ${{ !cancelled() && always() && (github.event_name != 'pull_request' || !github.event.pull_request.draft) }}
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Checkout timing summary helper
uses: actions/checkout@v6
with:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || needs.preflight.outputs.checkout_revision || github.sha }}
fetch-depth: 1
fetch-tags: false
persist-credentials: false
submodules: false
- name: Write CI timing summary
env:
GH_TOKEN: ${{ github.token }}
run: |
node scripts/ci-run-timings.mjs "$GITHUB_RUN_ID" --limit 25 > ci-timings-summary.txt
cat ci-timings-summary.txt >> "$GITHUB_STEP_SUMMARY"
- name: Upload CI timing summary
uses: actions/upload-artifact@v7
with:
name: ci-timings-summary
path: ci-timings-summary.txt
retention-days: 14

View File

@@ -24,7 +24,14 @@ concurrency:
jobs:
dispatch:
runs-on: ubuntu-latest
if: ${{ github.event_name == 'issue_comment' || !(endsWith(github.actor, '[bot]') && (github.event.action == 'labeled' || github.event.action == 'unlabeled')) }}
if: >-
${{
github.event_name == 'issue_comment' ||
!(
endsWith(github.actor, '[bot]') &&
(github.event.action == 'labeled' || github.event.action == 'unlabeled')
)
}}
env:
HAS_CLAWSWEEPER_APP_PRIVATE_KEY: ${{ secrets.CLAWSWEEPER_APP_PRIVATE_KEY != '' }}
CLAWSWEEPER_APP_CLIENT_ID: Iv23liOECG0slfuhz093

View File

@@ -106,13 +106,13 @@ on:
- "src/gateway/**/*auth*.ts"
- "src/gateway/*secret*.ts"
- "src/gateway/**/*secret*.ts"
- "src/gateway/protocol/**/*secret*.ts"
- "packages/gateway-protocol/src/**/*secret*.ts"
- "src/gateway/resolve-configured-secret-input-string*.ts"
- "src/gateway/security-path*.ts"
- "src/gateway/server-methods/secrets*.ts"
- "src/gateway/server-startup-memory.ts"
- "src/gateway/method-scopes.ts"
- "src/gateway/protocol/**"
- "packages/gateway-protocol/src/**"
- "src/gateway/server-methods/**"
- "src/gateway/server-methods.ts"
- "src/gateway/server-methods-list.ts"
@@ -244,14 +244,14 @@ jobs:
src/config/*)
config=true
;;
src/gateway/protocol/*secret*.ts|src/gateway/server-methods/secrets*.ts)
packages/gateway-protocol/src/*secret*.ts|packages/gateway-protocol/src/**/*secret*.ts|src/gateway/server-methods/secrets*.ts)
core_auth_secrets=true
gateway=true
;;
src/agents/*auth*.ts|src/agents/auth-health*.ts|src/agents/auth-profiles|src/agents/auth-profiles/*|src/agents/bash-tools.exec-host-shared.ts|src/agents/sandbox|src/agents/sandbox.ts|src/agents/sandbox-*.ts|src/agents/sandbox/*|src/cron/service/jobs.ts|src/cron/stagger.ts|src/gateway/*auth*.ts|src/gateway/*secret*.ts|src/gateway/resolve-configured-secret-input-string*.ts|src/gateway/security-path*.ts|src/infra/secret-file*.ts|src/secrets/*|src/security/*)
core_auth_secrets=true
;;
src/gateway/method-scopes.ts|src/gateway/protocol/*|src/gateway/server-methods/*|src/gateway/server-methods.ts|src/gateway/server-methods-list.ts)
packages/gateway-protocol/src/*|packages/gateway-protocol/src/**/*|src/gateway/method-scopes.ts|src/gateway/server-methods/*|src/gateway/server-methods.ts|src/gateway/server-methods-list.ts)
gateway=true
;;
packages/memory-host-sdk/*|src/commands/doctor-cron-dreaming-payload-migration.ts|src/commands/doctor-memory-search.ts|src/gateway/server-startup-memory.ts|src/memory/*|src/memory-host-sdk/*)

View File

@@ -20,7 +20,7 @@ permissions:
jobs:
macos:
name: Critical Security (macOS)
runs-on: blacksmith-6vcpu-macos-latest
runs-on: blacksmith-6vcpu-macos-15
timeout-minutes: 45
steps:
- name: Checkout

View File

@@ -85,10 +85,21 @@ jobs:
config_file: ./.github/codeql/codeql-actions-critical-security.yml
steps:
- name: Checkout
if: ${{ matrix.category != 'actions' }}
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
submodules: false
- name: Checkout Actions security sources
if: ${{ matrix.category == 'actions' }}
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
submodules: false
sparse-checkout: |
.github/actions
.github/workflows
.github/codeql
- name: Initialize CodeQL
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
with:

View File

@@ -138,7 +138,7 @@ jobs:
OPENAI_API_KEY: ${{ secrets.OPENCLAW_DOCS_I18N_OPENAI_API_KEY || secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
OPENCLAW_CONTROL_UI_I18N_PROVIDER: ${{ secrets.ANTHROPIC_API_KEY != '' && 'anthropic' || 'openai' }}
OPENCLAW_CONTROL_UI_I18N_MODEL: ${{ secrets.ANTHROPIC_API_KEY != '' && 'claude-opus-4-7' || vars.OPENCLAW_CI_OPENAI_MODEL_BARE }}
OPENCLAW_CONTROL_UI_I18N_MODEL: ${{ secrets.ANTHROPIC_API_KEY != '' && 'claude-opus-4-8' || vars.OPENCLAW_CI_OPENAI_MODEL_BARE }}
OPENCLAW_CONTROL_UI_I18N_THINKING: low
OPENCLAW_CONTROL_UI_I18N_AUTH_OPTIONAL: "1"
LOCALE: ${{ matrix.locale }}

View File

@@ -137,7 +137,10 @@ jobs:
set -euo pipefail
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
git fetch --no-tags --depth=50 origin "+refs/heads/main:refs/remotes/origin/main"
timeout --signal=TERM --kill-after=10s 30s git \
-c protocol.version=2 \
fetch --no-tags --prune --no-recurse-submodules --depth=50 origin \
"+refs/heads/main:refs/remotes/origin/main"
fi
- name: Prepare Crabbox shell
@@ -318,7 +321,24 @@ jobs:
$ErrorActionPreference = "Stop"
if (git rev-parse --is-inside-work-tree 2>$null) {
git fetch --no-tags --depth=50 origin "+refs/heads/main:refs/remotes/origin/main"
$fetch = Start-Process git -NoNewWindow -PassThru -ArgumentList @(
"-c",
"protocol.version=2",
"fetch",
"--no-tags",
"--prune",
"--no-recurse-submodules",
"--depth=50",
"origin",
"+refs/heads/main:refs/remotes/origin/main"
)
if (-not $fetch.WaitForExit(30000)) {
Stop-Process -Id $fetch.Id -Force -ErrorAction SilentlyContinue
throw "git fetch timed out after 30 seconds"
}
if ($fetch.ExitCode -ne 0) {
throw "git fetch failed with exit code $($fetch.ExitCode)"
}
}
- name: Setup pnpm and dependencies
@@ -513,7 +533,10 @@ jobs:
set -euo pipefail
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
git fetch --no-tags --depth=50 origin "+refs/heads/main:refs/remotes/origin/main"
timeout --signal=TERM --kill-after=10s 30s git \
-c protocol.version=2 \
fetch --no-tags --prune --no-recurse-submodules --depth=50 origin \
"+refs/heads/main:refs/remotes/origin/main"
fi
node_bin="$(dirname "$(node -p 'process.execPath')")"

View File

@@ -1,176 +0,0 @@
name: Dependency Change Awareness
on:
pull_request_target: # zizmor: ignore[dangerous-triggers] metadata-only workflow; no checkout or untrusted code execution
types: [opened, reopened, synchronize, ready_for_review]
permissions:
pull-requests: write
issues: write
concurrency:
group: dependency-change-awareness-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
dependency-change-awareness:
if: ${{ !github.event.pull_request.draft }}
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Label and comment on dependency changes
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
const marker = "<!-- openclaw:dependency-change-awareness -->";
const labelName = "dependencies-changed";
const maxListedFiles = 25;
const pullRequest = context.payload.pull_request;
if (!pullRequest) {
core.info("No pull_request payload found; skipping.");
return;
}
const isDependencyFile = (filename) =>
filename === "package.json" ||
filename === "package-lock.json" ||
filename === "npm-shrinkwrap.json" ||
filename === "pnpm-lock.yaml" ||
filename === "pnpm-workspace.yaml" ||
filename === "ui/package.json" ||
filename.startsWith("patches/") ||
/^packages\/[^/]+\/package\.json$/u.test(filename) ||
/^extensions\/[^/]+\/package-lock\.json$/u.test(filename) ||
/^extensions\/[^/]+\/npm-shrinkwrap\.json$/u.test(filename) ||
/^extensions\/[^/]+\/package\.json$/u.test(filename);
const sanitizeDisplayValue = (value) =>
String(value)
.replace(/[\u0000-\u001f\u007f]/gu, "?")
.slice(0, 240);
const markdownCode = (value) =>
`\`${sanitizeDisplayValue(value).replaceAll("`", "\\`")}\``;
const ignoreUnavailableWritePermission = (action) => (error) => {
if (error?.status === 403) {
core.warning(
`Skipping dependency change ${action}; token does not have issue write permission.`,
);
return;
}
if (error?.status === 404 || error?.status === 422) {
core.warning(`Dependency change ${action} is unavailable.`);
return;
}
throw error;
};
const files = await github.paginate(github.rest.pulls.listFiles, {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pullRequest.number,
per_page: 100,
});
const dependencyFiles = files
.map((file) => file.filename)
.filter((filename) => typeof filename === "string" && isDependencyFile(filename))
.sort((left, right) => left.localeCompare(right));
const comments = await github.paginate(github.rest.issues.listComments, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullRequest.number,
per_page: 100,
});
const existingComment = comments.find(
(comment) =>
comment.user?.login === "github-actions[bot]" && comment.body?.includes(marker),
);
const labels = await github.paginate(github.rest.issues.listLabelsOnIssue, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullRequest.number,
per_page: 100,
});
const hasLabel = labels.some((label) => label.name === labelName);
if (dependencyFiles.length === 0) {
if (hasLabel) {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullRequest.number,
name: labelName,
}).catch(ignoreUnavailableWritePermission("label removal"));
}
if (existingComment) {
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existingComment.id,
}).catch(ignoreUnavailableWritePermission("comment deletion"));
}
await core.summary
.addHeading("Dependency Change Awareness")
.addRaw("No dependency-related file changes detected.")
.write();
core.info("No dependency-related file changes detected.");
return;
}
if (!hasLabel) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullRequest.number,
labels: [labelName],
}).catch(ignoreUnavailableWritePermission(`label "${labelName}" update`));
}
const listedFiles = dependencyFiles.slice(0, maxListedFiles);
const omittedCount = dependencyFiles.length - listedFiles.length;
const fileLines = listedFiles.map((filename) => `- ${markdownCode(filename)}`);
if (omittedCount > 0) {
fileLines.push(`- ${omittedCount} additional dependency-related files not shown`);
}
const body = [
marker,
"",
"### Dependency Changes Detected",
"",
"This PR changes dependency-related files. Maintainers should confirm these changes are intentional.",
"",
"Changed files:",
...fileLines,
"",
"Maintainer follow-up:",
"- Review whether the dependency changes are intentional.",
"- Inspect resolved package deltas when lockfile, shrinkwrap, or workspace dependency policy changes are present.",
"- Treat `package-lock.json` and `npm-shrinkwrap.json` diffs as security-review surfaces.",
"- Run `pnpm deps:changes:report -- --base-ref origin/main --markdown /tmp/dependency-changes.md --json /tmp/dependency-changes.json` locally for detailed release-style evidence.",
].join("\n");
if (existingComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existingComment.id,
body,
}).catch(ignoreUnavailableWritePermission("comment update"));
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullRequest.number,
body,
}).catch(ignoreUnavailableWritePermission("comment creation"));
}
await core.summary
.addHeading("Dependency Change Awareness")
.addRaw(`Detected ${dependencyFiles.length} dependency-related file change(s).`)
.addList(dependencyFiles.map((filename) => markdownCode(filename)))
.write();
core.notice(`Detected ${dependencyFiles.length} dependency-related file change(s).`);

33
.github/workflows/dependency-guard.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Dependency Guard
on:
pull_request_target: # zizmor: ignore[dangerous-triggers] checks trusted base script only; never checks out PR head
types: [opened, reopened, synchronize, ready_for_review]
permissions:
contents: read
pull-requests: write
issues: write
concurrency:
group: dependency-guard-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
dependency-guard:
if: ${{ !github.event.pull_request.draft }}
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Check out trusted base workflow scripts
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: ${{ github.event.pull_request.base.sha }}
persist-credentials: false
- name: Label, comment, and guard dependency changes
env:
GITHUB_TOKEN: ${{ github.token }}
OPENCLAW_SECURITY_APPROVERS: vincentkoc,steipete,joshavant
OPENCLAW_SECURITY_TEAM_SLUG: openclaw-secops
run: node scripts/github/dependency-guard.mjs

View File

@@ -143,7 +143,7 @@ jobs:
for (const [dep, rel] of Object.entries(workspace.patchedDependencies ?? {})) {
const absolute = path.join(\"/app\", rel);
if (!fs.existsSync(absolute)) {
throw new Error(`missing patch for ${dep}: ${rel}`);
throw new Error(\"missing patch for \" + dep + \": \" + rel);
}
}
"
@@ -337,7 +337,7 @@ jobs:
for (const [dep, rel] of Object.entries(workspace.patchedDependencies ?? {})) {
const absolute = path.join(\"/app\", rel);
if (!fs.existsSync(absolute)) {
throw new Error(`missing patch for ${dep}: ${rel}`);
throw new Error(\"missing patch for \" + dep + \": \" + rel);
}
}
"

View File

@@ -377,6 +377,7 @@ jobs:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENCLAW_QA_CONVEX_SITE_URL: ${{ secrets.OPENCLAW_QA_CONVEX_SITE_URL }}
OPENCLAW_QA_CONVEX_SECRET_CI: ${{ secrets.OPENCLAW_QA_CONVEX_SECRET_CI }}
OPENCLAW_QA_CREDENTIAL_ACQUIRE_TIMEOUT_MS: "1800000"
OPENCLAW_QA_REDACT_PUBLIC_METADATA: "1"
OPENCLAW_QA_TELEGRAM_CAPTURE_CONTENT: "1"
CRABBOX_COORDINATOR: ${{ secrets.CRABBOX_COORDINATOR }}

View File

@@ -218,6 +218,7 @@ jobs:
OPENCLAW_NPM_TELEGRAM_CREDENTIAL_ROLE: ci
OPENCLAW_QA_CONVEX_SITE_URL: ${{ secrets.OPENCLAW_QA_CONVEX_SITE_URL }}
OPENCLAW_QA_CONVEX_SECRET_CI: ${{ secrets.OPENCLAW_QA_CONVEX_SECRET_CI }}
OPENCLAW_QA_CREDENTIAL_ACQUIRE_TIMEOUT_MS: "1800000"
OPENCLAW_QA_REDACT_PUBLIC_METADATA: "1"
OPENCLAW_QA_TELEGRAM_CAPTURE_CONTENT: "1"
INPUT_SCENARIO: ${{ inputs.scenario }}

View File

@@ -451,7 +451,7 @@ jobs:
OUTPUT_DIR: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare/baseline
run: |
mkdir -p "${OUTPUT_DIR}"
npm pack --ignore-scripts --json "${BASELINE_SPEC}" --pack-destination "${OUTPUT_DIR}" > "${OUTPUT_DIR}/pack.json"
timeout --preserve-status 300s npm pack --ignore-scripts --json "${BASELINE_SPEC}" --pack-destination "${OUTPUT_DIR}" > "${OUTPUT_DIR}/pack.json"
- name: Capture candidate metadata
id: candidate_metadata

View File

@@ -480,6 +480,35 @@ jobs:
fi
exit 1
plan_release_workflow_matrices:
needs: validate_selected_ref
runs-on: ubuntu-24.04
outputs:
docker_e2e_count: ${{ steps.plan.outputs.docker_e2e_count }}
docker_e2e_matrix: ${{ steps.plan.outputs.docker_e2e_matrix }}
docker_e2e_omitted_json: ${{ steps.plan.outputs.docker_e2e_omitted_json }}
live_models_count: ${{ steps.plan.outputs.live_models_count }}
live_models_matrix: ${{ steps.plan.outputs.live_models_matrix }}
live_models_omitted_json: ${{ steps.plan.outputs.live_models_omitted_json }}
steps:
- name: Checkout trusted release harness
uses: actions/checkout@v6
with:
persist-credentials: false
ref: ${{ github.sha }}
fetch-depth: 1
- name: Plan release workflow matrices
id: plan
env:
DOCKER_LANES: ${{ inputs.docker_lanes }}
INCLUDE_LIVE_SUITES: ${{ inputs.include_live_suites }}
INCLUDE_RELEASE_PATH_SUITES: ${{ inputs.include_release_path_suites }}
LIVE_MODEL_PROVIDERS: ${{ inputs.live_model_providers }}
LIVE_SUITE_FILTER: ${{ inputs.live_suite_filter }}
RELEASE_TEST_PROFILE: ${{ inputs.release_test_profile }}
run: node scripts/plan-release-workflow-matrix.mjs >> "$GITHUB_OUTPUT"
validate_release_live_cache:
needs: validate_selected_ref
if: inputs.include_live_suites && !inputs.live_models_only && (inputs.live_suite_filter == '' || inputs.live_suite_filter == 'live-cache')
@@ -636,72 +665,15 @@ jobs:
run: ${{ matrix.command }}
validate_docker_e2e:
needs: [validate_selected_ref, prepare_docker_e2e_image]
if: inputs.include_release_path_suites && inputs.docker_lanes == ''
needs: [validate_selected_ref, prepare_docker_e2e_image, plan_release_workflow_matrices]
if: inputs.include_release_path_suites && inputs.docker_lanes == '' && needs.plan_release_workflow_matrices.outputs.docker_e2e_count != '0'
name: Docker E2E (${{ matrix.label }})
continue-on-error: ${{ inputs.advisory }}
runs-on: ${{ inputs.use_github_hosted_runners && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
timeout-minutes: ${{ matrix.timeout_minutes }}
strategy:
fail-fast: false
matrix:
include:
- chunk_id: core
label: core
timeout_minutes: 60
profiles: stable full
- chunk_id: package-update-openai
label: package/update OpenAI install
timeout_minutes: 45
profiles: beta minimum stable full
- chunk_id: package-update-anthropic
label: package/update Anthropic install
timeout_minutes: 60
profiles: beta minimum stable full
- chunk_id: package-update-core
label: package/update core
timeout_minutes: 60
profiles: beta minimum stable full
- chunk_id: plugins-runtime-plugins
label: plugins/runtime plugins
timeout_minutes: 60
profiles: stable full
- chunk_id: plugins-runtime-services
label: plugins/runtime services
timeout_minutes: 60
profiles: stable full
- chunk_id: plugins-runtime-install-a
label: plugins/runtime install A
timeout_minutes: 60
profiles: stable full
- chunk_id: plugins-runtime-install-b
label: plugins/runtime install B
timeout_minutes: 60
profiles: stable full
- chunk_id: plugins-runtime-install-c
label: plugins/runtime install C
timeout_minutes: 60
profiles: stable full
- chunk_id: plugins-runtime-install-d
label: plugins/runtime install D
timeout_minutes: 60
profiles: stable full
- chunk_id: plugins-runtime-install-e
label: plugins/runtime install E
timeout_minutes: 60
profiles: stable full
- chunk_id: plugins-runtime-install-f
label: plugins/runtime install F
timeout_minutes: 60
profiles: stable full
- chunk_id: plugins-runtime-install-g
label: plugins/runtime install G
timeout_minutes: 60
profiles: stable full
- chunk_id: plugins-runtime-install-h
label: plugins/runtime install H
timeout_minutes: 60
profiles: stable full
matrix: ${{ fromJson(needs.plan_release_workflow_matrices.outputs.docker_e2e_matrix) }}
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
@@ -1631,42 +1603,14 @@ jobs:
validate_live_models_docker:
name: Docker live models (${{ matrix.provider_label }})
needs: [validate_selected_ref, prepare_live_test_image]
if: inputs.include_live_suites && inputs.live_model_providers == '' && (inputs.live_suite_filter == '' || inputs.live_suite_filter == 'docker-live-models')
needs: [validate_selected_ref, prepare_live_test_image, plan_release_workflow_matrices]
if: inputs.include_live_suites && inputs.live_model_providers == '' && (inputs.live_suite_filter == '' || inputs.live_suite_filter == 'docker-live-models') && needs.plan_release_workflow_matrices.outputs.live_models_count != '0'
continue-on-error: ${{ inputs.advisory }}
runs-on: ${{ inputs.use_github_hosted_runners && 'ubuntu-24.04' || 'blacksmith-32vcpu-ubuntu-2404' }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
- provider_label: Anthropic
providers: anthropic
profiles: stable full
- provider_label: Google
providers: google
profiles: stable full
- provider_label: MiniMax
providers: minimax
profiles: stable full
- provider_label: OpenAI
providers: openai
profiles: beta minimum stable full
- provider_label: OpenCode
providers: opencode-go
profiles: full
- provider_label: OpenRouter
providers: openrouter
profiles: full
- provider_label: xAI
providers: xai
profiles: full
- provider_label: Z.ai
providers: zai
profiles: full
- provider_label: Fireworks
providers: fireworks
profiles: full
matrix: ${{ fromJson(needs.plan_release_workflow_matrices.outputs.live_models_matrix) }}
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
@@ -1744,6 +1688,8 @@ jobs:
- name: Validate provider credential
if: contains(matrix.profiles, inputs.release_test_profile)
shell: bash
env:
LIVE_MODEL_PROVIDERS: ${{ matrix.providers }}
run: |
set -euo pipefail
@@ -1760,7 +1706,7 @@ jobs:
exit 1
}
case "${{ matrix.providers }}" in
case "${LIVE_MODEL_PROVIDERS}" in
anthropic) require_any Anthropic ANTHROPIC_API_KEY ANTHROPIC_API_KEY_OLD ANTHROPIC_API_TOKEN ;;
google) require_any Google GEMINI_API_KEY GOOGLE_API_KEY ;;
minimax) require_any MiniMax MINIMAX_API_KEY ;;
@@ -1771,7 +1717,7 @@ jobs:
zai) require_any Z.ai ZAI_API_KEY Z_AI_API_KEY ;;
fireworks) require_any Fireworks FIREWORKS_API_KEY ;;
*)
echo "Unhandled live model provider shard: ${{ matrix.providers }}" >&2
echo "Unhandled live model provider shard: ${LIVE_MODEL_PROVIDERS}" >&2
exit 1
;;
esac
@@ -1986,7 +1932,7 @@ jobs:
- suite_id: native-live-src-gateway-profiles-anthropic-opus
suite_group: native-live-src-gateway-profiles-anthropic
label: Native live gateway profiles Anthropic Opus
command: OPENCLAW_LIVE_GATEWAY_THINKING=low OPENCLAW_LIVE_GATEWAY_PROVIDERS=anthropic OPENCLAW_LIVE_GATEWAY_MODELS=anthropic/claude-opus-4-7 node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles
command: OPENCLAW_LIVE_GATEWAY_THINKING=low OPENCLAW_LIVE_GATEWAY_PROVIDERS=anthropic OPENCLAW_LIVE_GATEWAY_MODELS=anthropic/claude-opus-4-8 node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles
timeout_minutes: 30
profile_env_only: false
advisory: true
@@ -2013,7 +1959,7 @@ jobs:
profiles: stable full
- suite_id: native-live-src-gateway-profiles-openai
label: Native live gateway profiles OpenAI
command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=openai OPENCLAW_LIVE_GATEWAY_MODELS=openai/gpt-5.5 node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles
command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=openai OPENCLAW_LIVE_GATEWAY_MODELS=openai/gpt-5.5 OPENCLAW_LIVE_GATEWAY_STEP_TIMEOUT_MS=180000 OPENCLAW_LIVE_GATEWAY_MODEL_TIMEOUT_MS=600000 node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles
timeout_minutes: 60
profile_env_only: false
profiles: beta minimum stable full

View File

@@ -813,7 +813,7 @@ jobs:
alt_model="openai/gpt-5.5-alt"
;;
baseline)
model="anthropic/claude-opus-4-7"
model="anthropic/claude-opus-4-8"
alt_model="anthropic/claude-sonnet-4-6"
;;
*)
@@ -885,7 +885,7 @@ jobs:
--candidate-summary .artifacts/qa-e2e/openai-candidate/qa-suite-summary.json \
--baseline-summary .artifacts/qa-e2e/anthropic-baseline/qa-suite-summary.json \
--candidate-label "${OPENCLAW_CI_OPENAI_MODEL}" \
--baseline-label anthropic/claude-opus-4-7 \
--baseline-label anthropic/claude-opus-4-8 \
--output-dir .artifacts/qa-e2e/parity
- name: Upload parity artifacts
@@ -1207,6 +1207,7 @@ jobs:
env:
OPENCLAW_QA_CONVEX_SITE_URL: ${{ secrets.OPENCLAW_QA_CONVEX_SITE_URL }}
OPENCLAW_QA_CONVEX_SECRET_CI: ${{ secrets.OPENCLAW_QA_CONVEX_SECRET_CI }}
OPENCLAW_QA_CREDENTIAL_ACQUIRE_TIMEOUT_MS: "1800000"
OPENCLAW_QA_REDACT_PUBLIC_METADATA: "1"
OPENCLAW_QA_TELEGRAM_CAPTURE_CONTENT: "1"
run: |

View File

@@ -122,6 +122,10 @@ jobs:
echo "publish_openclaw_npm=true requires dispatching this workflow from main, release/YYYY.M.D, or a Tideclaw alpha branch for alpha prereleases." >&2
exit 1
fi
if [[ "${PUBLISH_OPENCLAW_NPM}" == "true" && "${PLUGIN_PUBLISH_SCOPE}" != "all-publishable" ]]; then
echo "publish_openclaw_npm=true requires plugin_publish_scope=all-publishable so every publishable official plugin is released with OpenClaw." >&2
exit 1
fi
if [[ "${PLUGIN_PUBLISH_SCOPE}" == "selected" && -z "${PLUGINS}" ]]; then
echo "plugin_publish_scope=selected requires plugins." >&2
exit 1

View File

@@ -32,11 +32,11 @@ jobs:
- name: Install opengrep
env:
# Pin both the install script (by commit SHA) and the binary version.
# The script SHA must match the v1.19.0 release tag in opengrep/opengrep
# The script SHA must match the v1.22.0 release tag in opengrep/opengrep
# so a compromised or force-pushed `main` cannot RCE in our CI runner.
# Bump both together when upgrading.
OPENGREP_VERSION: v1.19.0
OPENGREP_INSTALL_SHA: 9a4c0a68220618441608cd2bad4ff2eddccf8113
OPENGREP_VERSION: v1.22.0
OPENGREP_INSTALL_SHA: f458d7f0d52cc58eae1ca3cf3d5caf101e637519
run: |
curl -fsSL "https://raw.githubusercontent.com/opengrep/opengrep/${OPENGREP_INSTALL_SHA}/install.sh" \
| bash -s -- -v "$OPENGREP_VERSION"

View File

@@ -44,7 +44,7 @@ jobs:
uses: actions/checkout@v6
with:
ref: ${{ github.sha }}
fetch-depth: 0
fetch-depth: 1
fetch-tags: false
persist-credentials: false
submodules: false
@@ -58,11 +58,11 @@ jobs:
- name: Install opengrep
env:
# Pin both the install script (by commit SHA) and the binary version.
# The script SHA must match the v1.19.0 release tag in opengrep/opengrep
# The script SHA must match the v1.22.0 release tag in opengrep/opengrep
# so a compromised or force-pushed `main` cannot RCE in our CI runner.
# Bump both together when upgrading.
OPENGREP_VERSION: v1.19.0
OPENGREP_INSTALL_SHA: 9a4c0a68220618441608cd2bad4ff2eddccf8113
OPENGREP_VERSION: v1.22.0
OPENGREP_INSTALL_SHA: f458d7f0d52cc58eae1ca3cf3d5caf101e637519
run: |
curl -fsSL "https://raw.githubusercontent.com/opengrep/opengrep/${OPENGREP_INSTALL_SHA}/install.sh" \
| bash -s -- -v "$OPENGREP_VERSION"

View File

@@ -431,7 +431,8 @@ jobs:
EOF
echo "CLAWHUB_CONFIG_PATH=${RUNNER_TEMP}/clawhub-config.json" >> "$GITHUB_ENV"
- name: Ensure version is not already published
- name: Check ClawHub package version
id: clawhub_package_version
env:
PACKAGE_NAME: ${{ matrix.plugin.packageName }}
PACKAGE_VERSION: ${{ matrix.plugin.version }}
@@ -456,14 +457,17 @@ jobs:
done
if [[ "${status}" =~ ^2 ]]; then
echo "${PACKAGE_NAME}@${PACKAGE_VERSION} is already published on ClawHub."
exit 1
echo "already_published=true" >> "$GITHUB_OUTPUT"
exit 0
fi
if [[ "${status}" != "404" ]]; then
echo "Unexpected ClawHub response (${status}) for ${PACKAGE_NAME}@${PACKAGE_VERSION}."
exit 1
fi
echo "already_published=false" >> "$GITHUB_OUTPUT"
- name: Publish
if: steps.clawhub_package_version.outputs.already_published != 'true'
env:
CLAWHUB_REGISTRY: ${{ env.CLAWHUB_REGISTRY }}
SOURCE_REPO: ${{ github.repository }}

View File

@@ -263,7 +263,8 @@ jobs:
node-version: ${{ env.NODE_VERSION }}
install-bun: "false"
- name: Ensure version is not already published
- name: Check npm package version
id: npm_package_version
env:
PACKAGE_NAME: ${{ matrix.plugin.packageName }}
PACKAGE_VERSION: ${{ matrix.plugin.version }}
@@ -271,10 +272,13 @@ jobs:
set -euo pipefail
if npm view "${PACKAGE_NAME}@${PACKAGE_VERSION}" version >/dev/null 2>&1; then
echo "${PACKAGE_NAME}@${PACKAGE_VERSION} is already published on npm."
exit 1
echo "already_published=true" >> "$GITHUB_OUTPUT"
else
echo "already_published=false" >> "$GITHUB_OUTPUT"
fi
- name: Publish
if: steps.npm_package_version.outputs.already_published != 'true'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -199,13 +199,13 @@ jobs:
--alt-model openai/gpt-5.5-alt \
--output-dir .artifacts/qa-e2e/openai-candidate
- name: Run Opus 4.7 lane
- name: Run Opus 4.8 lane
run: |
pnpm openclaw qa suite \
--provider-mode mock-openai \
--parity-pack agentic \
--concurrency "${QA_PARITY_CONCURRENCY}" \
--model anthropic/claude-opus-4-7 \
--model anthropic/claude-opus-4-8 \
--alt-model anthropic/claude-sonnet-4-6 \
--output-dir .artifacts/qa-e2e/anthropic-baseline
@@ -216,7 +216,7 @@ jobs:
--candidate-summary .artifacts/qa-e2e/openai-candidate/qa-suite-summary.json \
--baseline-summary .artifacts/qa-e2e/anthropic-baseline/qa-suite-summary.json \
--candidate-label "${OPENCLAW_CI_OPENAI_MODEL}" \
--baseline-label anthropic/claude-opus-4-7 \
--baseline-label anthropic/claude-opus-4-8 \
--output-dir .artifacts/qa-e2e/parity
- name: Upload parity artifacts
@@ -530,6 +530,7 @@ jobs:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENCLAW_QA_CONVEX_SITE_URL: ${{ secrets.OPENCLAW_QA_CONVEX_SITE_URL }}
OPENCLAW_QA_CONVEX_SECRET_CI: ${{ secrets.OPENCLAW_QA_CONVEX_SECRET_CI }}
OPENCLAW_QA_CREDENTIAL_ACQUIRE_TIMEOUT_MS: "1800000"
OPENCLAW_QA_REDACT_PUBLIC_METADATA: "1"
OPENCLAW_QA_TELEGRAM_CAPTURE_CONTENT: "1"
INPUT_SCENARIO: ${{ github.event_name == 'workflow_dispatch' && inputs.scenario || '' }}

View File

@@ -90,7 +90,7 @@ jobs:
bash -lc 'apt-get update -y && apt-get install -y curl && bash /tmp/install-cli.sh --prefix /tmp/openclaw --no-onboard --version latest && /tmp/openclaw/bin/openclaw --version'
macos-installer:
runs-on: macos-latest
runs-on: macos-15
steps:
- name: Checkout
uses: actions/checkout@v6

View File

@@ -34,7 +34,10 @@ jobs:
git init "$GITHUB_WORKSPACE"
git -C "$GITHUB_WORKSPACE" config gc.auto 0
git -C "$GITHUB_WORKSPACE" remote add origin "https://github.com/${CHECKOUT_REPO}.git"
git -C "$GITHUB_WORKSPACE" fetch --no-tags --depth=1 origin "+${CHECKOUT_SHA}:refs/remotes/origin/checkout"
timeout --signal=TERM --kill-after=10s 30s git -C "$GITHUB_WORKSPACE" \
-c protocol.version=2 \
fetch --no-tags --prune --no-recurse-submodules --depth=1 origin \
"+${CHECKOUT_SHA}:refs/remotes/origin/checkout"
git -C "$GITHUB_WORKSPACE" checkout --detach refs/remotes/origin/checkout
- name: Fail on tabs in workflow files
@@ -75,7 +78,10 @@ jobs:
git init "$GITHUB_WORKSPACE"
git -C "$GITHUB_WORKSPACE" config gc.auto 0
git -C "$GITHUB_WORKSPACE" remote add origin "https://github.com/${CHECKOUT_REPO}.git"
git -C "$GITHUB_WORKSPACE" fetch --no-tags --depth=1 origin "+${CHECKOUT_SHA}:refs/remotes/origin/checkout"
timeout --signal=TERM --kill-after=10s 30s git -C "$GITHUB_WORKSPACE" \
-c protocol.version=2 \
fetch --no-tags --prune --no-recurse-submodules --depth=1 origin \
"+${CHECKOUT_SHA}:refs/remotes/origin/checkout"
git -C "$GITHUB_WORKSPACE" checkout --detach refs/remotes/origin/checkout
- name: Install actionlint
@@ -116,7 +122,10 @@ jobs:
git init "$GITHUB_WORKSPACE"
git -C "$GITHUB_WORKSPACE" config gc.auto 0
git -C "$GITHUB_WORKSPACE" remote add origin "https://github.com/${CHECKOUT_REPO}.git"
git -C "$GITHUB_WORKSPACE" fetch --no-tags --depth=1 origin "+${CHECKOUT_SHA}:refs/remotes/origin/checkout"
timeout --signal=TERM --kill-after=10s 30s git -C "$GITHUB_WORKSPACE" \
-c protocol.version=2 \
fetch --no-tags --prune --no-recurse-submodules --depth=1 origin \
"+${CHECKOUT_SHA}:refs/remotes/origin/checkout"
git -C "$GITHUB_WORKSPACE" checkout --detach refs/remotes/origin/checkout
- name: Setup Node environment

1
.gitignore vendored
View File

@@ -178,6 +178,7 @@ mantis/
/local/
/client_secret_*.json
package-lock.json
!src/commands/copilot-sdk-install-manifest/package-lock.json
.claude/
.agent/
skills-lock.json

View File

@@ -35,9 +35,9 @@ Skills own workflows; root owns hard policy and routing.
## Map
- Core TS: `src/`, `ui/`, `packages/`; plugins: `extensions/`; SDK: `src/plugin-sdk/*`; channels: `src/channels/*`; loader: `src/plugins/*`; protocol: `src/gateway/protocol/*`; docs/apps: `docs/`, `apps/`.
- Core TS: `src/`, `ui/`, `packages/`; plugins: `extensions/`; SDK: `src/plugin-sdk/*`; channels: `src/channels/*`; loader: `src/plugins/*`; protocol: `packages/gateway-protocol/*`; docs/apps: `docs/`, `apps/`.
- Installers: sibling `../openclaw.ai`.
- Scoped guides: `extensions/`, `src/{plugin-sdk,channels,plugins,gateway,gateway/protocol,agents}/`, `test/helpers*/`, `docs/`, `ui/`, `scripts/`.
- Scoped guides: `extensions/`, `src/{plugin-sdk,channels,plugins,gateway,agents}/`, `packages/`, `test/helpers*/`, `docs/`, `ui/`, `scripts/`.
## Docs
@@ -57,6 +57,7 @@ Skills own workflows; root owns hard policy and routing.
- External official plugins own package/deps and are excluded from core dist; core uses registry-aware `facade-runtime` or generic contracts.
- Externalizing a bundled plugin: update package excludes, official catalogs, docs, tests, and prove core runtime paths resolve installed plugin roots before root-dep removal.
- Runtime reads canonical config only. No silent compat for old/malformed config keys. If a config change invalidates existing files, add a matching `openclaw doctor --fix` migration. Core/auth config repairs live in core doctor; plugin-owned config repairs live in that plugin's doctor contract (`legacyConfigRules` / `normalizeCompatibilityConfig`).
- CLI setup flows are public API when external docs, installers, or integrations can copy them. Changes to `openclaw onboard`, `openclaw configure`, their documented flags, non-interactive behavior, or generated config shape are compatibility-sensitive API contract changes; prefer additive flags/aliases, deprecation windows, and backward-preserving migrations over breaking existing snippets.
- Fix shape: default to clean bounded refactor, not smallest patch. Move ownership to right boundary; delete stale abstractions, duplicate policy, dead branches, wrappers, fallback stacks.
- Fix observed local failures with generic product rules; do not hardcode names, ids, log phrases, or user examples in prod code unless they are an explicit contract.
- Tests may use observed examples, but prod literals need a short contract reason.

View File

@@ -7,8 +7,11 @@ Docs: https://docs.openclaw.ai
### Highlights
- Agent and Codex runtime recovery is steadier: subagents keep cwd/workspace separation, hook context stays prompt-local, session locks release on timeout abort, stale restart continuations are avoided, and Codex app-server/helper failures no longer tear down shared runtime state. (#87218, #86875, #87409, #87399, #87375)
- Channel delivery and session identity got safer across outbound plugin hooks, Matrix room ids, iMessage reactions/approvals, Slack final replies, Discord recovered tool warnings, and Microsoft Teams service URL trust checks. (#73706, #75670, #87366, #87451, #87334)
- CLI, auth, doctor, and provider paths fail faster and recover more clearly: malformed numeric/version options are rejected, OAuth and local service startup requests are bounded, legacy `api_key` auth profiles migrate to canonical form, and restart guidance is actionable. (#87398, #86281, #87361)
- Channel delivery and session identity got safer across outbound plugin hooks, Matrix room ids, iMessage reactions/approvals, Slack final replies, Discord recovered tool warnings, WhatsApp profile auth roots, Telegram polling, and Microsoft Teams service URL trust checks. (#73706, #75670, #87366, #87451, #87334, #82492, #83304, #87160)
- Mobile and chat surfaces got a broader refresh: the iOS Pro UI, Gateway chat transport, onboarding, Talk permissions, WebChat reconnect delivery, and session picker behavior now preserve more state across reconnects and empty searches. (#87367, #87531, #87682)
- Browser, channel, and automation inputs are stricter: Browser tool timeouts, viewport/tab indices, Gateway ports, cron retry handling, Discord component ids, schema array refs, Telegram callback pages, and channel progress callbacks now reject malformed values earlier and preserve the intended delivery context. (#82887)
- Provider, media, and document coverage expands with Claude Opus 4.8, Fal Krea image schemas, NVIDIA featured models, MiniMax streaming music responses, encrypted PDF extraction, voice model catalogs, GitHub Copilot agent runtime support, and a Codex Supervisor plugin path for delegated Codex workflows. (#87845, #87890, #80775, #84764, #87751, #87794)
- CLI, auth, doctor, and provider paths fail faster and recover more clearly: malformed numeric/version options are rejected, workspace dotenv provider credentials are ignored, OAuth and local service startup requests are bounded, legacy `api_key` auth profiles migrate to canonical form, and restart guidance is actionable. (#87398, #86281, #87361, #83655, #87559)
- Plugin and Gateway hot paths do less repeated work while preserving cache correctness for install records, config JSON parsing, tool search catalogs, session stores, manifest model rows, auto-enabled plugin config, browser tokens, and viewer assets. (#86699)
- Release, QA, and E2E validation now bound more log, artifact, harness, and cross-OS waits so failing lanes produce proof instead of hanging or false-greening.
@@ -18,19 +21,37 @@ Docs: https://docs.openclaw.ai
- Diffs: split the default language pack and expand default Diffs language coverage while keeping the host floor aligned. (#87370, #87372) Thanks @RomneyDa.
- ClawHub: add plugin display names plus skill verification and trust surfaces. (#87354, #86699) Thanks @thewilloftheshadow and @Patrick-Erichsen.
- iOS: refresh the dev app with Pro Command, Chat, Agents, and Settings tabs wired to gateway sessions, diagnostics, chat, and realtime Talk. (#87367) Thanks @Solvely-Colin.
- Docs: clarify Codex computer-use setup, paste-token stdin auth setup, macOS gateway sleep troubleshooting, native Codex hook relay recovery, container model auth, install deployment cards, device-token admin gating, and backport targets. (#87313, #63050) Thanks @bdjben, @liaoandi, and @thewilloftheshadow.
- Docs: clarify Codex computer-use setup, paste-token stdin auth setup, macOS gateway sleep troubleshooting, native Codex hook relay recovery, container model auth, install deployment cards, device-token admin gating, CLI setup flow compatibility, and backport targets. (#87313, #63050, #87685) Thanks @bdjben, @liaoandi, and @thewilloftheshadow.
- PDF/tools: use ClawPDF for PDF extraction, support encrypted PDF extraction, and surface MCP structured content in agent tool results. (#87670, #87751)
- Providers: add Claude Opus 4.8 support, Fal Krea image model schemas, NVIDIA featured model catalogs, MiniMax streaming music responses, and provider-backed voice model catalogs. (#87845, #87890, #80775, #84764, #87794) Thanks @eleqtrizit and @vincentkoc.
- Codex/GitHub: add the GitHub Copilot agent runtime and the Codex Supervisor plugin package.
- Discord: show commentary in progress drafts so live Discord runs expose useful in-progress context. (#85200)
- Plugin SDK: add a reply payload sending hook for plugins that need to deliver channel-owned replies and flatten package types for SDK declarations. (#82823, #87165) Thanks @RomneyDa.
- Policy: add policy comparison, ingress-channel conformance, and sandbox-posture conformance checks. (#85572, #85744, #86768)
### Fixes
- Agents: fall back to local config pruning when the optional `agents delete` Gateway probe cannot authenticate, so offline installs can still delete agents without removing shared workspaces.
- Tighten phone-control mutation authorization [AI]. (#87150) Thanks @pgondhi987.
- Clarify directive persistence authorization policy [AI]. (#86369) Thanks @pgondhi987.
- Agents/Codex: keep spawned agent cwd/workspace state separated, keep hook context prompt-local, release session locks on timeout abort, avoid session event queue self-wait, preserve shared app-server state across startup or helper failures, keep native hook relay alive across restarts, route workspace memory through tools, resolve Codex runtime models first, report quarantined dynamic tools, format `skills` command output, and bound compaction/steering retries. (#87218, #86875, #86123, #87399, #87375, #87383, #87400) Thanks @mbelinky, @Alix-007, @luoyanglang, @yetval, and @sjf.
- Channels: thread canonical session keys into outbound hooks, preserve Matrix room-id case, keep fallback tool warnings mention-inert, retain delivered Slack final replies during late cleanup, continue iMessage polling after denied reactions, suppress duplicate native exec approvals, preserve Telegram SecretRef prompt config, suppress Discord recovered tool warnings, and block untrusted Teams service URLs. (#73706, #75670, #87366, #87451, #87334) Thanks @zeroaltitude, @lukeboyett, @xiaotian, and @eleqtrizit.
- CLI/auth/doctor/providers: reject malformed numeric/timeout/subcommand-version inputs, wait for respawn child shutdown, bound Codex and GitHub Copilot OAuth/token requests, warm provider auth off the main thread, honor Codex response timeouts, bound local service startup, resolve GPT-5.5 without cached catalog, migrate legacy memory auto-provider config, rewrite non-canonical `api_key` auth profiles, and make doctor restart follow-ups actionable. (#87398, #86281, #87361) Thanks @Patrick-Erichsen, @samzong, @giodl73-repo, and @alkor2000.
- Gateway/security/session state: expire browser tokens after auth rotation, scope assistant idempotency dedupe, drain probe client closes, avoid stale restart continuation reuse, preserve retry-after fallbacks, bound webchat image and artifact transcript scans, include seconds in inbound metadata timestamps, and evict current plugin-state namespaces at row caps.
- Agents/Codex: keep spawned agent cwd/workspace state separated, keep hook context prompt-local, release session locks on timeout abort and runtime teardown, avoid session event queue self-wait, clean up exec abort listeners, stream assistant deltas incrementally, recover raw missing-thread compaction failures, preserve shared app-server state across startup or helper failures, keep native hook relay alive across restarts and prune stale bridge files, keep Claude live tool progress visible for watchdog recovery, suppress abandoned requester completion handoff, route workspace memory through tools, resolve Codex runtime models first, report quarantined dynamic tools, format `skills` command output, and bound compaction/steering retries. (#87218, #86875, #86123, #87399, #87375, #72574, #87383, #87400, #83022, #87671, #87738, #87747, #87706, #87546, #87541) Thanks @mbelinky, @Alix-007, @luoyanglang, @yetval, @sjf, and @joshavant.
- Channels: thread canonical session keys into outbound hooks, preserve Matrix room-id case, keep fallback tool warnings mention-inert, retain delivered Slack final replies during late cleanup, continue iMessage polling after denied reactions, suppress duplicate native exec approvals, preserve Telegram SecretRef prompt config and polling keepalives, preserve WhatsApp profile auth roots, QR display, document filenames, and plugin hook config, suppress Discord recovered tool warnings, preserve the Discord voice outbound helper, and block untrusted Teams service URLs while keeping TeamsSDK patterns aligned. (#73706, #75670, #87366, #87451, #87465, #87334, #76262, #83304, #82492, #87581, #77114, #86426, #85529, #87160) Thanks @zeroaltitude, @lukeboyett, @xiaotian, @eleqtrizit, @heyitsaamir, @amittell, @liorb-mountapps, @masatohoshino, @bladin, and @giodl73-repo.
- CLI/auth/doctor/providers: reject malformed numeric/timeout/subcommand-version inputs, ignore workspace dotenv provider credentials, wait for respawn child shutdown, bound Codex and GitHub Copilot OAuth/token requests, harden Codex auth probes, warm provider auth off the main thread, honor Codex response timeouts, stop migrating current Claude Haiku 4.5 profiles to Sonnet, bound local service startup, resolve GPT-5.5 without cached catalog, migrate legacy memory auto-provider config, rewrite non-canonical `api_key` auth profiles, and make doctor restart follow-ups actionable. (#87398, #86281, #87361, #83655, #87559, #87719) Thanks @Patrick-Erichsen, @samzong, @giodl73-repo, @alkor2000, @mmaps, and @nxmxbbd.
- Gateway/security/session state: expire browser tokens after auth rotation, scope assistant idempotency dedupe, drain probe client closes, avoid stale restart continuation reuse, preserve retry-after fallbacks and stale rate-limit cooldown probes, bound webchat image and artifact transcript scans, include seconds in inbound metadata timestamps, clear completed session active runs, and evict current plugin-state namespaces at row caps. (#87810, #87833) Thanks @joshavant.
- Config/parsing/network: reject partial numeric parsing, parse provider/Discord retry headers and dates strictly, honor IPv6 and bare IPv6 `no_proxy` entries, canonicalize secret target array indexes, and reject malformed media content lengths, inspected TCP ports, marketplace content lengths, cron epochs, sandbox stat fields, unsafe duration values, empty config path segments, noncanonical schema array refs, unsafe Telegram callback pages, and invalid Teams attachment-fetch DNS targets.
- Browser/input hardening: reject invalid tab indexes, excessive viewport resizes, explicit zero CDP ports, malformed geolocation options, unsafe screenshot or permission-grant timeouts, loose response-body limits, invalid cookie expiries, and non-finite Browser tool delays/timeouts.
- Cron/automation: retry recurring jobs after transient model rate limits before waiting for the next scheduled slot, and preflight model fallbacks before skipping scheduled work. (#82887)
- Auto-reply/directives: respect provider and relayed channel metadata during directive persistence so channel-originated decisions keep their intended context. (#87683)
- WhatsApp: resolve the auth directory from the active profile so profile-scoped WhatsApp installs do not drift to the wrong credential root. (#82492)
- Gateway/session state: clear completed session active runs, avoid cold-loading providers for MCP inventory, cache single-session child indexes, cap handshake timers, and bound preauth, auth-guard, media, transcript, readiness, and port options.
- Channels/replies: preserve channel-owned progress callbacks when verbose output is off, keep group-room progress suppression intact, prefer external session delivery context, escape Discord component id delimiters, force final TUI chat repaints, show Slack reasoning previews, and normalize Discord/Matrix/Mattermost channel numeric options. (#87476, #87423)
- Agents/tool args: harden smart-quoted argument repair for edit arrays and exact escaped arguments so model-produced tool calls recover without corrupting valid input. (#86611)
- Providers/agents: preserve seeded Anthropic signatures, preserve signed thinking payloads, concatenate signature-delta chunks, preserve DeepSeek `reasoning_content` replay across tier suffixes, apply OpenRouter strict9 ids to Mistral routes, promote Ollama plain-text tool calls, load NVIDIA featured model catalogs, stream MiniMax music generation responses, and recover empty preflight compaction. (#87593, #87493, #80775, #84764) Thanks @eleqtrizit.
- Media/images: skip CLI image cache refs when resolving generated images and bound generated video downloads so stale refs and slow providers fail cleanly. (#87523)
- File transfer: handle late tar stdin pipe errors after archive validation or unpacking has already settled.
- Performance: trust install-record caches between reloads, prefer native JSON parsing, reuse unchanged tool-search catalogs, skip unchanged store serialization, add precomputed session patch writers, reduce store clone allocations, cache manifest model catalog rows and auto-enabled plugin config, and slim current metadata identity caches.
- Docker/release/QA: package runtime workspace templates, stream cross-OS served artifacts, preserve sparse Crabbox run artifacts, bound OpenClaw instance logs, plugin gauntlet relay logs, MCP channel buffers, kitchen-sink scans, agent-turn assertions, and release scenario logs, and keep release/google live guards current.
- Performance: trust install-record caches between reloads, prefer native JSON parsing, reuse unchanged tool-search catalogs, skip unchanged store serialization, add precomputed session patch writers, reduce store clone allocations, cache manifest model catalog rows and auto-enabled plugin config, avoid full session snapshots for entry reads, defer configured Slack full startup, prefer bundled plugin dist entries, and slim current metadata identity caches. (#87760)
- Docker/release/QA: package runtime workspace templates, stream cross-OS served artifacts, preserve sparse Crabbox run artifacts, isolate npm plugin installs per package, reject incompatible package plugin API installs, bound OpenClaw instance logs, plugin gauntlet relay logs, MCP channel buffers, kitchen-sink scans, agent-turn assertions, and release scenario logs, and keep release/google live guards current. (#87647, #87477) Thanks @rohitjavvadi.
- Release/CI: bound manual git fetches, ClawHub verifier responses, ClawHub owner metadata, Parallels limits, startup/test/memory budget parsing, and diffs viewer build warnings so release lanes fail with useful proof instead of hanging. (#87839)
## 2026.5.27

View File

@@ -48,6 +48,7 @@ RUN --mount=type=bind,source=packages,target=/tmp/packages,readonly \
FROM ${OPENCLAW_BUN_IMAGE} AS bun-binary
FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS build
ARG OPENCLAW_BUNDLED_PLUGIN_DIR
ARG OPENCLAW_EXTENSIONS
# Copy pinned Bun binary from the official image instead of fetching via curl.
COPY --from=bun-binary /usr/local/bin/bun /usr/local/bin/bun
@@ -77,7 +78,12 @@ RUN --mount=type=cache,id=openclaw-pnpm-store,target=/root/.local/share/pnpm/sto
# pnpm v10+ may append peer-resolution hashes to virtual-store folder names; do not hardcode `.pnpm/...`
# paths. Matrix's native downloader can hit transient release CDN errors while
# still exiting successfully, so retry the package downloader before failing.
# Skip the entire check when matrix is not a bundled extension (e.g. msteams-only builds).
RUN set -eux; \
if ! printf '%s\n' "$OPENCLAW_EXTENSIONS" | tr ',' ' ' | tr ' ' '\n' | grep -qx 'matrix'; then \
echo "==> matrix not bundled, skipping matrix-sdk-crypto check"; \
exit 0; \
fi; \
echo "==> Verifying critical native addons..."; \
for attempt in 1 2 3 4 5; do \
if find /app/node_modules -name "matrix-sdk-crypto*.node" 2>/dev/null | grep -q .; then \

View File

@@ -170,6 +170,8 @@ final class AppState {
}
}
var voiceWakeMeterActive = false
var talkEnabled: Bool {
didSet {
self.ifNotPreview {

View File

@@ -63,6 +63,14 @@ extension CritterStatusLabel {
.frame(width: 6, height: 6)
.padding(1)
}
if self.voiceWakeMeterActive {
Circle()
.fill(.orange)
.frame(width: 5, height: 5)
.padding(2)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomLeading)
}
}
.frame(width: 18, height: 18)
}
@@ -239,7 +247,8 @@ extension CritterStatusLabel {
sendCelebrationTick: 1,
gatewayStatus: .running(details: nil),
animationsEnabled: true,
iconState: .workingMain(.tool(.bash)))
iconState: .workingMain(.tool(.bash)),
voiceWakeMeterActive: true)
_ = label.body
_ = label.iconImage
@@ -275,7 +284,8 @@ extension CritterStatusLabel {
sendCelebrationTick: 0,
gatewayStatus: .failed("boom"),
animationsEnabled: false,
iconState: .idle)
iconState: .idle,
voiceWakeMeterActive: false)
_ = failed.gatewayNeedsAttention
_ = failed.gatewayBadgeColor
@@ -288,7 +298,8 @@ extension CritterStatusLabel {
sendCelebrationTick: 0,
gatewayStatus: .stopped,
animationsEnabled: false,
iconState: .idle)
iconState: .idle,
voiceWakeMeterActive: false)
_ = stopped.gatewayNeedsAttention
_ = stopped.gatewayBadgeColor

View File

@@ -10,6 +10,7 @@ struct CritterStatusLabel: View {
var gatewayStatus: GatewayProcessManager.Status
var animationsEnabled: Bool
var iconState: IconState
var voiceWakeMeterActive: Bool = false
@State var blinkAmount: CGFloat = 0
@State var nextBlink = Date().addingTimeInterval(Double.random(in: 3.5...8.5))

View File

@@ -50,7 +50,8 @@ struct OpenClawApp: App {
sendCelebrationTick: self.state.sendCelebrationTick,
gatewayStatus: self.gatewayManager.status,
animationsEnabled: self.state.iconAnimationsEnabled && !self.isGatewaySleeping,
iconState: self.effectiveIconState)
iconState: self.effectiveIconState,
voiceWakeMeterActive: self.state.voiceWakeMeterActive)
.background(SettingsWindowOpenRegistrar())
}
.menuBarExtraAccess(isPresented: self.$isMenuPresented) { item in
@@ -75,6 +76,9 @@ struct OpenClawApp: App {
.onChange(of: self.gatewayManager.status) { _, _ in
self.applyStatusItemAppearance(paused: self.state.isPaused, sleeping: self.isGatewaySleeping)
}
.onChange(of: self.state.voiceWakeMeterActive) { _, _ in
self.applyStatusItemAppearance(paused: self.state.isPaused, sleeping: self.isGatewaySleeping)
}
.onChange(of: self.state.connectionMode) { _, mode in
Task { await ConnectionModeCoordinator.shared.apply(mode: mode, paused: self.state.isPaused) }
CLIInstallPrompter.shared.checkAndPromptIfNeeded(reason: "connection-mode")
@@ -107,6 +111,9 @@ struct OpenClawApp: App {
// The SwiftUI label already renders those states; AppKit's disabled appearance can
// leak into menu item validation and grey out app-level commands like Settings.
self.statusItem?.button?.appearsDisabled = false
self.statusItem?.button?.toolTip = self.state.voiceWakeMeterActive
? "OpenClaw - Voice Wake live meter active"
: "OpenClaw"
}
private static func applyAttachOnlyOverrideIfNeeded() {

View File

@@ -8,12 +8,18 @@ actor MicLevelMonitor {
private var update: (@Sendable (Double) -> Void)?
private var running = false
private var smoothedLevel: Double = 0
private var lastUpdate = ContinuousClock.now
private var lastPublishedLevel: Double = 0
private let minimumUpdateInterval: Duration = .milliseconds(125)
private let minimumLevelDelta = 0.02
func start(onLevel: @Sendable @escaping (Double) -> Void) async throws {
self.update = onLevel
if self.running { return }
self.logger.info(
"mic level monitor start (\(AudioInputDeviceObserver.defaultInputDeviceSummary(), privacy: .public))")
self.lastUpdate = .now
self.lastPublishedLevel = self.smoothedLevel
guard AudioInputDeviceObserver.hasUsableDefaultInputDevice() else {
self.engine = nil
throw NSError(
@@ -56,7 +62,13 @@ actor MicLevelMonitor {
private func push(level: Double) {
self.smoothedLevel = (self.smoothedLevel * 0.45) + (level * 0.55)
guard let update else { return }
let now = ContinuousClock.now
guard now - self.lastUpdate >= self.minimumUpdateInterval ||
abs(self.smoothedLevel - self.lastPublishedLevel) >= self.minimumLevelDelta
else { return }
self.lastUpdate = now
let value = self.smoothedLevel
self.lastPublishedLevel = value
Task { @MainActor in update(value) }
}

View File

@@ -3,12 +3,12 @@ import Foundation
enum SoundEffectCatalog {
/// All discoverable system sound names, with "Glass" pinned first.
static var systemOptions: [String] {
static let systemOptions: [String] = {
var names = Set(Self.discoveredSoundMap.keys).union(Self.fallbackNames)
names.remove("Glass")
let sorted = names.sorted { $0.localizedCaseInsensitiveCompare($1) == .orderedAscending }
return ["Glass"] + sorted
}
}()
static func displayName(for raw: String) -> String {
raw

View File

@@ -20,6 +20,7 @@ struct VoiceWakeSettings: View {
private let meter = MicLevelMonitor()
@State private var micObserver = AudioInputDeviceObserver()
@State private var micRefreshTask: Task<Void, Never>?
@State private var meterStartupTask: Task<Void, Never>?
@State private var availableLocales: [Locale] = []
@State private var triggerEntries: [TriggerEntry] = []
private let fieldLabelWidth: CGFloat = 140
@@ -188,59 +189,68 @@ struct VoiceWakeSettings: View {
}
.settingsDetailContent()
}
.task {
guard !self.isPreview else { return }
await self.loadMicsIfNeeded()
}
.task {
guard !self.isPreview else { return }
await self.loadLocalesIfNeeded()
}
.task {
guard !self.isPreview else { return }
await self.restartMeter()
}
.onAppear {
guard !self.isPreview else { return }
self.startMicObserver()
self.loadTriggerEntries()
guard self.isActive else { return }
self.activateLivePreview()
}
.onChange(of: self.state.voiceWakeMicID) { _, _ in
guard !self.isPreview else { return }
self.updateSelectedMicName()
Task { await self.restartMeter() }
guard self.isActive else { return }
self.scheduleMeterRestart()
}
.onChange(of: self.isActive) { _, active in
guard !self.isPreview else { return }
if !active {
self.tester.stop()
self.isTesting = false
self.testState = .idle
self.testTimeoutTask?.cancel()
self.micRefreshTask?.cancel()
self.micRefreshTask = nil
Task { await self.meter.stop() }
self.micObserver.stop()
self.deactivateLivePreview()
self.syncTriggerEntriesToState()
} else {
self.startMicObserver()
self.loadTriggerEntries()
self.activateLivePreview()
}
}
.onDisappear {
guard !self.isPreview else { return }
self.tester.stop()
self.isTesting = false
self.testState = .idle
self.testTimeoutTask?.cancel()
self.micRefreshTask?.cancel()
self.micRefreshTask = nil
self.micObserver.stop()
Task { await self.meter.stop() }
self.deactivateLivePreview()
self.syncTriggerEntriesToState()
}
}
private func activateLivePreview() {
self.meterStartupTask?.cancel()
self.startMicObserver()
self.loadTriggerEntries()
self.meterStartupTask = Task { @MainActor in
await self.loadMicsIfNeeded()
guard !Task.isCancelled, self.isActive else { return }
await self.loadLocalesIfNeeded()
guard !Task.isCancelled, self.isActive else { return }
await self.restartMeter()
}
}
private func deactivateLivePreview() {
self.tester.stop()
self.isTesting = false
self.testState = .idle
self.testTimeoutTask?.cancel()
self.micRefreshTask?.cancel()
self.micRefreshTask = nil
self.meterStartupTask?.cancel()
self.meterStartupTask = nil
self.micObserver.stop()
self.state.voiceWakeMeterActive = false
Task { await self.meter.stop() }
}
private func scheduleMeterRestart() {
self.meterStartupTask?.cancel()
self.meterStartupTask = Task { @MainActor in
guard !Task.isCancelled, self.isActive else { return }
await self.restartMeter()
}
}
private func loadTriggerEntries() {
self.triggerEntries = self.state.swabbleTriggerWords.map { TriggerEntry(id: UUID(), value: $0) }
}
@@ -652,6 +662,7 @@ struct VoiceWakeSettings: View {
@MainActor
private func scheduleMicRefresh() {
guard self.isActive else { return }
MicRefreshSupport.schedule(refreshTask: &self.micRefreshTask) {
await self.loadMicsIfNeeded(force: true)
await self.restartMeter()
@@ -713,8 +724,17 @@ struct VoiceWakeSettings: View {
@MainActor
private func restartMeter() async {
guard self.isActive else {
self.state.voiceWakeMeterActive = false
await self.meter.stop()
return
}
self.meterError = nil
await self.meter.stop()
guard !Task.isCancelled, self.isActive else {
self.state.voiceWakeMeterActive = false
return
}
do {
try await self.meter.start { [weak state] level in
Task { @MainActor in
@@ -722,7 +742,14 @@ struct VoiceWakeSettings: View {
self.meterLevel = level
}
}
guard !Task.isCancelled, self.isActive else {
self.state.voiceWakeMeterActive = false
await self.meter.stop()
return
}
self.state.voiceWakeMeterActive = true
} catch {
self.state.voiceWakeMeterActive = false
self.meterError = error.localizedDescription
}
}

View File

@@ -2379,6 +2379,7 @@ public struct SessionsCompactParams: Codable, Sendable {
public struct SessionsUsageParams: Codable, Sendable {
public let key: String?
public let agentid: String?
public let agentscope: String?
public let startdate: String?
public let enddate: String?
public let mode: AnyCodable?
@@ -2392,6 +2393,7 @@ public struct SessionsUsageParams: Codable, Sendable {
public init(
key: String?,
agentid: String? = nil,
agentscope: String? = nil,
startdate: String?,
enddate: String?,
mode: AnyCodable?,
@@ -2404,6 +2406,7 @@ public struct SessionsUsageParams: Codable, Sendable {
{
self.key = key
self.agentid = agentid
self.agentscope = agentscope
self.startdate = startdate
self.enddate = enddate
self.mode = mode
@@ -2418,6 +2421,7 @@ public struct SessionsUsageParams: Codable, Sendable {
private enum CodingKeys: String, CodingKey {
case key
case agentid = "agentId"
case agentscope = "agentScope"
case startdate = "startDate"
case enddate = "endDate"
case mode
@@ -5458,6 +5462,8 @@ public struct CronListParams: Codable, Sendable {
public let offset: Int?
public let query: String?
public let enabled: AnyCodable?
public let schedulekind: AnyCodable?
public let lastrunstatus: AnyCodable?
public let sortby: AnyCodable?
public let sortdir: AnyCodable?
public let agentid: String?
@@ -5468,6 +5474,8 @@ public struct CronListParams: Codable, Sendable {
offset: Int?,
query: String?,
enabled: AnyCodable?,
schedulekind: AnyCodable?,
lastrunstatus: AnyCodable?,
sortby: AnyCodable?,
sortdir: AnyCodable?,
agentid: String?)
@@ -5477,6 +5485,8 @@ public struct CronListParams: Codable, Sendable {
self.offset = offset
self.query = query
self.enabled = enabled
self.schedulekind = schedulekind
self.lastrunstatus = lastrunstatus
self.sortby = sortby
self.sortdir = sortdir
self.agentid = agentid
@@ -5488,6 +5498,8 @@ public struct CronListParams: Codable, Sendable {
case offset
case query
case enabled
case schedulekind = "scheduleKind"
case lastrunstatus = "lastRunStatus"
case sortby = "sortBy"
case sortdir = "sortDir"
case agentid = "agentId"

View File

@@ -7,7 +7,7 @@ on:
jobs:
build-and-test:
runs-on: macos-latest
runs-on: macos-15
defaults:
run:
shell: bash

View File

@@ -27,7 +27,7 @@ const bundledPluginEntries = [
"setup-entry.ts!",
"{api,contract-api,helper-api,runtime-api,light-runtime-api,update-offset-runtime-api,channel-plugin-api,provider-plugin-api,setup-api}.ts!",
"subagent-hooks-api.ts!",
"src/{api,runtime-api,light-runtime-api,update-offset-runtime-api,channel-plugin-api,provider-plugin-api,doctor-contract,setup-surface}.ts!",
"src/{api,runtime-api,light-runtime-api,update-offset-runtime-api,channel-plugin-api,provider-plugin-api,doctor-contract,setup-surface,mcp-serve}.ts!",
"src/subagent-hooks-api.ts!",
] as const;
@@ -50,7 +50,7 @@ const bundledPluginIgnoredRuntimeDependencies = [
"lit",
"linkedom",
"openclaw",
"pdfjs-dist",
"clawpdf",
] as const;
const rootBundledPluginRuntimeDependencies = [
@@ -70,7 +70,7 @@ const rootBundledPluginRuntimeDependencies = [
"minimatch",
"node-edge-tts",
"openshell",
"pdfjs-dist",
"clawpdf",
"tokenjuice",
] as const;
@@ -168,6 +168,19 @@ const config = {
entry: ["src/index.ts!", "src/*.ts!", "src/harness/**/*.ts!"],
project: ["src/**/*.ts!"],
},
"packages/gateway-client": {
entry: ["src/index.ts!"],
project: ["src/**/*.ts!"],
},
"packages/gateway-protocol": {
entry: ["src/index.ts!", "src/schema.ts!"],
project: ["src/**/*.ts!"],
},
"packages/speech-core": {
entry: ["api.ts!", "runtime-api.ts!", "speaker.ts!", "voice-models.ts!"],
project: ["**/*.ts!"],
ignoreDependencies: ["openclaw"],
},
"packages/*": {
entry: ["index.js!", "scripts/postinstall.js!"],
project: ["index.js!", "scripts/**/*.js!"],

View File

@@ -63,6 +63,7 @@ services:
ports:
- "${OPENCLAW_GATEWAY_PORT:-18789}:18789"
- "${OPENCLAW_BRIDGE_PORT:-18790}:18790"
- "${OPENCLAW_MSTEAMS_PORT:-3978}:3978"
init: true
restart: unless-stopped
command:

View File

@@ -1,4 +1,4 @@
a69acd971a7d54d3086f26c52fde4084eaeef350f71b918fb8e7338f329bff95 config-baseline.json
ee4c0f0fb15cda02268f2e83d0c5e1c8d0ec0a2c1b2fdb89cdfce308dadb2b8b config-baseline.core.json
b901fb766edfd9df630690281476fc4032c64772f69d1d8f7b2e0e913a90f229 config-baseline.channel.json
1b763a5524aca2d7ecf1eea38f845ad1ffed5c1b37e85e62f6a7902a3ee0f920 config-baseline.plugin.json
d037e191137195d05b4136ae7934b4da4a31756ea824e4813ea1ba7743ca2f5c config-baseline.json
c460b34e53eae7a8a1208043ecfe17e8673d4eea6ffdafed6293460b30217934 config-baseline.core.json
6fea71ca36eafc07c4a67abef806177fd58914b57b1bf9a73502b2efc8f873a3 config-baseline.channel.json
865a35e8a3d38cb2832b658c3663334bf72b8ad638cace5e14005829c65f6d71 config-baseline.plugin.json

View File

@@ -1,2 +1,2 @@
7039b60f2cea732a90db633328952faaddd919f0d098b303b29d554e64184073 plugin-sdk-api-baseline.json
1a78f4df81562af070c5379c6369a8bea9c704f985b5382a463364757b26db0d plugin-sdk-api-baseline.jsonl
260286745285203c06b49fb0397217439bd8ab48be87b9de05fd62603bd720c1 plugin-sdk-api-baseline.json
9934014d22861b813f02c3fcb01e3b79d13a47d06a75e9096f2465f3c2f1dd5a plugin-sdk-api-baseline.jsonl

View File

@@ -175,6 +175,26 @@
"source": "Agent harness plugins",
"target": "Agent harness plugins"
},
{
"source": "Agent harness plugins (SDK reference)",
"target": "Agent harness plugins (SDK reference)"
},
{
"source": "Copilot SDK harness",
"target": "Copilot SDK harness"
},
{
"source": "Copilot plugin",
"target": "Copilot plugin"
},
{
"source": "GitHub Copilot agent runtime",
"target": "GitHub Copilot agent runtime"
},
{
"source": "copilot",
"target": "copilot"
},
{
"source": "Agent loop",
"target": "Agent loop"

View File

@@ -15,9 +15,8 @@ Cron is the Gateway's built-in scheduler. It persists jobs, wakes the agent at t
<Steps>
<Step title="Add a one-shot reminder">
```bash
openclaw cron add \
openclaw cron create "2026-02-01T16:00:00Z" \
--name "Reminder" \
--at "2026-02-01T16:00:00Z" \
--session main \
--system-event "Reminder: check the cron docs draft" \
--wake now \
@@ -146,6 +145,8 @@ This fires ~56 times per month instead of 01 times per month. OpenClaw use
Cron jobs can also carry payload-level `fallbacks`. When present, that list replaces the configured fallback chain for the job. Use `fallbacks: []` in the job payload/API when you want a strict cron run that tries only the selected model. If a job has `--model` but neither payload nor configured fallbacks, OpenClaw passes an explicit empty fallback override so the agent primary is not appended as a hidden extra retry target.
Local-provider preflight checks walk configured fallbacks before marking a cron run `skipped`; `fallbacks: []` keeps that preflight path strict.
Model-selection precedence for isolated jobs is:
1. Gmail hook model override (when the run came from Gmail and that override is allowed)
@@ -215,12 +216,11 @@ Failure notifications follow a separate destination path:
</Tab>
<Tab title="Recurring isolated job">
```bash
openclaw cron add \
openclaw cron create "0 7 * * *" \
"Summarize overnight updates." \
--name "Morning brief" \
--cron "0 7 * * *" \
--tz "America/Los_Angeles" \
--session isolated \
--message "Summarize overnight updates." \
--announce \
--channel slack \
--to "channel:C1234567890"
@@ -239,6 +239,14 @@ Failure notifications follow a separate destination path:
--announce
```
</Tab>
<Tab title="Webhook output">
```bash
openclaw cron create "0 18 * * 1-5" \
"Summarize today's deploys as JSON." \
--name "Deploy digest" \
--webhook "https://example.invalid/openclaw/cron"
```
</Tab>
</Tabs>
## Webhooks
@@ -411,12 +419,14 @@ openclaw cron runs --id <jobId> --run-id <runId>
openclaw cron remove <jobId>
# Agent selection (multi-agent setups)
openclaw cron add --name "Ops sweep" --cron "0 6 * * *" --session isolated --message "Check ops queue" --agent ops
openclaw cron create "0 6 * * *" "Check ops queue" --name "Ops sweep" --session isolated --agent ops
openclaw cron edit <jobId> --clear-agent
```
`openclaw cron run <jobId>` returns after enqueueing the manual run. Use `--wait` for shutdown hooks, maintenance scripts, or other automation that must block until the queued run finishes. Wait mode polls the exact returned `runId`; it exits `0` for status `ok` and non-zero for `error`, `skipped`, or a wait timeout.
`openclaw cron create` is an alias for `openclaw cron add`, and new jobs can use a positional schedule (`"0 9 * * 1"`, `"every 1h"`, `"20m"`, or an ISO timestamp) followed by a positional agent prompt. Use `--webhook <url>` on `cron add|create` or `cron edit` to POST the finished run payload to an HTTP endpoint. Webhook delivery cannot be combined with chat delivery flags such as `--announce`, `--channel`, `--to`, `--thread-id`, or `--account`.
<Note>
Model override note:

View File

@@ -696,6 +696,7 @@ Default slash command settings:
maxLines: 8,
maxLineChars: 120,
toolProgress: true,
commentary: false,
},
},
},
@@ -708,6 +709,7 @@ Default slash command settings:
- Media, error, and explicit-reply finals cancel pending preview edits.
- `streaming.preview.toolProgress` (default `true`) controls whether tool/progress updates reuse the preview message.
- Tool/progress rows render as compact emoji + title + detail when available, for example `🛠️ Bash: run tests` or `🔎 Web Search: for "query"`.
- `streaming.progress.commentary` (default `false`) opts into assistant commentary/preamble text in the temporary progress draft. Commentary is cleaned before display, stays transient, and does not change final answer delivery.
- `streaming.progress.maxLineChars` controls the per-line progress preview budget. Prose is shortened on word boundaries; command and path details keep useful suffixes.
- `streaming.preview.commandText` / `streaming.progress.commandText` controls command/exec detail in compact progress lines: `raw` (default) or `status` (tool label only).
@@ -1215,7 +1217,7 @@ Auto-join example:
realtime: {
provider: "openai",
model: "gpt-realtime-2",
voice: "cedar",
speakerVoice: "cedar",
},
},
},
@@ -1225,20 +1227,20 @@ Auto-join example:
Notes:
- `voice.tts` overrides `messages.tts` for `stt-tts` voice playback only. Realtime modes use `voice.realtime.voice`.
- `voice.tts` overrides `messages.tts` for `stt-tts` voice playback only. Realtime modes use `voice.realtime.speakerVoice`.
- `voice.mode` controls the conversation path. The default is `agent-proxy`: a realtime voice front end handles turn timing, interruption, and playback, delegates substantive work to the routed OpenClaw agent through `openclaw_agent_consult`, and treats the result like a typed Discord prompt from that speaker. `stt-tts` keeps the older batch STT plus TTS flow. `bidi` lets the realtime model converse directly while exposing `openclaw_agent_consult` for the OpenClaw brain.
- `voice.agentSession` controls which OpenClaw conversation receives voice turns. Leave it unset for the voice channel's own session, or set `{ mode: "target", target: "channel:<text-channel-id>" }` to make the voice channel act as the microphone/speaker extension of an existing Discord text channel session such as `#maintainers`.
- `voice.model` overrides the OpenClaw agent brain for Discord voice responses and realtime consults. Leave it unset to inherit the routed agent model. It is separate from `voice.realtime.model`.
- `voice.followUsers` lets the bot join, move, and leave Discord voice with selected users. See [Follow users in voice](#follow-users-in-voice) for behavior rules and examples.
- `agent-proxy` routes speech through `discord-voice`, which preserves normal owner/tool authorization for the speaker and target session but hides the agent `tts` tool because Discord voice owns playback. By default, `agent-proxy` gives the consult full owner-equivalent tool access for owner speakers (`voice.realtime.toolPolicy: "owner"`) and strongly prefers consulting the OpenClaw agent before substantive answers (`voice.realtime.consultPolicy: "always"`). In that default `always` mode, the realtime layer does not auto-speak filler before the consult answer; it captures and transcribes speech, then speaks the routed OpenClaw answer. If multiple forced consult answers finish while Discord is still playing the first answer, later exact-speech answers are queued until playback idles instead of replacing speech mid-sentence.
- In `stt-tts` mode, STT uses `tools.media.audio`; `voice.model` does not affect transcription.
- In realtime modes, `voice.realtime.provider`, `voice.realtime.model`, and `voice.realtime.voice` configure the realtime audio session. For OpenAI Realtime 2 plus the Codex brain, use `voice.realtime.model: "gpt-realtime-2"` and `voice.model: "openai-codex/gpt-5.5"`.
- In realtime modes, `voice.realtime.provider`, `voice.realtime.model`, and `voice.realtime.speakerVoice` configure the realtime audio session. For OpenAI Realtime 2 plus the Codex brain, use `voice.realtime.model: "gpt-realtime-2"` and `voice.model: "openai-codex/gpt-5.5"`.
- Realtime voice modes include small `IDENTITY.md`, `USER.md`, and `SOUL.md` profile files in the realtime provider instructions by default so fast direct turns keep the same identity, user grounding, and persona as the routed OpenClaw agent. Set `voice.realtime.bootstrapContextFiles` to a subset to customize this, or `[]` to disable it. The supported realtime bootstrap files are limited to those profile files; `AGENTS.md` stays in the normal agent context. The injected profile context does not replace `openclaw_agent_consult` for workspace work, current facts, memory lookup, or tool-backed actions.
- In OpenAI `agent-proxy` realtime mode, set `voice.realtime.requireWakeName: true` to keep Discord realtime voice silent until a transcript starts or ends with a wake name. Configured wake names must be one or two words. If `voice.realtime.wakeNames` is unset, OpenClaw uses the routed agent `name` plus `OpenClaw`, falling back to the agent id plus `OpenClaw`. Wake-name gating disables realtime provider auto-response, routes accepted turns through the OpenClaw agent consult path, and gives a short spoken acknowledgement when a leading wake name is recognized from partial transcription before the final transcript arrives.
- The OpenAI realtime provider accepts current Realtime 2 event names and legacy Codex-compatible aliases for output audio and transcript events, so compatible provider snapshots can drift without dropping assistant audio.
- `voice.realtime.bargeIn` controls whether Discord speaker-start events interrupt active realtime playback. If unset, it follows the realtime provider's input-audio interruption setting.
- `voice.realtime.minBargeInAudioEndMs` controls the minimum assistant playback duration before an OpenAI realtime barge-in truncates audio. Default: `250`. Set `0` for immediate interruption in low-echo rooms, or raise it for echo-heavy speaker setups.
- For an OpenAI voice on Discord playback, set `voice.tts.provider: "openai"` and choose a Text-to-speech voice under `voice.tts.openai.voice` or `voice.tts.providers.openai.voice`. `cedar` is a good masculine-sounding choice on the current OpenAI TTS model.
- For an OpenAI voice on Discord playback, set `voice.tts.provider: "openai"` and choose a Text-to-speech voice under `voice.tts.providers.openai.speakerVoice`. `cedar` is a good masculine-sounding choice on the current OpenAI TTS model.
- Per-channel Discord `systemPrompt` overrides apply to voice transcript turns for that voice channel.
- Voice transcript turns derive owner status from Discord `allowFrom` (or `dm.allowFrom`) for owner-gated commands and channel actions. Agent tool visibility follows the configured tool policy for the routed session.
- Discord voice is opt-in for text-only configs; set `channels.discord.voice.enabled=true` (or keep an existing `channels.discord.voice` block) to enable `/vc` commands, the voice runtime, and the `GuildVoiceStates` gateway intent.
@@ -1329,7 +1331,7 @@ Default agent-proxy voice-channel session example:
realtime: {
provider: "openai",
model: "gpt-realtime-2",
voice: "cedar",
speakerVoice: "cedar",
},
},
},
@@ -1351,9 +1353,11 @@ Legacy STT plus TTS example:
model: "openai/gpt-5.4-mini",
tts: {
provider: "openai",
openai: {
model: "gpt-4o-mini-tts",
voice: "cedar",
providers: {
openai: {
model: "gpt-4o-mini-tts",
speakerVoice: "cedar",
},
},
},
},
@@ -1375,7 +1379,7 @@ Realtime bidi example:
realtime: {
provider: "openai",
model: "gpt-realtime-2",
voice: "cedar",
speakerVoice: "cedar",
toolPolicy: "safe-read-only",
consultPolicy: "always",
},
@@ -1402,7 +1406,7 @@ Voice as an extension of an existing Discord channel session:
realtime: {
provider: "openai",
model: "gpt-realtime-2",
voice: "cedar",
speakerVoice: "cedar",
},
},
},
@@ -1433,7 +1437,7 @@ Echo-heavy OpenAI Realtime example:
realtime: {
provider: "openai",
model: "gpt-realtime-2",
voice: "cedar",
speakerVoice: "cedar",
bargeIn: true,
minBargeInAudioEndMs: 500,
consultPolicy: "always",

View File

@@ -666,6 +666,58 @@ Teams delivers messages via HTTP webhook. If processing takes too long (e.g., sl
OpenClaw handles this by returning quickly and sending replies proactively, but very slow responses may still cause issues.
### Teams cloud and service URL support
This SDK-backed Teams path is live-validated for Microsoft Teams public cloud.
Inbound replies use the incoming Teams SDK turn context. Out-of-context proactive operations - sends, edits, deletes, cards, polls, file-consent messages, and queued long-running replies - use the stored conversation reference `serviceUrl`. Public cloud defaults to the Teams SDK public cloud environment and allows stored references on the public Teams Connector host: `https://smba.trafficmanager.net/`.
Public cloud is the default. You do not need to set `channels.msteams.cloud` or `channels.msteams.serviceUrl` for normal public-cloud bots.
For non-public Teams clouds, set `cloud` and the matching proactive boundary when Microsoft publishes one:
- `channels.msteams.cloud` selects the Teams SDK cloud preset for authentication, JWT validation, token services, and Graph scope.
- `channels.msteams.serviceUrl` selects the Bot Connector endpoint boundary used to validate stored conversation references before proactive sends, edits, deletes, cards, polls, file-consent messages, and queued long-running replies. It is required for USGov and DoD SDK clouds. For China/21Vianet, OpenClaw uses the SDK `China` preset and accepts stored/configured service URLs only on Azure China Bot Framework channel hosts.
Microsoft publishes the global proactive Bot Connector endpoints in the [Create the conversation](https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages?tabs=dotnet#create-the-conversation) section of the Teams proactive messaging docs. Use the incoming activity's `serviceUrl` when available; if you need a global proactive endpoint, use Microsoft's table.
| Teams environment | OpenClaw config | Proactive `serviceUrl` |
| ----------------- | ----------------------------------------------------------- | -------------------------------------------------- |
| Public | no cloud/serviceUrl config needed | `https://smba.trafficmanager.net/teams` |
| GCC | set `serviceUrl`; no separate Teams SDK cloud preset exists | `https://smba.infra.gcc.teams.microsoft.com/teams` |
| GCC High | `cloud: "USGov"` + `serviceUrl` | `https://smba.infra.gov.teams.microsoft.us/teams` |
| DoD | `cloud: "USGovDoD"` + `serviceUrl` | `https://smba.infra.dod.teams.microsoft.us/teams` |
| China/21Vianet | `cloud: "China"` | use the incoming activity's `serviceUrl` |
Example for GCC, where Microsoft documents a separate proactive service URL but the Teams SDK does not expose a separate GCC cloud preset:
```json
{
"channels": {
"msteams": {
"serviceUrl": "https://smba.infra.gcc.teams.microsoft.com/teams"
}
}
}
```
Example for GCC High:
```json
{
"channels": {
"msteams": {
"cloud": "USGov",
"serviceUrl": "https://smba.infra.gov.teams.microsoft.us/teams"
}
}
}
```
`channels.msteams.serviceUrl` is restricted to supported Microsoft Teams Bot Connector hosts. When a service URL is configured, OpenClaw checks that the stored conversation `serviceUrl` uses the same host before proactive sends, edits, deletes, cards, polls, or queued long-running replies run. With the default public-cloud config, OpenClaw fails closed if a stored conversation points outside the public Teams Connector host. Receive a fresh message from the conversation after changing cloud/service URL settings so the stored conversation reference is current.
China/21Vianet does not have a separate global proactive `smba` URL in Microsoft's Teams proactive endpoint table. Configure `cloud: "China"` so the Teams SDK uses Azure China auth, token, and JWT endpoints. Proactive sends then require a stored conversation reference from an incoming China Teams activity, or an explicitly configured service URL, on the Azure China Bot Framework channel boundary (`*.botframework.azure.cn`). Graph-backed Teams helpers are currently disabled for `cloud: "China"` until OpenClaw routes Graph requests through the Azure China Graph endpoint.
### Formatting
Teams markdown is more limited than Slack or Discord:
@@ -680,6 +732,8 @@ Key settings (see `/gateway/configuration` for shared channel patterns):
- `channels.msteams.enabled`: enable/disable the channel.
- `channels.msteams.appId`, `channels.msteams.appPassword`, `channels.msteams.tenantId`: bot credentials.
- `channels.msteams.cloud`: Teams SDK cloud environment (`Public`, `USGov`, `USGovDoD`, or `China`; default `Public`). Set this with `serviceUrl` for USGov/DoD SDK clouds; China uses the SDK preset and stored Azure China Bot Framework conversation references, with Graph-backed helpers disabled until Azure China Graph routing is implemented.
- `channels.msteams.serviceUrl`: Bot Connector service URL boundary for SDK proactive operations. Public cloud uses the SDK default; set this for GCC (`https://smba.infra.gcc.teams.microsoft.com/teams`), GCC High, or DoD. China accepts Azure China Bot Framework channel hosts when the stored conversation reference comes from Teams operated by 21Vianet.
- `channels.msteams.webhook.port` (default `3978`)
- `channels.msteams.webhook.path` (default `/api/messages`)
- `channels.msteams.dmPolicy`: `pairing | allowlist | open | disabled` (default: pairing)

View File

@@ -1120,6 +1120,8 @@ Hide raw command/exec text while keeping compact progress lines:
`channels.slack.streaming.nativeTransport` controls Slack native text streaming when `channels.slack.streaming.mode` is `partial` (default: `true`).
Slack native progress task cards are opt-in for progress mode. Set `channels.slack.streaming.progress.nativeTaskCards` to `true` with `channels.slack.streaming.mode="progress"` to send a Slack-native plan/task card while work is running, then update the same task card at completion. Without this flag, progress mode keeps the portable draft-preview behavior.
- A reply thread must be available for native text streaming and Slack assistant thread status to appear. Thread selection still follows `replyToMode`.
- Channel, group-chat, and top-level DM roots can still use the normal draft preview when native streaming is unavailable or no reply thread exists.
- Top-level Slack DMs stay off-thread by default, so they do not show Slack's thread-style native stream/status preview; OpenClaw posts and edits a draft preview in the DM instead.
@@ -1142,6 +1144,24 @@ Use draft preview instead of Slack native text streaming:
}
```
Opt in to Slack native progress task cards:
```json5
{
channels: {
slack: {
streaming: {
mode: "progress",
progress: {
nativeTaskCards: true,
render: "rich",
},
},
},
},
}
```
Legacy keys:
- `channels.slack.streamMode` (`replace | status_final | append`) is a legacy runtime alias for `channels.slack.streaming.mode`.

View File

@@ -43,7 +43,9 @@ OpenClaw CI runs on every push to `main` and every pull request. The `preflight`
GitHub may mark superseded jobs as `cancelled` when a newer push lands on the same PR or `main` ref. Treat that as CI noise unless the newest run for the same ref is also failing. Matrix jobs use `fail-fast: false`, and `build-artifacts` reports embedded channel, core-support-boundary, and gateway-watch failures directly instead of queuing tiny verifier jobs. The automatic CI concurrency key is versioned (`CI-v7-*`) so a GitHub-side zombie in an old queue group cannot indefinitely block newer main runs. Manual full-suite runs use `CI-manual-v1-*` and do not cancel in-progress runs.
The `ci-timings-summary` job uploads a compact `ci-timings-summary` artifact for each non-draft CI run. It records wall time, queue time, slowest jobs, and failed jobs for the current run, so CI health checks do not need to scrape the full Actions payload repeatedly. The `build-artifacts` job also runs the blocking startup-memory smoke and uploads a `startup-memory` artifact with per-command RSS values for `--help`, `status --json`, and `gateway status`.
Use `pnpm ci:timings`, `pnpm ci:timings:recent`, or `node scripts/ci-run-timings.mjs <run-id>` to summarize wall time, queue time, slowest jobs, failures, and the `pnpm-store-warmup` fanout barrier from GitHub Actions. CI also uploads the same run summary as a `ci-timings-summary` artifact. For build timing, check the `build-artifacts` job's `Build dist` step: `pnpm build:ci-artifacts` prints `[build-all] phase timings:` and includes `ui:build`; the job also uploads the `startup-memory` artifact.
For pull request runs, the terminal timing-summary job runs the helper from the trusted base revision before passing `GH_TOKEN` to `gh run view`. That keeps the tokened query out of branch-controlled code while still summarizing the pull request's current CI run.
## Real behavior proof
@@ -120,17 +122,17 @@ gh workflow run full-release-validation.yml --ref main -f ref=<branch-or-sha>
## Runners
| Runner | Jobs |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ubuntu-24.04` | `preflight`, docs checks, Python skills, workflow-sanity, labeler, auto-response; install-smoke preflight also uses GitHub-hosted Ubuntu so the Blacksmith matrix can queue earlier |
| `blacksmith-4vcpu-ubuntu-2404` | `CodeQL Critical Quality`, `security-fast`, lower-weight extension shards, `checks-fast-core`, plugin/channel contract shards, `checks-node-compat-node22`, `check-guards`, `check-prod-types`, and `check-test-types` |
| `blacksmith-8vcpu-ubuntu-2404` | Linux Node test shards, bundled plugin test shards, `check-additional-*` shards, `android` |
| `blacksmith-16vcpu-ubuntu-2404` | `build-artifacts`, `check-lint` (CPU-sensitive enough that 8 vCPU cost more than they saved); install-smoke Docker builds (32-vCPU queue time cost more than it saved) |
| `blacksmith-16vcpu-windows-2025` | `checks-windows` |
| `blacksmith-6vcpu-macos-latest` | `macos-node` on `openclaw/openclaw`; forks fall back to `macos-latest` |
| `blacksmith-12vcpu-macos-latest` | `macos-swift` on `openclaw/openclaw`; forks fall back to `macos-latest` |
| Runner | Jobs |
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ubuntu-24.04` | Manual CI dispatch and non-canonical repository fallbacks, workflow-sanity, labeler, auto-response, docs workflows outside CI, and install-smoke preflight so the Blacksmith matrix can queue earlier |
| `blacksmith-4vcpu-ubuntu-2404` | `CodeQL Critical Quality`, `preflight`, `security-fast`, lower-weight extension shards, `checks-fast-core`, plugin/channel contract shards, `checks-node-compat-node22`, `check-guards`, `check-prod-types`, and `check-test-types` |
| `blacksmith-8vcpu-ubuntu-2404` | Linux Node test shards, bundled plugin test shards, `check-additional-*` shards, `check-dependencies`, and `android` |
| `blacksmith-16vcpu-ubuntu-2404` | `build-artifacts`, `check-lint` (CPU-sensitive enough that 8 vCPU cost more than they saved); install-smoke Docker builds (32-vCPU queue time cost more than it saved) |
| `blacksmith-16vcpu-windows-2025` | `checks-windows` |
| `blacksmith-6vcpu-macos-15` | `macos-node` on `openclaw/openclaw`; forks fall back to `macos-15` |
| `blacksmith-12vcpu-macos-26` | `macos-swift` on `openclaw/openclaw`; forks fall back to `macos-26` |
Canonical-repo CI keeps Blacksmith as the default runner path. During `preflight`, `scripts/ci-runner-labels.mjs` checks recent queued and in-progress Actions runs for queued Blacksmith jobs. If a specific Blacksmith label already has queued jobs, downstream jobs that would use that exact label fall back to the matching GitHub-hosted runner (`ubuntu-24.04`, `windows-2025`, or `macos-latest`) for that run only. Other Blacksmith sizes in the same OS family stay on their primary labels. If the API probe fails, no fallback is applied.
Canonical-repo CI keeps Blacksmith as the default runner path for normal push and pull-request runs. `workflow_dispatch` and non-canonical repository runs use GitHub-hosted runners, but normal canonical runs do not currently probe Blacksmith queue health or automatically fall back to GitHub-hosted labels when Blacksmith is unavailable.
## Local equivalents

View File

@@ -157,8 +157,8 @@ order and tells you what it chose:
- existing explicit model, if already configured
- `OPENAI_API_KEY` -> `openai/gpt-5.5`
- `ANTHROPIC_API_KEY` -> `anthropic/claude-opus-4-7`
- Claude Code CLI -> `claude-cli/claude-opus-4-7`
- `ANTHROPIC_API_KEY` -> `anthropic/claude-opus-4-8`
- Claude Code CLI -> `claude-cli/claude-opus-4-8`
- Codex -> `openai/gpt-5.5` through the Codex app-server harness
If none are available, setup still writes the default workspace and leaves the
@@ -173,7 +173,7 @@ planner turn through OpenClaw's normal runtime paths. It first uses the
configured OpenClaw model. If no configured model is usable yet, it can fall
back to local runtimes already present on the machine:
- Claude Code CLI: `claude-cli/claude-opus-4-7`
- Claude Code CLI: `claude-cli/claude-opus-4-8`
- Codex app-server harness: `openai/gpt-5.5`
The model-assisted planner cannot mutate config directly. It must translate the

View File

@@ -14,6 +14,26 @@ Manage cron jobs for the Gateway scheduler.
Run `openclaw cron --help` for the full command surface. See [Cron jobs](/automation/cron-jobs) for the conceptual guide.
</Tip>
## Create jobs quickly
`openclaw cron create` is an alias for `openclaw cron add`. For new jobs, put the schedule first and the prompt second:
```bash
openclaw cron create "0 7 * * *" \
"Summarize overnight updates." \
--name "Morning brief" \
--agent ops
```
Use `--webhook <url>` when the job should POST the finished payload instead of delivering to a chat target:
```bash
openclaw cron create "0 18 * * 1-5" \
"Summarize today's deploys as JSON." \
--name "Deploy digest" \
--webhook "https://example.invalid/openclaw/cron"
```
## Sessions
`--session` accepts `main`, `isolated`, `current`, or `session:<id>`.
@@ -50,6 +70,8 @@ Isolated cron chat delivery is shared between the agent and the runner:
- `webhook` posts the finished payload to a URL.
- `none` disables runner fallback delivery.
Use `cron add|create --webhook <url>` or `cron edit <job-id> --webhook <url>` to set webhook delivery. Do not combine `--webhook` with chat delivery flags such as `--announce`, `--no-deliver`, `--channel`, `--to`, `--thread-id`, or `--account`.
`--announce` is runner fallback delivery for the final reply. `--no-deliver` disables that fallback but does not remove the agent's `message` tool when a chat route is available.
Reminders created from an active chat preserve the live chat delivery target for fallback announce delivery. Internal session keys may be lowercase; do not use them as a source of truth for case-sensitive provider IDs such as Matrix room IDs.
@@ -133,6 +155,7 @@ Cron `--model` is a **job primary**, not a chat-session `/model` override. That
- Per-job payload `fallbacks` replaces the configured fallback list when present.
- An empty per-job fallback list (`fallbacks: []` in the job payload/API) makes the cron run strict.
- When a job has `--model` but no fallback list is configured, OpenClaw passes an explicit empty fallback override so the agent primary is not appended as a hidden retry target.
- Local-provider preflight checks walk configured fallbacks before marking a cron run `skipped`.
`openclaw doctor` reports jobs that already have `payload.model` set, including provider namespace counts and mismatches against `agents.defaults.model`. Use that check when auth, provider, or billing behavior looks different between live chat and scheduled jobs.
@@ -219,11 +242,10 @@ openclaw cron edit <job-id> --announce --channel telegram --to "-1001234567890"
Create an isolated job with lightweight bootstrap context:
```bash
openclaw cron add \
openclaw cron create "0 7 * * *" \
"Summarize overnight updates." \
--name "Lightweight morning brief" \
--cron "0 7 * * *" \
--session isolated \
--message "Summarize overnight updates." \
--light-context \
--no-deliver
```
@@ -270,6 +292,7 @@ Delivery tweaks:
```bash
openclaw cron edit <job-id> --announce --channel slack --to "channel:C1234567890"
openclaw cron edit <job-id> --webhook "https://example.invalid/openclaw/cron"
openclaw cron edit <job-id> --best-effort-deliver
openclaw cron edit <job-id> --no-best-effort-deliver
openclaw cron edit <job-id> --no-deliver

View File

@@ -40,6 +40,7 @@ openclaw doctor
openclaw doctor --lint
openclaw doctor --lint --json
openclaw doctor --lint --severity-min warning
openclaw doctor --lint --allow-exec
openclaw doctor --deep
openclaw doctor --fix
openclaw doctor --fix --non-interactive
@@ -64,6 +65,7 @@ The targeted Discord capabilities probe reports the bot's effective channel perm
- `--force`: apply aggressive repairs, including overwriting custom service config when needed
- `--non-interactive`: run without prompts; safe migrations and non-service repairs only
- `--generate-gateway-token`: generate and configure a gateway token
- `--allow-exec`: allow doctor to execute configured exec SecretRefs while verifying secrets
- `--deep`: scan system services for extra gateway installs and report recent Gateway supervisor restart handoffs
- `--lint`: run modernized health checks in read-only mode and emit diagnostic findings
- `--json`: with `--lint`, emit JSON findings instead of human output
@@ -84,6 +86,7 @@ are only accepted with `--lint`.
openclaw doctor --lint
openclaw doctor --lint --severity-min warning
openclaw doctor --lint --json
openclaw doctor --lint --allow-exec
openclaw doctor --lint --only core/doctor/gateway-config --json
```
@@ -191,6 +194,7 @@ Notes:
- Interactive prompts (like keychain/OAuth fixes) only run when stdin is a TTY and `--non-interactive` is **not** set. Headless runs (cron, Telegram, no terminal) will skip prompts.
- Performance: non-interactive `doctor` runs skip eager plugin loading so headless health checks stay fast. Interactive doctor sessions still load the plugin surfaces needed by the legacy health and repair flow.
- `--lint` is stricter than `--non-interactive`: it is always read-only, never prompts, and never applies safe migrations. Run `doctor --fix` or `doctor --repair` when you want doctor to make changes.
- By default, doctor does not execute `exec` SecretRefs while checking secrets. Use `openclaw doctor --allow-exec` or `openclaw doctor --lint --allow-exec` only when you intentionally want doctor to run those configured secret resolvers.
- `--fix` (alias for `--repair`) writes a backup to `~/.openclaw/openclaw.json.bak` and drops unknown config keys, listing each removal.
- Modernized health checks can expose a `repair()` path for `doctor --fix`; checks that do not expose one continue through the existing doctor repair flow.
- `doctor --fix --non-interactive` reports missing or stale gateway service definitions but does not install or rewrite them outside update repair mode. Run `openclaw gateway install` for a missing service, or `openclaw gateway install --force` when you intentionally want to replace the launcher.
@@ -214,7 +218,7 @@ Notes:
- 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 `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.
- 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.
- Telegram `allowFrom` username auto-resolution (`doctor --fix`) requires a resolvable Telegram token in the current command path. If token inspection is unavailable, doctor reports a warning and skips auto-resolution for that pass.

View File

@@ -170,7 +170,7 @@ Notes:
- `memory status` includes any extra paths configured via `memorySearch.extraPaths`.
- If effectively active memory remote API key fields are configured as SecretRefs, the command resolves those values from the active gateway snapshot. If gateway is unavailable, the command fails fast.
- Gateway version skew note: this command path requires a gateway that supports `secrets.resolve`; older gateways return an unknown-method error.
- Tune scheduled sweep cadence with `dreaming.frequency`. Deep promotion policy is otherwise internal; use CLI flags on `memory promote` when you need one-off manual overrides.
- Tune scheduled sweep cadence with `dreaming.frequency`. Deep promotion policy is otherwise internal except for `dreaming.phases.deep.maxPromotedSnippetTokens`, which bounds promoted snippet length while keeping provenance visible. Use CLI flags on `memory promote` when you need one-off manual threshold overrides.
- `memory rem-harness --path <file-or-dir> --grounded` previews grounded `What Happened`, `Reflections`, and `Possible Lasting Updates` from historical daily notes without writing anything.
- `memory rem-backfill --path <file-or-dir>` writes reversible grounded diary entries into `DREAMS.md` for UI review.
- `memory rem-backfill --path <file-or-dir> --stage-short-term` also seeds grounded durable candidates into the live short-term promotion store so the normal deep phase can rank them.

View File

@@ -169,7 +169,7 @@ is available, then fall back to `latest`.
<Accordion title="Hook packs and npm specs">
`plugins install` is also the install surface for hook packs that expose `openclaw.hooks` in `package.json`. Use `openclaw hooks` for filtered hook visibility and per-hook enablement, not package installation.
Npm specs are **registry-only** (package name + optional **exact version** or **dist-tag**). Git/URL/file specs and semver ranges are rejected. Dependency installs run project-local with `--ignore-scripts` for safety, even when your shell has global npm install settings. Managed plugin npm roots inherit OpenClaw's package-level npm `overrides`, so host security pins apply to hoisted plugin dependencies too.
Npm specs are **registry-only** (package name + optional **exact version** or **dist-tag**). Git/URL/file specs and semver ranges are rejected. Dependency installs run in one managed npm project per plugin with `--ignore-scripts` for safety, even when your shell has global npm install settings. Managed plugin npm projects inherit OpenClaw's package-level npm `overrides`, so host security pins apply to hoisted plugin dependencies too.
Use `npm:<package>` when you want to make npm resolution explicit. Bare package specs also install directly from npm during the launch cutover unless they match an official plugin id.
@@ -177,6 +177,8 @@ is available, then fall back to `latest`.
Bare specs and `@latest` stay on the stable track. OpenClaw date-stamped correction versions such as `2026.5.3-1` are stable releases for this check. If npm resolves either of those to a prerelease, OpenClaw stops and asks you to opt in explicitly with a prerelease tag such as `@beta`/`@rc` or an exact prerelease version such as `@1.2.3-beta.4`.
For npm installs without an exact version (`npm:<package>` or `npm:<package>@latest`), OpenClaw checks the resolved package metadata before install. If the latest stable package requires a newer OpenClaw plugin API or minimum host version, OpenClaw inspects older stable versions and installs the newest compatible release instead. Exact versions and explicit dist-tags such as `@beta` remain strict: if the selected package is incompatible, the command fails and asks you to upgrade OpenClaw or choose a compatible version.
If a bare install spec matches an official plugin id (for example `diffs`), OpenClaw installs the catalog entry directly. To install an npm package with the same name, use an explicit scoped spec (for example `@scope/diffs`).
</Accordion>
@@ -192,10 +194,10 @@ is available, then fall back to `latest`.
Supported archives: `.zip`, `.tgz`, `.tar.gz`, `.tar`. Native OpenClaw plugin archives must contain a valid `openclaw.plugin.json` at the extracted plugin root; archives that only contain `package.json` are rejected before OpenClaw writes install records.
Use `npm-pack:<path.tgz>` when the file is an npm-pack tarball and you want
to test the same managed npm-root install path used by registry installs,
including `package-lock.json` verification, hoisted dependency scanning, and
npm install records. Plain archive paths still install as local archives
under the plugin extensions root.
to test the same per-plugin managed npm project path used by registry
installs, including `package-lock.json` verification, hoisted dependency
scanning, and npm install records. Plain archive paths still install as local
archives under the plugin extensions root.
Claude marketplace installs are also supported.
@@ -435,7 +437,7 @@ The local plugin registry is OpenClaw's persisted cold read model for installed
Use `plugins registry` to inspect whether the persisted registry is present, current, or stale. Use `--refresh` to rebuild it from the persisted plugin index, config policy, and manifest/package metadata. This is a repair path, not a runtime activation path.
`openclaw doctor --fix` also repairs registry-adjacent managed npm drift: if an orphaned or recovered `@openclaw/*` package under the managed plugin npm root shadows a bundled plugin, doctor removes that stale package and rebuilds the registry so startup validates against the bundled manifest. Doctor also relinks the host `openclaw` package into managed npm plugins that declare `peerDependencies.openclaw`, so package-local runtime imports such as `openclaw/plugin-sdk/*` resolve after updates or npm repairs.
`openclaw doctor --fix` also repairs registry-adjacent managed npm drift: if an orphaned or recovered `@openclaw/*` package under a managed plugin npm project or the legacy flat managed npm root shadows a bundled plugin, doctor removes that stale package and rebuilds the registry so startup validates against the bundled manifest. Doctor also relinks the host `openclaw` package into managed npm plugins that declare `peerDependencies.openclaw`, so package-local runtime imports such as `openclaw/plugin-sdk/*` resolve after updates or npm repairs.
<Warning>
`OPENCLAW_DISABLE_PERSISTED_PLUGIN_REGISTRY=1` is a deprecated break-glass compatibility switch for registry read failures. Prefer `plugins registry --refresh` or `openclaw doctor --fix`; the env fallback is only for emergency startup recovery while the migration rolls out.

View File

@@ -18,12 +18,13 @@ report drift through `doctor --lint`. The final conformance signal is a clean
instead of creating a separate health gate.
Policy currently manages configured channels, MCP servers, model providers,
network SSRF posture, Gateway exposure posture, agent workspace posture,
network SSRF posture, ingress/channel access posture, Gateway exposure posture, agent workspace posture,
OpenClaw config secret provider/auth profile posture, and governed tool
declarations. For example, IT or a workspace operator can record that Telegram
is not an approved channel provider, restrict MCP servers and model refs to
approved entries, require private-network fetch/browser access to remain
disabled, require Gateway bind/auth/HTTP exposure to stay within reviewed
disabled, require direct-message session isolation and channel ingress posture
to stay within reviewed bounds, require Gateway bind/auth/HTTP exposure to stay within reviewed
bounds, require agent workspace access and tool denies to stay in a reviewed
posture, require OpenClaw config SecretRefs to use managed providers, require
config auth profiles to carry provider/mode metadata, require governed tools to
@@ -49,9 +50,9 @@ arbitrary plugins. The plugin remains enabled if `policy.jsonc` is missing, so
doctor can report the missing artifact.
Policy is authored, not generated from the user's current settings. A minimal
policy for channels, MCP servers, model providers, network posture, Gateway
exposure, agent workspace posture, OpenClaw config secret provider/auth profile
posture, and tool metadata looks like this:
policy for channels, MCP servers, model providers, network posture, ingress/channel access, Gateway
exposure, agent workspace posture, configured sandbox runtime posture, OpenClaw
config secret provider/auth profile posture, and tool metadata looks like this:
```jsonc
{
@@ -81,6 +82,16 @@ posture, and tool metadata looks like this:
"allow": false,
},
},
"ingress": {
"session": {
"requireDmScope": "per-channel-peer",
},
"channels": {
"allowDmPolicies": ["pairing", "allowlist", "disabled"],
"denyOpenGroups": true,
"requireMentionInGroups": true,
},
},
"gateway": {
"exposure": {
"allowNonLoopbackBind": false,
@@ -142,8 +153,9 @@ posture, and tool metadata looks like this:
The rules are the authority. A category block is only a namespace; checks run
when a concrete rule is present. OpenClaw reads current `channels.*` settings
`mcp.servers.*`, `models.providers.*`, selected agent model refs, network SSRF
settings, Gateway bind/auth/Control UI/Tailscale/remote/HTTP posture, OpenClaw
config agent sandbox workspace access and tool deny posture, config secret
settings, direct-message session scope, channel DM policy, channel group policy,
channel/group mention gates, Gateway bind/auth/Control UI/Tailscale/remote/HTTP
posture, OpenClaw config agent sandbox workspace access and tool deny posture, config secret
provider and SecretRef provenance, config auth profile metadata, configured
global/per-agent tool posture, and `TOOLS.md` declarations as evidence, then
reports observed state that does not conform. If a policy denies non-loopback
@@ -172,21 +184,102 @@ present in `policy.jsonc`. The observed state is existing OpenClaw config or
workspace metadata; policy reports drift but does not rewrite runtime behavior
unless a repair path is explicitly available and enabled.
Agent-specific policy overlays keep broad `tools.*` and `agents.workspace`
posture global, then let named scope blocks add stricter normal policy sections
for explicit `agentIds` under `scopes.<scopeName>`. The initial scoped
sections are `tools` and `agents.workspace`; sandbox and ingress can use the
same container once their evidence is attributable to an agent. Scoped fields
carry strictness metadata such as allowlist subset, denylist superset, required
boolean, and exact-list semantics so future policy-file conformance can reuse
the same rule inventory instead of guessing. The overlay is additive: global
claims still run, and a scoped claim can emit its own finding against the same
observed config. See [Agent-scoped policy overlays](/plan/policy-agent-scoped-overlays).
Every scope present in `policy.jsonc` must be valid and enforceable. Scopes
currently require `agentIds`, and that selector supports only `tools.*` and
`agents.workspace.*`. If an `agentIds` entry is not present in `agents.list[]`,
the scoped rule is evaluated against the inherited global/default posture for
that runtime agent id instead of being skipped.
Policy overlays keep broad top-level rules global, then let named scope blocks
add stricter normal policy sections for explicit selectors. A scope name is a
descriptive bucket only; matching uses the selector values inside the scope.
The overlay is additive: global claims still run, and a scoped claim can emit
its own finding against the same observed config.
#### Scoped overlays
Use `scopes.<scopeName>` when one set of agents or channels needs stricter
policy than the top-level baseline. Agent-scoped sections use `agentIds`, which
supports `tools.*`, `agents.workspace.*`, and `sandbox.*`. Channel-scoped
ingress uses `channelIds`, which supports `ingress.channels.*`. Unsupported
sections are rejected instead of being ignored. If an `agentIds` entry is not
present in `agents.list[]`, OpenClaw evaluates the scoped rule against inherited
global/default posture for that runtime agent id.
```jsonc
{
"tools": {
"exec": {
"allowHosts": ["sandbox", "node"],
},
},
"sandbox": {
"requireMode": ["all", "non-main"],
},
"scopes": {
"release-workspace": {
"agentIds": ["release-agent", "review-agent"],
"agents": {
"workspace": {
"allowedAccess": ["none", "ro"],
},
},
},
"release-lockdown": {
"agentIds": ["release-agent"],
"tools": {
"exec": {
"allowHosts": ["sandbox"],
"allowSecurity": ["deny", "allowlist"],
"requireAsk": ["always"],
},
"denyTools": ["exec", "process", "write", "edit", "apply_patch"],
},
"sandbox": {
"requireMode": ["all"],
"allowBackends": ["docker"],
},
},
"shell-sandbox": {
"agentIds": ["shell-agent"],
"sandbox": {
"allowBackends": ["openshell"],
"containers": {
"requireReadOnlyMounts": false,
},
},
},
"telegram-ingress": {
"channelIds": ["telegram"],
"ingress": {
"channels": {
"allowDmPolicies": ["pairing"],
"denyOpenGroups": true,
"requireMentionInGroups": true,
},
},
},
},
}
```
The same agent can appear in multiple scopes when each scope governs different
fields, as shown above. A repeated scoped field for the same agent must be
equally or more restrictive according to policy metadata; weaker duplicate
claims are rejected. Strictness metadata treats allow-lists as subsets,
deny-lists as supersets, and required booleans as fixed requirements.
Container posture policy is evaluated only against evidence OpenClaw can
observe for the matched agent. If an enabled `sandbox.containers.*` rule applies
to an agent whose sandbox backend cannot expose that field, policy reports
`policy/sandbox-container-posture-unobservable` instead of treating the claim as
passing. Use separate `agentIds` scopes for agent groups that use different
sandbox backends, and leave unsupported container rules unset or false for the
groups where those fields cannot be observed.
Top-level `ingress.session.requireDmScope` remains global because
`session.dmScope` is not channel-attributable evidence.
| Selector | Supported sections | Use when |
| ------------ | ------------------------------------------ | ------------------------------------------------- |
| `agentIds` | `tools`, `agents.workspace`, and `sandbox` | One or more runtime agents need stricter rules. |
| `channelIds` | `ingress.channels` | One or more channels need stricter ingress rules. |
Every scope present in `policy.jsonc` must be valid and enforceable.
#### Channels
@@ -215,6 +308,15 @@ that runtime agent id instead of being skipped.
| ------------------------------ | ----------------------------------- | ------------------------------------------------------------------ |
| `network.privateNetwork.allow` | Private-network SSRF escape hatches | Set to `false` to require private-network access to stay disabled. |
#### Ingress and channel access
| Policy field | Observed state | Use when |
| ----------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------ |
| `ingress.session.requireDmScope` | `session.dmScope` | Require a reviewed direct-message isolation scope. |
| `ingress.channels.allowDmPolicies` | `channels.*.dmPolicy` and legacy channel DM policy fields | Allow only reviewed direct-message channel policies. |
| `ingress.channels.denyOpenGroups` | Channel, account, and group ingress policy | Deny open group ingress for configured channels and accounts. |
| `ingress.channels.requireMentionInGroups` | Channel, account, group, guild, and nested mention gate config | Require mention gates when group ingress is open or mention-gated. |
#### Gateway
| Policy field | Observed state | Use when |
@@ -235,6 +337,23 @@ that runtime agent id instead of being skipped.
| `agents.workspace.allowedAccess` | `agents.defaults.sandbox.workspaceAccess` and `agents.list[].sandbox.workspaceAccess` | Allow only sandbox workspace access values such as `none` or `ro`. |
| `agents.workspace.denyTools` | Global and per-agent tool deny config | Require workspace/runtime mutation tools such as `exec`, `process`, `write`, `edit`, or `apply_patch` to be denied. |
#### Sandbox posture
| Policy field | Observed state | Use when |
| ----------------------------------------------------- | ------------------------------------------------------- | -------------------------------------------------------------- |
| `sandbox.requireMode` | `agents.defaults.sandbox.mode` and per-agent mode | Allow only reviewed sandbox modes such as `all` or `non-main`. |
| `sandbox.allowBackends` | `agents.defaults.sandbox.backend` and per-agent backend | Allow only reviewed sandbox backends such as `docker`. |
| `sandbox.containers.denyHostNetwork` | Container-backed sandbox/browser network mode | Deny host network mode. |
| `sandbox.containers.denyContainerNamespaceJoin` | Container-backed sandbox/browser network mode | Deny joining another container network namespace. |
| `sandbox.containers.requireReadOnlyMounts` | Container-backed sandbox/browser mount mode | Require mounts to be read-only. |
| `sandbox.containers.denyContainerRuntimeSocketMounts` | Container-backed sandbox/browser mount targets | Deny container runtime socket mounts. |
| `sandbox.containers.denyUnconfinedProfiles` | Container security profile posture | Deny unconfined container security profiles. |
| `sandbox.browser.requireCdpSourceRange` | Sandbox browser CDP source range | Require browser CDP exposure to declare a source range. |
Policy treats missing `sandbox.mode` as the implicit default `off`, so
`sandbox.requireMode` reports a fresh or unconfigured sandbox as outside an
allowlist such as `["all"]`.
#### Secrets
| Policy field | Observed state | Use when |
@@ -281,8 +400,41 @@ openclaw policy check --severity-min error
attestation hashes. The same findings also appear in `openclaw doctor --lint`
when the Policy plugin is enabled.
Example clean JSON output includes stable hashes that can be recorded by an
operator or supervisor:
Compare an operator policy file to an authored baseline policy file:
```bash
openclaw policy compare --baseline official.policy.jsonc
openclaw policy compare --baseline official.policy.jsonc --policy policy.jsonc --json
```
`policy compare` compares policy file syntax to policy file syntax. It does not
inspect OpenClaw runtime state, evidence, credentials, or secrets. The command
uses the same policy rule metadata that governs scoped overlays: allowlists must
stay equal or narrower, denylists must stay equal or broader, required booleans
must keep their required value, ordered strings must move only toward the more
restrictive end of the configured order, and exact lists must match.
The baseline file can be an organization-authored policy. The checked policy can
use stricter values or add extra policy rules. A top-level checked rule can also
satisfy a scoped baseline rule when it is equally or more restrictive because
top-level policy applies broadly. Scope names do not need to match; scoped
comparison is keyed by selector value such as `agentIds` or `channelIds` and by
the policy field being checked.
Example clean compare JSON output reports only policy-file comparison state:
```json
{
"ok": true,
"baselinePath": "official.policy.jsonc",
"policyPath": "policy.jsonc",
"rulesChecked": 3,
"findings": []
}
```
Example clean `policy check --json` output includes stable hashes that can be
recorded by an operator or supervisor:
```json
{
@@ -522,47 +674,63 @@ choose a different interval.
Policy currently verifies:
| Check id | Finding |
| -------------------------------------------- | -------------------------------------------------------------------------------- |
| `policy/policy-jsonc-missing` | Policy is enabled but `policy.jsonc` is missing. |
| `policy/policy-jsonc-invalid` | Policy cannot be parsed or contains malformed rule entries. |
| `policy/policy-hash-mismatch` | Policy does not match configured `expectedHash`. |
| `policy/attestation-hash-mismatch` | Current policy evidence no longer matches the accepted attestation. |
| `policy/channels-denied-provider` | An enabled channel matches a channel deny rule. |
| `policy/mcp-denied-server` | A configured MCP server is denied by policy. |
| `policy/mcp-unapproved-server` | A configured MCP server is outside the allowlist. |
| `policy/models-denied-provider` | A configured model provider or model ref uses a denied provider. |
| `policy/models-unapproved-provider` | A configured model provider or model ref is outside the allowlist. |
| `policy/network-private-access-enabled` | A private-network SSRF escape hatch is enabled when policy denies it. |
| `policy/gateway-non-loopback-bind` | Gateway bind posture permits non-loopback exposure when policy denies it. |
| `policy/gateway-auth-disabled` | Gateway authentication is disabled when policy requires auth. |
| `policy/gateway-rate-limit-missing` | Gateway auth rate-limit posture is not explicit when policy requires it. |
| `policy/gateway-control-ui-insecure` | Gateway Control UI insecure exposure toggles are enabled. |
| `policy/gateway-tailscale-funnel` | Gateway Tailscale Funnel exposure is enabled when policy denies it. |
| `policy/gateway-remote-enabled` | Gateway remote mode is active when policy denies it. |
| `policy/gateway-http-endpoint-enabled` | A Gateway HTTP API endpoint is enabled while denied by policy. |
| `policy/gateway-http-url-fetch-unrestricted` | Gateway HTTP URL-fetch input lacks a required URL allowlist. |
| `policy/agents-workspace-access-denied` | Agent sandbox mode or workspace access is outside the policy allowlist. |
| `policy/agents-tool-not-denied` | An agent or default config does not deny a tool required by policy. |
| `policy/tools-profile-unapproved` | A configured global or per-agent tool profile is outside the allowlist. |
| `policy/tools-fs-workspace-only-required` | Filesystem tools are not configured with workspace-only path posture. |
| `policy/tools-exec-security-unapproved` | Exec security mode is outside the policy allowlist. |
| `policy/tools-exec-ask-unapproved` | Exec ask mode is outside the policy allowlist. |
| `policy/tools-exec-host-unapproved` | Exec host routing is outside the policy allowlist. |
| `policy/tools-elevated-enabled` | Elevated tool mode is enabled when policy denies it. |
| `policy/tools-also-allow-missing` | A configured `alsoAllow` list is missing an entry required by policy. |
| `policy/tools-also-allow-unexpected` | A configured `alsoAllow` list includes an entry not expected by policy. |
| `policy/tools-required-deny-missing` | A global or per-agent tool deny list does not include a required denied tool. |
| `policy/secrets-unmanaged-provider` | A config SecretRef references a provider not declared under `secrets.providers`. |
| `policy/secrets-denied-provider-source` | A config secret provider or SecretRef uses a source denied by policy. |
| `policy/secrets-insecure-provider` | A secret provider opts into insecure posture when policy denies it. |
| `policy/auth-profile-invalid-metadata` | A config auth profile is missing valid provider or mode metadata. |
| `policy/auth-profile-unapproved-mode` | A config auth profile mode is outside the policy allowlist. |
| `policy/tools-missing-risk-level` | A governed tool declaration is missing risk metadata. |
| `policy/tools-unknown-risk-level` | A governed tool declaration uses an unknown risk value. |
| `policy/tools-missing-sensitivity-token` | A governed tool declaration is missing sensitivity metadata. |
| `policy/tools-missing-owner` | A governed tool declaration is missing owner metadata. |
| `policy/tools-unknown-sensitivity-token` | A governed tool declaration uses an unknown sensitivity value. |
| Check id | Finding |
| ------------------------------------------------- | --------------------------------------------------------------------------------- |
| `policy/policy-jsonc-missing` | Policy is enabled but `policy.jsonc` is missing. |
| `policy/policy-jsonc-invalid` | Policy cannot be parsed or contains malformed rule entries. |
| `policy/policy-hash-mismatch` | Policy does not match configured `expectedHash`. |
| `policy/attestation-hash-mismatch` | Current policy evidence no longer matches the accepted attestation. |
| `policy/policy-conformance-invalid` | A baseline or checked policy file has invalid comparison syntax. |
| `policy/policy-conformance-missing` | A checked policy file is missing a rule required by the baseline policy file. |
| `policy/policy-conformance-weaker` | A checked policy file has a weaker value than the baseline policy file. |
| `policy/channels-denied-provider` | An enabled channel matches a channel deny rule. |
| `policy/mcp-denied-server` | A configured MCP server is denied by policy. |
| `policy/mcp-unapproved-server` | A configured MCP server is outside the allowlist. |
| `policy/models-denied-provider` | A configured model provider or model ref uses a denied provider. |
| `policy/models-unapproved-provider` | A configured model provider or model ref is outside the allowlist. |
| `policy/network-private-access-enabled` | A private-network SSRF escape hatch is enabled when policy denies it. |
| `policy/ingress-dm-policy-unapproved` | A channel DM policy is outside the policy allowlist. |
| `policy/ingress-dm-scope-unapproved` | `session.dmScope` does not match the policy-required DM isolation scope. |
| `policy/ingress-open-groups-denied` | A channel group policy is `open` while policy denies open group ingress. |
| `policy/ingress-group-mention-required` | A channel or group entry disables mention gates while policy requires them. |
| `policy/gateway-non-loopback-bind` | Gateway bind posture permits non-loopback exposure when policy denies it. |
| `policy/gateway-auth-disabled` | Gateway authentication is disabled when policy requires auth. |
| `policy/gateway-rate-limit-missing` | Gateway auth rate-limit posture is not explicit when policy requires it. |
| `policy/gateway-control-ui-insecure` | Gateway Control UI insecure exposure toggles are enabled. |
| `policy/gateway-tailscale-funnel` | Gateway Tailscale Funnel exposure is enabled when policy denies it. |
| `policy/gateway-remote-enabled` | Gateway remote mode is active when policy denies it. |
| `policy/gateway-http-endpoint-enabled` | A Gateway HTTP API endpoint is enabled while denied by policy. |
| `policy/gateway-http-url-fetch-unrestricted` | Gateway HTTP URL-fetch input lacks a required URL allowlist. |
| `policy/agents-workspace-access-denied` | Agent sandbox mode or workspace access is outside the policy allowlist. |
| `policy/agents-tool-not-denied` | An agent or default config does not deny a tool required by policy. |
| `policy/tools-profile-unapproved` | A configured global or per-agent tool profile is outside the allowlist. |
| `policy/tools-fs-workspace-only-required` | Filesystem tools are not configured with workspace-only path posture. |
| `policy/tools-exec-security-unapproved` | Exec security mode is outside the policy allowlist. |
| `policy/tools-exec-ask-unapproved` | Exec ask mode is outside the policy allowlist. |
| `policy/tools-exec-host-unapproved` | Exec host routing is outside the policy allowlist. |
| `policy/tools-elevated-enabled` | Elevated tool mode is enabled when policy denies it. |
| `policy/tools-also-allow-missing` | A configured `alsoAllow` list is missing an entry required by policy. |
| `policy/tools-also-allow-unexpected` | A configured `alsoAllow` list includes an entry not expected by policy. |
| `policy/tools-required-deny-missing` | A global or per-agent tool deny list does not include a required denied tool. |
| `policy/sandbox-mode-unapproved` | Sandbox mode is outside the policy allowlist. |
| `policy/sandbox-backend-unapproved` | Sandbox backend is outside the policy allowlist. |
| `policy/sandbox-container-posture-unobservable` | A container posture rule is enabled for a backend that cannot observe it. |
| `policy/sandbox-container-host-network-denied` | A container-backed sandbox or browser uses host network mode. |
| `policy/sandbox-container-namespace-join-denied` | A container-backed sandbox or browser joins another container namespace. |
| `policy/sandbox-container-mount-mode-required` | A container-backed sandbox or browser mount is not read-only. |
| `policy/sandbox-container-runtime-socket-mount` | A container-backed sandbox or browser mount exposes the container runtime socket. |
| `policy/sandbox-container-unconfined-profile` | Container sandbox profile is unconfined when policy denies it. |
| `policy/sandbox-browser-cdp-source-range-missing` | Sandbox browser CDP source range is missing when policy requires one. |
| `policy/secrets-unmanaged-provider` | A config SecretRef references a provider not declared under `secrets.providers`. |
| `policy/secrets-denied-provider-source` | A config secret provider or SecretRef uses a source denied by policy. |
| `policy/secrets-insecure-provider` | A secret provider opts into insecure posture when policy denies it. |
| `policy/auth-profile-invalid-metadata` | A config auth profile is missing valid provider or mode metadata. |
| `policy/auth-profile-unapproved-mode` | A config auth profile mode is outside the policy allowlist. |
| `policy/tools-missing-risk-level` | A governed tool declaration is missing risk metadata. |
| `policy/tools-unknown-risk-level` | A governed tool declaration uses an unknown risk value. |
| `policy/tools-missing-sensitivity-token` | A governed tool declaration is missing sensitivity metadata. |
| `policy/tools-missing-owner` | A governed tool declaration is missing owner metadata. |
| `policy/tools-unknown-sensitivity-token` | A governed tool declaration uses an unknown sensitivity value. |
Policy findings can include both `target` and `requirement`. `target` is the
observed workspace thing that does not conform. `requirement` is the authored
@@ -706,10 +874,11 @@ configured channel:
## Exit codes
| Command | `0` | `1` | `2` |
| -------------- | ----------------------------------------- | ------------------------------------------------ | ---------------------------- |
| `policy check` | No findings at the threshold. | One or more findings met the threshold. | Argument or runtime failure. |
| `policy watch` | No findings and accepted hash is current. | Findings exist or accepted attestation is stale. | Argument or runtime failure. |
| Command | `0` | `1` | `2` |
| ---------------- | ------------------------------------------------------ | ------------------------------------------------------------------- | ---------------------------- |
| `policy check` | No findings at the threshold. | One or more findings met the threshold. | Argument or runtime failure. |
| `policy compare` | The policy file is at least as strict as the baseline. | The policy file is invalid, missing, or weaker than baseline rules. | Argument or runtime failure. |
| `policy watch` | No findings and accepted hash is current. | Findings exist or accepted attestation is stale. | Argument or runtime failure. |
## Related

View File

@@ -110,7 +110,7 @@ openclaw sessions cleanup --json
- `--dry-run`: preview how many entries would be pruned/capped without writing.
- In text mode, dry-run prints a per-session action table (`Action`, `Key`, `Age`, `Model`, `Flags`) so you can see what would be kept vs removed.
- `--enforce`: apply maintenance even when `session.maintenance.mode` is `warn`.
- `--fix-missing`: remove entries whose transcript files are missing, even if they would not normally age/count out yet.
- `--fix-missing`: remove entries whose transcript files are missing or header-only/empty, even if they would not normally age/count out yet.
- `--fix-dm-scope`: when `session.dmScope` is `main`, retire stale peer-keyed direct-DM rows left behind by earlier `per-peer`, `per-channel-peer`, or `per-account-channel-peer` routing. Use `--dry-run` first; applying the cleanup removes those rows from `sessions.json` and preserves their transcripts as deleted archives.
- `--active-key <key>`: protect a specific active key from disk-budget eviction. Durable external conversation pointers, such as group sessions and thread-scoped chat sessions, are also kept by age/count/disk-budget maintenance.
- `--agent <id>`: run cleanup for one configured agent store.

View File

@@ -14,12 +14,12 @@ the finished turn to OpenClaw.
Runtimes are easy to confuse with providers because both show up near model
configuration. They are different layers:
| Layer | Examples | What it means |
| ------------- | ------------------------------------- | ------------------------------------------------------------------- |
| Provider | `openai`, `anthropic`, `openai-codex` | How OpenClaw authenticates, discovers models, and names model refs. |
| Model | `gpt-5.5`, `claude-opus-4-6` | The model selected for the agent turn. |
| Agent runtime | `openclaw`, `codex`, `claude-cli` | The low level loop or backend that executes the prepared turn. |
| Channel | Telegram, Discord, Slack, WhatsApp | Where messages enter and leave OpenClaw. |
| Layer | Examples | What it means |
| ------------- | -------------------------------------------- | ------------------------------------------------------------------- |
| Provider | `openai`, `anthropic`, `openai-codex` | How OpenClaw authenticates, discovers models, and names model refs. |
| Model | `gpt-5.5`, `claude-opus-4-6` | The model selected for the agent turn. |
| Agent runtime | `openclaw`, `codex`, `copilot`, `claude-cli` | The low level loop or backend that executes the prepared turn. |
| Channel | Telegram, Discord, Slack, WhatsApp | Where messages enter and leave OpenClaw. |
You will also see the word **harness** in code. A harness is the implementation
that provides an agent runtime. For example, the bundled Codex harness
@@ -33,13 +33,17 @@ There are two runtime families:
- **Embedded harnesses** run inside OpenClaw's prepared agent loop. Today this
is the built-in `openclaw` runtime plus registered plugin harnesses such as
`codex`.
`codex` and `copilot`.
- **CLI backends** run a local CLI process while keeping the model ref
canonical. For example, `anthropic/claude-opus-4-7` with
canonical. For example, `anthropic/claude-opus-4-8` with
a model-scoped `agentRuntime.id: "claude-cli"` means "select the Anthropic
model, execute through Claude CLI." `claude-cli` is not an embedded harness id
and must not be passed to AgentHarness selection.
The `copilot` harness is a separate, opt-in plugin harness for the
GitHub Copilot CLI; see [GitHub Copilot agent runtime](/plugins/copilot)
for the user-facing decision between PI, Codex, and GitHub Copilot agent runtime.
## Codex surfaces
Most confusion comes from several different surfaces sharing the Codex name:
@@ -170,9 +174,9 @@ Claude CLI form is:
{
agents: {
defaults: {
model: "anthropic/claude-opus-4-7",
model: "anthropic/claude-opus-4-8",
models: {
"anthropic/claude-opus-4-7": {
"anthropic/claude-opus-4-8": {
agentRuntime: { id: "claude-cli" },
},
},
@@ -201,6 +205,34 @@ If `openclaw doctor` warns that the `codex` plugin is enabled while
`openai-codex/*` remains in config, treat that as legacy route state. Run
`openclaw doctor --fix` to rewrite it to `openai/*` with the Codex runtime.
## GitHub Copilot agent runtime
The bundled `copilot` extension registers an opt-in `copilot` runtime
backed by the GitHub Copilot CLI (`@github/copilot-sdk`). It claims the
canonical subscription `github-copilot` provider and is **never** selected by
`auto`. Opt in per-model or per-provider via `agentRuntime.id`:
```json5
{
agents: {
defaults: {
model: "github-copilot/gpt-5.5",
models: {
"github-copilot/gpt-5.5": {
agentRuntime: { id: "copilot" },
},
},
},
},
}
```
The harness claims its provider, runtime, CLI session key, and auth profile
prefix in `extensions/copilot/doctor-contract-api.ts`, which
`openclaw doctor` auto-loads. For configuration, auth, transcript mirroring,
compaction, the doctor probe surface, and the broader PI vs Codex vs Copilot
SDK decision, see [GitHub Copilot agent runtime](/plugins/copilot).
## Compatibility contract
When a runtime is not OpenClaw, it should document what OpenClaw surfaces it supports.
@@ -236,6 +268,7 @@ runtime policy first. Legacy session runtime pins no longer decide routing.
- [Codex harness](/plugins/codex-harness)
- [Codex harness runtime](/plugins/codex-harness-runtime)
- [GitHub Copilot agent runtime](/plugins/copilot)
- [OpenAI](/providers/openai)
- [Agent harness plugins](/plugins/sdk-agent-harness)
- [Agent loop](/concepts/agent-loop)

View File

@@ -65,6 +65,10 @@ OpenClaw loads skills from these locations (highest precedence first):
- Bundled (shipped with the install)
- Extra skill folders: `skills.load.extraDirs`
Skill roots can contain grouped folders such as
`<workspace>/skills/personal/foo/SKILL.md`; the skill is still exposed by its
flat frontmatter name, for example `foo`.
Skills can be gated by config/env (see `skills` in [Gateway configuration](/gateway/configuration)).
## Runtime boundaries

View File

@@ -251,6 +251,20 @@ Native Codex and OpenClaw embedded agent runs satisfy `assemble-before-prompt`.
Generic CLI backends do not, so engines that require it are rejected before the
CLI process starts.
### Failure isolation
OpenClaw isolates the selected plugin engine from the core reply path. If a
non-legacy engine is missing, fails contract validation, throws during factory
creation, or throws from a lifecycle method, OpenClaw quarantines that engine
for the current Gateway process and downgrades context-engine work to the
built-in `legacy` engine. The error is logged with the failed operation so the
operator can repair, update, or disable the plugin without the agent going
silent.
Host requirement failures are different: when an engine declares that a runtime
lacks a required capability, OpenClaw fails closed before starting the run. That
protects engines that would corrupt state if they ran in an unsupported host.
### ownsCompaction
`ownsCompaction` controls whether OpenClaw runtime's built-in in-attempt auto-compaction stays enabled for the run:
@@ -321,7 +335,7 @@ The slot is exclusive at run time - only one registered context engine is resolv
- Use `openclaw doctor` to verify your engine is loading correctly.
- If switching engines, existing sessions continue with their current history. The new engine takes over for future runs.
- Engine errors are logged and surfaced in diagnostics. If a plugin engine fails to register or the selected engine id cannot be resolved, OpenClaw does not fall back automatically; runs fail until you fix the plugin or switch `plugins.slots.contextEngine` back to `"legacy"`.
- Engine errors are logged and the selected plugin engine is quarantined for the current Gateway process. OpenClaw falls back to `legacy` for user turns so replies can continue, but you should still repair, update, disable, or uninstall the broken plugin.
- For development, use `openclaw plugins install -l ./my-engine` to link a local plugin directory without copying.
## Related

View File

@@ -229,13 +229,16 @@ All settings live under `plugins.entries.memory-core.config.dreaming`.
<ParamField path="model" type="string">
Optional Dream Diary subagent model override. Use a canonical `provider/model` value when also setting a subagent `allowedModels` allowlist.
</ParamField>
<ParamField path="phases.deep.maxPromotedSnippetTokens" type="number" default="160">
Maximum estimated token count kept from each short-term recall snippet promoted into `MEMORY.md`. Ranking provenance remains visible.
</ParamField>
<Warning>
`dreaming.model` requires `plugins.entries.memory-core.subagent.allowModelOverride: true`. To restrict it, also set `plugins.entries.memory-core.subagent.allowedModels`. Trust or allowlist failures stay visible instead of falling back silently; the retry only covers model-unavailable errors.
</Warning>
<Note>
Phase policy, thresholds, and storage behavior are internal implementation details (not user-facing config). See [Memory configuration reference](/reference/memory-config#dreaming) for the full key list.
Most phase policy, thresholds, and storage behavior are internal implementation details. See [Memory configuration reference](/reference/memory-config#dreaming) for the full key list.
</Note>
## Dreams UI

View File

@@ -116,7 +116,7 @@ Official provider plugins publish their own model catalog rows. These providers
- CLI: `openclaw onboard --auth-choice apiKey`
- Direct public Anthropic requests support the shared `/fast` toggle and `params.fastMode`, including API-key and OAuth-authenticated traffic sent to `api.anthropic.com`; OpenClaw maps that to Anthropic `service_tier` (`auto` vs `standard_only`)
- Preferred Claude CLI config keeps the model ref canonical and selects the CLI
backend separately: `anthropic/claude-opus-4-7` with
backend separately: `anthropic/claude-opus-4-8` with
model-scoped `agentRuntime.id: "claude-cli"`. Legacy
`claude-cli/claude-opus-4-7` refs still work for compatibility.
@@ -290,32 +290,32 @@ See [/providers/kilocode](/providers/kilocode) for setup details.
### Other bundled provider plugins
| Provider | Id | Auth env | Example model |
| ----------------------- | -------------------------------- | ------------------------------------------------------------ | --------------------------------------------- |
| BytePlus | `byteplus` / `byteplus-plan` | `BYTEPLUS_API_KEY` | `byteplus-plan/ark-code-latest` |
| Cerebras | `cerebras` | `CEREBRAS_API_KEY` | `cerebras/zai-glm-4.7` |
| Cloudflare AI Gateway | `cloudflare-ai-gateway` | `CLOUDFLARE_AI_GATEWAY_API_KEY` | - |
| DeepInfra | `deepinfra` | `DEEPINFRA_API_KEY` | `deepinfra/deepseek-ai/DeepSeek-V4-Flash` |
| DeepSeek | `deepseek` | `DEEPSEEK_API_KEY` | `deepseek/deepseek-v4-flash` |
| GitHub Copilot | `github-copilot` | `COPILOT_GITHUB_TOKEN` / `GH_TOKEN` / `GITHUB_TOKEN` | - |
| Groq | `groq` | `GROQ_API_KEY` | - |
| Hugging Face Inference | `huggingface` | `HUGGINGFACE_HUB_TOKEN` or `HF_TOKEN` | `huggingface/deepseek-ai/DeepSeek-R1` |
| Kilo Gateway | `kilocode` | `KILOCODE_API_KEY` | `kilocode/kilo/auto` |
| Kimi Coding | `kimi` | `KIMI_API_KEY` or `KIMICODE_API_KEY` | `kimi/kimi-for-coding` |
| MiniMax | `minimax` / `minimax-portal` | `MINIMAX_API_KEY` / `MINIMAX_OAUTH_TOKEN` | `minimax/MiniMax-M2.7` |
| Mistral | `mistral` | `MISTRAL_API_KEY` | `mistral/mistral-large-latest` |
| Moonshot | `moonshot` | `MOONSHOT_API_KEY` | `moonshot/kimi-k2.6` |
| NVIDIA | `nvidia` | `NVIDIA_API_KEY` | `nvidia/nvidia/nemotron-3-super-120b-a12b` |
| OpenRouter | `openrouter` | `OPENROUTER_API_KEY` | `openrouter/auto` |
| Qianfan | `qianfan` | `QIANFAN_API_KEY` | `qianfan/deepseek-v3.2` |
| Qwen Cloud | `qwen` | `QWEN_API_KEY` / `MODELSTUDIO_API_KEY` / `DASHSCOPE_API_KEY` | `qwen/qwen3.5-plus` |
| StepFun | `stepfun` / `stepfun-plan` | `STEPFUN_API_KEY` | `stepfun/step-3.5-flash` |
| Together | `together` | `TOGETHER_API_KEY` | `together/moonshotai/Kimi-K2.5` |
| Venice | `venice` | `VENICE_API_KEY` | - |
| Vercel AI Gateway | `vercel-ai-gateway` | `AI_GATEWAY_API_KEY` | `vercel-ai-gateway/anthropic/claude-opus-4.6` |
| Volcano Engine (Doubao) | `volcengine` / `volcengine-plan` | `VOLCANO_ENGINE_API_KEY` | `volcengine-plan/ark-code-latest` |
| xAI | `xai` | SuperGrok/X Premium OAuth or `XAI_API_KEY` | `xai/grok-4.3` |
| Xiaomi | `xiaomi` | `XIAOMI_API_KEY` | `xiaomi/mimo-v2-flash` |
| Provider | Id | Auth env | Example model |
| ----------------------- | -------------------------------- | ------------------------------------------------------------ | -------------------------------------------------- |
| BytePlus | `byteplus` / `byteplus-plan` | `BYTEPLUS_API_KEY` | `byteplus-plan/ark-code-latest` |
| Cerebras | `cerebras` | `CEREBRAS_API_KEY` | `cerebras/zai-glm-4.7` |
| Cloudflare AI Gateway | `cloudflare-ai-gateway` | `CLOUDFLARE_AI_GATEWAY_API_KEY` | - |
| DeepInfra | `deepinfra` | `DEEPINFRA_API_KEY` | `deepinfra/deepseek-ai/DeepSeek-V4-Flash` |
| DeepSeek | `deepseek` | `DEEPSEEK_API_KEY` | `deepseek/deepseek-v4-flash` |
| GitHub Copilot | `github-copilot` | `COPILOT_GITHUB_TOKEN` / `GH_TOKEN` / `GITHUB_TOKEN` | - |
| Groq | `groq` | `GROQ_API_KEY` | - |
| Hugging Face Inference | `huggingface` | `HUGGINGFACE_HUB_TOKEN` or `HF_TOKEN` | `huggingface/deepseek-ai/DeepSeek-R1` |
| Kilo Gateway | `kilocode` | `KILOCODE_API_KEY` | `kilocode/kilo/auto` |
| Kimi Coding | `kimi` | `KIMI_API_KEY` or `KIMICODE_API_KEY` | `kimi/kimi-for-coding` |
| MiniMax | `minimax` / `minimax-portal` | `MINIMAX_API_KEY` / `MINIMAX_OAUTH_TOKEN` | `minimax/MiniMax-M2.7` |
| Mistral | `mistral` | `MISTRAL_API_KEY` | `mistral/mistral-large-latest` |
| Moonshot | `moonshot` | `MOONSHOT_API_KEY` | `moonshot/kimi-k2.6` |
| NVIDIA | `nvidia` | `NVIDIA_API_KEY` | `nvidia/nvidia/nemotron-3-super-120b-a12b` |
| OpenRouter | `openrouter` | `OPENROUTER_API_KEY` | `openrouter/auto` |
| Qianfan | `qianfan` | `QIANFAN_API_KEY` | `qianfan/deepseek-v3.2` |
| Qwen Cloud | `qwen` | `QWEN_API_KEY` / `MODELSTUDIO_API_KEY` / `DASHSCOPE_API_KEY` | `qwen/qwen3.5-plus` |
| StepFun | `stepfun` / `stepfun-plan` | `STEPFUN_API_KEY` | `stepfun/step-3.5-flash` |
| Together | `together` | `TOGETHER_API_KEY` | `together/meta-llama/Llama-3.3-70B-Instruct-Turbo` |
| Venice | `venice` | `VENICE_API_KEY` | - |
| Vercel AI Gateway | `vercel-ai-gateway` | `AI_GATEWAY_API_KEY` | `vercel-ai-gateway/anthropic/claude-opus-4.6` |
| Volcano Engine (Doubao) | `volcengine` / `volcengine-plan` | `VOLCANO_ENGINE_API_KEY` | `volcengine-plan/ark-code-latest` |
| xAI | `xai` | SuperGrok/X Premium OAuth or `XAI_API_KEY` | `xai/grok-4.3` |
| Xiaomi | `xiaomi` | `XIAOMI_API_KEY` | `xiaomi/mimo-v2-flash` |
#### Quirks worth knowing

View File

@@ -23,7 +23,7 @@ sidebarTitle: "Models CLI"
</Card>
</CardGroup>
Model refs choose a provider and model. They do not usually choose the low-level agent runtime. OpenAI agent refs are the main exception: `openai/gpt-5.5` runs through the Codex app-server runtime by default on the official OpenAI provider. Explicit runtime overrides belong on provider/model policy, not on the whole agent or session. In Codex runtime mode, the `openai/gpt-*` ref does not imply API-key billing; auth can come from a Codex account or `openai-codex` auth profile. See [Agent runtimes](/concepts/agent-runtimes).
Model refs choose a provider and model. They do not usually choose the low-level agent runtime. OpenAI agent refs are the main exception: `openai/gpt-5.5` runs through the Codex app-server runtime by default on the official OpenAI provider. Subscription Copilot refs (`github-copilot/*`) can additionally be opted into the bundled GitHub Copilot agent runtime — that path stays explicit (no `auto` fallback). Explicit runtime overrides belong on provider/model policy, not on the whole agent or session. In Codex runtime mode, the `openai/gpt-*` ref does not imply API-key billing; auth can come from a Codex account or `openai-codex` auth profile. See [Agent runtimes](/concepts/agent-runtimes) and [GitHub Copilot agent runtime](/plugins/copilot).
## How model selection works
@@ -340,7 +340,7 @@ When live probes run in a TTY, you can select fallbacks interactively. In non-in
## Models registry (`models.json`)
Custom providers in `models.providers` are written into `models.json` under the agent directory (default `~/.openclaw/agents/<agentId>/agent/models.json`). This file is merged by default unless `models.mode` is set to `replace`.
Custom providers in `models.providers` are written into `models.json` under the agent directory (default `~/.openclaw/agents/<agentId>/agent/models.json`). Provider-plugin catalogs are stored as generated plugin-owned catalog shards under the agent's plugin state and loaded automatically. This file is merged by default unless `models.mode` is set to `replace`.
<AccordionGroup>
<Accordion title="Merge mode precedence">

View File

@@ -665,6 +665,48 @@ pnpm openclaw qa slack \
A green run completes in well under 30 seconds and `slack-qa-report.md` shows both `slack-canary` and `slack-mention-gating` at status `pass`. If the lane hangs for ~90 seconds and exits with `Convex credential pool exhausted for kind "slack"`, either the pool is empty or every row is leased - `qa credentials list --kind slack --status all --json` will tell you which.
### WhatsApp QA
```bash
pnpm openclaw qa whatsapp
```
Targets two dedicated WhatsApp Web accounts: a driver account controlled by
the harness and a SUT account started by the child OpenClaw gateway through the
bundled WhatsApp plugin.
Required env when `--credential-source env`:
- `OPENCLAW_QA_WHATSAPP_DRIVER_PHONE_E164`
- `OPENCLAW_QA_WHATSAPP_SUT_PHONE_E164`
- `OPENCLAW_QA_WHATSAPP_DRIVER_AUTH_ARCHIVE_BASE64`
- `OPENCLAW_QA_WHATSAPP_SUT_AUTH_ARCHIVE_BASE64`
Optional:
- `OPENCLAW_QA_WHATSAPP_GROUP_JID` enables `whatsapp-mention-gating`.
- `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`):
- `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.
Output artifacts:
- `whatsapp-qa-report.md`
- `whatsapp-qa-summary.json`
- `whatsapp-qa-observed-messages.json` - bodies redacted unless `OPENCLAW_QA_WHATSAPP_CAPTURE_CONTENT=1`.
### Convex credential pool
Telegram, Discord, Slack, and WhatsApp lanes can lease credentials from a shared Convex pool instead of reading the env vars above. Pass `--credential-source convex` (or set `OPENCLAW_QA_CREDENTIAL_SOURCE=convex`); QA Lab acquires an exclusive lease, heartbeats it for the duration of the run, and releases it on shutdown. Pool kinds are `"telegram"`, `"discord"`, `"slack"`, and `"whatsapp"`.
@@ -847,13 +889,13 @@ pnpm openclaw qa character-eval \
--model openai/gpt-5.5,thinking=medium,fast \
--model openai/gpt-5.2,thinking=xhigh \
--model openai/gpt-5,thinking=xhigh \
--model anthropic/claude-opus-4-7,thinking=high \
--model anthropic/claude-opus-4-8,thinking=high \
--model anthropic/claude-sonnet-4-6,thinking=high \
--model zai/glm-5.1,thinking=high \
--model moonshot/kimi-k2.5,thinking=high \
--model google/gemini-3.1-pro-preview,thinking=high \
--judge-model openai/gpt-5.5,thinking=xhigh,fast \
--judge-model anthropic/claude-opus-4-7,thinking=high \
--judge-model anthropic/claude-opus-4-8,thinking=high \
--blind-judge-models \
--concurrency 16 \
--judge-concurrency 16
@@ -884,13 +926,13 @@ Candidate and judge model runs both default to concurrency 16. Lower
`--concurrency` or `--judge-concurrency` when provider limits or local gateway
pressure make a run too noisy.
When no candidate `--model` is passed, the character eval defaults to
`openai/gpt-5.5`, `openai/gpt-5.2`, `openai/gpt-5`, `anthropic/claude-opus-4-7`,
`openai/gpt-5.5`, `openai/gpt-5.2`, `openai/gpt-5`, `anthropic/claude-opus-4-8`,
`anthropic/claude-sonnet-4-6`, `zai/glm-5.1`,
`moonshot/kimi-k2.5`, and
`google/gemini-3.1-pro-preview` when no `--model` is passed.
When no `--judge-model` is passed, the judges default to
`openai/gpt-5.5,thinking=xhigh,fast` and
`anthropic/claude-opus-4-7,thinking=high`.
`anthropic/claude-opus-4-8,thinking=high`.
## Related docs

View File

@@ -258,6 +258,10 @@ 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.
The location can point at a nested skill, such as
`skills/personal/foo/SKILL.md`. Nesting is only organizational; the prompt still
uses the flat skill name from `SKILL.md` frontmatter.
Eligibility includes skill metadata gates, runtime environment/config checks,
and the effective agent skill allowlist when `agents.defaults.skills` or
`agents.list[].skills` is configured.

View File

@@ -53,8 +53,8 @@ Authoritative advertised **discovery** inventory lives in
## Where the schemas live
- Source: `src/gateway/protocol/schema.ts`
- Runtime validators (AJV): `src/gateway/protocol/index.ts`
- Source: `packages/gateway-protocol/src/schema.ts`
- Runtime validators (AJV): `packages/gateway-protocol/src/index.ts`
- Advertised feature/discovery registry: `src/gateway/server-methods-list.ts`
- Server handshake + method dispatch: `src/gateway/server.impl.ts`
- Node client: `src/gateway/client.ts`
@@ -195,7 +195,7 @@ Example: add a new `system.echo` request that returns `{ ok: true, text }`.
1. **Schema (source of truth)**
Add to `src/gateway/protocol/schema.ts`:
Add to `packages/gateway-protocol/src/schema.ts`:
```ts
export const SystemEchoParamsSchema = Type.Object(
@@ -223,7 +223,7 @@ export type SystemEchoResult = Static<typeof SystemEchoResultSchema>;
2. **Validation**
In `src/gateway/protocol/index.ts`, export an AJV validator:
In `packages/gateway-protocol/src/index.ts`, export an AJV validator:
```ts
export const validateSystemEchoParams = ajv.compile<SystemEchoParams>(SystemEchoParamsSchema);
@@ -272,7 +272,7 @@ Unknown frame types are preserved as raw payloads for forward compatibility.
## Versioning + compatibility
- `PROTOCOL_VERSION` lives in `src/gateway/protocol/version.ts`.
- `PROTOCOL_VERSION` lives in `packages/gateway-protocol/src/version.ts`.
- Clients send `minProtocol` + `maxProtocol`; the server rejects ranges that
do not include its current protocol.
- The Swift models keep unknown frame types to avoid breaking older clients.

View File

@@ -1554,6 +1554,7 @@
"gateway/security/index",
"gateway/security/exposure-runbook",
"gateway/security/secure-file-operations",
"gateway/security/shrinkwrap",
"gateway/security/audit-checks",
"gateway/operator-scopes",
"gateway/sandboxing",
@@ -1815,6 +1816,7 @@
"pages": [
"reference/RELEASING",
"reference/full-release-validation",
"reference/release-performance-sweep",
"reference/test",
"ci",
"help/scripts"

View File

@@ -334,7 +334,7 @@ Higher values preserve more visual detail.
Image-tool compression/detail preference for images loaded from file paths, URLs, and media references.
Default: `auto`.
OpenClaw adapts the resize ladder to the selected image model. For example, Claude Opus 4.7, OpenAI GPT-5.5, Qwen VL, and hosted Llama 4 vision models can use larger images than older/default high-detail vision paths, while multi-image turns are compressed more aggressively in `auto` mode to control token and latency cost.
OpenClaw adapts the resize ladder to the selected image model. For example, Claude Opus 4.8, OpenAI GPT-5.5, Qwen VL, and hosted Llama 4 vision models can use larger images than older/default high-detail vision paths, while multi-image turns are compressed more aggressively in `auto` mode to control token and latency cost.
Values:
@@ -483,7 +483,7 @@ Time format in system prompt. Default: `auto` (OS preference).
defaults: {
model: "openai/gpt-5.5",
models: {
"anthropic/claude-opus-4-7": {
"anthropic/claude-opus-4-8": {
agentRuntime: { id: "claude-cli" },
},
"vllm/*": {
@@ -501,7 +501,7 @@ Time format in system prompt. Default: `auto` (OS preference).
- Runtime precedence is exact model policy first (`agents.list[].models["provider/model"]`, `agents.defaults.models["provider/model"]`, or `models.providers.<provider>.models[]`), then `agents.list[]` / `agents.defaults.models["provider/*"]`, then provider-wide policy at `models.providers.<provider>.agentRuntime`.
- Whole-agent runtime keys are legacy. `agents.defaults.agentRuntime`, `agents.list[].agentRuntime`, session runtime pins, and `OPENCLAW_AGENT_RUNTIME` are ignored by runtime selection. Run `openclaw doctor --fix` to remove stale values.
- OpenAI agent models use the Codex harness by default; provider/model `agentRuntime.id: "codex"` remains valid when you want to make that explicit.
- For Claude CLI deployments, prefer `model: "anthropic/claude-opus-4-7"` plus model-scoped `agentRuntime.id: "claude-cli"`. Legacy `claude-cli/claude-opus-4-7` model refs still work for compatibility, but new config should keep provider/model selection canonical and put the execution backend in provider/model runtime policy.
- For Claude CLI deployments, prefer `model: "anthropic/claude-opus-4-8"` plus model-scoped `agentRuntime.id: "claude-cli"`. Legacy `claude-cli/claude-opus-4-7` model refs still work for compatibility, but new config should keep provider/model selection canonical and put the execution backend in provider/model runtime policy.
- This only controls text agent-turn execution. Media generation, vision, PDF, music, video, and TTS still use their provider/model settings.
**Built-in alias shorthands** (only apply when the model is in `agents.defaults.models`):
@@ -521,7 +521,7 @@ Your configured aliases always win over defaults.
Z.AI GLM-4.x models automatically enable thinking mode unless you set `--thinking off` or define `agents.defaults.models["zai/<model>"].params.thinking` yourself.
Z.AI models enable `tool_stream` by default for tool call streaming. Set `agents.defaults.models["zai/<model>"].params.tool_stream` to `false` to disable it.
Anthropic Claude 4.6 models default to `adaptive` thinking when no explicit thinking level is set.
Anthropic Claude Opus 4.8 keeps thinking off by default in OpenClaw; when adaptive thinking is explicitly enabled, Anthropic's provider-owned effort default is `high`. Claude 4.6 models default to `adaptive` when no explicit thinking level is set.
### `agents.defaults.cliBackends`
@@ -562,20 +562,6 @@ Optional CLI backends for text-only fallback runs (no tool calls). Useful as a b
first compaction summary exists. Auth profile or credential-epoch changes
still never raw-reseed.
### `agents.defaults.systemPromptOverride`
Replace the entire OpenClaw-assembled system prompt with a fixed string. Set at the default level (`agents.defaults.systemPromptOverride`) or per agent (`agents.list[].systemPromptOverride`). Per-agent values take precedence; an empty or whitespace-only value is ignored. Useful for controlled prompt experiments.
```json5
{
agents: {
defaults: {
systemPromptOverride: "You are a helpful assistant.",
},
},
}
```
### `agents.defaults.promptOverlays`
Provider-independent prompt overlays applied by model family on OpenClaw-assembled prompt surfaces. GPT-5-family model ids receive the shared behavior contract across OpenClaw/provider routes; `personality` controls only the friendly interaction-style layer. Native Codex app-server routes keep Codex-owned base/model instructions instead of this OpenClaw GPT-5 overlay, and OpenClaw disables Codex's built-in personality for native threads.
@@ -1070,7 +1056,7 @@ for provider examples and precedence.
params: { cacheRetention: "none" }, // overrides matching defaults.models params by key
tts: {
providers: {
elevenlabs: { voiceId: "EXAVITQu4vr4xnSDxMaL" },
elevenlabs: { speakerVoiceId: "EXAVITQu4vr4xnSDxMaL" },
},
},
skills: ["docs-search"], // replaces agents.defaults.skills when set
@@ -1429,7 +1415,7 @@ Batches rapid text-only messages from the same sender into a single agent turn.
elevenlabs: {
apiKey: "elevenlabs_api_key",
baseUrl: "https://api.elevenlabs.io",
voiceId: "voice_id",
speakerVoiceId: "voice_id",
modelId: "eleven_multilingual_v2",
seed: 42,
applyTextNormalization: "auto",
@@ -1443,7 +1429,7 @@ Batches rapid text-only messages from the same sender into a single agent turn.
},
},
microsoft: {
voice: "en-US-AvaMultilingualNeural",
speakerVoice: "en-US-AvaMultilingualNeural",
lang: "en-US",
outputFormat: "audio-24khz-48kbitrate-mono-mp3",
},
@@ -1451,7 +1437,7 @@ Batches rapid text-only messages from the same sender into a single agent turn.
apiKey: "openai_api_key",
baseUrl: "https://api.openai.com/v1",
model: "gpt-4o-mini-tts",
voice: "alloy",
speakerVoice: "alloy",
},
},
},
@@ -1479,7 +1465,7 @@ Defaults for Talk mode (macOS/iOS/Android).
provider: "elevenlabs",
providers: {
elevenlabs: {
voiceId: "elevenlabs_voice_id",
speakerVoiceId: "elevenlabs_voice_id",
voiceAliases: {
Clawd: "EXAVITQu4vr4xnSDxMaL",
Roger: "CwhRBWXzGAHq8TQ4Fs17",
@@ -1503,7 +1489,7 @@ Defaults for Talk mode (macOS/iOS/Android).
providers: {
openai: {
model: "gpt-realtime-2",
voice: "cedar",
speakerVoice: "cedar",
},
},
instructions: "Speak warmly and keep answers brief.",

View File

@@ -488,7 +488,8 @@ Configuring a custom/local provider `baseUrl` is also the narrow network trust d
- Empty or missing agent `apiKey`/`baseUrl` fall back to `models.providers` in config.
- Matching model `contextWindow`/`maxTokens` use the higher value between explicit config and implicit catalog values.
- Matching model `contextTokens` preserves an explicit runtime cap when present; use it to limit effective context without changing native model metadata.
- Use `models.mode: "replace"` when you want config to fully rewrite `models.json`.
- Provider-plugin catalogs are stored as generated plugin-owned catalog shards under the agent's plugin state.
- Use `models.mode: "replace"` when you want config to fully rewrite `models.json` and active plugin catalog shards.
- Marker persistence is source-authoritative: markers are written from the active source config snapshot (pre-resolution), not from resolved runtime secret values.
</Accordion>

View File

@@ -1268,11 +1268,11 @@ Current builds no longer include the TCP bridge. Nodes connect over the Gateway
}
```
- `maxAttempts`: maximum retries for one-shot jobs on transient errors (default: `3`; range: `0`-`10`).
- `maxAttempts`: maximum retries for cron jobs on transient errors (default: `3`; range: `0`-`10`).
- `backoffMs`: array of backoff delays in ms for each retry attempt (default: `[30000, 60000, 300000]`; 1-10 entries).
- `retryOn`: error types that trigger retries - `"rate_limit"`, `"overloaded"`, `"network"`, `"timeout"`, `"server_error"`. Omit to retry all transient types.
Applies only to one-shot cron jobs. Recurring jobs use separate failure handling.
One-shot jobs stay enabled until retry attempts are exhausted, then disable while keeping the final error state. Recurring jobs use the same transient retry policy to run again after backoff before their next scheduled slot; permanent errors or exhausted transient retries fall back to the normal recurring schedule with error backoff.
### `cron.failureAlert`

View File

@@ -264,6 +264,7 @@ That stages grounded durable candidates into the short-term dreaming store while
- `routing.transcribeAudio` → `tools.media.audio.models`
- `messages.tts.<provider>` (`openai`/`elevenlabs`/`microsoft`/`edge`) → `messages.tts.providers.<provider>`
- `messages.tts.provider: "edge"` and `messages.tts.providers.edge` → `messages.tts.provider: "microsoft"` and `messages.tts.providers.microsoft`
- TTS speaker selection fields (`voice`/`voiceName`/`voiceId`) → `speakerVoice`/`speakerVoiceId`
- `channels.discord.voice.tts.<provider>` (`openai`/`elevenlabs`/`microsoft`/`edge`) → `channels.discord.voice.tts.providers.<provider>`
- `channels.discord.accounts.<id>.voice.tts.<provider>` (`openai`/`elevenlabs`/`microsoft`/`edge`) → `channels.discord.accounts.<id>.voice.tts.providers.<provider>`
- `plugins.entries.voice-call.config.tts.<provider>` (`openai`/`elevenlabs`/`microsoft`/`edge`) → `plugins.entries.voice-call.config.tts.providers.<provider>`

View File

@@ -175,9 +175,9 @@ Current behavior:
rasterized into images and passed to the model, and the injected file block uses
the placeholder `[PDF content rendered to images]`.
PDF parsing is provided by the bundled `document-extract` plugin, which uses the
Node-friendly `pdfjs-dist` legacy build (no worker). The modern PDF.js build
expects browser workers/DOM globals, so it is not used in the Gateway.
PDF parsing is provided by the bundled `document-extract` plugin, which uses
`clawpdf` and its packaged PDFium WebAssembly runtime for text extraction and
page rendering.
URL fetch defaults:

View File

@@ -104,7 +104,7 @@ within their overall connection budget instead of surfacing it as a terminal
handshake failure.
`server`, `features`, `snapshot`, and `policy` are all required by the schema
(`src/gateway/protocol/schema/frames.ts`). `auth` is also required and reports
(`packages/gateway-protocol/src/schema/frames.ts`). `auth` is also required and reports
the negotiated role/scopes. `pluginSurfaceUrls` is optional and maps plugin
surface names, such as `canvas`, to scoped hosted URLs.
@@ -347,9 +347,11 @@ enumeration of `src/gateway/server-methods/*.ts`.
- `models.list` returns the runtime-allowed model catalog. Pass `{ "view": "configured" }` for picker-sized configured models (`agents.defaults.models` first, then `models.providers.*.models`), or `{ "view": "all" }` for the full catalog.
- `usage.status` returns provider usage windows/remaining quota summaries.
- `usage.cost` returns aggregated cost usage summaries for a date range.
Pass `agentId` for one agent, or `agentScope: "all"` to aggregate configured agents.
- `doctor.memory.status` returns vector-memory / cached embedding readiness for the active default agent workspace. Pass `{ "probe": true }` or `{ "deep": true }` only when the caller explicitly wants a live embedding provider ping.
- `doctor.memory.remHarness` returns a bounded, read-only REM harness preview for remote control-plane clients. It can include workspace paths, memory snippets, rendered grounded markdown, and deep promotion candidates, so callers need `operator.read`.
- `sessions.usage` returns per-session usage summaries.
- `sessions.usage` returns per-session usage summaries. Pass `agentId` for one
agent, or `agentScope: "all"` to list configured agents together.
- `sessions.usage.timeseries` returns timeseries usage for one session.
- `sessions.usage.logs` returns usage log entries for one session.
@@ -562,8 +564,14 @@ terminal summary, and sanitized error text.
- `sessionKey` is required.
- The gateway derives trusted runtime context from the session server-side instead of accepting
caller-supplied auth or delivery context.
- The response is session-scoped and reflects what the active conversation can use right now,
including core, plugin, and channel tools.
- The response is a session-scoped server-derived projection of the active inventory,
including core, plugin, channel, and already-discovered MCP server tools.
- `tools.effective` is read-only for MCP: it may project a warm session MCP catalog through the
final tool policy, but it does not create MCP runtimes, connect transports, or issue
`tools/list`. If no matching warm catalog exists, the response may include a notice such as
`mcp-not-yet-connected`, `mcp-not-yet-listed`, or `mcp-stale-catalog`.
- Effective tool entries use `source="core"`, `source="plugin"`, `source="channel"`, or
`source="mcp"`.
- Operators may call `tools.invoke` (`operator.write`) to invoke one available tool through the
same gateway policy path as `/tools/invoke`.
- `name` is required. `args`, `sessionKey`, `agentId`, `confirm`, and
@@ -640,7 +648,7 @@ terminal summary, and sanitized error text.
## Versioning
- `PROTOCOL_VERSION` lives in `src/gateway/protocol/version.ts`.
- `PROTOCOL_VERSION` lives in `packages/gateway-protocol/src/version.ts`.
- Clients send `minProtocol` + `maxProtocol`; the server rejects ranges that
do not include its current protocol. Current clients and servers require
protocol v4.
@@ -656,8 +664,8 @@ stable across protocol v4 and are the expected baseline for third-party clients.
| Constant | Default | Source |
| ----------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `PROTOCOL_VERSION` | `4` | `src/gateway/protocol/version.ts` |
| `MIN_CLIENT_PROTOCOL_VERSION` | `4` | `src/gateway/protocol/version.ts` |
| `PROTOCOL_VERSION` | `4` | `packages/gateway-protocol/src/version.ts` |
| `MIN_CLIENT_PROTOCOL_VERSION` | `4` | `packages/gateway-protocol/src/version.ts` |
| Request timeout (per RPC) | `30_000` ms | `src/gateway/client.ts` (`requestTimeoutMs`) |
| Preauth / connect-challenge timeout | `15_000` ms | `src/gateway/handshake-timeouts.ts` (config/env can raise the paired server/client budget) |
| Initial reconnect backoff | `1_000` ms | `src/gateway/client.ts` (`backoffMs`) |
@@ -810,7 +818,7 @@ Migration target:
This protocol exposes the **full gateway API** (status, channels, models, chat,
agent, sessions, nodes, approvals, etc.). The exact surface is defined by the
TypeBox schemas in `src/gateway/protocol/schema.ts`.
TypeBox schemas in `packages/gateway-protocol/src/schema.ts`.
## Related

View File

@@ -63,67 +63,11 @@ OpenClaw source checkouts use `pnpm-lock.yaml`. The published `openclaw` npm
package and OpenClaw-owned npm plugin packages include `npm-shrinkwrap.json`,
npm's publishable dependency lockfile, so package installs use the reviewed
transitive dependency graph from the release instead of resolving a fresh graph
at install time. Suitable OpenClaw-owned npm plugin packages can also publish
with explicit `bundledDependencies`, so their runtime dependency files are
carried in the plugin tarball instead of depending only on install-time
resolution.
at install time.
This is a supply-chain hardening measure:
- release installs are more reproducible;
- transitive dependency updates become visible review surfaces;
- the package tarball contains the dependency graph that release validators
checked;
- suitable OpenClaw-owned plugin tarballs contain the dependency files from
that graph;
- `package-lock.json` stays out of the published package, because npm does not
treat it as the publishable lock contract.
Shrinkwrap is not a sandbox and does not make every dependency trustworthy. It
does not replace `openclaw security audit`, host isolation, npm provenance,
signature/audit checks, or `--ignore-scripts` install smoke tests when those are
appropriate. Treat it as a release reproducibility and review-control boundary.
Maintainers should update and verify shrinkwrap whenever the root package or an
OpenClaw-owned published plugin package changes its published dependency graph:
```bash
pnpm deps:shrinkwrap:generate
pnpm deps:shrinkwrap:check
```
The generator resolves npm's publishable lock format but rejects generated
package versions that are not already present in `pnpm-lock.yaml`, preserving
the pnpm dependency age, override, and patch review boundary.
Use `pnpm deps:shrinkwrap:root:generate` and
`pnpm deps:shrinkwrap:root:check` only when you intentionally want to refresh
the root `openclaw` package without touching plugin packages.
Review `pnpm-lock.yaml`, `npm-shrinkwrap.json`, bundled plugin dependency
payloads, and any `package-lock.json` diff as security-sensitive. The package
validators require shrinkwrap in new root package tarballs and the plugin npm
publish path checks plugin-local shrinkwrap, installs package-local bundled
dependencies, and then packs or publishes. Package validators reject
`package-lock.json`.
To inspect a published package:
```bash
npm pack openclaw@<version> --json --pack-destination /tmp/openclaw-pack
tar -tf /tmp/openclaw-pack/openclaw-<version>.tgz | grep '^package/npm-shrinkwrap.json$'
```
To inspect an OpenClaw-owned plugin package, replace the package spec and check
the same tar entry:
```bash
npm pack @openclaw/discord@<version> --json --pack-destination /tmp/openclaw-plugin-pack
tar -tf /tmp/openclaw-plugin-pack/openclaw-discord-<version>.tgz | grep '^package/npm-shrinkwrap.json$'
tar -tf /tmp/openclaw-plugin-pack/openclaw-discord-<version>.tgz | grep '^package/node_modules/'
```
Background: [npm-shrinkwrap.json](https://docs.npmjs.com/cli/v11/configuring-npm/npm-shrinkwrap-json).
Shrinkwrap is a supply-chain hardening and release reproducibility boundary,
not a sandbox. For the plain-English model, maintainer commands, and package
inspection checks, see [npm shrinkwrap](/gateway/security/shrinkwrap).
### Deployment and host trust

View File

@@ -0,0 +1,111 @@
---
summary: "Plain-English and technical explanation of npm shrinkwrap in OpenClaw releases"
read_when:
- You want to know what npm shrinkwrap means in an OpenClaw release
- You are reviewing package lockfiles, dependency changes, or supply-chain risk
- You are validating root or plugin npm packages before publishing
title: "npm shrinkwrap"
---
OpenClaw source checkouts use `pnpm-lock.yaml`. Published OpenClaw npm
packages use `npm-shrinkwrap.json`, npm's publishable dependency lockfile, so
package installs use the dependency graph reviewed during release.
## The easy version
Shrinkwrap is a receipt for the dependency tree that ships with an npm package.
It tells npm which exact transitive package versions to install.
For OpenClaw releases, that means:
- the published package does not ask npm to invent a fresh dependency graph at
install time;
- dependency changes become easier to review because they appear in a lockfile;
- release validation can test the same graph users will install;
- package-size or native-dependency surprises are easier to spot before
publishing.
Shrinkwrap is not a sandbox. It does not make a dependency safe by itself, and
it does not replace host isolation, `openclaw security audit`, package
provenance, or install smoke tests.
The short mental model:
| File | Where it matters | What it means |
| --------------------- | ------------------------ | --------------------------------- |
| `pnpm-lock.yaml` | OpenClaw source checkout | Maintainer dependency graph |
| `npm-shrinkwrap.json` | Published npm package | npm install graph for users |
| `package-lock.json` | Local npm apps | Not the OpenClaw publish contract |
## Why OpenClaw uses it
OpenClaw is a gateway, plugin host, model router, and agent runtime. A default
install can affect startup time, disk use, native package downloads, and
supply-chain exposure.
Shrinkwrap gives release review a stable boundary:
- reviewers can see transitive dependency movement;
- package validators can reject unexpected lockfile drift;
- package acceptance can test installs with the graph that will ship;
- plugin packages can carry their own locked dependency graph instead of
relying on the root package to own plugin-only dependencies.
The goal is not "more lockfiles." The goal is reproducible release installs
with clear ownership.
## Technical details
The root `openclaw` npm package and OpenClaw-owned npm plugin packages include
`npm-shrinkwrap.json` when they publish. Suitable OpenClaw-owned plugin
packages can also publish with explicit `bundledDependencies`, so their runtime
dependency files are carried in the plugin tarball instead of depending only on
install-time resolution.
Maintain the boundary like this:
```bash
pnpm deps:shrinkwrap:generate
pnpm deps:shrinkwrap:check
```
The generator resolves npm's publishable lock format but rejects generated
package versions that are not already present in `pnpm-lock.yaml`. That keeps
the pnpm dependency age, override, and patch-review boundary intact.
Use root-only commands only when intentionally refreshing the root package
without touching plugin packages:
```bash
pnpm deps:shrinkwrap:root:generate
pnpm deps:shrinkwrap:root:check
```
Review these files as security-sensitive:
- `pnpm-lock.yaml`
- `npm-shrinkwrap.json`
- bundled plugin dependency payloads
- any `package-lock.json` diff
OpenClaw package validators require shrinkwrap in new root package tarballs.
The plugin npm publish path checks plugin-local shrinkwrap, installs
package-local bundled dependencies, and then packs or publishes. Package
validators reject `package-lock.json` for published OpenClaw packages.
To inspect a published root package:
```bash
npm pack openclaw@<version> --json --pack-destination /tmp/openclaw-pack
tar -tf /tmp/openclaw-pack/openclaw-<version>.tgz | grep '^package/npm-shrinkwrap.json$'
```
To inspect an OpenClaw-owned plugin package:
```bash
npm pack @openclaw/discord@<version> --json --pack-destination /tmp/openclaw-plugin-pack
tar -tf /tmp/openclaw-plugin-pack/openclaw-discord-<version>.tgz | grep '^package/npm-shrinkwrap.json$'
tar -tf /tmp/openclaw-plugin-pack/openclaw-discord-<version>.tgz | grep '^package/node_modules/'
```
Background: [npm-shrinkwrap.json](https://docs.npmjs.com/cli/v11/configuring-npm/npm-shrinkwrap-json).

View File

@@ -282,7 +282,7 @@ troubleshooting, see the main [FAQ](/help/faq).
<Accordion title="Are opus / sonnet / gpt built-in shortcuts?">
Yes. OpenClaw ships a few default shorthands (only applied when the model exists in `agents.defaults.models`):
- `opus` → `anthropic/claude-opus-4-7`
- `opus` → `anthropic/claude-opus-4-8`
- `sonnet` → `anthropic/claude-sonnet-4-6`
- `gpt` → `openai/gpt-5.4`
- `gpt-mini` → `openai/gpt-5.4-mini`
@@ -536,7 +536,11 @@ Related: [/concepts/oauth](/concepts/oauth) (OAuth flows, token storage, multi-a
<Accordion title="OAuth vs API key - what is the difference?">
OpenClaw supports both:
- **OAuth** often leverages subscription access (where applicable).
- **OAuth / CLI login** often leverages subscription access where the
provider supports it. For Anthropic, OpenClaw's Claude CLI backend uses
Claude Code `claude -p`; Anthropic currently treats that as Agent
SDK/programmatic usage, with a separate monthly Agent SDK credit starting
June 15, 2026.
- **API keys** use pay-per-token billing.
The wizard explicitly supports Anthropic Claude CLI, OpenAI Codex OAuth, and API keys.

View File

@@ -71,12 +71,13 @@ Live tests are split into two layers so we can isolate failures:
- Run a small completion per model (and targeted regressions where needed)
- How to enable:
- `pnpm test:live` (or `OPENCLAW_LIVE_TEST=1` if invoking Vitest directly)
- Set `OPENCLAW_LIVE_MODELS=modern` (or `all`, alias for modern) to actually run this suite; otherwise it skips to keep `pnpm test:live` focused on gateway smoke
- Set `OPENCLAW_LIVE_MODELS=modern`, `small`, or `all` (alias for modern) to actually run this suite; otherwise it skips to keep `pnpm test:live` focused on gateway smoke
- How to select models:
- `OPENCLAW_LIVE_MODELS=modern` to run the modern allowlist (Opus/Sonnet 4.6+, GPT-5.2 + Codex, Gemini 3, DeepSeek V4, GLM 4.7, MiniMax M2.7, Grok 4.3)
- `OPENCLAW_LIVE_MODELS=small` to run the constrained small-model allowlist (Qwen 8B/9B local-compatible routes, OpenRouter Qwen/GLM, and Z.AI GLM)
- `OPENCLAW_LIVE_MODELS=all` is an alias for the modern allowlist
- or `OPENCLAW_LIVE_MODELS="openai/gpt-5.5,openai-codex/gpt-5.5,anthropic/claude-opus-4-6,..."` (comma allowlist)
- Modern/all sweeps default to a curated high-signal cap; set `OPENCLAW_LIVE_MAX_MODELS=0` for an exhaustive modern sweep or a positive number for a smaller cap.
- Modern/all and small sweeps default to their curated caps; set `OPENCLAW_LIVE_MAX_MODELS=0` for an exhaustive selected-profile sweep or a positive number for a smaller cap.
- Exhaustive sweeps use `OPENCLAW_LIVE_TEST_TIMEOUT_MS` for the whole direct-model test timeout. Default: 60 minutes.
- Direct-model probes run with 20-way parallelism by default; set `OPENCLAW_LIVE_MODEL_CONCURRENCY` to override.
- How to select providers:
@@ -339,6 +340,12 @@ Narrow, explicit allowlists are fastest and least flaky:
- Single model, direct (no gateway):
- `OPENCLAW_LIVE_MODELS="openai/gpt-5.5" pnpm test:live src/agents/models.profiles.live.test.ts`
- Small-model direct profile:
- `OPENCLAW_LIVE_MODELS=small pnpm test:live src/agents/models.profiles.live.test.ts`
- Ollama Cloud API smoke:
- `OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_OLLAMA=1 OPENCLAW_LIVE_OLLAMA_BASE_URL=https://ollama.com OPENCLAW_LIVE_OLLAMA_MODEL=glm-5.1:cloud OPENCLAW_LIVE_OLLAMA_WEB_SEARCH=0 pnpm test:live -- extensions/ollama/ollama.live.test.ts`
- Single model, gateway smoke:
- `OPENCLAW_LIVE_GATEWAY_MODELS="openai/gpt-5.5" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts`

View File

@@ -30,9 +30,9 @@ Update and plugin tests protect these contracts:
plugin state.
- Plugin installs work from local directories, git repos, npm packages, and the
ClawHub registry path.
- Plugin npm dependencies are installed in the managed npm root, scanned before
trust, and removed through npm during uninstall so hoisted dependencies do not
linger.
- Plugin npm dependencies are installed in one managed npm project per plugin,
scanned before trust, and removed through npm during uninstall so hoisted
dependencies do not linger.
- Plugin update is stable when nothing changed: install records, resolved
source, installed dependency layout, and enabled state stay intact.
@@ -276,9 +276,9 @@ can fail for the right reason:
- Registry/package source behavior: `test:docker:plugins` fixture or ClawHub
fixture server.
- Dependency layout or cleanup behavior: assert both runtime execution and the
filesystem boundary. npm dependencies may be hoisted under the managed npm
root, so tests should prove the root is scanned/cleaned instead of assuming a
package-local `node_modules` tree.
filesystem boundary. npm dependencies may be hoisted inside the plugin's
managed npm project, so tests should prove that project is scanned/cleaned
instead of assuming only the plugin package-local `node_modules` tree.
Keep new Docker fixtures hermetic by default. Use local fixture registries and
fake packages unless the point of the test is live registry behavior.

View File

@@ -84,11 +84,12 @@ When debugging real providers/models (requires real creds):
- Codex on-demand install smoke: `pnpm test:docker:codex-on-demand`
- Installs the packaged OpenClaw tarball in Docker, runs OpenAI API-key
onboarding, and verifies the Codex plugin plus `@openai/codex` dependency
were downloaded into the managed npm root on demand.
were downloaded into the managed npm project root on demand.
- Live plugin tool dependency smoke: `pnpm test:docker:live-plugin-tool`
- Packs a fixture plugin with a real `slugify` dependency, installs it through
`npm-pack:`, verifies the dependency under the managed npm root, then asks a
live OpenAI model to call the plugin tool and return the hidden slug.
`npm-pack:`, verifies the dependency under the managed npm project root,
then asks a live OpenAI model to call the plugin tool and return the hidden
slug.
- Crestodian rescue command smoke: `pnpm test:live:crestodian-rescue-channel`
- Opt-in belt-and-suspenders check for the message-channel rescue command
surface. It exercises `/crestodian status`, queues a persistent model
@@ -738,13 +739,13 @@ plugin validation checklist, see
These Docker runners split into two buckets:
- Live-model runners: `test:docker:live-models` and `test:docker:live-gateway` run only their matching profile-key live file inside the repo Docker image (`src/agents/models.profiles.live.test.ts` and `src/gateway/gateway-models.profiles.live.test.ts`), mounting your local config dir, workspace, and optional profile env file. The matching local entrypoints are `test:live:models-profiles` and `test:live:gateway-profiles`.
- Docker live runners default to a smaller smoke cap so a full Docker sweep stays practical:
`test:docker:live-models` defaults to `OPENCLAW_LIVE_MAX_MODELS=12`, and
- Docker live runners keep their own practical caps where needed:
`test:docker:live-models` defaults to the curated supported high-signal set, and
`test:docker:live-gateway` defaults to `OPENCLAW_LIVE_GATEWAY_SMOKE=1`,
`OPENCLAW_LIVE_GATEWAY_MAX_MODELS=8`,
`OPENCLAW_LIVE_GATEWAY_STEP_TIMEOUT_MS=45000`, and
`OPENCLAW_LIVE_GATEWAY_MODEL_TIMEOUT_MS=90000`. Override those env vars when you
explicitly want the larger exhaustive scan.
`OPENCLAW_LIVE_GATEWAY_MODEL_TIMEOUT_MS=90000`. Set `OPENCLAW_LIVE_MAX_MODELS`
or the gateway env vars when you explicitly want a smaller cap or larger scan.
- `test:docker:all` builds the live Docker image once via `test:docker:live-build`, packs OpenClaw once as an npm tarball through `scripts/package-openclaw-for-docker.mjs`, then builds/reuses two `scripts/e2e/Dockerfile` images. The bare image is only the Node/Git runner for install/update/plugin-dependency lanes; those lanes mount the prebuilt tarball. The functional image installs the same tarball into `/app` for built-app functionality lanes. Docker lane definitions live in `scripts/lib/docker-e2e-scenarios.mjs`; planner logic lives in `scripts/lib/docker-e2e-plan.mjs`; `scripts/test-docker-all.mjs` executes the selected plan. The aggregate uses a weighted local scheduler: `OPENCLAW_DOCKER_ALL_PARALLELISM` controls process slots, while resource caps keep heavy live, npm-install, and multi-service lanes from all starting at once. If a single lane is heavier than the active caps, the scheduler can still start it when the pool is empty and then keeps it running alone until capacity is available again. Defaults are 10 slots, `OPENCLAW_DOCKER_ALL_LIVE_LIMIT=9`, `OPENCLAW_DOCKER_ALL_NPM_LIMIT=10`, and `OPENCLAW_DOCKER_ALL_SERVICE_LIMIT=7`; tune `OPENCLAW_DOCKER_ALL_WEIGHT_LIMIT` or `OPENCLAW_DOCKER_ALL_DOCKER_LIMIT` only when the Docker host has more headroom. The runner performs a Docker preflight by default, removes stale OpenClaw E2E containers, prints status every 30 seconds, stores successful lane timings in `.artifacts/docker-tests/lane-timings.json`, and uses those timings to start longer lanes first on later runs. Use `OPENCLAW_DOCKER_ALL_DRY_RUN=1` to print the weighted lane manifest without building or running Docker, or `node scripts/test-docker-all.mjs --plan-json` to print the CI plan for selected lanes, package/image needs, and credentials.
- `Package Acceptance` is the GitHub-native package gate for "does this installable tarball work as a product?" It resolves one candidate package from `source=npm`, `source=ref`, `source=url`, or `source=artifact`, uploads it as `package-under-test`, then runs the reusable Docker E2E lanes against that exact tarball instead of repacking the selected ref. Profiles are ordered by breadth: `smoke`, `package`, `product`, and `full`. See [Testing updates and plugins](/help/testing-updates-plugins) for the package/update/plugin contract, published-upgrade survivor matrix, release defaults, and failure triage.
- Build and release checks run `scripts/check-cli-bootstrap-imports.mjs` after tsdown. The guard walks the static built graph from `dist/entry.js` and `dist/cli/run-main.js` and fails if pre-dispatch startup imports package dependencies such as Commander, prompt UI, undici, or logging before command dispatch; it also keeps the bundled gateway run chunk under budget and rejects static imports of known cold gateway paths. Packaged CLI smoke also covers root help, onboard help, doctor help, status, config schema, and a model-list command.

View File

@@ -1,205 +0,0 @@
---
summary: "Per-agent Policy plugin overlays layered on top of global policy rules."
read_when:
- You are designing per-agent policy requirements
- You need to distinguish tool posture policy from workspace policy
- You are configuring stricter policy for one named agent
title: "Agent-scoped policy overlays"
---
# Agent-scoped policy overlays
OpenClaw policy supports global requirements and stricter requirements for
explicit runtime agent ids. Some deployments need one agent to use a tighter
workspace and tool posture than other agents, but deployment-wide rules should
not force every agent to use the same posture.
This page describes the agent-scoped overlay model. The field reference remains
[`openclaw policy`](/cli/policy).
## Design goals
- Keep global policy as the deployment baseline.
- Let a named agent add stricter requirements without weakening global rules.
- Reuse existing policy section shapes where the evidence can be attributed to
an agent.
- Avoid making `agents.workspace` a second tool-permission system.
- Leave global-only checks global until their evidence can be mapped to an
agent.
## Shape
Use `scopes.<scopeName>` for purpose-named agent policy scopes. Each
scope lists the runtime `agentIds` it applies to, then reuses the normal
top-level policy section grammar where the section evidence can be attributed to
those agents. The initial shipped scoped sections are `tools` and
`agents.workspace`; sandbox and ingress stay out of this PR and can join the
same container once those policy PRs land and their evidence carries agent
identity. The scoped field inventory is backed by policy rule metadata that
records each field's strictness semantics for later policy-file conformance.
```jsonc
{
"tools": {
"denyTools": ["process"],
},
"agents": {
"workspace": {
"allowedAccess": ["none", "ro"],
},
},
"scopes": {
"release-agent-lockdown": {
"agentIds": ["release-agent"],
"agents": {
"workspace": {
"allowedAccess": ["none", "ro"],
},
},
"tools": {
"profiles": { "allow": ["minimal", "messaging"] },
"fs": { "requireWorkspaceOnly": true },
"exec": {
"allowSecurity": ["deny", "allowlist"],
"requireAsk": ["always"],
"allowHosts": ["sandbox"],
},
"elevated": { "allow": false },
"alsoAllow": { "expected": ["message", "read"] },
"denyTools": ["exec", "process", "write", "edit", "apply_patch"],
},
},
},
}
```
`agents.workspace` remains the existing all-agent workspace baseline.
`scopes.<scopeName>` is a scoped overlay, not a replacement for global
policy. The scope name is descriptive only; matching uses `agentIds`, not
display names. It deliberately contains normal section names instead of a
bespoke per-agent mini-grammar.
Every scope present in `policy.jsonc` must be valid and enforceable. In this
PR, the only supported selector is `agentIds`, and it supports only `tools.*`
and `agents.workspace.*`.
## Layering semantics
Policy evaluation is additive:
1. Top-level policy applies to all matching evidence.
2. Existing `agents.workspace` applies to defaults and every listed agent.
3. `scopes.<scopeName>` applies to evidence for each normalized runtime
id in `agentIds`.
4. Multiple scope blocks may target the same agent when they govern
different fields, or when a later value for the same field is equally or
more restrictive according to policy metadata.
5. A named-agent overlay can tighten policy, but it cannot make a global
violation acceptable.
If both global and agent-scoped rules fail, findings should point at the rule
that was violated:
```text
oc://policy.jsonc/tools/denyTools
oc://policy.jsonc/scopes/release-agent-lockdown/tools/denyTools
oc://policy.jsonc/scopes/release-agent-lockdown/agents/workspace/allowedAccess
```
That keeps broad tool posture, named-agent tool posture, and workspace posture
auditable as separate requirements even when they observe the same config
fields.
Exact-list claims such as `tools.alsoAllow.expected` compare the configured list
to the expected list and report both missing expected entries and unexpected
extra entries. This is intended for additive posture such as `alsoAllow`, where
one extra entry can widen an agent beyond its reviewed role.
## Policy and config layering
The overlay model separates where policy is authored from where OpenClaw config
is observed:
| Policy scope | Observed config | Applies to | Example result |
| --------------------------------------- | ---------------------------------------------------- | --------------------------------- | ----------------------------------------------------------------------------- |
| Top-level `tools.*` | Global `tools.*` and inherited agent tool posture | All agents using matching posture | Deny `gateway` exec host for every agent unless the global policy allows it. |
| Top-level `tools.*` | `agents.list[].tools.*` overrides | Any agent with an override | Flag one agent that overrides `tools.exec.host` to an unapproved value. |
| `scopes.<scopeName>.tools.*` | Matching `agents.list[]` entry and inherited posture | Only that named agent | Let most agents use `node` exec host while one agent must use only `sandbox`. |
| `agents.workspace` | Defaults and every listed agent workspace posture | Defaults and all listed agents | Require every agent workspace access to be `none` or `ro`. |
| `scopes.<scopeName>.agents.workspace.*` | Matching `agents.list[]` workspace posture | Only that named agent | Require one agent to be read-only without requiring the same for `main`. |
Per-agent overlays are additive. A named-agent rule can be stricter than the
top-level rule, but it cannot make a global violation acceptable. For allow-list
rules, the effective allowed set is the intersection of the global rule and the
named-agent overlay when both are present.
For example, if top-level `tools.exec.allowHosts` permits `["sandbox", "node"]`
and `scopes.release-agent-lockdown.tools.exec.allowHosts` permits only
`["sandbox"]`, `release-agent` fails when its effective exec host is `node`;
another agent can still pass
with `node`.
## Tool posture versus workspace posture
Tool posture belongs under `tools` because it describes what tool behavior a
configuration may expose. The existing `tools.*` policy observes both global
`tools.*` config and per-agent `agents.list[].tools.*` overrides.
Workspace posture belongs under `workspace` because it describes sandbox mode
and workspace access. The workspace section should not grow into a general tool
policy namespace. If one agent needs stricter tool restrictions to make its
workspace posture meaningful, put those restrictions in the same agent overlay
under `scopes.<scopeName>.tools`.
For a restricted release agent, the intended split is:
```jsonc
{
"scopes": {
"release-agent-lockdown": {
"agentIds": ["release-agent"],
"agents": {
"workspace": { "allowedAccess": ["none", "ro"] },
},
"tools": {
"denyTools": ["exec", "process", "write", "edit", "apply_patch"],
},
},
},
}
```
## Section eligibility
An agent-scoped section should be added only when policy evidence carries an
agent id or can be attributed to one without guessing.
| Section | Initial agent-scoped status | Reason |
| ----------- | --------------------------- | ------------------------------------------------------------------------ |
| `workspace` | Include | Agent sandbox/workspace evidence already has agent identity. |
| `tools` | Include | Tool posture evidence includes global and per-agent tool config. |
| `sandbox` | Pipeline follow-up | Keep out until the sandbox posture PR lands and evidence can be scoped. |
| `ingress` | Pipeline follow-up | Keep out until ingress/channel posture lands with agent attribution. |
| `models` | Include when mapped | Selected model refs can be agent-specific. |
| `mcp` | Include when mapped | Use only when MCP server evidence is attributable to an agent. |
| `auth` | Defer | Auth profile metadata is a config catalog unless agent binding is clear. |
| `channels` | Defer | Channel provider posture is deployment-level until routing is scoped. |
| `gateway` | Keep global | Gateway exposure/auth/http posture is process-level. |
| `network` | Keep global | Private-network SSRF posture is runtime-level. |
| `secrets` | Keep global first | Secret provider posture is shared unless refs are agent-attributed. |
## Compatibility
The implementation is additive:
- keep all existing top-level policy fields valid;
- keep `agents.workspace` semantics unchanged;
- validate `scopes` before evaluating scoped rules;
- reject unsupported scoped sections clearly until their evidence and policy
contracts are implemented;
- do not reinterpret top-level `tools.requireMetadata` as agent-scoped, because
tool metadata describes the declared workspace tool catalog;
- include agent-scoped evidence in the attestation hash when any scoped rule is
present.
This lets broad tool posture remain a top-level policy contract while named
agents add stricter observable claims without weakening the global baseline.

View File

@@ -281,8 +281,9 @@ fresh OpenClaw session.
**A Computer Use tool says `Native hook relay unavailable`.** The Codex-native
tool hook could not reach an active OpenClaw relay through the local bridge or
Gateway fallback. Start a fresh OpenClaw session with `/new` or `/reset`. If it
keeps happening, restart the gateway so old app-server threads and hook
registrations are dropped, then retry.
works once and then fails again on a later tool call, `/new` is only clearing the
current attempt; restart the Codex app-server or OpenClaw Gateway so old threads
and hook registrations are dropped, then retry in a fresh session.
**Turn-start auto-install refuses a source.** This is intentional. Add the
source with explicit `/codex computer-use install --source <marketplace-source>`

View File

@@ -99,6 +99,16 @@ OpenClaw can mirror selected events, but it cannot rewrite the native Codex
thread unless Codex exposes that operation through app-server or native hook
callbacks.
Codex app-server report-mode `PreToolUse` events defer plugin approval requests
to the matching app-server approval. If an OpenClaw `before_tool_call` hook
returns `requireApproval` while the native payload sets report approval mode
(`openclaw_approval_mode` is `"report"`), the native hook relay records the
plugin approval requirement and returns no native decision. When Codex sends the
app-server approval request for the same tool use, OpenClaw opens the plugin
approval prompt and maps the decision back to Codex. Codex `PermissionRequest`
events are a separate approval path and can still route through OpenClaw
approvals when the runtime is configured for that bridge.
Codex app-server item notifications also provide async `after_tool_call`
observations for native tool completions that are not already covered by the
native `PostToolUse` relay. These observations are for telemetry and plugin

View File

@@ -738,9 +738,11 @@ protocol version.
the Codex thread is still trying to use a native hook relay id that OpenClaw no
longer has registered. This is a native Codex hook transport problem, not an ACP
backend, provider, GitHub, or shell-command failure. Start a fresh session in
the affected chat with `/new` or `/reset`, then retry a harmless command. If the
same fresh session still fails, restart the Codex app-server or OpenClaw Gateway
so native hook registrations are recreated.
the affected chat with `/new` or `/reset`, then retry a harmless command. If that
works once but the next native tool call fails again, treat `/new` as a temporary
workaround only: copy the prompt into a fresh session after restarting the Codex
app-server or OpenClaw Gateway so old threads are dropped and native hook
registrations are recreated.
**A non-Codex model uses the built-in harness:** that is expected unless
provider or model runtime policy routes it to another harness. Plain non-OpenAI

374
docs/plugins/copilot.md Executable file
View File

@@ -0,0 +1,374 @@
---
summary: "Run OpenClaw embedded agent turns through the bundled GitHub Copilot SDK harness"
title: "Copilot SDK harness"
read_when:
- You want to use the bundled GitHub Copilot SDK harness for an agent
- You need configuration examples for the `copilot` runtime
- You are wiring an agent to subscription Copilot (github / openclaw / copilot) and want it to run through the Copilot CLI
---
The bundled `copilot` extension lets OpenClaw run embedded subscription
Copilot agent turns through the GitHub Copilot CLI (`@github/copilot-sdk`)
instead of the built-in PI harness.
Use the Copilot SDK harness when you want the Copilot CLI session to own the
low-level agent loop: native tool execution, native compaction
(`infiniteSessions`), and CLI-managed thread state under `copilotHome`.
OpenClaw still owns chat channels, session files, model selection, OpenClaw
dynamic tools (bridged), approvals, media delivery, the visible transcript
mirror, `/btw` side questions (handled by the in-tree PI fallback — see
[Side questions (`/btw`)](#side-questions-btw)), and `openclaw doctor`.
For the broader model/provider/runtime split, start with
[Agent runtimes](/concepts/agent-runtimes).
## Requirements
- OpenClaw with the bundled `copilot` extension available.
- If your config uses `plugins.allow`, include `copilot` (the manifest
id in `extensions/copilot/openclaw.plugin.json`). A restrictive
allowlist that uses the npm-style `@openclaw/copilot` package name
will leave the bundled plugin blocked and the runtime will not load
even with `agentRuntime.id: "copilot"`.
- A GitHub Copilot subscription that can drive the Copilot CLI (or a
`gitHubToken` env / auth-profile entry for headless / cron runs).
- A writable `copilotHome` directory. The harness defaults to
`~/.openclaw/agents/<agentId>/copilot` for full per-agent isolation. The
platform default (`%APPDATA%\copilot` on Windows, `$XDG_CONFIG_HOME/copilot`
or `~/.config/copilot` elsewhere) is used as the doctor probe fallback when
no explicit home is set.
`openclaw doctor` runs the bundled
[doctor contract](#doctor-and-probes) for the extension; failures there are
the canonical way to confirm the environment is ready before opting an agent
in.
## On-demand SDK install
The Copilot agent runtime ships its small TypeScript code bundled inside
the openclaw tarball, but the underlying `@github/copilot-sdk` package
(and its platform-specific `@github/copilot-<platform>-<arch>` CLI
binary) is **not** installed by default — together they add ~260 MB to
your openclaw install footprint, and most openclaw users do not select
a Copilot model.
The wizard offers to install the SDK the first time you select a
`github-copilot/*` model **and** your config opts the model (or its
provider) into the Copilot agent runtime via
`agentRuntime: { id: "copilot" }` (see [Quickstart](#quickstart) below).
Without the opt-in, openclaw uses its built-in GitHub Copilot provider
and never prompts for the SDK install:
```
The Copilot agent runtime needs @github/copilot-sdk (~260 MB on first
install, downloads the @github/copilot CLI binary for your platform).
Install now? [Y/n]
```
If you accept, the SDK is installed into
`~/.openclaw/npm-runtime/copilot/` and detected on subsequent runs. The
install runs `npm ci` against a checked-in `package-lock.json` shipped
with openclaw at
`src/commands/copilot-sdk-install-manifest/package-lock.json`, so the
exact transitive graph reviewed for this release lands on disk on every
user machine.
If you decline, the runtime will fail at first invocation with an
actionable install message; re-run `openclaw setup` to retry the install
(or copy the pinned manifest into `~/.openclaw/npm-runtime/copilot/` and
run `npm ci` yourself if you need to install offline).
The runtime resolves the SDK in this order:
1. `import("@github/copilot-sdk")` against the host openclaw install
(covers source/dev checkouts and any environment that pre-installs
the SDK alongside openclaw).
2. The well-known fallback dir `~/.openclaw/npm-runtime/copilot/` (the
wizard install target).
A missing SDK surfaces a single error with code `COPILOT_SDK_MISSING`
and the manual install command above.
## Quickstart
Pin one model (or one provider) to the harness:
```json5
{
agents: {
defaults: {
model: "github-copilot/gpt-5.5",
models: {
"github-copilot/gpt-5.5": {
agentRuntime: { id: "copilot" },
},
},
},
},
}
```
Both routes are equivalent. Use `agentRuntime.id` on a single model entry
when only that model should be routed through the harness; set
`agentRuntime.id` on a provider when every model under that provider should
use it.
## Supported providers
The harness advertises support for the canonical `github-copilot` provider
(the same id owned by `extensions/github-copilot`):
- `github-copilot`
Anything outside that set falls through `selection.ts`'s `auto_pi` branch back
to PI.
## Auth
Per-agent precedence, applied during `runCopilotAttempt`:
1. **Explicit `useLoggedInUser: true`** on the attempt input. Uses the Copilot
CLI's logged-in user resolved under the agent's `copilotHome`.
2. **Explicit `gitHubToken`** on the attempt input (with `profileId` +
`profileVersion`). Useful for direct CLI invocations and tests where the
caller wants to bypass auth-profile resolution.
3. **Contract-resolved `resolvedApiKey` + `authProfileId`** from the
`EmbeddedRunAttemptParams` shape. This is the **production main path**:
core resolves the agent's configured `github-copilot` auth profile
(via `src/infra/provider-usage.auth.ts:resolveProviderAuths`) before
invoking the harness, and the harness consumes both fields directly.
This makes a `github-copilot:<profile>` auth profile work end-to-end
for headless / cron / multi-profile setups without env vars.
4. **Env-var fallback** for direct CLI / dogfood runs where no auth
profile is configured. The runtime checks the following vars in
precedence order, mirroring the shipped `github-copilot` provider
(`extensions/github-copilot/auth.ts`) and the documented Copilot SDK
setup:
1. `OPENCLAW_GITHUB_TOKEN` -- harness-specific override; set this
to pin a token for the OpenClaw harness without disturbing
system-wide `gh` / Copilot CLI config.
2. `COPILOT_GITHUB_TOKEN` -- standard Copilot SDK / CLI env var.
3. `GH_TOKEN` -- standard `gh` CLI env var (matches the existing
`github-copilot` provider precedence).
4. `GITHUB_TOKEN` -- generic GitHub token fallback.
The first non-empty value wins; empty strings are treated as
absent. The synthesised pool profile id is `env:<NAME>` and the
profileVersion is a non-reversible sha256 fingerprint of the
token, so rotating the env value cleanly busts the client pool.
5. **Default `useLoggedInUser`** when no token signal is available.
Each agent gets a dedicated `copilotHome` so Copilot CLI tokens, sessions, and
config do not leak between agents on the same machine. The default is
`<agentDir>/copilot` when the host hands the harness an agent directory
(isolating SDK state from OpenClaw's `models.json` / `auth-profiles.json` in
the same directory), or `~/.openclaw/agents/<agentId>/copilot` otherwise.
Override with `copilotHome: <path>` on the attempt input when you need a
custom location (for example, a shared mount for migration).
`probeCopilotAuthShape` (see [Doctor and probes](#doctor-and-probes)) is the
pure shape check that validates which of the modes above will be used.
It does not perform a live SDK handshake.
## Configuration surface
The harness reads its config from per-attempt input
(`runCopilotAttempt({...})`) plus a small set of env defaults inside
`extensions/copilot/src/`:
- `copilotHome` — per-agent CLI state directory (defaults documented above).
- `model` — string or `{ provider, id, api? }`. When omitted, OpenClaw uses
the agent's normal model selection and the harness verifies the resolved
provider is in the supported set.
- `reasoningEffort``"low" | "medium" | "high" | "xhigh"`. Maps from
OpenClaw's `ThinkLevel` / `ReasoningLevel` resolution in
`auto-reply/thinking.ts`.
- `infiniteSessionConfig` — optional override for the SDK
`infiniteSessions` block driven by `harness.compact`. Defaults are safe to
leave as-is.
- `hooksConfig` — optional bridge config exposing OpenClaw
before/after-message-write hooks to the SDK loop.
- `permissionPolicy` — optional override for the SDK's
`onPermissionRequest` handler used for built-in SDK tool kinds
(`shell`, `write`, `read`, `url`, `mcp`, `memory`, `hook`). Defaults
to `rejectAllPolicy` as a safety net; in practice the SDK never
invokes any of those kinds because every bridged OpenClaw tool is
registered with `overridesBuiltInTool: true` and
`skipPermission: true` so 100% of tool calls flow through OpenClaw's
wrapped `execute()`. See [Permissions and ask_user](#permissions-and-ask_user).
- `enableSessionTelemetry` — opt-in OpenTelemetry routing via
`telemetry-bridge.ts`.
Nothing in the rest of OpenClaw needs to know about these fields. Other
plugins, channels, and core code only see the standard
`AgentHarnessAttemptParams` / `AgentHarnessAttemptResult` shape.
## Compaction
When `harness.compact` runs, the Copilot SDK harness:
1. Enables `infiniteSessions` on the SDK session.
2. Lets the SDK perform its native compaction.
3. Writes an OpenClaw-shaped marker at
`workspacePath/files/openclaw-compaction-<ts>.json` so existing OpenClaw
transcript readers still see a familiar artifact.
The OpenClaw side transcript mirror (see below) continues to receive the
post-compaction messages, so user-facing chat history stays consistent.
## Transcript mirroring
`runCopilotAttempt` dual-writes each turn's mirrorable messages into the
OpenClaw audit transcript via
`extensions/copilot/src/dual-write-transcripts.ts`. The mirror is
per-session scoped (`copilot:${sessionId}`) and uses a per-message
identity (`${role}:${sha256_16(role,content)}`) so re-emits of prior-turn
entries collide with existing on-disk keys and do not duplicate.
The mirror is wrapped in two layers of failure containment so a transcript
write failure cannot fail the attempt: an internal best-effort wrapper and a
defense-in-depth `.catch(...)` at the attempt level. Failures are logged but
not surfaced.
## Side questions (`/btw`)
`/btw` is **not** native on this harness. `createCopilotAgentHarness()`
deliberately leaves `harness.runSideQuestion` undefined, so OpenClaw's `/btw`
dispatcher (`src/agents/btw.ts`) falls through to the same in-tree PI fallback
path it uses for every non-Codex runtime: the configured model provider is
called directly with a short side-question prompt and streamed back via
`streamSimple` (no CLI session, no extra pool slot).
This keeps Copilot CLI sessions reserved for the agent's main turn loop, and
keeps `/btw` behavior identical to other PI-backed runtimes. The contract is
asserted in
[`extensions/copilot/harness.test.ts`](https://github.com/openclaw/openclaw/blob/main/extensions/copilot/harness.test.ts)
under `describe("runSideQuestion")`.
## Doctor and probes
`extensions/copilot/doctor-contract-api.ts` is auto-loaded by
`src/plugins/doctor-contract-registry.ts`. It contributes:
- An empty `legacyConfigRules` (no retired fields at MVP).
- A no-op `normalizeCompatibilityConfig` (kept so future field retirements
have a stable in-tree home).
- One `sessionRouteStateOwners` entry claiming provider `github-copilot`;
runtime `copilot`; CLI session key `copilot`; auth profile
prefix `github-copilot:`.
`extensions/copilot/src/doctor-probes.ts` exports three imperative probes
that hosts (including `openclaw doctor`) can call to verify the environment:
| Probe | What it checks | Reasons it can fail |
| -------------------------- | --------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `probeCopilotCliVersion` | `copilot --version` exits 0 with a non-empty version string | `non-zero-exit`, `empty-version`, `spawn-failed`, `spawn-error`, `probe-timeout` |
| `probeCopilotHomeWritable` | `mkdir -p copilotHome` + write + rm a marker file | `copilothome-not-writable` (with the underlying fs error in `details.rawError`) |
| `probeCopilotAuthShape` | At least one of `useLoggedInUser`, `gitHubToken`, or `profileId`+`profileVersion` | `no-auth-source` |
Each probe accepts a DI seam (`spawnFn`, `fsApi`) so tests do not spawn the
real Copilot CLI or touch the host fs.
## Limitations
- The harness only claims the canonical `github-copilot` provider at MVP.
Additional providers (BYOK or otherwise) should land in follow-up PRs that
ship the adapter alongside the wire-up.
- The harness does not deliver TUI; PI's TUI is unaffected and remains the
fallback for whatever runtimes do not have a peer surface.
- PI session state is not migrated when an agent switches to `copilot`.
Selection is per attempt; existing PI sessions remain valid.
- **Interactive `ask_user` is not yet wired.** The SDK's
`onUserInputRequest` handler is intentionally not registered, which
per the SDK contract hides the `ask_user` tool from the model
entirely. Agents running under this harness make best-judgment
decisions from the initial prompt rather than asking clarifying
questions mid-turn. A follow-up will port the codex pattern at
`extensions/codex/src/app-server/user-input-bridge.ts` to route SDK
`UserInputRequest`s through the OpenClaw channel/TUI prompt path; the
dormant scaffolding in `extensions/copilot/src/user-input-bridge.ts`
is the surface that follow-up will wire.
## Permissions and ask_user
Permission enforcement for bridged OpenClaw tools happens **inside the
tool wrapper**, not via the SDK's `onPermissionRequest` callback. The
same `wrapToolWithBeforeToolCallHook` that PI uses
(`src/agents/pi-tools.before-tool-call.ts`) is applied by
`createOpenClawCodingTools` to every coding tool: loop detection,
trusted plugin policies, before-tool-call hooks, and two-phase plugin
approvals via the gateway (`plugin.approval.request`) all run with the
exact same code path as native PI attempts.
To let that wrapper own the decision, the SDK Tool returned by
`convertOpenClawToolToSdkTool` is marked with:
- `overridesBuiltInTool: true` — replaces the Copilot CLI's built-in
tool of the same name (edit, read, write, bash, …) so every tool
invocation routes back to OpenClaw.
- `skipPermission: true` — tells the SDK not to fire
`onPermissionRequest({kind: "custom-tool"})` before invoking the tool.
The wrapped `execute()` performs the richer OpenClaw policy check
internally; an SDK-level prompt would either short-circuit OpenClaw's
enforcement (if we allow-all) or block every tool call (if we
reject-all) — neither matches PI parity.
The in-tree codex harness uses the same split: bridged OpenClaw tools
are wrapped (`extensions/codex/src/app-server/dynamic-tools.ts`) and
the codex-app-server's _own_ native approval kinds
(`item/commandExecution/requestApproval`,
`item/fileChange/requestApproval`,
`item/permissions/requestApproval`) are routed through
`plugin.approval.request`
(`extensions/codex/src/app-server/approval-bridge.ts`). The Copilot SDK
equivalent — fail-closed `rejectAllPolicy` for any non-`custom-tool`
kind that ever reaches `onPermissionRequest` — is the same safety net,
and it does not fire in practice because `overridesBuiltInTool: true`
displaces every built-in.
For the wrapped-tool layer to make policy decisions equivalent to PI,
the harness forwards the full PI attempt-tool context to
`createOpenClawCodingTools` — identity (`senderIsOwner`,
`memberRoleIds`, `ownerOnlyToolAllowlist`, …), channel/routing
(`groupId`, `currentChannelId`, `replyToMode`, message-tool toggles),
auth (`authProfileStore`), run identity
(`sessionKey`/`runSessionKey` derived from `sandboxSessionKey`,
`runId`), model context (`modelApi`, `modelContextWindowTokens`,
`modelCompat`, `modelHasVision`), and run hooks (`onToolOutcome`,
`onYield`). Without those fields, owner-only allowlists silently
behave as deny-by-default, plugin-trust policies cannot resolve to the
right scope, and `session_status: "current"` resolves to a stale
sandbox key. The bridge builder is in
`extensions/copilot/src/tool-bridge.ts` and mirrors the PI
authoritative call at
`src/agents/pi-embedded-runner/run/attempt.ts:1029-1117`. Two PI fields
are intentionally **not** forwarded at MVP and tracked as follow-ups:
`sandbox` (the harness does not yet route through `resolveSandboxContext`)
and the PI tool-search/code-mode machinery
(`toolSearchCatalogRef`, `includeCoreTools`,
`includeToolSearchControls`, `toolSearchCatalogExecutor`,
`toolConstructionPlan`), which has no analog at the SDK boundary.
### Session-level GitHub token
The Copilot SDK contract distinguishes the **client-level** GitHub
token (`CopilotClientOptions.gitHubToken`, used to authenticate the
CLI process itself) from the **session-level** token
(`SessionConfig.gitHubToken`, which determines content exclusion,
model routing, and quota for that session and is honored on both
`createSession` and `resumeSession`). The harness resolves auth once
via `resolveCopilotAuth` and sets both fields when the auth mode is
`gitHubToken` (an explicit `auth.gitHubToken` or a contract-resolved
`resolvedApiKey` from a configured `github-copilot` auth profile).
When the resolved mode is `useLoggedInUser`, the session-level field
is omitted so the SDK keeps deriving identity from the logged-in
identity.
`ask_user` is intentionally hidden — see Limitations above.
## Related
- [Agent runtimes](/concepts/agent-runtimes)
- [Codex harness](/plugins/codex-harness)
- [Agent harness plugins (SDK reference)](/plugins/sdk-agent-harness)

View File

@@ -34,34 +34,36 @@ OpenClaw owns only the plugin lifecycle:
OpenClaw uses stable per-source roots:
- npm packages install under `~/.openclaw/npm`
- npm packages install into per-plugin projects under
`~/.openclaw/npm/projects/<encoded-package>`
- git packages clone under `~/.openclaw/git`
- local/path/archive installs are copied or referenced without dependency repair
npm installs run in the npm root with:
npm installs run in that per-plugin project root with:
```bash
cd ~/.openclaw/npm
cd ~/.openclaw/npm/projects/<encoded-package>
npm install --omit=dev --omit=peer --legacy-peer-deps --ignore-scripts --no-audit --no-fund
```
`openclaw plugins install npm-pack:<path.tgz>` uses that same managed npm root
for a local npm-pack tarball. OpenClaw reads the tarball's npm metadata, adds it
to the managed root as a copied `file:` dependency, runs the normal npm install,
and then verifies the installed lockfile metadata before trusting the plugin.
`openclaw plugins install npm-pack:<path.tgz>` uses that same per-plugin npm
project root for a local npm-pack tarball. OpenClaw reads the tarball's npm
metadata, adds it to the managed project as a copied `file:` dependency, runs
the normal npm install, and then verifies the installed lockfile metadata before
trusting the plugin.
This is intended for package-acceptance and release-candidate proof where a
local pack artifact should behave like the registry artifact it simulates.
npm may hoist transitive dependencies to `~/.openclaw/npm/node_modules` beside
the plugin package. OpenClaw scans the managed npm root before trusting the
install and uses npm to remove npm-managed packages during uninstall, so hoisted
runtime dependencies stay inside the managed cleanup boundary.
npm may hoist transitive dependencies to the per-plugin project's
`node_modules` beside the plugin package. OpenClaw scans the managed project
root before trusting the install and removes that project during uninstall, so
hoisted runtime dependencies stay inside that plugin's cleanup boundary.
Published npm plugin packages can ship `npm-shrinkwrap.json`. npm uses that
publishable lockfile during install, and OpenClaw's managed npm root supports it
through the normal npm install path. OpenClaw-owned publishable plugin packages
must include a package-local shrinkwrap generated from that plugin package's
published dependency graph:
publishable lockfile during install, and OpenClaw's managed npm project root
supports it through the normal npm install path. OpenClaw-owned publishable
plugin packages must include a package-local shrinkwrap generated from that
plugin package's published dependency graph:
```bash
pnpm deps:shrinkwrap:generate
@@ -87,11 +89,11 @@ instead of embedding every platform binary in the plugin tarball. The root
Plugins that import `openclaw/plugin-sdk/*` declare `openclaw` as a peer
dependency. OpenClaw does not let npm install a separate registry copy of the
host package into the managed root, because stale host packages can affect npm
peer resolution during later plugin installs. Managed npm installs skip npm peer
resolution/materialization for the shared root and OpenClaw reasserts
plugin-local `node_modules/openclaw` links for installed packages that declare
the host peer after install, update, or uninstall.
host package into a managed project, because stale host packages can affect npm
peer resolution inside that plugin. Managed npm installs skip npm peer
resolution/materialization and OpenClaw reasserts plugin-local
`node_modules/openclaw` links for installed packages that declare the host peer
after install or update.
git installs clone or refresh the repository, then run:
@@ -155,7 +157,7 @@ not a supported way to prepare bundled plugin dependencies.
| -------------------------------- | ------------------------------------- | -------------------------------------------------------------------- |
| `npm install -g openclaw` | Built runtime tree inside the package | OpenClaw package and explicit plugin install/update/doctor flows |
| Git checkout plus `pnpm install` | `extensions/<id>` workspace packages | The pnpm workspace, including each plugin package's own dependencies |
| `openclaw plugins install ...` | Managed npm/git/ClawHub plugin root | The plugin install/update flow |
| `openclaw plugins install ...` | Managed npm project/git/ClawHub root | The plugin install/update flow |
## Legacy cleanup
@@ -168,4 +170,7 @@ stage directories, and package-local pnpm stores. Packaged postinstall also
removes those global symlinks before pruning the legacy target roots so upgrades
do not leave dangling ESM package imports.
These paths are legacy debris only. New installs should not create them.
Older npm installs also used a shared `~/.openclaw/npm/node_modules` root.
Current install, update, uninstall, and doctor flows still recognize that legacy
flat root only for recovery and cleanup. New npm installs should create
per-plugin project roots instead.

View File

@@ -1124,7 +1124,7 @@ Optional overrides:
introMessage: "Say exactly: I'm here.",
providers: {
google: {
voice: "Kore",
speakerVoice: "Kore",
},
},
},
@@ -1141,7 +1141,7 @@ ElevenLabs for both agent-mode listening and speaking:
providers: {
elevenlabs: {
modelId: "eleven_v3",
voiceId: "pMsXgVXv3BLzUgSXRplE",
speakerVoiceId: "pMsXgVXv3BLzUgSXRplE",
},
},
},
@@ -1169,11 +1169,11 @@ ElevenLabs for both agent-mode listening and speaking:
```
The persistent Meet voice comes from
`messages.tts.providers.elevenlabs.voiceId`. Agent replies can also use
per-reply `[[tts:voiceId=... model=eleven_v3]]` directives when TTS model
`messages.tts.providers.elevenlabs.speakerVoiceId`. Agent replies can also use
per-reply `[[tts:speakerVoiceId=... model=eleven_v3]]` directives when TTS model
overrides are enabled, but config is the deterministic default for meetings.
On join, the logs should show `transcriptionProvider=elevenlabs` and each
spoken reply should log `provider=elevenlabs model=eleven_v3 voice=<voiceId>`.
spoken reply should log `provider=elevenlabs model=eleven_v3 speakerVoiceId=<voiceId>`.
Twilio-only config:

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