Compare commits

..

1310 Commits

Author SHA1 Message Date
Vincent Koc
54e5105c12 test(plugin-sdk): refresh callable export budget expectation 2026-06-22 19:00:17 +08:00
ly-wang19
b9a7bf83a4 fix(device-pairing): guard role normalization against non-string entries (#93504)
normalizeRoleList in src/shared/device-pairing-access.ts called .trim() on every roles[] entry and the singular role without a typeof === "string" guard, so a malformed/legacy on-disk pairing record (roles/role loaded via blind-cast JSON in coercePairingStateRecord) threw "TypeError: role.trim is not a function" and crashed resolvePendingDeviceApprovalState -- and thus `openclaw devices list`, which calls it per pending request with no try/catch.

Route each item through the shared non-string-safe normalizer normalizeUniqueSingleOrTrimmedStringList, mirroring the #90654/#92178 fix that already guarded the sibling mergeRoles/mergeScopes (src/infra/device-pairing.ts) and the in-file scopes path (normalizeDeviceAuthScopes). Non-string entries are dropped; valid roles are still trimmed, deduped, and sorted. Net -10 LOC.

Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 18:56:01 +08:00
Vincent Koc
5c5a8a49d7 fix(matrix): handle missing secret storage facade 2026-06-22 12:53:19 +02:00
Vincent Koc
35be382e56 refactor(msteams): share response release wrapper 2026-06-22 18:50:59 +08:00
Vincent Koc
bdf75474b9 refactor(oc-path): share JSONL line selection 2026-06-22 18:47:24 +08:00
Vincent Koc
f13a10c798 fix(scripts): run gh without terminal formatting 2026-06-22 18:44:21 +08:00
Vincent Koc
2ba9d6eabe refactor(providers): share Qwen chat-template thinking patch 2026-06-22 18:42:40 +08:00
clawsweeper[bot]
6f17c4cc6d fix(doctor): stop promising --fix for working isolated shell-prompt cron jobs (#94655) (#94784)
Summary:
- Merged fix(doctor): stop promising --fix for working isolated shell-prompt cron jobs (#94655) after ClawSweeper review.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(doctor): stop promising --fix for working isolated shell-prompt c…

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

Prepared head SHA: 0d71970a16
Review: https://github.com/openclaw/openclaw/pull/94784#issuecomment-4767423033

Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: ZengWen-DT <290981215+ZengWen-DT@users.noreply.github.com>
Co-authored-by: Altay <altay@hey.com>
Approved-by: altaywtf
2026-06-22 10:42:21 +00:00
chenyangjun-xy
e6f3912347 fix(agents): count message-tool source reply as user-facing reply for tool error warnings (#94072)
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-06-22 10:39:23 +00:00
Vincent Koc
c6d9977902 test(sdk): resolve npm runner in package e2e 2026-06-22 18:28:27 +08:00
Vincent Koc
c67bb1c5aa fix(vercel-ai-gateway): resolve dynamic model selections (#95710)
Merged via squash.

Prepared head SHA: 0f136acbb3
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-22 18:26:10 +08:00
Vincent Koc
b8b2f5d98f refactor(gateway): share session event field projection 2026-06-22 18:17:55 +08:00
Vincent Koc
6456790287 refactor(infra): share dotenv file parsing 2026-06-22 18:11:30 +08:00
Vincent Koc
f247ef320a fix(ui): bump dompurify to patched release (#95691)
Merged via squash.

Prepared head SHA: 9658e3a802
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-22 18:09:10 +08:00
Marko Milosevic
b08555ef55 fix(gateway): report draining state in readiness (#94915)
Merged via squash.

Prepared head SHA: 0e8d1890c1
Co-authored-by: markoub <2418548+markoub@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-22 18:05:58 +08:00
teamclaw
7fe287b0d3 fix(agent-core): stop loop after aborted tool run (#94412)
Merged via squash.

Prepared head SHA: e11d9718e3
Co-authored-by: szsip239 <88223778+szsip239@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-22 18:04:50 +08:00
Vincent Koc
f77a74dec7 refactor(channels): share plugin config persistence 2026-06-22 18:03:12 +08:00
Sash Zats
0c1f963532 test: save ~79 CI hours/mo in gateway session utils (#95602)
Merged via squash.

Prepared head SHA: 53574bd4d1
Co-authored-by: zats <2688806+zats@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-22 18:03:10 +08:00
Vincent Koc
530658dc29 fix(active-memory): exclude dreaming-narrative session keys from eligibility gate (#95721)
Merged via squash.

Prepared head SHA: fc8717e8f4
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-22 18:01:31 +08:00
Shakker
8cc5b371f1 fix: route config cli env setup 2026-06-22 10:51:51 +01:00
Vincent Koc
afa97a4b10 fix(cli): sync capability inspect metadata flags with registered options (#95719)
Merged via squash.

Prepared head SHA: ef0bf06ee0
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-22 17:50:58 +08:00
Vincent Koc
d9482063a9 refactor(channels): share supplemental context facts type 2026-06-22 17:49:07 +08:00
Vincent Koc
89eb493d1d fix(whatsapp): remove dead watchdog timeout clamp (#95706)
Merged via squash.

Prepared head SHA: 0b17b1f051
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-22 17:47:10 +08:00
Vincent Koc
387b5337ec fix(synology-chat): remove duplicate local deliver timeout (#95707)
Merged via squash.

Prepared head SHA: a9860099c9
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-22 17:45:59 +08:00
Vincent Koc
03a71f3b46 fix(matrix): prevent double bootstrapCrossSigning reset in forced reset (#95720)
Merged via squash.

Prepared head SHA: afa7684e4b
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-22 02:45:56 -07:00
Jason O'Neal
2220f43f69 fix(ci): increase timeouts in flaky process-group signal test (#95466)
Merged via squash.

Prepared head SHA: 5ebe334a96
Co-authored-by: jason-allen-oneal <8335428+jason-allen-oneal@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-22 02:44:49 -07:00
wood fish
9ce4c92736 fix(gateway): honor remote status probe timeout (#89859)
Merged via squash.

Prepared head SHA: 056707dbc7
Co-authored-by: mushuiyu886 <266724580+mushuiyu886@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-22 02:44:16 -07:00
zhang-guiping
8625b8a92b fix(doctor): prevent non-interactive --fix from auto-restarting gateway (#94148)
Merged via squash.

Prepared head SHA: 60d9d1c242
Co-authored-by: zhangguiping-xydt <275915537+zhangguiping-xydt@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-22 02:43:41 -07:00
Vincent Koc
5571c786d3 refactor(gateway): reuse shared session change broadcaster 2026-06-22 17:40:23 +08:00
jianxing zhang
b9d254f2b0 fix(googlechat): support spaceType field for DM vs Space detection (#58993)
Merged via squash.

Prepared head SHA: 467d289c32
Co-authored-by: Starhappysh <221244539+Starhappysh@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-22 17:39:25 +08:00
Alberto Gonzalez Trastoy
9f675920bf fix(codex): stream non-final-answer assistant deltas as partials (#95404)
Merged via squash.

Prepared head SHA: 6ab4d9dcf8
Co-authored-by: agonza1 <16296681+agonza1@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-22 17:38:57 +08:00
Shakker
78b33b86d3 test: route onboard gateway env setup 2026-06-22 10:35:38 +01:00
Vincent Koc
3ca3b97a21 refactor(config): share MCP mutation pipeline 2026-06-22 17:31:38 +08:00
Vincent Koc
ceb69221ec test(scripts): run kitchen sink bash tests on Windows 2026-06-22 17:29:51 +08:00
Shakker
729de383bc fix: track onboard auth state env 2026-06-22 10:28:26 +01:00
Vincent Koc
d1026a3a1a fix(agents): trust load-path harness owners 2026-06-22 17:26:27 +08:00
Vincent Koc
2fbce1c036 fix(agents): canonicalize harness plugin routing 2026-06-22 17:26:27 +08:00
Vincent Koc
741bac9fdf fix(agents): load manifest-owned harnesses 2026-06-22 17:26:27 +08:00
Vincent Koc
8cf0d7dd33 chore(plugin-sdk): refresh API baseline hash 2026-06-22 11:23:02 +02:00
Peter Steinberger
25e2017062 test(config): isolate auto-enable discovery cache case 2026-06-22 05:20:39 -04:00
Vincent Koc
cb301cd16f fix(ci): skip stable closeout without rollback vars 2026-06-22 17:17:36 +08:00
Vincent Koc
d8e6ee04d0 refactor(cli): share root option prefix scanning 2026-06-22 17:17:11 +08:00
Vincent Koc
493e418fa5 test(canvas): isolate A2UI host fixtures 2026-06-22 17:15:40 +08:00
Vincent Koc
4bde68ed38 refactor(transcripts): share rewrite match accounting 2026-06-22 17:09:13 +08:00
Vincent Koc
a289146344 fix(ci): accept matrix node shard timeout 2026-06-22 11:05:34 +02:00
Vincent Koc
95093303c8 chore(deadcode): remove test-only channel helpers 2026-06-22 16:57:03 +08:00
Vincent Koc
607b2e9663 fix(ci): debounce canonical main runner admission (#95681)
Compacts canonical pull request CI to 18 bounded Node jobs, preserves isolated subprocess execution, and delays canonical main runner admission to smooth GitHub runner-registration bursts.

Verification: focused CI planner/workflow tests passed; fresh autoreview clean. Hosted CI had two pre-existing runtime-config failures on the current main baseline; merged with explicit maintainer override.
2026-06-22 16:55:56 +08:00
Vincent Koc
aed6f0a14e test(doctor): mock external channel env vars 2026-06-22 16:50:58 +08:00
Vincent Koc
e20edd753b fix(canvas): guard A2UI asset copy roots 2026-06-22 16:43:08 +08:00
Vincent Koc
a89e65c167 fix(canvas): remove stale A2UI compatibility assets 2026-06-22 16:38:54 +08:00
Vincent Koc
f0afbd7e32 fix(crabbox): preflight macOS Swift toolchain 2026-06-22 16:34:57 +08:00
Vincent Koc
d9a38130b1 chore(deadcode): remove test-only task mutation wrappers 2026-06-22 16:24:57 +08:00
Vincent Koc
f2eca94391 feat(plugins): externalize additional official plugins (#95683) 2026-06-22 16:12:51 +08:00
Vincent Koc
4e9dc6b5d5 fix(skills): harden ClawHub update policy
Pass runtime config into CLI ClawHub skill updates so install policy sees configured safety rules, and update the bundled ClawHub skill docs to prefer openclaw skills for normal skill management. Keeps update-all limited to tracked ClawHub installs and intentionally leaves bundled-skill deprecation, legacy bootstrap, and Sherpa packaging for separate follow-up. Proof: focused ClawHub/CLI tests passed, autoreview clean, remote check:changed passed on Blacksmith Testbox tbx_01kvq0ywztsvw9vdc8zz1xktea; wrapper install/build/check passed, with full local pnpm test failing in unrelated baseline areas already reproduced on latest origin/main.
2026-06-22 16:03:19 +08:00
Vincent Koc
1711d0123c chore(deadcode): remove task registry test-only queries 2026-06-22 15:56:48 +08:00
Vincent Koc
d3f7f7d1fc chore(deadcode): remove unused test-only helpers 2026-06-22 15:48:43 +08:00
Vincent Koc
7cc21ef59d fix(qa): stabilize smoke-ci scenarios 2026-06-22 15:41:53 +08:00
Vincent Koc
f9ebb8d91b chore(deadcode): remove unused plugin version validator 2026-06-22 15:37:50 +08:00
zhang-guiping
783d5c19dd fix #89466: [Bug]: Control UI chat input text not cleared after sending (#95503)
Merged via squash.

Prepared head SHA: 32e5fd9cc3
Co-authored-by: zhangguiping-xydt <275915537+zhangguiping-xydt@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-22 15:34:01 +08:00
Peter Steinberger
15a0609a6b test(plugin-sdk): restore private surface budget 2026-06-22 03:30:14 -04:00
Vincent Koc
44ae2fd936 chore(deadcode): remove obsolete cron execution wrapper 2026-06-22 15:25:37 +08:00
ZOOWH
8e76feb482 fix(cron): use main-session systemEvent for silent quick-create preset (#95459)
Merged via squash.

Prepared head SHA: 1e966d4993
Co-authored-by: ZOOWH <265460861+ZOOWH@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-22 15:23:07 +08:00
Vincent Koc
f1c6057cd7 chore(deadcode): remove unused main-lane queue wrapper 2026-06-22 15:18:05 +08:00
Vincent Koc
c57fee8239 chore(deadcode): remove stale preview helper APIs 2026-06-22 15:07:07 +08:00
Peter Steinberger
23b4f33195 fix(plugin-sdk): keep CLI quoting helper private 2026-06-22 02:59:57 -04:00
Vincent Koc
078044a2cf test(sdk): refresh plugin surface budget 2026-06-22 14:57:53 +08:00
Vincent Koc
8ad231c241 chore(deadcode): remove obsolete session maintenance wrapper 2026-06-22 14:53:17 +08:00
Vincent Koc
04994f1046 test(scripts): normalize RPC RTT path assertions 2026-06-22 14:38:15 +08:00
Vincent Koc
9b4c5822eb test(raft): satisfy gateway lint 2026-06-22 14:36:12 +08:00
Vincent Koc
b39e905b69 fix(raft): support current bridge protocol 2026-06-22 14:36:12 +08:00
Vincent Koc
df6c71736c fix(raft): satisfy channel type contracts 2026-06-22 14:36:12 +08:00
Vincent Koc
2b50bbf152 feat(raft): add wake bridge channel 2026-06-22 14:36:12 +08:00
Wynne668
1bd85e3cc3 fix(openai-completions): seal native reasoning before the answer under /reasoning on (#95283)
* fix(openai-completions): seal native reasoning before the answer

deepseek-style providers stream reasoning via reasoning_content deltas
then switch to the answer via content deltas with no boundary event.
thinking_end was only emitted by the end-of-stream finishBlock loop, so
it landed after the answer's text_delta and channels merged the answer
into the reasoning block.

Seal the open native thinking block when visible text (or a tool call)
begins so thinking_end precedes the answer; tag-based <think> reasoning
is unaffected (closed by the partitioner). finishBlock is now idempotent
so the end-of-stream loop never re-emits thinking_end.

* fix(openai-completions): preserve co-streamed reasoning

* fix(openai-completions): order co-streamed reasoning

* fix(openai-completions): seal co-streamed reasoning

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-06-22 14:33:44 +08:00
Peter Steinberger
984efdb0b6 fix(discord): finish voice log preview rename 2026-06-22 02:30:08 -04:00
Vincent Koc
abdd81db11 test(qa): include long-context soak scenario 2026-06-22 14:28:04 +08:00
NIO
3cc05d590c fix(docs): show inline read_when hints in docs:list (#95243)
* fix(docs): show inline read_when hints in docs:list

* test(scripts): use shared temp directory helper

* test(scripts): route docs list helper test

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-06-22 14:14:20 +08:00
Vincent Koc
8246e8dace refactor(plugins): share channel command normalization 2026-06-22 14:10:40 +08:00
Vincent Koc
5df513c895 refactor(channels): share account enabled predicate 2026-06-22 14:08:46 +08:00
Vincent Koc
1529958067 refactor(models): reuse normalized provider lookup 2026-06-22 14:06:50 +08:00
Vincent Koc
7d3bc4d944 fix(ci): bundle test shards and right-size runners
Bundles only isolated low-risk Node shards, keeps stateful suites isolated, and right-sizes Blacksmith runners to reduce runner-registration bursts. Exact-head CI run 27932220655 passed the changed CI planning lanes; its two remaining agent test reds match latest main baseline run 27929063460 and are unrelated to this CI-only diff.
2026-06-22 14:05:40 +08:00
Vincent Koc
5fbb5f75ed refactor(discord): share voice log preview formatting 2026-06-22 14:04:38 +08:00
Vincent Koc
4506d8bad6 fix(qa): surface tool-search bridge results 2026-06-22 08:01:44 +02:00
Vincent Koc
7668ef2d35 refactor(memory): share workspace key normalization 2026-06-22 14:01:23 +08:00
Bryan Tegomoh, MD, MPH
8e4213b1c4 fix(gateway): accept port for health and probe (#94687)
* fix(gateway): accept port for health and probe

* fix(gateway): repair health port override

* fix(gateway): repair health port override

* fix(gateway): accept port for health and probe

---------

Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
2026-06-22 14:00:57 +08:00
Vincent Koc
a0ab5c00a8 test(scripts): harden kitchen sink timeout tests 2026-06-22 13:57:15 +08:00
Ayaan Zaidi
dc09324ec2 fix(telegram): narrow live claim recovery 2026-06-22 11:25:58 +05:30
mikasa0818
3be0fe722a fix(telegram): recover stuck live-owned spool claims
Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-22 11:25:58 +05:30
Vincent Koc
6be98022da fix(qa-matrix): surface gateway stderr in failures 2026-06-22 13:55:49 +08:00
Vincent Koc
cc89e155c3 refactor(imessage): share direct chat normalization 2026-06-22 13:55:36 +08:00
Vincent Koc
0111afe9e2 refactor(cli): share shell argument quoting 2026-06-22 13:54:17 +08:00
Vincent Koc
674b4f3372 refactor(plugins): move owner skills into plugins
Move Canvas, Discord, Slack, voice-call, and WhatsApp skill docs from the root bundled skill tree into their owning plugin packages, with manifest skills declarations and docs/test path updates.

Validation:
- node --import tsx scripts/sync-plugin-versions.ts --check
- node scripts/run-vitest.mjs src/skills/loading/env-path-guidance.test.ts
- autoreview clean
- Crabbox Azure pnpm check:changed run_59ba76511d57 / lease cbx_cbc6750dad72
- wrapper prepare passed pnpm install --frozen-lockfile, pnpm build, and pnpm check before the oversized full local pnpm test was stopped

Follow-up: #95132 remains as the stacked PR for the rest of the starter-skill/Sherpa/ClawHub work.
2026-06-22 13:54:08 +08:00
wangmiao0668000666
4db829646a fix(sdk): type-narrow manifest.files in pack staging root helper (#95465) 2026-06-22 13:52:51 +08:00
Vincent Koc
e046dbb52d test(agents): repair fallback runtime CI coverage 2026-06-22 13:49:43 +08:00
lizeyu-xydt
a964132d80 fix(config): add stdio to McpServerSchema transport union (#95102)
* fix(config): add "stdio" to McpServerSchema transport union

Add z.literal("stdio") to the transport union so that
config validate accepts transport: "stdio" on command-bearing
MCP servers, matching the runtime behavior.

Fixes #95082

* fix(config): align McpServerConfig transport type and add schema coverage

- Widened McpServerConfig.transport to include "stdio"
- Added schema test for command-bearing server with explicit stdio transport
- Added schema test rejecting unsupported transport values

* fix(config): constrain transport stdio to command-bearing MCP servers

* fix(config): align stdio refine with runtime trimmed command check
2026-06-22 13:48:23 +08:00
MaHaoHao-ch
0374892fd8 docs(plugins): document subagent_ended hook fields (#95191)
The Subagents section of docs/plugins/hooks.md listed subagent_ended
but did not describe its payload. Plugin authors reaching for
agentId (as on subagent_spawned) silently got undefined because
PluginHookSubagentEndedEvent uses targetSessionKey as its identity
field and has no agentId/childSessionKey.

Document all fields from PluginHookSubagentEndedEvent and explicitly
note the missing identity fields so handlers can correlate events
correctly.

Closes #95186

Co-authored-by: MaHaoHao-ch <MaHaoHao-ch@users.noreply.github.com>
2026-06-22 13:48:17 +08:00
miorbnli
a2b3aab7b0 fix(ssh): reject hostnames with stray leading or trailing colons in parseSshTarget (#93887)
* fix(ssh): reject hostnames with stray leading or trailing colons in parseSshTarget

parseSshTarget would previously return host values like "host:" or ":22"
when the input had a trailing colon with no port, or a leading colon with
no host. These values flow directly into SSH config HostName fields and the
ssh CLI argv, which causes connections to fail.

Add a guard that rejects host parts starting or ending with ":" before
the existing "-" prefix check. Existing valid inputs ("host", "host:22",
"user@host:22") are unaffected.

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

* fix(ssh): validate stray-colon host in explicit-port parse branch

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-22 13:48:10 +08:00
Yuval Dinodia
2ece2945ae fix(compaction): count user-message image blocks in cut-point estimator (#95128)
estimateTokens charged 4800 chars per image in the toolResult branch but
counted only text in the user branch, so image blocks in recent user turns
scored zero. findCutPoint never reached keepRecentTokens and left the cut at
the earliest point, so image-heavy sessions compacted to a no-op and looped on
context overflow. Fold the per-image accounting into one shared helper used by
both branches.
2026-06-22 13:48:02 +08:00
Vincent Koc
f05fd56d66 fix(googlechat): remove useless test string concatenation 2026-06-22 13:42:34 +08:00
Ayaan Zaidi
a9a75b2b77 test(telegram): tighten spooled drain race proof 2026-06-22 11:08:03 +05:30
weiqinl
e29381a172 fix #86957: drain worker-spooled Telegram updates immediately
Wake the isolated polling drain immediately after a worker-spooled
update is written to channel_ingress_events, instead of waiting for
the next drain interval.

- Add requestImmediateDrain() calls after worker write and spooled message
- Track pending drain requests while drain is active (fix race condition)
- Add regression test for updates arriving during active drain

Fixes #86957.
2026-06-22 11:08:03 +05:30
clawsweeper[bot]
62456d65eb Avoid copying process.env in ingress queue state DB opens (#95278)
* Avoid copying process.env in ingress queue state DB opens

Co-authored-by: kaka-srp <178872848+kaka-srp@users.noreply.github.com>

* Repair #95278: avoid copying process.env in ingress queue DB opens

---------

Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: kaka-srp <178872848+kaka-srp@users.noreply.github.com>
Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
2026-06-22 13:37:51 +08:00
Javier Ailbirt
696c624008 fix(googlechat): sanitize internal tool-trace lines from outbound text (#90684) (#95084)
* fix(googlechat): sanitize internal tool-trace lines from outbound text (#90684)

Google Chat ran only sanitizeForPlainText() on outbound text, so internal
assistant trace lines leaked to users — most visibly the
`⚠️ 🛠️ <command> (agent) failed` banner emitted for benign non-zero shell
exits (e.g. a grep with no match, exit 1). The turn succeeds but the user
gets an alarming "failed" message.

Apply sanitizeAssistantVisibleText() before sanitizeForPlainText() in the
googlechat outbound sanitizeText hook, mirroring Discord and WhatsApp. The
hook runs pre-chunking in the shared deliver path, covering all text sends.

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

* fix(googlechat): sanitize internal tool-trace lines from outbound text (#90684)

---------

Co-authored-by: Claude Code <claude@anthropic.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
2026-06-22 13:37:06 +08:00
Ayaan Zaidi
b3b8b289dd test(telegram): keep live polling leases protected (#93378) (thanks @mmyzwl) 2026-06-22 11:05:06 +05:30
Vincent Koc
692c5e34f0 refactor(agents): share thinking block predicate 2026-06-22 13:30:22 +08:00
Vincent Koc
536c8a840b refactor(llm): share cache retention resolution 2026-06-22 13:29:23 +08:00
Wynne668
99551c499b fix(skills): point gog brew install at homebrew-core gogcli (#95017) (#95019)
Merged via squash.

Prepared head SHA: a4f489c616
Co-authored-by: ZengWen-DT <290981215+ZengWen-DT@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-22 13:02:26 +08:00
Vincent Koc
2c3519c1d7 test(agents): cover fallback probe attempt position 2026-06-22 12:58:10 +08:00
Vincent Koc
e114001cca fix(channels): preserve keyed progress state updates 2026-06-22 12:51:33 +08:00
Vincent Koc
3ff59df960 test(agents): cover fallback attempt position 2026-06-22 12:33:38 +08:00
Vincent Koc
a594d2ce73 fix(channels): remove unusable post-final progress override 2026-06-22 12:06:42 +08:00
Vincent Koc
c92f366c14 fix(ci): repair main validation gates 2026-06-22 11:58:27 +08:00
Alex Knight
2b92706dcf feat(mattermost): register /oc_queue as a native slash command
Add oc_queue to DEFAULT_COMMAND_SPECS so the native slash-command registrar exposes /oc_queue, mapped (originalName: queue) to the core /queue directive via the existing trigger-map path. Additive; rides the same registration/callback machinery as the other oc_* commands.
2026-06-22 13:56:40 +10:00
Vincent Koc
a9be81d510 fix(ci): repair baseline lint and test gates 2026-06-22 11:44:21 +08:00
joshavant
97a015bace fix(ios): drop unused bootstrap test hook 2026-06-21 23:40:56 -04:00
joshavant
93c7ec645a fix(ios): remove stale notification authorization seam 2026-06-21 23:40:56 -04:00
joshavant
920bd04e19 fix(ios): make notification guidance suppression a button 2026-06-21 23:40:56 -04:00
joshavant
5ae53cf9fb fix(ios): refine notification permission copy 2026-06-21 23:40:56 -04:00
joshavant
1168ac2fcd fix(ios): update notification permission copy 2026-06-21 23:40:56 -04:00
joshavant
112a0ddaf8 fix(ios): neutralize exec approval prompt copy 2026-06-21 23:40:56 -04:00
joshavant
c3ab1feb61 test(ios): cover notification permission UX 2026-06-21 23:40:56 -04:00
joshavant
0bd2aa8ee0 feat(ios): consolidate notification permission UX 2026-06-21 23:40:56 -04:00
Vincent Koc
7d4d8a7f3d test(agents): add large prompt cache coverage (#95653)
Adds large OpenAI and Anthropic prompt-cache live coverage plus a QA Lab long-context tool-result scenario.

Co-authored-by: vincentkoc <vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-22 11:39:37 +08:00
Vincent Koc
e9be15ff19 test(scripts): use true package timer overflow inputs 2026-06-22 05:31:53 +02:00
Vincent Koc
05580342f7 fix(agents): remove unused context guard import 2026-06-22 11:18:52 +08:00
Vincent Koc
17dc9902f2 test(scripts): use true overflow timer inputs 2026-06-22 05:11:25 +02:00
ly-wang19
540ec53f99 fix(agents): reject bind specs with extra colon segments (#95572)
Summary:
- The PR changes `parseBindingSpecs` to reject `<channel>:<account>:...` bind specs and adds Matrix parser regression coverage for the malformed and valid forms.
- PR surface: Source +8, Tests +26. Total +34 across 2 files.
- Reproducibility: yes. Source inspection of current main shows `parseBindingSpecs` truncating with `split(":", 2)`, and the PR body provides before/after terminal output from a harness importing the real parser.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(agents): reject bind specs with extra colon segments

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

Prepared head SHA: 3e1af31fc4
Review: https://github.com/openclaw/openclaw/pull/95572#issuecomment-4762320150

Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
2026-06-22 03:07:50 +00:00
Vincent Koc
a182811070 fix(ci): smooth PR runner-registration bursts (#95625)
Merged via squash.

Prepared head SHA: 72b2b00bf8
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-22 10:53:33 +08:00
Vincent Koc
4e2f0157c7 test(scripts): harden kitchen sink timer proof 2026-06-22 04:42:34 +02:00
Vincent Koc
0f64e3c052 fix(scripts): clamp secret proof timers 2026-06-22 04:38:45 +02:00
Vincent Koc
65388233e2 chore(deadcode): remove duplicate session transcript reader 2026-06-22 10:37:42 +08:00
Vincent Koc
044df2516e fix(test): keep Gemini CLI docker lane advisory 2026-06-22 04:32:34 +02:00
Vincent Koc
190ca52882 chore(deadcode): remove stale reply tag helper 2026-06-22 10:31:18 +08:00
Vincent Koc
ad304e790d chore(deadcode): remove unused speech contract registry 2026-06-22 10:19:01 +08:00
Vincent Koc
e913e0739d fix(scripts): clamp openwebui probe timers 2026-06-22 04:11:45 +02:00
Vincent Koc
8e24695a8d chore(deadcode): remove redundant observation aliases 2026-06-22 10:09:48 +08:00
Vincent Koc
b0ecf6e1e7 chore(deadcode): trim private attempt facade 2026-06-22 10:07:58 +08:00
Vincent Koc
dc5c2f6360 fix(core): restore typecheck helpers 2026-06-22 04:06:11 +02:00
Vincent Koc
2405d029d4 test(gateway): classify live provider drift 2026-06-22 04:06:11 +02:00
Vincent Koc
4e96ca0d12 chore(deadcode): prune unused attempt reexports 2026-06-22 10:05:03 +08:00
Vincent Koc
84ccba6b32 chore(deadcode): remove stale embedded-runner reexports 2026-06-22 10:03:04 +08:00
Vincent Koc
d17bb9c3e9 chore(deadcode): collapse session helper barrels 2026-06-22 09:59:31 +08:00
Vincent Koc
2f9107f672 chore(deadcode): remove stale internal reexport shims 2026-06-22 09:57:04 +08:00
Vincent Koc
6328c8637b chore(deadcode): remove stale agent helper reexports 2026-06-22 09:52:40 +08:00
Vincent Koc
3053cbc8a5 Merge branch 'main' of https://github.com/openclaw/openclaw into fix/stable-tool-result-prompt-cache
* 'main' of https://github.com/openclaw/openclaw: (39 commits)
  chore(deadcode): remove stale agent helper reexports
  chore(ui): refresh fa control ui locale
  chore(ui): refresh nl control ui locale
  chore(ui): refresh th control ui locale
  chore(ui): refresh vi control ui locale
  chore(ui): refresh id control ui locale
  chore(ui): refresh pl control ui locale
  chore(ui): refresh uk control ui locale
  chore(ui): refresh tr control ui locale
  chore(ui): refresh it control ui locale
  chore(ui): refresh ar control ui locale
  chore(ui): refresh fr control ui locale
  chore(ui): refresh ko control ui locale
  chore(ui): refresh ja-JP control ui locale
  chore(ui): refresh es control ui locale
  chore(ui): refresh zh-TW control ui locale
  chore(ui): refresh zh-CN control ui locale
  chore(ui): refresh pt-BR control ui locale
  chore(ui): refresh de control ui locale
  fix: preserve fast auto delivery semantics
  ...
2026-06-22 09:48:14 +08:00
Vincent Koc
eb9318e953 chore(deadcode): remove stale agent helper reexports 2026-06-22 09:47:02 +08:00
github-actions[bot]
f80f4a8b95 chore(ui): refresh fa control ui locale 2026-06-22 01:42:45 +00:00
github-actions[bot]
034629404d chore(ui): refresh nl control ui locale 2026-06-22 01:42:28 +00:00
github-actions[bot]
ef67ffd697 chore(ui): refresh th control ui locale 2026-06-22 01:42:04 +00:00
github-actions[bot]
fe524d2a46 chore(ui): refresh vi control ui locale 2026-06-22 01:41:57 +00:00
github-actions[bot]
6c8dcc9d35 chore(ui): refresh id control ui locale 2026-06-22 01:41:53 +00:00
github-actions[bot]
34ab295734 chore(ui): refresh pl control ui locale 2026-06-22 01:41:51 +00:00
github-actions[bot]
a5139a8c5c chore(ui): refresh uk control ui locale 2026-06-22 01:41:20 +00:00
github-actions[bot]
7f99824164 chore(ui): refresh tr control ui locale 2026-06-22 01:41:17 +00:00
github-actions[bot]
cbaeaa8856 chore(ui): refresh it control ui locale 2026-06-22 01:41:15 +00:00
github-actions[bot]
a6cac347b6 chore(ui): refresh ar control ui locale 2026-06-22 01:41:03 +00:00
github-actions[bot]
4d0aec8095 chore(ui): refresh fr control ui locale 2026-06-22 01:40:44 +00:00
github-actions[bot]
fa51a624c0 chore(ui): refresh ko control ui locale 2026-06-22 01:40:40 +00:00
github-actions[bot]
075e328c62 chore(ui): refresh ja-JP control ui locale 2026-06-22 01:40:34 +00:00
github-actions[bot]
0d21d489ab chore(ui): refresh es control ui locale 2026-06-22 01:40:18 +00:00
github-actions[bot]
d8f1000600 chore(ui): refresh zh-TW control ui locale 2026-06-22 01:40:05 +00:00
github-actions[bot]
227b4c81ed chore(ui): refresh zh-CN control ui locale 2026-06-22 01:39:58 +00:00
github-actions[bot]
d28d6c2399 chore(ui): refresh pt-BR control ui locale 2026-06-22 01:39:53 +00:00
github-actions[bot]
d09f728208 chore(ui): refresh de control ui locale 2026-06-22 01:39:40 +00:00
Vincent Koc
9e8ab083dd fix: preserve fast auto delivery semantics 2026-06-22 09:37:11 +08:00
Vincent Koc
6eb72a830e fix: gate fallback fast reset notices 2026-06-22 09:37:11 +08:00
Vincent Koc
6b1eef9959 fix: type fallback state for CLI runs 2026-06-22 09:37:11 +08:00
Vincent Koc
14e448e0e1 fix: carry fast mode fallback state through runners 2026-06-22 09:37:11 +08:00
Vincent Koc
aa3797c8d0 fix: complete fast mode fallback and status wiring 2026-06-22 09:37:10 +08:00
Vincent Koc
93ad397725 fix: preserve normalization and ACP fast mode contracts 2026-06-22 09:37:10 +08:00
Vincent Koc
cf1b6fef44 fix: mark embedded auto fast mode explicitly 2026-06-22 09:37:10 +08:00
Vincent Koc
d990115d19 fix: preserve fast mode across retries 2026-06-22 09:37:10 +08:00
Vincent Koc
8a75c4dd5f fix: preserve repeated channel progress events 2026-06-22 09:37:10 +08:00
Vincent Koc
efd3172662 fix: resolve fallback fast cutoff per model 2026-06-22 09:37:10 +08:00
Vincent Koc
8afc1f770b fix(codex): normalize cleared service tier in bindings 2026-06-22 09:37:10 +08:00
Vincent Koc
77012f9807 fix: clear Codex tier and render auto fast status 2026-06-22 09:37:10 +08:00
Vincent Koc
2732f58215 test: align fast auto forward-port with current fixtures 2026-06-22 09:37:10 +08:00
Vincent Koc
3eeccbe782 fix: remove stale fast auto event import 2026-06-22 09:37:09 +08:00
Vincent Koc
2b75806197 feat: forward-port fast talks auto mode (#85104) 2026-06-22 09:37:09 +08:00
Vincent Koc
adb9abe721 chore(deadcode): collapse stale provider registry views 2026-06-22 09:31:39 +08:00
Vincent Koc
2a6554ac12 fix(scripts): clamp audit request timer 2026-06-22 03:30:19 +02:00
Vincent Koc
4113982fa8 fix(scripts): clamp memory fd repro timers 2026-06-22 03:25:02 +02:00
Vincent Koc
2800ce4e28 chore(deadcode): remove unused plugin contract registry paths 2026-06-22 09:21:24 +08:00
Vincent Koc
45a93b8450 fix(scripts): clamp openwebui probe timer 2026-06-22 03:18:24 +02:00
Vincent Koc
708c1b31e0 fix(agents): preserve transformed tool results 2026-06-22 09:17:50 +08:00
Vincent Koc
3fc1284fe6 test(agents): type complete projection fixtures 2026-06-22 09:17:50 +08:00
Vincent Koc
1a075c375c fix(agents): avoid ambiguous prompt projection reuse 2026-06-22 09:17:50 +08:00
Vincent Koc
95f314e822 fix(agents): stabilize repeated tool result identities 2026-06-22 09:17:50 +08:00
Vincent Koc
efe3cbd695 fix(agents): finish aggregate prompt reduction 2026-06-22 09:17:50 +08:00
Vincent Koc
79fac9fda9 fix(agents): cap new prompt projection output 2026-06-22 09:17:50 +08:00
Vincent Koc
ef6dc8f7e5 fix(agents): count frozen prompt bytes 2026-06-22 09:17:50 +08:00
Vincent Koc
f31306eb4e fix(agents): reproject converted tool results safely 2026-06-22 09:17:50 +08:00
Vincent Koc
db24112617 fix(agents): keep converted tool-result blocks intact 2026-06-22 09:17:50 +08:00
Vincent Koc
4fd19adf25 fix(agents): preserve filtered tool-result blocks 2026-06-22 09:17:50 +08:00
Vincent Koc
62dcc9bc3b fix(agents): freeze prompt projection candidates 2026-06-22 09:17:50 +08:00
Vincent Koc
bda4404f69 fix(agents): reuse memoized prompt projections 2026-06-22 09:17:50 +08:00
Vincent Koc
30925601ae fix(agents): memoize prompt truncation projections 2026-06-22 09:17:49 +08:00
Vincent Koc
a2675756b8 test(agents): stabilize Claude live spawn coverage 2026-06-22 09:16:31 +08:00
Vincent Koc
095a44c8de fix(scripts): clamp run-with-env kill timer 2026-06-22 03:15:26 +02:00
Vincent Koc
29eba5aaef fix(test): clamp qa otel child timer 2026-06-22 03:12:25 +02:00
Vincent Koc
b99812b3b1 fix(scripts): clamp additional boundary timers 2026-06-22 03:08:19 +02:00
Vincent Koc
4f7d1f4977 fix(scripts): clamp boundary artifact timers 2026-06-22 03:05:11 +02:00
Vincent Koc
c310f8cfa4 fix(scripts): clamp package download timers 2026-06-22 03:01:52 +02:00
Vincent Koc
08442c4b38 fix(scripts): clamp boundary check timers 2026-06-22 02:54:30 +02:00
Vincent Koc
fe7b78b05f chore(deadcode): prune stale test hooks 2026-06-22 08:51:49 +08:00
Vincent Koc
dd89898133 fix(scripts): clamp package candidate timers 2026-06-22 02:51:05 +02:00
Vincent Koc
851b65c060 fix(scripts): clamp package docker timers 2026-06-22 02:47:41 +02:00
Vincent Koc
75c6a8fff5 fix(scripts): clamp docker all timers 2026-06-22 02:44:00 +02:00
Vincent Koc
f9fc380e90 fix(scripts): clamp gauntlet command timers 2026-06-22 02:38:04 +02:00
Vincent Koc
8ef73be8e8 fix(scripts): clamp plugin lifecycle timers 2026-06-22 02:33:55 +02:00
Vincent Koc
afadf1f7da chore(deadcode): prune stale codeql paths 2026-06-22 08:33:19 +08:00
Vincent Koc
29185aed68 test(e2e): refresh docker setup fixtures 2026-06-22 02:32:05 +02:00
Vincent Koc
3e5ca880bf fix(scripts): clamp parallels update timeouts 2026-06-22 02:29:26 +02:00
Vincent Koc
a09e1b9aa0 chore(deadcode): prune stale routing config 2026-06-22 08:28:19 +08:00
Vincent Koc
11959ad100 fix(scripts): clamp parallels phase timeouts 2026-06-22 02:24:52 +02:00
snowzlmbot
e37b0f8cd3 fix(telegram): render progress drafts with clean HTML transport
Render Telegram progress draft rows with clean plain previews while preserving Telegram HTML formatting through parse_mode.

The progress HTML path now stays transport-owned, including richMessages=false progress messages, while debug/plain/sanitized text remains readable without raw markup.

Thanks @snowzlmbot!
2026-06-22 05:54:44 +05:30
Vincent Koc
dbb58341b5 chore(deadcode): remove stale vitest coverage excludes 2026-06-22 08:20:03 +08:00
Vincent Koc
790dfb66a8 fix(scripts): clamp parallels host timeouts 2026-06-22 02:18:53 +02:00
Jesse Merhi
4cb94cc2cf fix: keep trusted policies with hook registry (#94545)
* fix: keep trusted policies with hook registry

* fix: compose trusted policies with live hooks

* fix: preserve trusted policy order

* test: update hook runner mock

* fix: keep hook policy registry internal
2026-06-22 10:15:24 +10:00
anagnorisis2peripeteia
298a0cd55f fix(claude-cli): disable unsupported background tools (#95008) (thanks @anagnorisis2peripeteia)
Disable Claude CLI native background Bash and Monitor in OpenClaw-managed print runs, matching the existing fail-closed scheduler-tool behavior. This keeps deferred work on OpenClaw-owned wake paths instead of native Claude callbacks OpenClaw cannot deliver.

Thanks @anagnorisis2peripeteia!
2026-06-22 05:44:48 +05:30
Vincent Koc
c578608b78 fix(scripts): clamp telegram proof timeouts 2026-06-22 02:13:45 +02:00
Vincent Koc
8c0767ffa4 chore(deadcode): remove unused tooling helpers 2026-06-22 08:13:03 +08:00
Omar Shahine
a0714a3d68 fix(imessage): keep split-send coalescing opt-in (#93143)
Merged via squash.

Prepared head SHA: 7fc3eca084
Co-authored-by: omarshahine <10343873+omarshahine@users.noreply.github.com>
Co-authored-by: omarshahine <10343873+omarshahine@users.noreply.github.com>
Reviewed-by: @omarshahine
2026-06-21 17:12:21 -07:00
Vincent Koc
f719813a7e fix(scripts): clamp kitchen sink timeouts 2026-06-22 02:08:47 +02:00
Vincent Koc
11a3903ede chore(deadcode): remove stale changed-test routes 2026-06-22 08:05:30 +08:00
Vincent Koc
1f6ae32cab fix(process): clamp queue task timeouts 2026-06-22 02:01:39 +02:00
Vincent Koc
880425b03c chore(deadcode): trim unused discord vitest runtime shims 2026-06-22 07:59:00 +08:00
Vincent Koc
4c736df975 fix(agents): clamp session owner wait timeouts 2026-06-22 01:56:37 +02:00
Vincent Koc
6c85b90469 fix(replies): clamp block delivery timeouts 2026-06-22 01:49:55 +02:00
Vincent Koc
ef41560059 chore(deadcode): remove unused vitest helper config 2026-06-22 07:48:28 +08:00
Vincent Koc
88b21fc30b fix(media): clamp image description timeouts 2026-06-22 01:40:44 +02:00
Vincent Koc
4b6182ee2a fix(minimax): clamp vlm request timeouts 2026-06-22 01:31:13 +02:00
Vincent Koc
d43bc3760e chore(deadcode): remove unused test bindings 2026-06-22 07:31:03 +08:00
Vincent Koc
8cd0c11227 fix(clawhub): clamp request timeouts 2026-06-22 01:22:15 +02:00
Vincent Koc
66b94ba577 fix(process): clamp execfile timeouts 2026-06-22 01:10:52 +02:00
Vincent Koc
77b6ca9a9b fix(sdk): tighten surface report budgets 2026-06-22 01:04:53 +02:00
Vincent Koc
1425bb3a03 fix(process): clamp command timeouts 2026-06-22 01:00:41 +02:00
Peter Steinberger
11484f8a14 fix(ollama): support GLM-5.2 cloud discovery 2026-06-21 19:00:15 -04:00
mikasa
ec7a548062 fix #95378: https://github.com/openclaw/openclaw/issues/95378 (#95390)
* fix(telegram): use session transcript for direct context

* fix(telegram): account for proof and SDK checks

* fix(telegram): address review findings

* fix(telegram): tighten session transcript context

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-06-22 04:29:44 +05:30
Vincent Koc
8ecdb97b63 fix(channels): bound capabilities probes 2026-06-22 00:53:26 +02:00
Vincent Koc
a39e548ede fix(mcp): cap channel bridge request limits 2026-06-22 00:32:32 +02:00
Vincent Koc
328a44695f chore(deadcode): remove unused agent-core prompt formatter 2026-06-22 06:23:07 +08:00
Vincent Koc
c21dcfc7c2 fix(tui): clamp local shutdown grace timeout 2026-06-22 00:22:08 +02:00
Vincent Koc
2bdcc8314d fix(sdk): preserve zero run timeout watchdog 2026-06-22 00:13:51 +02:00
Vincent Koc
464adfe5e5 chore(deadcode): remove unused agent-core harness APIs 2026-06-22 06:08:32 +08:00
Vincent Koc
b66b4504f8 fix(gateway): cap plugin session message reads 2026-06-22 00:06:04 +02:00
Vincent Koc
f6d432e545 fix(gateway): expire default node pending work 2026-06-21 23:52:44 +02:00
Vincent Koc
fd13192adc fix(qa-lab): version UI assets from repo root 2026-06-21 23:43:48 +02:00
Vincent Koc
1c63da09d8 fix(qa-lab): cap agent wait timeout payloads 2026-06-21 23:34:38 +02:00
Vincent Koc
735505442c chore(deadcode): dedupe live assistant text extraction 2026-06-22 05:32:29 +08:00
Vincent Koc
108d6d7eca fix(gateway): reject malformed restart request params 2026-06-21 23:28:53 +02:00
Vincent Koc
984c8f6ea0 chore(deadcode): remove stale channel presence helper 2026-06-22 05:21:39 +08:00
Shakker
cd9060e06a test: restore host cleanup state env 2026-06-21 22:20:58 +01:00
Vincent Koc
5b96eb0172 fix(e2e): reject flag Parallels smoke values 2026-06-21 23:19:32 +02:00
Vincent Koc
b95b725c83 fix(e2e): reject help flag Crabbox proof values 2026-06-21 23:13:12 +02:00
Shakker
4b881509eb fix: restore media cache state env 2026-06-21 22:12:46 +01:00
Vincent Koc
a03032a272 chore(plugin-sdk): refresh API baseline hash 2026-06-21 23:09:27 +02:00
Shakker
c94ebdbebd test: restore heartbeat state env 2026-06-21 22:07:04 +01:00
Alex Knight
7b46167607 fix(channels): preserve migrated account policies 2026-06-22 07:05:45 +10:00
Vincent Koc
0e53358945 fix(e2e): reject flag Telegram credential values 2026-06-21 23:04:51 +02:00
Vincent Koc
6b45e9af7a fix(scripts): validate iOS node CLI values before help 2026-06-21 23:00:30 +02:00
Vincent Koc
2ef61eb782 chore(deadcode): drop unused exact session lookup 2026-06-22 04:58:21 +08:00
Vincent Koc
6823f56d8e fix(scripts): reject flag device-pair Telegram values 2026-06-21 22:56:54 +02:00
Shakker
3e1d3c5feb fix: isolate provider runtime env cache 2026-06-21 21:54:05 +01:00
Vincent Koc
dfbc9ab246 fix(scripts): reject short flag TUI PTY values 2026-06-21 22:52:21 +02:00
Vincent Koc
179eb15554 chore(deadcode): remove unused state path readers 2026-06-22 04:50:54 +08:00
Shakker
c3b1e926e8 test: route gateway call env resets 2026-06-21 21:46:55 +01:00
Vincent Koc
89768d456b fix(scripts): reject short flag Discord smoke values 2026-06-21 22:45:03 +02:00
Vincent Koc
609d7a14b1 chore(deadcode): remove test-only helper APIs 2026-06-22 04:41:04 +08:00
Vincent Koc
3bae0d6b82 fix(qa): reject short flag gateway smoke values 2026-06-21 22:40:40 +02:00
Vincent Koc
75a997dd7c fix(scripts): reject short flag shrinkwrap refs 2026-06-21 22:40:07 +02:00
Shakker
ab8dc3af52 fix: reuse doctor env snapshot helper 2026-06-21 21:37:56 +01:00
Vincent Koc
bebc5d847d fix(scripts): reject short flag CI timing limits 2026-06-21 22:34:29 +02:00
Vincent Koc
a0f28bd3f5 fix(scripts): reject short flag version values 2026-06-21 22:33:59 +02:00
Shakker
c638617897 test: share agent state-dir env guard 2026-06-21 21:31:10 +01:00
Vincent Koc
b77d6149e1 fix(scripts): reject short flag extension memory values 2026-06-21 22:28:14 +02:00
Vincent Koc
36db108fc1 fix(scripts): reject short flag boundary values 2026-06-21 22:26:44 +02:00
heichaowo
e00c1eebc4 fix(telegram): skip duplicate mirror replies (#95069)
Fix Telegram session transcript duplicate mirror writes after the primary assistant reply already exists.

Thanks @heichaowo!
2026-06-22 01:55:08 +05:30
NIO
32d22d04cc fix(telegram): honor outbound reaction directives (#94977) 2026-06-22 01:54:55 +05:30
Vincent Koc
648ef73bde fix(qa): reject short flag UX evidence paths 2026-06-21 22:20:56 +02:00
Vincent Koc
beebb35de4 fix(scripts): reject short flag values in helper CLIs 2026-06-21 22:19:54 +02:00
Vincent Koc
55959148ca chore(deadcode): remove unused infra wrappers 2026-06-22 04:18:02 +08:00
Vincent Koc
4684bbba97 fix(scripts): reject short flag values in benchmark CLIs 2026-06-21 22:11:19 +02:00
Vincent Koc
409adfbe10 chore(deadcode): drop stale helper APIs 2026-06-22 04:10:02 +08:00
Vincent Koc
2609b97222 fix(scripts): reject short flag values in bench parsers 2026-06-21 22:06:30 +02:00
Vincent Koc
03bc600e67 fix(scripts): reject short flag startup memory paths 2026-06-21 22:06:04 +02:00
Vincent Koc
8102d5ebc3 fix(scripts): reject short flag performance summary paths 2026-06-21 22:02:05 +02:00
Vincent Koc
6399eb8191 fix(scripts): reject short flag vitest profile dirs 2026-06-21 21:53:58 +02:00
Vincent Koc
1a5839fbd8 fix(scripts): reject short flag audit severity values 2026-06-21 21:51:02 +02:00
Vincent Koc
d17045db6f chore(deadcode): drop unused helper exports 2026-06-22 03:49:17 +08:00
Vincent Koc
2a8db1fc23 fix(scripts): reject short flag ownership report values 2026-06-21 21:45:36 +02:00
Vincent Koc
82f43f0a62 fix(scripts): reject short flag values in release docs parsers 2026-06-21 21:39:41 +02:00
Vincent Koc
9adf3d92bd chore(deadcode): remove unused helper paths 2026-06-22 03:39:19 +08:00
Vincent Koc
e21164933a fix(scripts): reject short flag report values 2026-06-21 21:36:26 +02:00
Vincent Koc
1b17517969 fix(scripts): reject short flag closeout values 2026-06-21 21:32:17 +02:00
Vincent Koc
86fea26797 fix(plugin-sdk): stabilize surface report after builds 2026-06-21 21:28:47 +02:00
Vincent Koc
2e7c3ace9c chore(plugin-sdk): refresh API baseline hashes 2026-06-21 21:28:47 +02:00
Vincent Koc
e34204a1e0 fix(scripts): reject short flag package roots 2026-06-21 21:27:57 +02:00
Vincent Koc
6daf9307e0 fix(scripts): reject short flag docker package values 2026-06-21 21:24:30 +02:00
Vincent Koc
ebb670b208 fix(scripts): reject short flag gauntlet values 2026-06-21 21:18:48 +02:00
Vincent Koc
52672c7af1 fix(scripts): reject short flag gateway cpu values 2026-06-21 21:15:21 +02:00
Vincent Koc
690efd2a16 chore(deadcode): inline constant helper stubs 2026-06-22 03:14:04 +08:00
Vincent Koc
102ab759e7 fix(scripts): reject option-shaped package candidate values 2026-06-21 21:11:25 +02:00
Vincent Koc
7da955fae4 fix(config): type slack secret refs 2026-06-21 20:58:33 +02:00
Vincent Koc
06a0148072 chore(deadcode): remove inert browser relay hook 2026-06-22 02:50:55 +08:00
Vincent Koc
edd1d3319c chore(deadcode): dedupe repeated literal lists 2026-06-22 02:35:03 +08:00
Vincent Koc
0ea39a2276 chore(deadcode): remove inert memory provider bootstrap 2026-06-22 02:30:56 +08:00
Sarah Fortune
6fa944e80f [codex] Add Slack relay mode for incoming messages (#94707) 2026-06-21 11:28:33 -07:00
Vincent Koc
4c453c931f fix(test): reject dependency report flag values 2026-06-21 20:23:23 +02:00
Vincent Koc
13b0976c70 fix(release): reject dependency evidence flag values 2026-06-21 20:19:49 +02:00
Vincent Koc
43e00c06c3 fix(docs): reject sync publish flag values 2026-06-21 20:13:50 +02:00
Vincent Koc
03ce3d41b1 fix(ci): reject hosted gate flag values 2026-06-21 20:09:53 +02:00
Vincent Koc
bda05dbc2f fix(release): reject validation flag values 2026-06-21 20:05:22 +02:00
Vincent Koc
7069d95720 fix(test): reject diff ref flag values 2026-06-21 19:59:55 +02:00
Vincent Koc
8086cffd17 fix(test): reject group report flag values 2026-06-21 19:56:25 +02:00
Vincent Koc
d91aee7220 fix(ci): reject flag refs in changed scope 2026-06-21 19:54:57 +02:00
Vincent Koc
c8ab37f6fe chore(deadcode): drop inert legacy workspace doctor check 2026-06-22 01:47:27 +08:00
Vincent Koc
a823cb2b30 fix(test): bound rpc process tree sampling 2026-06-21 19:41:51 +02:00
Vincent Koc
5230ec66ae chore(plugin-sdk): refresh API surface baselines 2026-06-21 19:32:49 +02:00
Vincent Koc
eea777c9fc chore(deadcode): trim stale facade re-exports 2026-06-22 01:16:08 +08:00
Vincent Koc
0b28a72be1 fix(test): reject kova help value bypasses 2026-06-21 19:07:02 +02:00
Vincent Koc
adcba85264 fix(test): reject cpuprofile limit help tokens 2026-06-21 18:56:26 +02:00
Vincent Koc
5b79fa13e2 chore(deadcode): trim doctor alias wrappers 2026-06-22 00:54:26 +08:00
Vincent Koc
124ea48549 fix(test): reject docker timing flag limits 2026-06-21 18:49:24 +02:00
Vincent Koc
12756fc4c8 fix(test): reject env report flag paths 2026-06-21 18:46:05 +02:00
Vincent Koc
5bf459e23b fix(test): reject attestation platform flags 2026-06-21 18:43:05 +02:00
Vincent Koc
d64a27feeb chore(deadcode): drop node daemon runtime alias 2026-06-22 00:41:56 +08:00
Vincent Koc
6c42f73619 fix(test): reject i18n report flag values 2026-06-21 18:37:35 +02:00
Vincent Koc
d460f00eb9 fix(test): reject metadata ref flags 2026-06-21 18:33:10 +02:00
Vincent Koc
b83dce7b33 fix(test): reject rpc rtt flag values 2026-06-21 18:28:28 +02:00
Vincent Koc
d3c907193f fix(test): route qa otel smoke parser 2026-06-21 18:26:26 +02:00
Vincent Koc
b47c930e7e chore(deadcode): trim runtime plugin selection wrappers 2026-06-22 00:24:26 +08:00
Vincent Koc
0befd3c8f2 fix(test): route android release wrappers 2026-06-21 18:19:07 +02:00
Vincent Koc
c2ee9b0be8 fix(gateway): preserve owner MCP tools for agent RPC 2026-06-21 18:14:38 +02:00
Vincent Koc
9d27583190 fix(test): route release signing args 2026-06-21 18:10:35 +02:00
Vincent Koc
04c8c50cc4 fix(test): route testbox env hydration 2026-06-21 18:04:47 +02:00
Vincent Koc
5abf4ce2e2 chore(deadcode): trim reply runtime dead helpers 2026-06-22 00:03:31 +08:00
Vincent Koc
07d5cdec99 fix(test): route ios release wrappers 2026-06-21 17:59:21 +02:00
Vincent Koc
f5f23e739e fix(test): route proxy CA installer 2026-06-21 17:51:44 +02:00
Vincent Koc
0c183283e5 fix(test): route release preflight script 2026-06-21 17:46:59 +02:00
Vincent Koc
514b3365b5 fix(deadcode): move restart sentinels to sqlite 2026-06-21 23:39:38 +08:00
Vincent Koc
2804c24dc6 fix(test): route plugin dependency helpers 2026-06-21 17:32:43 +02:00
Vincent Koc
94c7b5a874 fix(test): route release ref resolver 2026-06-21 17:27:29 +02:00
Vincent Koc
93ec8b8c5c fix(test): route install helper scripts 2026-06-21 17:14:12 +02:00
Vincent Koc
9d83eeaccf fix(test): route release wrapper scripts 2026-06-21 17:08:40 +02:00
Vincent Koc
33eb6ab9de fix(test): route release approval script 2026-06-21 16:59:51 +02:00
Vincent Koc
757ab933f4 fix(test): route release script owners 2026-06-21 16:53:49 +02:00
Vincent Koc
63fdc57b3a fix(test): route mac helper script owners 2026-06-21 16:39:41 +02:00
Vincent Koc
a39a3b74de fix(deadcode): move restart handoffs to sqlite 2026-06-21 22:36:42 +08:00
Vincent Koc
77a859f4ae fix(ci): route mac packaging scripts to macos checks 2026-06-21 16:32:57 +02:00
Peter Lee
84cf64770f fix(whatsapp): wire missing Baileys retry/cache hooks for group message reliability (#94338)
Merged via squash.

Prepared head SHA: ee6de071f7
Co-authored-by: xialonglee <22994703+xialonglee@users.noreply.github.com>
Co-authored-by: mcaxtr <7562095+mcaxtr@users.noreply.github.com>
Reviewed-by: @mcaxtr
2026-06-21 11:32:49 -03:00
Vincent Koc
0ad48dad2c fix(deadcode): move restart intents to sqlite 2026-06-21 22:29:05 +08:00
Marcus Castro
b50a5aebba fix(whatsapp): preserve native quote replies (#95483)
* fix(whatsapp): preserve native quote replies

* feat(plugin-sdk): add quote-reply live transport standard

* test(qa-lab): add WhatsApp quote-reply live scenarios
2026-06-21 11:26:58 -03:00
Vincent Koc
b84665222c fix(ci): regenerate Swift protocol model 2026-06-21 22:19:18 +08:00
snowzlmbot
6441e56465 fix(telegram): materialize streaming progress placeholders (#95183)
* fix(telegram): materialize streaming progress placeholders

* fix(telegram): cancel delayed progress drafts before final

* fix(telegram): satisfy progress placeholder lint

* fix(telegram): cancel delayed draft preview on clear

* refactor(telegram): simplify delayed preview flush

---------

Co-authored-by: snowzlmbot <snowzlmbot@users.noreply.github.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-06-21 19:48:41 +05:30
Vincent Koc
6daabd23f8 fix(deadcode): move config health state to sqlite 2026-06-21 22:07:43 +08:00
Vincent Koc
2f33999898 fix(agents): retain bounded preflight history 2026-06-21 21:58:57 +08:00
Vincent Koc
a60947fb3e fix(agents): keep live tool-result prompts cache-stable 2026-06-21 21:54:35 +08:00
Peter Lee
ac5d219be3 fix(telegram): stop clearing registered webhook on channel restart (#94506)
Prepared head SHA: f721e14a92

Co-authored-by: xialonglee <li.xialong@xydigit.com>
2026-06-21 19:22:49 +05:30
Vincent Koc
ae41b00922 fix(deadcode): move plugin binding approvals to sqlite 2026-06-21 21:52:20 +08:00
Vincent Koc
b28e68e0ce fix(macos): validate DMG layout values 2026-06-21 15:48:52 +02:00
Josh Avant
5d1e649aea fix: route mobile exec approvals to reviewer device (#95175)
* fix: route mobile exec approvals to reviewer device

* fix: surface iOS approval events in foreground

* fix: forward codex approval reviewer device

* test: harden approval reviewer device contract

* test: cover reviewer approval fallback resolvers
2026-06-21 08:47:52 -05:00
Ayaan Zaidi
d6d17709e8 fix(telegram): clear progress draft before tool output (#93002) (thanks @zhangguiping-xydt) 2026-06-21 19:14:57 +05:30
Ayaan Zaidi
6fd6bddb92 style(telegram): trim progress draft dispatch comment 2026-06-21 19:14:57 +05:30
张贵萍0668001030
f4dee99574 fix(telegram): clear progress draft before tool artifacts 2026-06-21 19:14:57 +05:30
张贵萍0668001030
db33402af0 fix(telegram): clear progress draft before verbose tool output 2026-06-21 19:14:57 +05:30
Vincent Koc
088cab5ee4 fix(macos): prefer repo pnpm for packaging 2026-06-21 15:39:17 +02:00
Vincent Koc
bd74a62118 fix(install): use repo pnpm for git installs 2026-06-21 15:34:56 +02:00
Vincent Koc
9f888d95e0 fix(deadcode): move current conversation bindings to sqlite 2026-06-21 21:30:06 +08:00
Vincent Koc
11a2e03bd4 fix(install): detect package manager launcher names 2026-06-21 15:18:57 +02:00
Vincent Koc
5693fcda78 fix(ci): validate artifact package source sha 2026-06-21 15:08:18 +02:00
Vincent Koc
eb00d499d1 fix(deadcode): move update check state to sqlite 2026-06-21 20:57:41 +08:00
Vincent Koc
8a7906c716 fix(qa): reject fractional live token usage 2026-06-21 14:49:29 +02:00
Vincent Koc
c85113e30e fix(qa): escape tool coverage markdown cells 2026-06-21 14:46:38 +02:00
Vincent Koc
bdf81a825f fix(deadcode): move voicewake settings to sqlite 2026-06-21 20:45:17 +08:00
Vincent Koc
d9dfcd6c8a fix(qa): reject impossible confidence counts 2026-06-21 14:38:42 +02:00
Alex Knight
2cafbd0774 fix(plugins): reconcile managed npm root overrides with managed peer pins 2026-06-21 22:33:18 +10:00
Vincent Koc
54b2836eab test(scripts): stabilize gauntlet termination timing 2026-06-21 14:27:10 +02:00
Vincent Koc
5b6f4b2919 fix(test): reject gauntlet flag values 2026-06-21 14:21:16 +02:00
Vincent Koc
c037a34ba7 fix(security): ignore Docker rerun artifact commands 2026-06-21 14:14:57 +02:00
Vincent Koc
12c34fc3a9 fix(security): bound trusted package URL prefixes 2026-06-21 13:51:24 +02:00
Vincent Koc
e366349730 test(deadcode): reuse gateway restart intent writer 2026-06-21 19:49:47 +08:00
Vincent Koc
89a73d08c8 test(deadcode): dedupe wizard prompter helpers 2026-06-21 19:49:46 +08:00
Vincent Koc
e6f41a4df0 fix(test): reject loose env report limits 2026-06-21 13:37:55 +02:00
Vincent Koc
b7fef7fca6 fix(mac): clean failed dSYM merges 2026-06-21 13:32:47 +02:00
Vincent Koc
d1cbe29f3d fix(qa): require sampled Kova metric counts 2026-06-21 13:24:30 +02:00
Alex Knight
9dbc21d283 fix(telegram): materialize rich message line breaks as <br>
Bot API 10.1 rich messages parse structured HTML, so bare newlines
collapse as insignificant whitespace and flatten multi-paragraph replies
and bullet runs into a single line. Materialize logical line breaks as
<br> in prepareTelegramRichHtml -- the shared rich send/edit/draft
chokepoint covering both the Markdown and explicit-HTML text modes --
while keeping newlines literal inside code/pre/math and where they only
separate structural tags (block elements plus table/figure/details
container children, so pretty-printed rich HTML keeps valid markup).

Refs #95409
2026-06-21 21:22:33 +10:00
Vincent Koc
5e86c7eef4 fix(qa): reject non-object mock Anthropic JSON 2026-06-21 13:13:37 +02:00
Vincent Koc
6f3af56952 fix(qa): reject non-object mock OpenAI JSON 2026-06-21 13:10:50 +02:00
Vincent Koc
03ee22666b chore(deadcode): drop retired memory wiki vault metadata 2026-06-21 19:08:39 +08:00
Vincent Koc
0d351b9875 fix(ci): filter ClawSweeper comment dispatches before token minting (#95308)
Merged via squash.

Prepared head SHA: b5389b59e4
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-21 19:05:08 +08:00
Vincent Koc
f69ba12a37 fix(qa): reject malformed mock OpenAI JSON 2026-06-21 12:58:20 +02:00
Vincent Koc
b796890b97 test(sdk): resolve Windows package taskkill path 2026-06-21 12:52:41 +02:00
Vincent Koc
b5fc9514c0 fix(qa): reject coerced evidence artifact indexes 2026-06-21 12:50:37 +02:00
Vincent Koc
2a140e6e6a fix(daemon): resolve Windows scheduled-task tools 2026-06-21 12:45:49 +02:00
Vincent Koc
6e5f4d685e chore(deadcode): remove stale readability compare script 2026-06-21 18:43:56 +08:00
Vincent Koc
600bace853 fix(qa): validate QA bus poll numbers 2026-06-21 12:41:22 +02:00
Vincent Koc
b8a5dac1a2 fix(qa-lab): resolve Windows PowerShell path 2026-06-21 12:40:57 +02:00
Vincent Koc
15c880aeff fix(infra): share trusted Windows process argv lookup 2026-06-21 12:31:27 +02:00
Vincent Koc
3a53eb5d77 test(deadcode): remove stale heartbeat transcript prune test 2026-06-21 18:25:28 +08:00
Vincent Koc
66e5cfdd86 fix(qa): reject QA Lab host port collisions 2026-06-21 12:23:52 +02:00
Vincent Koc
c4facb2bb3 fix(infra): resolve Windows port inspection tools 2026-06-21 12:22:18 +02:00
Vincent Koc
a70b34a3cb fix(qa-lab): resolve Windows node lookup tool 2026-06-21 12:14:39 +02:00
Vincent Koc
59bf85c586 chore(deadcode): remove stale cron usage helper 2026-06-21 18:12:36 +08:00
Vincent Koc
e486a1d1cf test(scripts): route Codex install assertions 2026-06-21 12:11:01 +02:00
Vincent Koc
72b9bc7303 fix(core): resolve Windows PATH locator tools 2026-06-21 12:09:33 +02:00
Vincent Koc
d3b44442f6 fix(infra): resolve Windows binary lookup tool 2026-06-21 12:02:02 +02:00
Vincent Koc
6ddbcbd460 chore(deadcode): remove stale proof scripts 2026-06-21 18:00:25 +08:00
Vincent Koc
7bd4aab21f test(scripts): route fixture common helper 2026-06-21 11:55:25 +02:00
Vincent Koc
a4c8b17b9e test(scripts): route Parallels lib helpers 2026-06-21 11:52:46 +02:00
Vincent Koc
d87f8325d0 test(agents): fix tools-manager mock typing 2026-06-21 11:49:00 +02:00
Vincent Koc
a5fde9119c fix(agents): resolve Windows extraction tools 2026-06-21 11:49:00 +02:00
Vincent Koc
e6c899dfa5 chore(deadcode): dedupe internal event formatting 2026-06-21 17:47:29 +08:00
Vincent Koc
425f512897 test(scripts): route ClawHub fixture server 2026-06-21 11:42:29 +02:00
Vincent Koc
735d70d9db test(extensions): pin Windows taskkill test roots 2026-06-21 11:37:59 +02:00
Vincent Koc
15300291ed fix(qa-matrix): resolve Windows taskkill path 2026-06-21 11:37:59 +02:00
Vincent Koc
eb7789c8cb test(scripts): route auth profile store assertions 2026-06-21 11:32:59 +02:00
Vincent Koc
f19052b3f3 test(scripts): route doctor install wrapper helper 2026-06-21 11:30:45 +02:00
Vincent Koc
61d1fd1f72 fix(qa-lab): resolve Windows taskkill path 2026-06-21 11:26:30 +02:00
Vincent Koc
1435fc123f test(scripts): route onboard config helpers 2026-06-21 11:23:08 +02:00
Vincent Koc
6c4028e073 fix(scripts): resolve Windows cmd shim launcher 2026-06-21 11:15:14 +02:00
Vincent Koc
9242137ca7 test(scripts): route config reload metadata helper 2026-06-21 11:13:13 +02:00
Vincent Koc
35d7cb0bff chore(deadcode): remove stale qa-matrix wrappers 2026-06-21 17:10:35 +08:00
Vincent Koc
c000e4811d fix(scripts): resolve Windows tools in kitchen sink walk 2026-06-21 11:06:56 +02:00
Vincent Koc
d25549f142 test(scripts): route plugin fixture commands 2026-06-21 11:04:38 +02:00
Vincent Koc
a192b2ea52 fix(windows): resolve taskkill in core spawns 2026-06-21 10:57:41 +02:00
Vincent Koc
7975ec0b11 test(scripts): route incremental line reader 2026-06-21 10:54:09 +02:00
Vincent Koc
e9b694ef9c fix(windows): resolve process inspection tools 2026-06-21 10:47:42 +02:00
Vincent Koc
3b332fd0a4 chore(deadcode): remove stale copilot doctor probes 2026-06-21 16:42:27 +08:00
Vincent Koc
7dd01d15c5 fix(windows): resolve cmd handoff path 2026-06-21 10:41:25 +02:00
Vincent Koc
675c56692a test(scripts): route mock OpenAI fixture helper 2026-06-21 10:31:00 +02:00
Vincent Koc
3f597619c8 test(scripts): route fixture config helper 2026-06-21 10:27:08 +02:00
Vincent Koc
91531ba35c fix(release): resolve taskkill in cross-os checks 2026-06-21 10:24:19 +02:00
Vincent Koc
206bbb01b0 fix(docs): resolve taskkill in i18n codex helper 2026-06-21 10:19:05 +02:00
Vincent Koc
c9758bf2a0 test(scripts): cover e2e text file utilities 2026-06-21 10:16:09 +02:00
Vincent Koc
282eb74128 fix(i18n): resolve taskkill in control ui runner 2026-06-21 10:14:00 +02:00
Vincent Koc
9940110b88 fix(telegram): resolve taskkill in crabbox proof 2026-06-21 10:10:27 +02:00
Vincent Koc
73b35cc3ca fix(bench): resolve taskkill in gateway child cleanup 2026-06-21 10:06:48 +02:00
Vincent Koc
ac0537e363 fix(dev): resolve taskkill in tui pty watcher 2026-06-21 10:02:31 +02:00
Vincent Koc
0321c04663 chore(deadcode): remove stale web artifact helpers 2026-06-21 16:00:38 +08:00
Vincent Koc
78b717a54c fix(plugins): resolve taskkill in bundled runtime smoke 2026-06-21 09:56:30 +02:00
Vincent Koc
7b00fd6c45 test(scripts): route plugin update registry helper 2026-06-21 09:54:59 +02:00
Vincent Koc
1f1155597b fix(telegram): resolve taskkill in credential helper 2026-06-21 09:52:23 +02:00
Vincent Koc
e9e42d5db4 fix(secret-provider): resolve taskkill in e2e cleanup 2026-06-21 09:49:07 +02:00
Vincent Koc
bc2f4ce923 test(scripts): route npm telegram runner 2026-06-21 09:47:52 +02:00
Vincent Koc
7dbae1b2cd fix(scripts): resolve taskkill in boundary artifacts 2026-06-21 09:44:42 +02:00
Vincent Koc
63f2c56222 test(scripts): route e2e helper owners 2026-06-21 09:43:00 +02:00
Vincent Koc
675cae58d7 fix(build): resolve taskkill in tsdown wrapper 2026-06-21 09:40:53 +02:00
Vincent Koc
c372f6ef0b fix(plugins): keep extension tests on public boundaries 2026-06-21 15:39:51 +08:00
Vincent Koc
1d4b712f9a fix(scripts): resolve taskkill in startup metadata 2026-06-21 09:37:35 +02:00
Vincent Koc
e016f0b496 fix(scripts): resolve taskkill in test group report 2026-06-21 09:34:05 +02:00
Vincent Koc
f8d2c4b25a chore(deadcode): remove stale migrate argv wrapper 2026-06-21 15:32:56 +08:00
Vincent Koc
b15f745a60 fix(package): resolve taskkill from system32 in candidate runner 2026-06-21 09:28:31 +02:00
Vincent Koc
8c9c8aad2e fix(qalab): resolve taskkill from system32 in cleanup probes 2026-06-21 09:24:16 +02:00
Vincent Koc
0a7b009647 fix(rpc): resolve taskkill from system32 in script probes 2026-06-21 09:18:06 +02:00
Vincent Koc
fc8542b377 chore(deadcode): remove stale internal exports 2026-06-21 15:14:36 +08:00
Vincent Koc
37a4b565ea test(scripts): mirror declaration route owners 2026-06-21 09:07:10 +02:00
Vincent Koc
6b0210a5fd fix(scripts): resolve taskkill from system32 2026-06-21 09:03:40 +02:00
Vincent Koc
c22e300084 fix(scripts): taskkill managed children via system32 on windows 2026-06-21 08:57:25 +02:00
Vincent Koc
5134dd0c54 test(scripts): route package runner declarations 2026-06-21 08:55:18 +02:00
Vincent Koc
830691b201 fix(memory-host-sdk): taskkill qmd process trees on windows 2026-06-21 08:51:36 +02:00
Vincent Koc
ab39bab52a chore(deadcode): dedupe session lineage patching 2026-06-21 14:48:50 +08:00
Vincent Koc
3f4d1cfcce test(scripts): run macOS app tests for app changes 2026-06-21 08:43:59 +02:00
Vincent Koc
06574920dd fix(sdk): taskkill package e2e trees on windows 2026-06-21 08:33:36 +02:00
Vincent Koc
d46b64df66 test(scripts): gate runtime sidecar baseline changes 2026-06-21 08:28:55 +02:00
Vincent Koc
b06e2f9149 fix(qa-matrix): taskkill scenario cli trees on windows 2026-06-21 08:25:25 +02:00
Vincent Koc
b574da57cf chore(deadcode): share levenshtein distance helper 2026-06-21 14:23:43 +08:00
Dallin Romney
bfe0caefd1 test: route broad flow tests out of unit-fast (#95499) 2026-06-20 23:22:39 -07:00
Vincent Koc
0004cfd59e test(scripts): route prompt snapshot helper changes 2026-06-21 08:16:47 +02:00
Vincent Koc
604aa30189 fix(qa): taskkill lifecycle probe trees on windows 2026-06-21 08:12:29 +02:00
Dallin Romney
5dd30c3995 test: fold HTTP API script proof into QA Lab (#94700)
* test: fold HTTP API script proof into QA Lab

* test: remove folded HTTP API script tests

* test: relax QA native scenario catalog inventory

* test: trim folded QA Lab script cruft

* test: align folded QA coverage ids

* test: keep native QA evidence out of parity tiers

* test: update mirrored QA routing expectation

* test: preserve chat tools profile build guard

* test: avoid overclaiming gateway tool API coverage

* test: pin folded QA coverage ids
2026-06-20 23:10:35 -07:00
Vincent Koc
5b22409389 fix(qa-lab): taskkill gateway children on windows 2026-06-21 08:07:00 +02:00
Vincent Koc
b970d57175 fix(qa-lab): taskkill timed-out cli trees on windows 2026-06-21 08:01:55 +02:00
Dallin Romney
9ab8e466d2 test(qa): make release scorecard categories explicit (#95406) 2026-06-20 23:01:23 -07:00
Vincent Koc
c43822077a fix(qa-lab): force taskkill scenario trees on windows 2026-06-21 07:56:03 +02:00
Vincent Koc
8797564254 chore(deadcode): share deferred test helper 2026-06-21 13:53:24 +08:00
Vincent Koc
273eb88874 fix(runtime-smoke): force taskkill bundled trees on windows 2026-06-21 07:50:42 +02:00
Vincent Koc
140a2fa520 fix(crabbox): force taskkill telegram proof trees on windows 2026-06-21 07:46:57 +02:00
Vincent Koc
c8b48c78d0 fix(qa): force taskkill telegram credential trees on windows 2026-06-21 07:44:10 +02:00
Vincent Koc
29033e67af fix(bench): force taskkill gateway bench trees on windows 2026-06-21 07:41:36 +02:00
Vincent Koc
e9a47fe554 test(scripts): route setup pnpm action helper 2026-06-21 07:40:06 +02:00
Vincent Koc
2f213a1606 test(scripts): route github yaml pinning guards 2026-06-21 07:40:06 +02:00
Vincent Koc
992ddf6310 test(scripts): cover workflow docker routes 2026-06-21 07:40:06 +02:00
Vincent Koc
7b259bd2a4 test(scripts): route github action metadata 2026-06-21 07:40:06 +02:00
Vincent Koc
e91ca8df86 test(scripts): route docs spellcheck config 2026-06-21 07:40:06 +02:00
Vincent Koc
af3e509ab8 fix(scripts): remove private auth monitor defaults 2026-06-21 07:40:06 +02:00
Vincent Koc
dec76bb5eb test(scripts): route pr wrapper scripts 2026-06-21 07:40:05 +02:00
Vincent Koc
862ef1cec1 test(scripts): route doctor switch shims 2026-06-21 07:40:05 +02:00
Vincent Koc
486c9e6ba3 test(scripts): route podman template metadata 2026-06-21 07:40:05 +02:00
Vincent Koc
b2d78abe94 test(scripts): route extensionless helper scripts 2026-06-21 07:40:05 +02:00
Vincent Koc
2f38b5aa2e test(scripts): route dockerfile metadata changes 2026-06-21 07:40:05 +02:00
Vincent Koc
4d17a52924 fix(scripts): force taskkill boundary node steps on windows 2026-06-21 07:36:02 +02:00
Vincent Koc
4b2298e8cb fix(package): force taskkill candidate runner trees on windows 2026-06-21 07:33:04 +02:00
Vincent Koc
f640ca11f9 fix(scripts): force taskkill run-with-env trees on windows 2026-06-21 07:30:11 +02:00
Vincent Koc
2029f87f29 fix(scripts): force taskkill managed trees on windows 2026-06-21 07:26:46 +02:00
Vincent Koc
ca1aa33eba fix(rpc): force taskkill rtt gateway trees on windows 2026-06-21 07:23:54 +02:00
Vincent Koc
5b212162d3 fix(rpc): force taskkill kitchen sink trees on windows 2026-06-21 07:21:04 +02:00
Vincent Koc
3df5207389 fix(testing): force taskkill group report trees on windows 2026-06-21 07:16:31 +02:00
Vincent Koc
78f30a010c fix(build): kill startup metadata trees on windows 2026-06-21 07:13:29 +02:00
Vincent Koc
83785a6e79 fix(build): kill tsdown trees on windows 2026-06-21 07:09:21 +02:00
Vincent Koc
195890f815 fix(dev): kill tui pty watch trees on windows 2026-06-21 07:06:48 +02:00
Vincent Koc
0f8df48a91 fix(release): forward-port 2026.6.9 closeout fixes 2026-06-21 13:05:11 +08:00
Vincent Koc
7b28b73e78 fix(e2e): preserve secret proof windows tree cleanup 2026-06-21 06:59:18 +02:00
Vincent Koc
3650766f26 fix(rpc): kill kitchen sink trees on windows 2026-06-21 06:53:14 +02:00
Vincent Koc
eb03d0ee2b chore(deadcode): share session cost totals 2026-06-21 12:52:23 +08:00
Vincent Koc
38c8b0c196 fix(crabbox): kill telegram proof trees on windows 2026-06-21 06:47:25 +02:00
Vincent Koc
0030a192c8 fix(qa): kill telegram credential trees on windows 2026-06-21 06:42:35 +02:00
Vincent Koc
34806b39cd fix(package): kill candidate resolver trees on windows 2026-06-21 06:37:51 +02:00
Vincent Koc
b0f21f8af7 fix(scripts): kill boundary prep trees on windows 2026-06-21 06:34:10 +02:00
Vincent Koc
5af318b95d fix(testing): kill group report trees on windows 2026-06-21 06:29:14 +02:00
Vincent Koc
8d2e6d7686 fix(scripts): kill run-with-env trees on windows 2026-06-21 06:24:47 +02:00
Vincent Koc
b039e949b6 chore(release): close out 2026.6.9 2026-06-21 12:24:15 +08:00
Vincent Koc
2ca5b7c93e fix(mac): retry DMG detach 2026-06-21 12:24:15 +08:00
Vincent Koc
51d1789cea fix(release): terminate command descendants on signal 2026-06-21 12:24:15 +08:00
Vincent Koc
89e73240a1 test(release): align full validation workflow contract 2026-06-21 12:24:15 +08:00
Vincent Koc
f3ee317f71 fix(ci): deduplicate release Telegram validation 2026-06-21 12:24:15 +08:00
Vincent Koc
ecb82f1be9 fix(plugins): restore StepFun ClawHub release 2026-06-21 12:24:14 +08:00
Vincent Koc
f1a48dac18 test(release): stabilize validation contracts 2026-06-21 12:24:14 +08:00
Vincent Koc
cba9c02095 chore(deadcode): share plugin snapshot fingerprint 2026-06-21 12:23:31 +08:00
Peter Steinberger
715dc718fc fix(opencode-go): align Kimi input with runtime 2026-06-21 00:22:52 -04:00
Vincent Koc
85f71f4c8f fix(scripts): kill managed child trees on windows 2026-06-21 06:19:23 +02:00
Peter Steinberger
66f84a9bf1 fix(opencode-go): add current Go models
Co-authored-by: samson1357924 <samson1357924@gmail.com>
2026-06-21 00:06:55 -04:00
Zak
50c2cc6a45 fix(zai): expose GLM-5.2 reasoning levels [AI-assisted] (#94136)
Merged via squash.

Prepared head SHA: 432214158a
Co-authored-by: BorClaw <268442329+BorClaw@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-20 23:57:49 -04:00
Chunyue Wang
e3ccf8743f fix(channels): resolve native /think menu levels via runtime catalog for live-discovered models (#94067)
Merged via squash.

Prepared head SHA: 079347b8b8
Co-authored-by: openperf <80630709+openperf@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-20 23:56:53 -04:00
Vincent Koc
d4c2fa7aed chore(deadcode): drop unused sdk specifier helper 2026-06-21 11:50:51 +08:00
Nik
6c1041339d fix(agents): classify Zhipu GLM overload as overloaded for failover (#93241)
Merged via squash.

Prepared head SHA: db79e94c89
Co-authored-by: 0xghost42 <151941421+0xghost42@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-20 23:49:39 -04:00
huangjianxiong
db54a3268b fix(zai): fall back to manifest baseUrl for synthesized GLM-5 models (#94461)
Merged via squash.

Prepared head SHA: 445a418187
Co-authored-by: Pandah97 <80405497+Pandah97@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-20 23:49:08 -04:00
Vincent Koc
9750d887f5 chore(deadcode): dedupe plugin lookup table types 2026-06-21 11:08:34 +08:00
Vincent Koc
fce586538a chore(deadcode): drop unused i18n config type 2026-06-21 10:45:56 +08:00
Vincent Koc
97b97a9999 chore(deadcode): drop unused private exports 2026-06-21 10:21:58 +08:00
Vincent Koc
cbbb466852 test(scripts): route docs i18n module changes 2026-06-21 04:08:30 +02:00
Vincent Koc
c2de9d0822 test(scripts): route docs i18n and k8s metadata 2026-06-21 04:02:14 +02:00
Vincent Koc
e46aaead2c chore(deadcode): drop duplicate script declarations 2026-06-21 09:57:10 +08:00
Vincent Koc
c191d7978b test(scripts): route script fixture metadata 2026-06-21 03:48:20 +02:00
Vincent Koc
1b5e1e2d53 test(scripts): route scripts lib metadata changes 2026-06-21 03:41:44 +02:00
Vincent Koc
ab41a311cf chore(deadcode): drop duplicate pnpm runner declaration 2026-06-21 09:40:02 +08:00
Vincent Koc
9e70d251b0 test(scripts): focus plugin sdk entry metadata routing 2026-06-21 03:32:25 +02:00
Vincent Koc
025f8fb087 test(scripts): focus plugin sdk metadata routing 2026-06-21 03:23:54 +02:00
Vincent Koc
e012f2cd3c chore(deadcode): share telegram send runtime loader 2026-06-21 09:12:41 +08:00
ZengWen-DT
73c988a9c8 fix(sessions): reset stale per-channel origin fields on channel switch (#95328)
Merged via squash.

Prepared head SHA: 3a946cb078
Co-authored-by: ZengWen-DT <290981215+ZengWen-DT@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
2026-06-20 18:09:37 -07:00
Vincent Koc
8b4eedf1bc test(scripts): focus extension package routing 2026-06-21 02:54:40 +02:00
Vincent Koc
b43eedbb18 chore(deadcode): drop duplicate provider default refs 2026-06-21 08:49:41 +08:00
Vincent Koc
ebbf506a77 test(scripts): focus deprecated config routing 2026-06-21 02:47:58 +02:00
Vincent Koc
e0c1add79a test(scripts): focus config guard routing 2026-06-21 02:39:11 +02:00
Vincent Koc
eae1e8f3f8 test(scripts): focus guard inventory routing 2026-06-21 02:26:34 +02:00
Vincent Koc
ba34052a0e chore(deadcode): share memory vector blob encoding 2026-06-21 08:23:16 +08:00
Vincent Koc
ce78ac3efb test(scripts): focus extension classifier routing 2026-06-21 02:10:59 +02:00
Vincent Koc
6c2c43d63f chore(deadcode): reuse gateway json responder 2026-06-21 08:06:38 +08:00
Vincent Koc
0168ff0c2e test(scripts): focus ts topology routing 2026-06-21 01:57:14 +02:00
Vincent Koc
22bdda2555 chore(deadcode): share nostr state account ids 2026-06-21 07:50:19 +08:00
Vincent Koc
2755112353 test(scripts): focus docker plan routing 2026-06-21 01:48:16 +02:00
Vincent Koc
57c7fa22bb test(scripts): focus workspace bootstrap routing 2026-06-21 01:36:00 +02:00
Vincent Koc
269e44e164 test(scripts): focus extension vitest routing 2026-06-21 01:28:05 +02:00
Vincent Koc
2e75d925ad chore(deadcode): share telegram state account ids 2026-06-21 07:22:52 +08:00
Vincent Koc
0e763c1499 test(scripts): focus changed extension routing 2026-06-21 01:21:24 +02:00
Vincent Koc
6ed9fb8ec2 test(scripts): focus direct run routing 2026-06-21 01:15:59 +02:00
Vincent Koc
a826d6a4a4 test(scripts): focus bundled build entry routing 2026-06-21 01:06:10 +02:00
Vincent Koc
9d519c1481 test(scripts): focus mobile version routing 2026-06-21 01:06:10 +02:00
Vincent Koc
b09b35c13c chore(deadcode): share ui helper predicates 2026-06-21 07:03:53 +08:00
Vincent Koc
15f2a56590 fix(harness): preserve empty prompt ranges 2026-06-21 07:00:11 +08:00
Vincent Koc
e66c36df37 test(copilot): fix harness test typings 2026-06-21 07:00:11 +08:00
Vincent Koc
42c504b8b1 fix(plugin-sdk): lazy-load harness agent-end effects 2026-06-21 07:00:11 +08:00
Vincent Koc
9ac3759ffc chore(plugin-sdk): refresh harness API budgets 2026-06-21 07:00:11 +08:00
Vincent Koc
d7f747af3b fix(harness): satisfy lifecycle lint gates 2026-06-21 07:00:11 +08:00
Vincent Koc
9cb3b4ea2b test(codex): prove bounded hook suffix preservation 2026-06-21 07:00:11 +08:00
Vincent Koc
f257116c92 test(codex): cover projected hook suffix bounds 2026-06-21 07:00:11 +08:00
Vincent Koc
a88ce96ee1 fix(copilot): ignore subagent idle during cleanup 2026-06-21 07:00:11 +08:00
Vincent Koc
448b3fa0be fix(copilot): retain timed-out sessions until idle 2026-06-21 07:00:11 +08:00
Vincent Koc
88ad407be2 fix(codex): retain bounded hook prompt context 2026-06-21 07:00:11 +08:00
Vincent Koc
13e77cc055 fix(copilot): normalize terminal prompt errors 2026-06-21 07:00:11 +08:00
Vincent Koc
b78718f42a fix(codex): bound delivery hint prompts 2026-06-21 07:00:11 +08:00
Vincent Koc
54dddda68d fix(copilot): preserve replacement session reuse 2026-06-21 07:00:11 +08:00
Vincent Koc
6084442ab6 fix(harness): retain empty prompt ranges 2026-06-21 07:00:11 +08:00
Vincent Koc
692c7e78f4 fix(harness): bound empty hook prompts 2026-06-21 07:00:11 +08:00
Vincent Koc
3f166b1f64 fix(codex): bound hook-expanded prompts 2026-06-21 07:00:11 +08:00
Vincent Koc
8e8905560b fix(codex): align protected prompt ranges 2026-06-21 07:00:11 +08:00
Vincent Koc
3968fea383 fix(harness): protect reset and prompt bounds 2026-06-21 07:00:11 +08:00
Vincent Koc
43e8c29fbf fix(codex): bound inbound projected context 2026-06-21 07:00:11 +08:00
Vincent Koc
7e80bb8abf fix(codex): retain projected context after hook expansion 2026-06-21 07:00:11 +08:00
Vincent Koc
9826619e22 fix(harness): pass config to agent-end side effects 2026-06-21 07:00:11 +08:00
Vincent Koc
8f3672beaa fix(copilot): settle timed-out compaction waits 2026-06-21 07:00:11 +08:00
Vincent Koc
0d25928fa4 fix(copilot): scope pending cleanup to session owner 2026-06-21 07:00:11 +08:00
Vincent Koc
807641548b fix(copilot): retain replacement session during reset 2026-06-21 07:00:11 +08:00
Vincent Koc
f87f30d429 fix(harness): preserve prompt input range 2026-06-21 07:00:11 +08:00
Vincent Koc
22c5ced69f fix(codex): preserve projected context after hooks 2026-06-21 07:00:11 +08:00
Vincent Koc
faa4f5a23b fix(copilot): ignore stale compaction cleanup 2026-06-21 07:00:11 +08:00
Vincent Koc
fc0b28cb73 fix(copilot): serialize compaction cleanup 2026-06-21 07:00:11 +08:00
Vincent Koc
8265acaacb docs(copilot): use portable model example 2026-06-21 07:00:11 +08:00
Vincent Koc
d20e96a650 fix(copilot): isolate root compaction events 2026-06-21 07:00:11 +08:00
Vincent Koc
8ff1d3e67b fix(harness): surface returned tool errors 2026-06-21 07:00:11 +08:00
Vincent Koc
49a9032705 fix(copilot): preserve timeout compaction state 2026-06-21 07:00:11 +08:00
Vincent Koc
aab1dd88e0 fix(copilot): defer background compaction hooks 2026-06-21 07:00:11 +08:00
Vincent Koc
a81a505c72 fix(copilot): defer background compaction cleanup 2026-06-21 07:00:11 +08:00
Vincent Koc
7d219bd6e7 fix(copilot): bound background compaction waits 2026-06-21 07:00:11 +08:00
Vincent Koc
ece7d0945c fix(copilot): cancel deferred compaction on abort 2026-06-21 07:00:11 +08:00
Vincent Koc
979238dbb3 fix(copilot): retain completed compaction sessions 2026-06-21 07:00:11 +08:00
Vincent Koc
ab165d119c fix(copilot): retain timed-out compaction hooks 2026-06-21 07:00:11 +08:00
Vincent Koc
6f3df79f17 fix(copilot): await background compaction hooks 2026-06-21 07:00:11 +08:00
Vincent Koc
7c2fb845db fix(harness): cover manual lifecycle hooks 2026-06-21 07:00:11 +08:00
Vincent Koc
00f67b845b fix(harness): preserve effective hook inputs 2026-06-21 07:00:11 +08:00
Vincent Koc
6ef9207201 refactor(harness): complete lifecycle parity 2026-06-21 07:00:11 +08:00
Vincent Koc
3283540c78 chore(copilot): preserve hook bridge file modes 2026-06-21 07:00:11 +08:00
Vincent Koc
03b022b88e refactor(copilot): unify harness lifecycle hooks 2026-06-21 07:00:11 +08:00
Vincent Koc
45f9358877 chore(deadcode): share chat token formatter 2026-06-21 06:35:53 +08:00
Vincent Koc
29ec5b331c chore(deadcode): share session model default check 2026-06-21 06:33:10 +08:00
Vincent Koc
fa08942396 test(scripts): focus plugin manifest routing 2026-06-21 00:31:29 +02:00
Vincent Koc
2c9192a9a8 test(scripts): focus plugin runtime build routing 2026-06-21 00:27:26 +02:00
Vincent Koc
955f3ed094 test(scripts): focus build metadata routing 2026-06-21 00:22:35 +02:00
Vincent Koc
7217477553 chore(deadcode): reuse session terminal status helper 2026-06-21 06:16:30 +08:00
Vincent Koc
97b0b559ad test(scripts): focus static asset routing 2026-06-21 00:04:49 +02:00
Vincent Koc
1b299f4dbf chore(deadcode): share runner tool-call type guard 2026-06-21 05:59:00 +08:00
Vincent Koc
7064d198a3 test(scripts): focus tsgo guard routing 2026-06-20 23:57:23 +02:00
Vincent Koc
f18ff7551e test(scripts): focus bundled metadata routing 2026-06-20 23:53:40 +02:00
Vincent Koc
58d295840e test(scripts): focus build stamp routing 2026-06-20 23:49:30 +02:00
Vincent Koc
896b3c612d test(scripts): focus extension boundary routing 2026-06-20 23:45:20 +02:00
Vincent Koc
2f240a4a4c test(scripts): focus sdk boundary routing 2026-06-20 23:41:37 +02:00
Vincent Koc
172412d756 chore(deadcode): remove stale exported type aliases 2026-06-21 05:38:16 +08:00
Vincent Koc
58578b3250 test(scripts): focus mcp temp state routing 2026-06-20 23:32:56 +02:00
Vincent Koc
ce9769faae test(scripts): focus install package routing 2026-06-20 23:28:48 +02:00
Vincent Koc
e8920f6f6b test(scripts): focus parallels shell routing 2026-06-20 23:23:04 +02:00
Vincent Koc
9be53b4aa2 test(scripts): focus github helper routing 2026-06-20 23:15:15 +02:00
Vincent Koc
3ec2a46907 test(scripts): focus release helper routing 2026-06-20 23:11:34 +02:00
Vincent Koc
15a2d74320 test(scripts): focus installer routing changes 2026-06-20 23:05:21 +02:00
Shakker
77f07a11e7 fix: share operator approval env snapshots 2026-06-20 22:02:27 +01:00
Josh Lehman
7a0d36f3d0 refactor: add SDK transcript identity target API (#95030) 2026-06-20 14:01:07 -07:00
Vincent Koc
0a707afb9a chore(deadcode): inline exec approval wait helper 2026-06-21 04:58:14 +08:00
Shakker
bdeda6553b test: finish gateway token env routing 2026-06-20 21:50:55 +01:00
Shakker
3499b277e3 fix: route gateway env setup through helpers 2026-06-20 21:50:55 +01:00
Vincent Koc
8c8857c3ef fix(qa): keep telegram credential tests sparse safe 2026-06-20 22:45:25 +02:00
Vincent Koc
d75613e794 chore(deadcode): reuse tool result details reader 2026-06-21 04:42:48 +08:00
Shakker
beb8897f49 test: keep Claude seed HOME fallback covered 2026-06-20 21:36:15 +01:00
Shakker
add5f76a1e fix: isolate Claude history HOME setup 2026-06-20 21:34:58 +01:00
Vincent Koc
9a9f4dbefe test(rpc): map rtt measurement script changes 2026-06-20 22:32:17 +02:00
Vincent Koc
5beaaf343c test(qa): map qa e2e script changes 2026-06-20 22:29:33 +02:00
Vincent Koc
1db811282c fix(release): validate plugin manifest runner args 2026-06-20 22:23:30 +02:00
Vincent Koc
aa23d9f34e chore(deadcode): inline approval abort classification 2026-06-21 04:22:12 +08:00
Vincent Koc
2962c95010 fix(release): validate plugin runtime build args 2026-06-20 22:19:50 +02:00
Vincent Koc
80d3b132a5 fix(release): validate package dist check args 2026-06-20 22:16:26 +02:00
Shakker
1a5d84d3fe test: reuse discovery env snapshot 2026-06-20 21:09:10 +01:00
Vincent Koc
71a75b9b28 fix(release): validate package tarball check args 2026-06-20 22:08:25 +02:00
Vincent Koc
b1f562570a fix(release): validate openclaw npm verifier args 2026-06-20 22:03:38 +02:00
Vincent Koc
bdcc691745 chore(deadcode): inline message provider tool filtering 2026-06-21 04:00:09 +08:00
Shakker
4461e257e3 fix: restore env warning flags with helper 2026-06-20 20:58:13 +01:00
Vincent Koc
76014cfe95 fix(release): validate plugin npm verifier args 2026-06-20 21:57:13 +02:00
Vincent Koc
498ff1fb5a fix(release): validate plugin clawhub publish args 2026-06-20 21:53:59 +02:00
Shakker
ae81aa018d test: reuse update method env wrapper 2026-06-20 20:52:09 +01:00
Vincent Koc
1706bfda2c fix(release): validate plugin npm publish args 2026-06-20 21:51:32 +02:00
Vincent Koc
a1201e99fc fix(release): validate npm publish wrapper args 2026-06-20 21:48:01 +02:00
Shakker
90d2f161c9 fix: scope config open path env 2026-06-20 20:46:29 +01:00
Vincent Koc
bff7134a69 fix(mac): validate notarization wrapper args 2026-06-20 21:44:09 +02:00
Vincent Koc
e59d0b540e fix(mac): reject invalid codesign args 2026-06-20 21:41:34 +02:00
Shakker
aa5fcf70f7 test: share gateway credential env guard 2026-06-20 20:40:57 +01:00
Vincent Koc
63ac2e2ce0 fix(mac): reject build-and-run wrapper args 2026-06-20 21:36:42 +02:00
Shakker
803064c6e0 fix: localize session transcript env 2026-06-20 20:35:32 +01:00
Vincent Koc
577e5a4692 fix(mac): reject unknown restart options 2026-06-20 21:33:48 +02:00
Vincent Koc
a49f3f9362 fix(qa): parse qa e2e wrapper flags 2026-06-20 21:29:18 +02:00
Vincent Koc
7b9ddbda99 chore(deadcode): inline inbound prompt prefix 2026-06-21 03:27:50 +08:00
Shakker
0f83051353 test: share release journey env wrapper 2026-06-20 20:22:18 +01:00
Vincent Koc
4341cf24cc fix(crabbox): detect node-wrapped changed gates 2026-06-20 21:19:03 +02:00
Shakker
6a3f990140 fix: isolate plugin index loader env 2026-06-20 20:13:24 +01:00
scotthuang
81abc2b21b fix: preserve cron delivery awareness for target sessions (#93580)
Merged via squash.

Prepared head SHA: 460562ceff
Co-authored-by: scotthuang <1670837+scotthuang@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
2026-06-20 12:13:10 -07:00
Shakker
09fcafffbc test: scope package root fallback env 2026-06-20 20:11:46 +01:00
Vincent Koc
2a93d7b9c5 chore(deadcode): inline runtime context builders 2026-06-21 03:09:43 +08:00
Shakker
0eaefc9050 fix: share npm verifier env guard 2026-06-20 20:02:45 +01:00
Shakker
52e01676be test: reuse memory fd env helper 2026-06-20 19:58:05 +01:00
Shakker
df68b81006 fix: isolate bundled probe env 2026-06-20 19:57:16 +01:00
Vincent Koc
a5417b5c6c chore(deadcode): inline bootstrap routing helpers 2026-06-21 02:55:16 +08:00
Shakker
da2c7e2d2b test: reuse startup bench env helper 2026-06-20 19:45:59 +01:00
Shakker
3a14f247ad fix: scope bundled skills env 2026-06-20 19:44:37 +01:00
Vincent Koc
5c36001fcb chore(deadcode): inline tool-search allowlist helpers 2026-06-21 02:40:32 +08:00
Shakker
05bed72a8d test: restore plugin trust env 2026-06-20 19:34:22 +01:00
Vincent Koc
c2433d41a7 fix(ci): reject release metadata option typos 2026-06-20 20:32:50 +02:00
Shakker
d368fd620c fix: restore clawhub home env 2026-06-20 19:31:26 +01:00
Vincent Koc
7dc7deaa13 fix(ci): reject mistyped changed gate options 2026-06-20 20:28:15 +02:00
Vincent Koc
a2ff59fdb2 chore(deadcode): inline same-model retry backoff 2026-06-21 02:24:56 +08:00
Vincent Koc
b12223a79f fix(qa): reject empty qa lab port flags 2026-06-20 20:17:52 +02:00
Vincent Koc
f519ceab9c fix(ci): allow gtimeout for docker pull retry 2026-06-20 20:12:30 +02:00
Vincent Koc
1f1b1aee6b chore(deadcode): remove duplicate Gemini schema helper 2026-06-21 02:09:19 +08:00
Vincent Koc
62b2e9ef14 fix(scripts): honor gtimeout in host setup wrappers 2026-06-20 20:07:50 +02:00
Vincent Koc
0f67474251 fix(docker): keep upgrade survivor auto-auth summary safe 2026-06-20 20:02:14 +02:00
Gio Della-Libera
e56fd1dc04 Keep core doctor health in contribution order (#86627)
Merged via squash.

Prepared head SHA: e0955797c1
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-06-20 10:59:31 -07:00
Vincent Koc
b3968f69c9 fix(package): accept uppercase artifact digests 2026-06-20 19:52:59 +02:00
Vincent Koc
b0df6dc10e fix(package): scope trusted URL auth to original origin 2026-06-20 19:50:09 +02:00
Vincent Koc
141fb2b119 fix(crabbox): bootstrap macOS stdin shell scripts 2026-06-20 19:44:40 +02:00
Vincent Koc
64b6488f6c fix(crabbox): bootstrap env-option macOS stdin scripts 2026-06-20 19:39:05 +02:00
Vincent Koc
e1fc4683bb chore(deadcode): remove unused cron run log reader 2026-06-21 01:32:51 +08:00
Vincent Koc
85ab952956 fix(release): reject zero correction tags 2026-06-20 19:30:26 +02:00
Vincent Koc
abd5fb4494 fix(release): guard appcast cleanup before notes path 2026-06-20 19:28:42 +02:00
Vincent Koc
aea050b43e fix(mac): clean failed notary zip staging 2026-06-20 19:25:38 +02:00
Vincent Koc
85f552bf37 fix(qa): clean failed Parallels package locks 2026-06-20 19:20:40 +02:00
Vincent Koc
dafd98dd98 chore(deadcode): drop unused llm provider helpers 2026-06-21 01:17:06 +08:00
Vincent Koc
3632c62f85 fix(qa): isolate OTEL smoke exporter env 2026-06-20 19:14:06 +02:00
Vincent Koc
ad5d2cbc1b fix(mac): clean dSYM staging on zip failure 2026-06-20 19:07:04 +02:00
Vincent Koc
7cda58c109 fix(package): keep artifact duplicate diagnostics relative 2026-06-20 19:02:54 +02:00
Vincent Koc
5c0b99ae2b chore(deadcode): remove unused task flow retry path 2026-06-21 01:00:42 +08:00
Vincent Koc
979925c194 fix(openwebui): redact failed chat diagnostics 2026-06-20 18:58:30 +02:00
Vincent Koc
2f9f45f734 fix(telegram): include session probe artifacts 2026-06-20 18:51:20 +02:00
Vincent Koc
32cbaecd09 fix(telegram): stage full proof artifacts safely 2026-06-20 18:47:12 +02:00
Vincent Koc
1989726eb6 chore(deadcode): remove unused cron failure target wrapper 2026-06-21 00:40:26 +08:00
Vincent Koc
2454acc287 fix(crabbox): bound macos bun bootstrap fetches 2026-06-20 18:38:00 +02:00
Vincent Koc
fce5db415b fix(crabbox): bound macos node bootstrap downloads 2026-06-20 18:33:48 +02:00
Vincent Koc
2166652eb3 fix(parallels): bound update tarball probe 2026-06-20 18:28:13 +02:00
Vincent Koc
7a9c269541 chore(deadcode): drop unused cron summary guard 2026-06-21 00:27:23 +08:00
Vincent Koc
aa893b9228 fix(parallels): bound linux smoke downloads 2026-06-20 18:25:57 +02:00
Vincent Koc
98a7741468 fix(parallels): bound windows smoke downloads 2026-06-20 18:24:13 +02:00
Vincent Koc
3df4341e5a fix(parallels): bound macos smoke downloads 2026-06-20 18:20:55 +02:00
Vincent Koc
ecac665bf3 fix(parallels): pace background launch probes 2026-06-20 18:14:08 +02:00
Vincent Koc
021fd5de2b chore(deadcode): remove unused channel sender validator 2026-06-21 00:11:51 +08:00
Vincent Koc
60159b9f00 fix(parallels): keep fresh malformed package locks 2026-06-20 18:10:32 +02:00
Vincent Koc
165440117e fix(canvas): ignore stale pnpm execpath 2026-06-20 18:05:23 +02:00
Vincent Koc
fddfcbe10e fix(canvas): use corepack for a2ui pnpm fallback 2026-06-20 18:02:17 +02:00
Vincent Koc
7c850bdf38 fix(test): kill SDK package command trees 2026-06-20 17:54:16 +02:00
Vincent Koc
2bc20f2ec5 fix(test): use pnpm runner for SDK package build 2026-06-20 17:51:21 +02:00
Vincent Koc
ed500dda25 fix(qa): use corepack for lab docker build fallback 2026-06-20 17:45:09 +02:00
Vincent Koc
bc754b3160 fix(ci): restore Vitest watchdog cleanup 2026-06-20 23:42:22 +08:00
Vincent Koc
b972956173 test(ci): use public Feishu temp-dir helper 2026-06-20 23:42:22 +08:00
Vincent Koc
29444b26f2 chore(deadcode): dedupe plugin JSON logger 2026-06-20 23:37:00 +08:00
Vincent Koc
7fc5a72433 fix(qa): cap chunked credential lease payloads 2026-06-20 17:34:38 +02:00
Vincent Koc
a590f7f690 fix(qa): require boundary entry shim outputs 2026-06-20 17:25:11 +02:00
Vincent Koc
2252674168 fix(qa): reject matrix output symlink escapes 2026-06-20 17:15:45 +02:00
Vincent Koc
60612ff492 chore(deadcode): inline auto-reply display wrappers 2026-06-20 23:14:23 +08:00
Vincent Koc
c5623e72f3 fix(qa): quote generated compose paths 2026-06-20 17:08:40 +02:00
Vincent Koc
947c21ee5a refactor(qa): reuse qa shell quote helper 2026-06-20 17:05:10 +02:00
Vincent Koc
99f58ae6d6 fix(qa): quote qa docker stop command 2026-06-20 16:59:14 +02:00
Vincent Koc
3f0e740f83 chore(deadcode): inline session visibility wrappers 2026-06-20 22:56:40 +08:00
Vincent Koc
106961b513 fix(e2e): resolve mounted macOS desktop homes 2026-06-20 16:51:20 +02:00
Vincent Koc
d0001f96f0 fix(e2e): ignore bundled plugin list diagnostics 2026-06-20 16:44:11 +02:00
Vincent Koc
527bd807b9 fix(e2e): ignore runtime smoke rpc log records 2026-06-20 16:40:14 +02:00
Vincent Koc
7546231762 fix(run-node): type signal process injection 2026-06-20 22:37:26 +08:00
Vincent Koc
a977dc843d chore(deadcode): delete unused route wrappers 2026-06-20 22:37:26 +08:00
Vincent Koc
6ad7f66af2 fix(e2e): ignore inline kitchen sink json diagnostics 2026-06-20 16:34:52 +02:00
Vincent Koc
1b4fb6291d fix(e2e): parse secret proof json records 2026-06-20 16:31:09 +02:00
Vincent Koc
ee69465fe9 fix(e2e): ignore embedded diagnostic reply json 2026-06-20 16:26:00 +02:00
Vincent Koc
7b329ade32 fix(e2e): reject malformed package lock pids 2026-06-20 16:21:27 +02:00
Vincent Koc
44422b2151 fix(e2e): isolate Windows background control markers 2026-06-20 16:17:04 +02:00
Vincent Koc
48b338a5a9 fix(e2e): report signaled host server startups 2026-06-20 16:14:16 +02:00
Vincent Koc
d4f68475fd fix(e2e): preserve spaced macOS desktop homes 2026-06-20 16:11:03 +02:00
Vincent Koc
d81ae7a441 chore(deadcode): inline unused CLI helpers 2026-06-20 22:09:32 +08:00
Vincent Koc
99d8549de6 fix(crabbox): always mark shell changed gates as remote 2026-06-20 16:04:05 +02:00
Vincent Koc
7a077ffead fix(run-node): bind process signal cleanup 2026-06-20 15:55:16 +02:00
Vincent Koc
b980d678a4 fix(run-node): clean child groups on forwarded signals 2026-06-20 15:55:16 +02:00
Vincent Koc
e02e3d6971 chore(deadcode): remove unused CLI helper exports 2026-06-20 21:51:36 +08:00
Vincent Koc
6fa05685ea fix(check): clean managed child groups after forwarded signals 2026-06-20 15:46:14 +02:00
Vincent Koc
6585cb3b44 fix(watch): clean child groups on watcher shutdown 2026-06-20 15:43:04 +02:00
Vincent Koc
730c7269ef fix(test): clean Vitest runner child groups on signal 2026-06-20 15:35:33 +02:00
Vincent Koc
d72f7edf2d chore(deadcode): move gateway live probe helper out of prod path 2026-06-20 21:21:19 +08:00
Vincent Koc
24b6e6ba96 fix(test-live): force cleanup shard child groups on parent signal 2026-06-20 15:19:22 +02:00
Vincent Koc
c33f8c20ef fix(test-live): force cleanup Vitest child groups on parent signal 2026-06-20 15:16:15 +02:00
Vincent Koc
1c0c072bc2 fix(boundary): force cleanup tsc child trees on parent signal 2026-06-20 15:10:53 +02:00
Vincent Koc
aaf335af04 fix(deadcode): clean Knip child trees on parent signal 2026-06-20 15:07:19 +02:00
Vincent Koc
ad049ef083 fix(build): clean tsdown child trees on parent signal 2026-06-20 15:03:37 +02:00
Vincent Koc
6dc121eb6a chore(deadcode): move gateway auth helper out of prod path 2026-06-20 21:01:29 +08:00
Vincent Koc
0742a2f37a fix(test-report): clean parent-signaled child trees 2026-06-20 14:59:00 +02:00
Vincent Koc
e2c567538d fix(boundary): clean active check child trees 2026-06-20 14:52:00 +02:00
Vincent Koc
5c8fa5da5c chore(deadcode): move plugin test mocks out of prod paths 2026-06-20 20:41:02 +08:00
Vincent Koc
9953b85e6d fix(install-smoke): clean Bun timeout child trees 2026-06-20 14:39:39 +02:00
Vincent Koc
048014d1ab fix(memory): clean extension profiler child trees 2026-06-20 14:30:28 +02:00
Vincent Koc
0cd6975352 fix(prompt-probe): clean direct prompt child trees 2026-06-20 14:20:09 +02:00
Vincent Koc
5384b91866 fix(prompt-probe): clean gateway child trees 2026-06-20 14:09:17 +02:00
Vincent Koc
19ec9d8979 chore(deadcode): remove msteams memory test stores 2026-06-20 20:03:23 +08:00
Vincent Koc
e65619dd0c fix(crabbox): clean wrapper child trees on parent signal 2026-06-20 13:52:34 +02:00
Vincent Koc
2f0f085826 chore(deadcode): remove bedrock test injection hooks 2026-06-20 19:44:10 +08:00
Vincent Koc
0cd8db97f9 fix(bench): kill gateway child trees on windows 2026-06-20 13:30:33 +02:00
Vincent Koc
087d999fce fix(secret-providers): clean PTY configure timeout trees 2026-06-20 13:29:56 +02:00
Vincent Koc
4514b5a387 fix(runtime-smoke): kill bundled child trees on windows 2026-06-20 13:24:20 +02:00
Vincent Koc
6b82d4ecb7 chore(deadcode): remove telegram topic cache test helpers 2026-06-20 19:22:42 +08:00
Vincent Koc
f719f0cf77 fix(rpc): kill measurement gateway trees on windows 2026-06-20 13:18:02 +02:00
Vincent Koc
8ee638236a fix(secret-providers): clean command trees on parent signal 2026-06-20 13:17:14 +02:00
Vincent Koc
36934fd9f5 fix(kitchen-sink): clean command groups on parent signal 2026-06-20 13:12:00 +02:00
Vincent Koc
84895e9276 fix(docker): clean active shell groups on parent signal 2026-06-20 13:04:14 +02:00
Vincent Koc
a6e41a0cc1 fix(qa-lab): kill script timeout trees on windows 2026-06-20 12:58:52 +02:00
Vincent Koc
1ede829fbf fix(qa-lab): leave vitest timeout cleanup to wrapper 2026-06-20 12:53:39 +02:00
Vincent Koc
b93b07ee1b test(qa-lab): use temp harness in scenario runner tests 2026-06-20 12:53:39 +02:00
Vincent Koc
405e5072fd fix(qa-lab): bound test file scenario commands 2026-06-20 12:53:39 +02:00
Vincent Koc
b79dfc739c fix(gauntlet): clean measured groups on parent signal 2026-06-20 12:49:02 +02:00
Vincent Koc
ff4808f94d chore(deadcode): remove stale feishu download helpers 2026-06-20 18:47:54 +08:00
Vincent Koc
602bc0baa9 fix(bench): clean timed-out sample process groups 2026-06-20 12:31:47 +02:00
Vincent Koc
a1d278b174 fix(crabbox): preserve telegram proof kill grace 2026-06-20 12:25:03 +02:00
Vincent Koc
0fd5dae36f test(ci): allow control ui runner startup 2026-06-20 18:22:56 +08:00
Vincent Koc
984e058624 fix(e2e): reap signaled PTY command trees 2026-06-20 12:20:16 +02:00
Vincent Koc
a6e4afe0fa fix(parallels): preserve npm update stream kill grace 2026-06-20 12:15:30 +02:00
Vincent Koc
66c62d52ad chore(deadcode): remove stale msteams mention helpers 2026-06-20 18:14:41 +08:00
Vincent Koc
9e3ef487eb test(ci): cover stable closeout retries 2026-06-20 18:13:27 +08:00
Vincent Koc
739636fc33 fix(parallels): reap signaled host command groups 2026-06-20 12:08:29 +02:00
Vincent Koc
ccc1415f6d fix(ui): clean up wrapper signal descendants 2026-06-20 12:08:07 +02:00
Vincent Koc
b1608b4a4e test(ci): refresh temp-dir helper routing 2026-06-20 18:05:14 +08:00
Vincent Koc
703dfbf453 chore(deadcode): remove stale auto-reply helpers 2026-06-20 17:58:59 +08:00
Vincent Koc
7cd58cca2a fix(qa-lab): keep lifecycle probe timeout trees tracked 2026-06-20 11:58:14 +02:00
Vincent Koc
2d603c90dc fix(i18n): reap control ui process groups on signal 2026-06-20 11:54:43 +02:00
Vincent Koc
4296ecb78c fix(qa-matrix): clean up killed CLI process groups 2026-06-20 11:50:38 +02:00
Vincent Koc
fe1d981a47 fix(ci): ignore ClawSweeper self-comments 2026-06-20 17:42:47 +08:00
Vincent Koc
5cf8ba973d fix(ci): cancel superseded main workflows 2026-06-20 17:42:47 +08:00
Vincent Koc
cb394309fe fix(qa-matrix): keep timed CLI process groups tracked 2026-06-20 11:41:46 +02:00
Vincent Koc
dd29a6de52 fix(scripts): reap startup metadata help descendants 2026-06-20 11:40:00 +02:00
Vincent Koc
93a0b5d353 fix(ci): handle missing closeout assets after backoff 2026-06-20 17:39:30 +08:00
Vincent Koc
4f8fd48ea7 fix(ci): cool down main workflow fanout 2026-06-20 17:37:04 +08:00
Vincent Koc
7679872ddf chore(deadcode): drop memory shadow trial scoring shims 2026-06-20 17:33:16 +08:00
Vincent Koc
cd7385c5c6 fix(rpc): preserve gateway signal cleanup grace 2026-06-20 11:22:36 +02:00
Vincent Koc
88cf142c98 fix(qa-lab): preserve model catalog abort grace 2026-06-20 11:18:43 +02:00
Vincent Koc
1988e1a0c5 chore(deadcode): remove memory wiki helper shims 2026-06-20 17:16:41 +08:00
Vincent Koc
138ffa2992 fix(e2e): keep telegram proof command groups tracked 2026-06-20 11:14:05 +02:00
Vincent Koc
6069a030c4 fix(scripts): keep closed runtime command groups tracked 2026-06-20 11:08:36 +02:00
Vincent Koc
0767118c26 fix(test): finish group report timeout cleanup promptly 2026-06-20 11:05:14 +02:00
Vincent Koc
120d08c730 fix(scripts): preserve boundary abort grace 2026-06-20 11:00:26 +02:00
Vincent Koc
6ee7714306 chore(deadcode): drop memory dreaming registration shim 2026-06-20 16:56:29 +08:00
Vincent Koc
84c96ddb14 fix(e2e): preserve bun smoke timeout grace 2026-06-20 10:54:18 +02:00
Vincent Koc
8b8d791472 fix(build): preserve tsdown timeout grace 2026-06-20 10:52:51 +02:00
Vincent Koc
8246b49cc5 fix(test): preserve gauntlet timeout grace 2026-06-20 10:49:38 +02:00
Vincent Koc
3d5aefb50c fix(e2e): finish telegram timeout cleanup promptly 2026-06-20 10:46:37 +02:00
Vincent Koc
628314f53c fix(test): keep report timeout cleanup alive 2026-06-20 10:44:34 +02:00
Vincent Koc
5c19699cb2 chore(deadcode): remove qqbot duplicate wrappers 2026-06-20 16:41:41 +08:00
Vincent Koc
75fd2464cc fix(release): finish candidate timeout cleanup promptly 2026-06-20 10:39:59 +02:00
Vincent Koc
bd5a5a0cfc fix(test): preserve lifecycle probe timeout failures 2026-06-20 10:38:46 +02:00
Vincent Koc
9111d8ed85 fix(e2e): preserve host command timeout grace 2026-06-20 10:33:04 +02:00
Vincent Koc
1c3da22bcd fix(release): require exact publish child runs 2026-06-20 10:29:42 +02:00
Vincent Koc
7de3e0e0bb fix(release): require exact candidate workflow runs 2026-06-20 10:24:59 +02:00
Vincent Koc
29df94382e chore(deadcode): remove duplicate compaction provider lister 2026-06-20 16:22:46 +08:00
Vincent Koc
629f78b77b fix(e2e): preserve fresh lane timeout grace 2026-06-20 10:19:03 +02:00
Vincent Koc
68790eb4b9 fix(scripts): preserve secret proof timeout grace 2026-06-20 10:16:59 +02:00
Vincent Koc
9a92c3d24a fix(ci): allow full release child queues 2026-06-20 16:15:59 +08:00
Vincent Koc
3fa12177dd fix(docker): preserve shell timeout kill grace 2026-06-20 10:13:26 +02:00
Vincent Koc
8855b21f99 fix(e2e): preserve rpc timeout kill grace 2026-06-20 10:01:06 +02:00
Vincent Koc
a4e2113e1b chore(deadcode): remove unused infra consumers 2026-06-20 15:59:37 +08:00
Vincent Koc
f26f45c050 fix(lint): reap timed-out oxlint shard groups 2026-06-20 09:54:36 +02:00
Vincent Koc
a127183094 fix(scripts): track import meta url package deps 2026-06-20 09:47:27 +02:00
Vincent Koc
22f696d010 fix(scripts): run npm cli with active node 2026-06-20 09:47:27 +02:00
Vincent Koc
3a1e49dbaa fix(test): preserve vitest force kill after idle timeout 2026-06-20 09:44:02 +02:00
Vincent Koc
0e14a3f09b chore(deadcode): remove unused web push sender 2026-06-20 15:41:30 +08:00
Vincent Koc
244857adbf fix(scripts): reap canceled boundary process groups 2026-06-20 09:38:35 +02:00
Vincent Koc
1dbf4dbd40 chore(deadcode): remove unused infra wrappers 2026-06-20 15:26:47 +08:00
Vincent Koc
fbea5b023a fix(e2e): abort live secret proof process groups 2026-06-20 09:26:22 +02:00
Vincent Koc
4e7a717868 fix(e2e): reap exited gateway process groups 2026-06-20 09:19:59 +02:00
Vincent Koc
00c2dc66b1 fix(e2e): avoid stale Crabbox recorder waits 2026-06-20 09:15:37 +02:00
Vincent Koc
df7e4788ed chore(deadcode): remove unused package inventory comparator 2026-06-20 15:10:15 +08:00
Vincent Koc
5b93f829e5 fix(scripts): finalize cross-os commands on signal 2026-06-20 09:08:53 +02:00
Vincent Koc
6f1cc2f8df fix(scripts): honor Windows platform overrides 2026-06-20 08:58:39 +02:00
Vincent Koc
d9ee08a76e fix(qa-lab): replace docker workspace repo link 2026-06-20 08:53:29 +02:00
Vincent Koc
da35f8b4d1 chore(deadcode): remove unused outbound json builder 2026-06-20 14:53:05 +08:00
Vincent Koc
bb7150de94 fix(gateway): reject malformed artifact base64 2026-06-20 08:49:05 +02:00
Bek
3d05e973f0 fix(slack): record canonical sent thread (#95250) 2026-06-20 02:45:58 -04:00
Vincent Koc
fb022a2b07 fix(qa-lab): use junctions for Windows workspace repo links 2026-06-20 08:39:32 +02:00
Vincent Koc
9afc333bd7 chore(deadcode): remove stale runtime query helpers 2026-06-20 14:37:46 +08:00
Vincent Koc
7b44157bc6 fix(e2e): reject escaped skill info paths 2026-06-20 08:31:42 +02:00
Vincent Koc
075965e32f fix(e2e): reject malformed npm fixture paths 2026-06-20 08:28:29 +02:00
Vincent Koc
73d393f812 fix(qa-lab): parse trailing cli json diagnostics 2026-06-20 08:26:25 +02:00
Vincent Koc
1cbcc3e1f0 test(e2e): use tracked ClawHub temp fixture 2026-06-20 08:23:29 +02:00
Vincent Koc
ee709f3b0f fix(e2e): verify ClawHub install path containment 2026-06-20 08:21:06 +02:00
Vincent Koc
eb5bb7f6a0 chore(deadcode): remove unused channel alias lister 2026-06-20 14:15:31 +08:00
Vincent Koc
592373f0ea fix(ci): quote Windows testbox phone-home payloads 2026-06-20 08:14:17 +02:00
Vincent Koc
a63230008c fix(ci): hydrate full testbox live auth 2026-06-20 08:07:39 +02:00
Vincent Koc
d0812126c8 chore(deadcode): prune stale model metadata wrappers 2026-06-20 14:00:10 +08:00
Vincent Koc
352141a1be fix(qa-lab): keep generated media fallback usable 2026-06-20 07:57:12 +02:00
Vincent Koc
904c035d1c fix(qa-lab): wait for qa cli stdio close 2026-06-20 07:55:23 +02:00
Vincent Koc
a085db6b64 fix(qa): release docker health probe bodies 2026-06-20 07:52:33 +02:00
Vincent Koc
819b1a3e3e fix(qa-lab): disable Telegram token URL capture 2026-06-20 07:47:04 +02:00
Vincent Koc
e19ad8c0fd fix(qa-lab): report mock cleanup startup failures 2026-06-20 07:43:03 +02:00
Vincent Koc
f8675b3b70 fix(gateway): normalize secret fallback values 2026-06-20 07:34:39 +02:00
Vincent Koc
3285a10c7f fix(scripts): shell quote Telegram Crabbox remotes 2026-06-20 07:31:21 +02:00
Vincent Koc
e451a4e875 fix(qa): measure chunked credential payload bytes 2026-06-20 07:26:49 +02:00
Vincent Koc
8e375242be fix(qa-lab): handle gateway child spawn errors 2026-06-20 07:18:07 +02:00
Vincent Koc
93cbd16c88 chore(deadcode): remove copied copilot token shim 2026-06-20 13:15:12 +08:00
Vincent Koc
7779bc64d2 fix(tui): wrap Windows auth shims explicitly 2026-06-20 07:01:27 +02:00
Vincent Koc
5e7bb9cf9b fix(hooks): share Windows gog command wrapping 2026-06-20 06:56:23 +02:00
Vincent Koc
2248aa4315 fix(process): wrap Windows command shims 2026-06-20 06:50:56 +02:00
Vincent Koc
6bfe7a2b06 fix(secrets): enforce canonical secret refs 2026-06-20 06:41:33 +02:00
Vincent Koc
2babcf026e fix(scripts): parse keyed npm tarball metadata 2026-06-20 06:37:27 +02:00
Vincent Koc
9192ff8416 fix(scripts): write Windows markers without BOM 2026-06-20 06:26:11 +02:00
Vincent Koc
118f3f3312 fix(scripts): clear RPC RTT send failure timers 2026-06-20 06:18:57 +02:00
Vincent Koc
7d658dfd97 fix(sdk): honor session send timeouts 2026-06-20 06:18:09 +02:00
Vincent Koc
84a36057e9 chore(deadcode): remove stale qwen model shim 2026-06-20 12:08:51 +08:00
Vincent Koc
b44e39b82c fix(scripts): redact openwebui probe diagnostics 2026-06-20 06:07:22 +02:00
Vincent Koc
e89c255a01 fix(sdk): require session key for effective tools 2026-06-20 06:00:03 +02:00
Vincent Koc
a635e97965 fix(sdk): tighten approval response params 2026-06-20 05:59:50 +02:00
Vincent Koc
4f278ef71c fix(sdk): type agent mutation RPC params 2026-06-20 05:59:36 +02:00
Vincent Koc
1df2cc5f02 fix(qa): preserve adjacent control ui redaction 2026-06-20 05:56:36 +02:00
Vincent Koc
1cda1fc9a0 fix(qa): strip control ui api key params 2026-06-20 05:52:49 +02:00
Vincent Koc
af9b026241 fix(qa): preserve cli flag redaction 2026-06-20 05:44:58 +02:00
Vincent Koc
6a23a72d74 fix(qa): redact gateway debug header secrets 2026-06-20 05:44:58 +02:00
Shakker
14d362039e test: restore doctor completion env 2026-06-20 04:37:18 +01:00
Shakker
9391dac56d fix: scope backup config env 2026-06-20 04:36:11 +01:00
Vincent Koc
61ee4ffdfc fix(scripts): guard reused testbox keys 2026-06-20 05:35:10 +02:00
Vincent Koc
78d1b4a9b3 fix(qa): remove personal capture CA path 2026-06-20 05:27:21 +02:00
Shakker
0d6e0a2263 test: isolate launchd process env 2026-06-20 04:26:00 +01:00
Vincent Koc
33a4845555 fix(qa): redact capture payload previews 2026-06-20 05:24:09 +02:00
Vincent Koc
4a75171190 fix(scripts): preserve kitchen sink RPC request errors 2026-06-20 11:23:59 +08:00
Shakker
c946df0239 fix: route skills home env restores 2026-06-20 04:13:52 +01:00
Shakker
9ce68d0920 test: isolate daemon status env 2026-06-20 04:09:09 +01:00
Vincent Koc
2c65b9b407 refactor(scripts): share mobile version arg parsing 2026-06-20 11:08:34 +08:00
Shakker
78a2a31a6b fix: scope completion install env 2026-06-20 04:01:08 +01:00
Shakker
c719ff3183 test: restore cli profile env 2026-06-20 03:56:15 +01:00
Vincent Koc
0479da9bfb refactor(qa): share live scenario reply assertion 2026-06-20 10:55:56 +08:00
Shakker
13e76544e5 fix: scope onboard reset env 2026-06-20 03:54:07 +01:00
Vincent Koc
c81391e270 fix(qa): hide evidence producer href paths 2026-06-20 04:53:15 +02:00
Vincent Koc
69216f1745 fix(qa): hide evidence artifact href paths 2026-06-20 04:39:15 +02:00
Vincent Koc
a824df2e35 refactor(qa): share live credential source inference 2026-06-20 10:38:59 +08:00
Vincent Koc
f60aec6e9d fix(qa): sanitize evidence gallery metadata 2026-06-20 04:32:43 +02:00
Vincent Koc
6293e6e3ca fix(qa): sanitize matrix runner evidence text 2026-06-20 04:25:09 +02:00
Vincent Koc
f4baeab47f refactor(qa): share thrown value normalization 2026-06-20 10:24:16 +08:00
Vincent Koc
8f06e65f33 fix(qa): sanitize matrix evidence artifact paths 2026-06-20 04:21:20 +02:00
Vincent Koc
3518fa575a fix(qa): sanitize evidence preview roots 2026-06-20 04:17:12 +02:00
Shakker
a5e33b3a6b test: restore manifest model env 2026-06-20 03:14:54 +01:00
Vincent Koc
86d1e397f4 fix(qa): hide absolute evidence source paths 2026-06-20 04:10:32 +02:00
Shakker
d6c7e95c7b fix: scope compact skill path env 2026-06-20 03:09:02 +01:00
Vincent Koc
445317a38b refactor(qa): share artifact write assertion 2026-06-20 10:08:19 +08:00
Shakker
2844ec2bb0 test: isolate exec approval env 2026-06-20 03:02:20 +01:00
Vincent Koc
459edec9ba fix(qa): hide absolute evidence artifact paths 2026-06-20 03:58:47 +02:00
Shakker
e27c9a9a41 fix: centralize dotenv env cleanup 2026-06-20 02:55:43 +01:00
Vincent Koc
c80f4c110e fix(qa): sanitize evidence gallery failure paths 2026-06-20 03:52:57 +02:00
Vincent Koc
cfc699d3f6 refactor(qa): reuse model ref splitter 2026-06-20 09:50:05 +08:00
Vincent Koc
f04c3d6575 fix(qa): sanitize ux evidence artifact paths 2026-06-20 03:46:06 +02:00
Vincent Koc
da03996ab7 fix(test): reject unselected media provider filters 2026-06-20 03:40:50 +02:00
Shakker
5fd947c661 test: route config guard home env 2026-06-20 02:39:03 +01:00
Vincent Koc
622955b3fc fix(test): guard issue labeler cli args 2026-06-20 03:36:50 +02:00
Vincent Koc
cd69760628 fix(release): guard plugin release cli values 2026-06-20 03:34:41 +02:00
Shakker
3e41587992 fix: scope best effort config env 2026-06-20 02:33:01 +01:00
Vincent Koc
214a28affd fix(test): reject invalid max loc args 2026-06-20 03:32:08 +02:00
Vincent Koc
9f6d5e4750 refactor(qa): share provider json writer 2026-06-20 09:31:43 +08:00
Vincent Koc
033455b6f1 fix(test): guard platform pin cli values 2026-06-20 03:29:15 +02:00
Vincent Koc
8b5b150e02 fix(test): guard platform sync cli values 2026-06-20 03:27:15 +02:00
Vincent Koc
4db7d6a90a fix(test): guard platform version cli values 2026-06-20 03:24:46 +02:00
Vincent Koc
d76c1daa52 fix(sdk): list helpers work without filters
SDK list helpers now send an empty params object when filters are omitted while preserving explicit invalid params for Gateway validation.\n\nVerification:\n- git diff --check origin/main...HEAD\n- node --check packages/sdk/src/client.ts\n- codex review --base origin/main\n- GitHub Actions CI release gate 27855603923 succeeded on 353f13c0d1
2026-06-20 09:22:48 +08:00
Vincent Koc
9491e9187d fix(test): add env mutation report help 2026-06-20 03:21:21 +02:00
Vincent Koc
e0ec42e0e0 fix(test): restore live media harness entrypoint 2026-06-20 03:18:26 +02:00
Vincent Koc
a971641a54 fix(test): guard claude usage debug args 2026-06-20 03:15:30 +02:00
Vincent Koc
50b5238b38 refactor(qa): share repo path resolution 2026-06-20 09:12:39 +08:00
Vincent Koc
0cf941344c fix(test): honor shell completion test args 2026-06-20 03:12:16 +02:00
Vincent Koc
e6823c3d16 fix(test): guard model benchmark cli args 2026-06-20 03:06:22 +02:00
Vincent Koc
4b2b70ec79 fix(test): guard gateway benchmark cli args 2026-06-20 03:04:34 +02:00
Vincent Koc
b6d91d96ef fix(test): guard sqlite benchmark cli args 2026-06-20 03:00:07 +02:00
Vincent Koc
dadec4500f fix(test): require abort leak snapshot dir value 2026-06-20 02:54:52 +02:00
Vincent Koc
f76a3a3bbe refactor(qa): share live approval result helpers 2026-06-20 08:54:15 +08:00
Vincent Koc
c2e26db61b fix(sdk): send exec approval resolve id (#95144) 2026-06-20 08:52:55 +08:00
Vincent Koc
41691a82d5 fix(test): guard discord acp smoke cli args 2026-06-20 02:47:36 +02:00
Vincent Koc
49b0487e5b fix(test): guard kitchen sink rpc cli args 2026-06-20 02:36:52 +02:00
Vincent Koc
4575734f59 fix(test): guard realtime perf cli args 2026-06-20 02:34:06 +02:00
Vincent Koc
7e7dc7505b test(docker): stabilize build signal probe (#95137) 2026-06-20 08:30:33 +08:00
Vincent Koc
7dca9210c9 fix(test): guard dev smoke cli args 2026-06-20 02:28:04 +02:00
Vincent Koc
208bed06e1 refactor(qa): share progress formatting helpers 2026-06-20 08:26:00 +08:00
Vincent Koc
87358d7a7c fix(test): guard model resolution profiler args 2026-06-20 02:22:59 +02:00
Vincent Koc
e02bee6aab fix(test): guard tui pty watch cli args 2026-06-20 02:19:53 +02:00
Vincent Koc
56c0405018 fix(test): guard benchmark qa cli args 2026-06-20 02:13:13 +02:00
Vincent Koc
b6d754e3cb fix(macos): create DMG output directories (#95133) 2026-06-20 08:11:23 +08:00
Vincent Koc
6e732b3063 refactor(qa): share parity comparison helpers 2026-06-20 08:09:38 +08:00
Vincent Koc
423b1b3a42 fix(test): clean release check cli errors 2026-06-20 02:08:16 +02:00
Vincent Koc
faeb731a29 fix(test): guard boundary check cli args 2026-06-20 02:05:20 +02:00
Vincent Koc
d6075c1694 fix(test): clean dependency report cli errors 2026-06-20 02:02:37 +02:00
Vincent Koc
a67f809b33 fix(test): clean perf summary cli errors 2026-06-20 02:00:34 +02:00
Vincent Koc
1f1c434ede fix(test): clean qa report cli errors 2026-06-20 01:58:54 +02:00
Vincent Koc
3c3f1010aa fix(test): preflight gauntlet missing builds 2026-06-20 01:53:05 +02:00
Vincent Koc
0e980be284 fix(package): ignore stale packed tarballs (#95126) 2026-06-20 07:49:25 +08:00
Vincent Koc
27450f6b42 fix(test): honor rpc rtt help flag 2026-06-20 01:44:59 +02:00
Dallin Romney
d491e9c69b fix(ci): cancel stale CodeQL runs (#95116)
* ci: cancel stale CodeQL runs

* fix(ci): let running CodeQL scans finish
2026-06-19 16:41:57 -07:00
Vincent Koc
6fc0a3a9bd fix(test): chunk broad script test routing 2026-06-20 01:32:13 +02:00
Vincent Koc
0a1ce14dd1 refactor(qa): reuse live transport option helper 2026-06-20 07:28:32 +08:00
Vincent Koc
f9f94e7dcd fix(test): stream QA Lab stdout artifacts (#95119)
* fix(test): bound QA Lab stdout artifact reads

* fix(test): scan QA Lab stdout artifacts incrementally
2026-06-20 07:16:14 +08:00
Andy Ye
1e105d5340 fix(doctor): repair legacy Codex route persistence (#94478)
Summary:
- The branch changes config write preparation and doctor regression coverage so `doctor --fix` persists repair ... rams under canonical `openai/*` with Codex runtime policy, plus a prerelease lane timeout assertion update.
- PR surface: Source +9, Tests +107. Total +116 across 4 files.
- Reproducibility: yes. at source level: current main can re-preserve stale source-authored `openai-codex/*` m ... the candidate config, while the PR body supplies after-fix command proof for the narrowed persistence path.

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

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

Prepared head SHA: 7b5bc00f31
Review: https://github.com/openclaw/openclaw/pull/94478#issuecomment-4739605890

Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
2026-06-19 23:09:45 +00:00
Vincent Koc
21c966616f refactor(qa): share mantis option helpers 2026-06-20 07:04:11 +08:00
Vincent Koc
be7807f65e fix(test): stabilize tooling guard probes (#95114)
* fix(test): release kitchen sink probe readers

* test(github): follow shared guard membership helper
2026-06-20 06:55:40 +08:00
Vincent Koc
7ee1dafd4f refactor(qa): share mantis phase timer 2026-06-20 06:48:17 +08:00
Dallin Romney
3a7a385baf fix(ci): cancel stale Testbox PR runs (#95105)
* ci: cancel stale testbox PR runs

* ci: cancel stale arm testbox PR runs
2026-06-19 15:23:54 -07:00
Vincent Koc
c4d1f37d33 fix(memory): abort batch upload response reads (#95111)
* fix(memory): abort batch upload response reads

* test(memory): stabilize batch upload abort proof
2026-06-20 06:22:23 +08:00
Vincent Koc
ba43be9424 refactor(github): share guard comment helpers 2026-06-20 06:10:37 +08:00
Vincent Koc
aa479ac7d8 refactor(github): share guard request helpers 2026-06-20 06:07:12 +08:00
Vincent Koc
d6cefe26f4 fix(agents): bound Anthropic error streams (#95108) 2026-06-20 06:02:12 +08:00
Vincent Koc
0eed410bd0 refactor(tooling): remove unused cleanup helpers 2026-06-20 05:52:30 +08:00
Vincent Koc
b073d7cc11 fix(gateway): bound pricing catalog streams
Bound gateway model pricing catalog reads through the shared streaming byte-limit helper so no-content-length LiteLLM/OpenRouter responses cannot be fully buffered past the 5 MiB cap before rejection. Adds a regression for streamed LiteLLM overflow while preserving OpenRouter fallback pricing.
2026-06-20 05:42:23 +08:00
Vincent Koc
d97574aae6 fix(dev): bound realtime SDP answer reads
Keep the OpenAI Realtime WebRTC smoke's SDP offer request in the browser fetch path while moving the browser-side SDP answer reader into a testable helper. Reject unsafe decimal Content-Length values before acquiring a body reader and preserve streamed byte limiting for responses without a safe declared length.

Proof: direct bounded-reader repro rejects unsafe content-length before getReader and cancels the body; node --check --experimental-strip-types scripts/dev/realtime-talk-live-smoke.ts; node --check --experimental-strip-types test/scripts/dev-tooling-safety.test.ts; git diff --check origin/main...HEAD; autoreview clean overall 0.84; exact-head release gate succeeded at https://github.com/openclaw/openclaw/actions/runs/27848673438.
2026-06-20 05:22:56 +08:00
Vincent Koc
a54a56fb98 refactor(theme): drop unused terminal detection 2026-06-20 05:20:35 +08:00
Vincent Koc
45971784c9 test(scripts): stabilize tsdown process group timeout 2026-06-19 23:05:48 +02:00
Alix-007
6a27300a5b fix(gateway): remove device-backed node pairings (#90373)
Merged via squash.

Prepared head SHA: 8bd0e964ec
Co-authored-by: Alix-007 <267018309+Alix-007@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-19 22:04:16 +01:00
Peter Steinberger
023993249f fix(queue): restart dormant followup drains (#95039)
Merged via squash.

Prepared head SHA: b6a81f07f1
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-19 22:03:48 +01:00
zerone0x
cd061a4c7b fix(agents): preserve delivered message send results (#84292)
Merged via squash.

Prepared head SHA: e5f948cf31
Co-authored-by: zerone0x <39543393+zerone0x@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-19 22:02:33 +01:00
Vincent Koc
b554c470a2 refactor(sessions): drop unused footer provider methods 2026-06-20 04:50:23 +08:00
brokemac79
8972bff98d [codex] docs: clarify PR body evidence updates (#95076) 2026-06-19 14:49:05 -06:00
Vincent Koc
6f5fdb1e6b fix(gateway): validate plugin descriptors and compact refresh 2026-06-19 22:25:15 +02:00
Vincent Koc
0f18e82932 fix(e2e): reject unsafe bounded response text lengths
Reject unsafe decimal Content-Length values in the E2E bounded response text helper before streaming response bodies. Keep non-decimal values on the streaming byte-limit path and add regression coverage proving unsafe declared lengths cancel without starting a read.

Proof: direct patched repro rejects before reading with code ETOOBIG; origin/main comparison entered the reader first; node --check scripts/e2e/lib/bounded-response-text.mjs; git diff --check origin/main...HEAD; autoreview clean overall 0.86; exact-head release gate succeeded at https://github.com/openclaw/openclaw/actions/runs/27846197115.
2026-06-20 04:20:02 +08:00
Vincent Koc
9594300f8c refactor(gateway): drop unused helper methods 2026-06-20 04:14:45 +08:00
Vincent Koc
c2c19a883d fix(scripts): reject unsafe bounded response lengths
Reject unsafe decimal Content-Length values in shared scripts bounded-response helpers before streaming response bodies.\n\nValidation:\n- node --check scripts/lib/bounded-response.mjs\n- direct MJS repro for unsafe Content-Length\n- git diff --check origin/main...HEAD\n- autoreview clean, overall patch correct 0.88\n- exact-head release gate https://github.com/openclaw/openclaw/actions/runs/27845767740
2026-06-20 04:04:40 +08:00
Hannes Rudolph
4a0f497f16 improve: simplify PR context and evidence (#94676)
* improve: simplify PR context and evidence

* improve: decouple PR context from proof labels

* fix: satisfy PR context lint
2026-06-19 14:00:38 -06:00
Vincent Koc
3706047d60 refactor(core): drop unused internal helpers 2026-06-20 03:58:55 +08:00
Alix-007
e35e5f123d feat(cli): add openclaw sessions compact and fail loudly on CLI /compact (fixes #90640) (#91378)
* feat(cli): add `sessions compact` command and fail loudly on CLI `/compact`

`sessions.compact` was reachable only as an internal Gateway RPC — no CLI
command, no docs — and `openclaw agent --message '/compact'` silently no-opped
with exit 0 because the slash-command handler rejects CLI-originated senders,
so the message fell through to an ordinary agent turn that compacted nothing.

- Add `openclaw sessions compact <key>` wrapping the existing `sessions.compact`
  RPC; exit non-zero on a transport error or an `ok:false` payload so automation
  never mistakes a silent no-op for success.
- Reject `openclaw agent --message '/compact'` with a redirect to the new
  command and exit 1 instead of a silent exit 0. The shared chat-side `/compact`
  handler is left untouched (no compatibility / message-delivery blast radius).
- Strictly validate `--max-lines` and `--timeout` (positive integers only).
- Document the command and the `sessions.compact` RPC in docs/cli/sessions.md.

Fixes #90640.

* fix(cli): inherit parent `sessions` options for `compact`

`openclaw sessions compact <key>` did not merge the parent `sessions`
command options the way its sibling subcommands (list/cleanup/info/…) do,
so a parent-level `--agent`/`--json` was silently dropped. In particular
`openclaw sessions --agent work compact <key>` compacted the default
agent's session instead of the work agent's — a wrong-target session-state
mutation.

Merge the parent options in the compact action (parent `--agent`/`--json`,
with the compact-level option taking precedence) and add regression
coverage for parent `--agent`, parent `--json`, and the compact-level
override.

Refs #90640.

* fix(cli): report pending Codex compaction and reject unsupported parent options

Address two ClawSweeper review findings on the `sessions compact` command:

- `sessions-compact.ts`: the Codex app-server `thread/compact/start` path
  returns `ok:true / compacted:false` with a pending marker, meaning the
  compaction was *started* asynchronously. The formatter collapsed every
  non-compacted success into "No compaction needed", so Codex users were told
  nothing happened. Report it as a started/pending compaction instead.
- `register.status-health-sessions.ts`: the parent `sessions` command defines
  list-only options (`--store`/`--all-agents`/`--active`/`--limit`) that the
  compact action previously ignored. Silently dropping a parent `--store` is
  dangerous — the gateway resolves the target store itself, so a user could
  believe they targeted one store while another is mutated. Reject any
  unsupported inherited parent option with a clear error and a non-zero exit.

Add regression tests for the pending-compaction message and the rejected
parent options.

Refs #90640.

* fix(gateway): guard sessions.compact maxLines truncation against active runs

The non-maxLines (LLM) compact branch interrupts an active session run before
compacting, but the maxLines truncate branch read the tail, archived, and
overwrote the transcript in place without that guard. Exposing `--max-lines`
as a documented CLI command (this PR) would make the active-run data-loss mode
tracked by #72765 easy to trigger from ordinary CLI usage.

Run the same interruptSessionRunIfActive guard in the maxLines branch before
reading the tail and truncating, matching the LLM compact path. Add gateway
regression coverage over a real in-process Gateway: with no active run, the
maxLines branch truncates the on-disk transcript 500 -> 50 and preserves the
original 500 lines in the .bak archive; with an active embedded run, the
maxLines branch fires the same interrupt (abort + wait-for-end) before
archiving and truncating.

* docs(cli): move sessions compact section above related links

The new "Compact a session" section was inserted between the cleanup
section's inline "Related:" list and the page's final "## Related"
block, splitting related-link content around the command docs. Move the
compact section above the related-links area and merge the orphaned
"Session config" link into the single final "## Related" block.

* fix(gateway): avoid no-op compact aborts

Signed-off-by: sallyom <somalley@redhat.com>

* fix(gateway): satisfy compact preflight lint

Signed-off-by: sallyom <somalley@redhat.com>

* fix(sessions): preserve compacted transcript structure

---------

Signed-off-by: sallyom <somalley@redhat.com>
Co-authored-by: sallyom <somalley@redhat.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-06-19 15:47:43 -04:00
Vincent Koc
b5811ea2b3 fix(ci): retry stable closeout package lookup 2026-06-19 21:42:41 +02:00
Vincent Koc
bb1043b14c fix(scripts): reject unsafe package download lengths
Reject unsafe decimal package_url Content-Length values before streaming response bodies.\n\nValidation:\n- node --check scripts/resolve-openclaw-package-candidate.mjs\n- direct injected downloadUrl repro for unsafe Content-Length\n- git diff --check origin/main...HEAD\n- autoreview clean, overall patch correct 0.9\n- exact-head release gate https://github.com/openclaw/openclaw/actions/runs/27844538401
2026-06-20 03:36:12 +08:00
Alix-007
16fba65cb6 fix(cron): honor configured retry.backoffMs for recurring error backoff floor (#93051)
Merged via squash.

Prepared head SHA: c8026d0aef
Co-authored-by: Alix-007 <267018309+Alix-007@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-19 20:35:42 +01:00
Gio Della-Libera
7e5901752d refactor(policy): split doctor modules (#94314)
Merged via squash.

Prepared head SHA: 0d876ce3c1
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-06-19 12:34:41 -07:00
Alix-007
806a37fca8 fix(cli): reject present-but-invalid --timeout on status/health fast path (#92996)
Merged via squash.

Prepared head SHA: eda96f9f80
Co-authored-by: Alix-007 <267018309+Alix-007@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-19 20:33:24 +01:00
Vincent Koc
753ff96771 refactor(workboard): drop unused parent-link helper 2026-06-20 03:31:26 +08:00
Alix-007
3fa4fdaec1 docs: fix two broken cross-reference anchors (#93941)
Merged via squash.

Prepared head SHA: 32c61da44d
Co-authored-by: Alix-007 <267018309+Alix-007@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-19 20:27:25 +01:00
Vincent Koc
efc36d71bd refactor(qa-lab): drop unused report type aliases 2026-06-20 03:16:55 +08:00
Vincent Koc
6cfb025143 fix(e2e): reject unsafe chat tools body lengths
Reject unsafe numeric Content-Length values in the OpenAI chat tools E2E client before waiting on the response stream.

Also hardens Docker E2E heartbeat timing coverage after the exact-head release gate exposed a brittle zero-padded heartbeat assertion.

Verification: direct mock gateway repro, docker heartbeat shell proof, autoreview clean, and exact-head CI release gate https://github.com/openclaw/openclaw/actions/runs/27843455246.
2026-06-20 03:09:51 +08:00
Vincent Koc
061a3705db test(plugin-sdk): isolate runtime facade tests 2026-06-19 20:55:49 +02:00
Vincent Koc
9e5ac0cea4 refactor(extensions): drop stale internal declarations 2026-06-20 02:52:05 +08:00
Vincent Koc
aff6e221a7 fix(lmstudio): bound model load error bodies 2026-06-19 20:43:17 +02:00
Vincent Koc
5df5aa1640 fix(openai): bound batch error bodies 2026-06-19 20:43:17 +02:00
Vincent Koc
59a93a817f fix(openai): bound device code auth bodies 2026-06-19 20:43:17 +02:00
Vincent Koc
23b8f5d037 refactor(discord): remove unused monitor hooks 2026-06-20 02:37:17 +08:00
Vincent Koc
17e2fbfa86 fix(test): harden script probe bounds (#95060)
Merged via squash.

Prepared head SHA: 3a51c3c2d7
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-20 02:31:40 +08:00
Vincent Koc
cbff4fa5bc refactor(extensions): drop unused internal type aliases 2026-06-20 02:22:31 +08:00
Vincent Koc
330545f3e9 refactor(voice-call): drop unused stream helpers 2026-06-20 02:07:08 +08:00
Vincent Koc
2b0a72bb48 fix(release): lazy-load sigstore verification 2026-06-19 20:02:21 +02:00
Lu Wang
583829a342 fix(ssh): scope tunnel port preflight to loopback (#94603) (#94607)
Merged via squash.

Prepared head SHA: 6798b718de
Co-authored-by: wangwllu <7668944+wangwllu@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-19 18:59:58 +01:00
Vincent Koc
7b94ae9944 refactor(discord): drop unused internal wrapper methods 2026-06-20 01:52:02 +08:00
Vincent Koc
1609365b3e test(state): canonicalize sqlite volume assertions 2026-06-19 19:45:40 +02:00
Josh Lehman
d216f7c876 refactor: use canonical transcript reader identity (#89581)
* refactor: use canonical transcript reader identity

* refactor: keep transcript reader dependency storage-neutral
2026-06-19 10:40:18 -07:00
Vincent Koc
d41a3d28a0 refactor(oc-path): drop unused repack helper 2026-06-20 01:32:16 +08:00
Vincent Koc
8aa58c5fb0 fix(minimax): bound oauth token bodies 2026-06-19 19:18:38 +02:00
Vincent Koc
e7e85f5436 fix(minimax): bound oauth error bodies 2026-06-19 19:18:38 +02:00
Vincent Koc
458904037f fix(parallel): bound search error bodies 2026-06-19 19:18:38 +02:00
Vincent Koc
1e53ee4fd5 fix(exa): bound search error bodies 2026-06-19 19:18:38 +02:00
Vincent Koc
6037d1a85c fix(ollama): bound stream error bodies 2026-06-19 19:18:38 +02:00
Vincent Koc
2c8d19d73e fix(ollama): bound embedding error bodies 2026-06-19 19:18:38 +02:00
Vincent Koc
70a48a680d fix(sdk): refresh plugin api baseline hash 2026-06-19 19:18:38 +02:00
Vincent Koc
0c210e5e52 fix(discord): deliver reasoning replies (#95029) 2026-06-20 01:18:14 +08:00
Vincent Koc
38807ffba4 test(plugins): isolate public surface runtime env 2026-06-19 19:08:32 +02:00
Vincent Koc
fb06df6cad refactor(voice-call): drop unused config type aliases 2026-06-20 01:07:03 +08:00
Vincent Koc
50614c51a8 test(ui): isolate chat browser layout fixtures 2026-06-19 18:54:19 +02:00
Vincent Koc
1f244f60ed test(secrets): load external plugin secret coverage 2026-06-19 18:35:29 +02:00
Vincent Koc
10b8b32380 refactor(codex): drop unused app-server helpers 2026-06-20 00:34:03 +08:00
Shakker
3b65f1d279 test: isolate sandbox registry state env 2026-06-19 17:32:09 +01:00
Yzx
1c711048f9 fix(agents): route plugin approvals through transport channel (#90918) 2026-06-19 12:31:06 -04:00
Vincent Koc
f69f81af9e fix(cli): use gateway skills status when available 2026-06-19 18:28:39 +02:00
Shakker
cdf4268540 fix: scope workspace default env 2026-06-19 17:24:03 +01:00
Vincent Koc
b4651f3781 refactor(codex): drop unused memory tool wrapper 2026-06-20 00:16:50 +08:00
Shakker
107c49e936 test: scope models config auth env 2026-06-19 17:10:24 +01:00
Shakker
ffd8c6e5d9 fix: scope model auth env helpers 2026-06-19 17:07:53 +01:00
Vincent Koc
9fced92710 test(wizard): align secret ref provider alias 2026-06-19 18:07:06 +02:00
Vincent Koc
3bcdf20a44 test(secrets): align secret ref fixtures 2026-06-19 18:07:06 +02:00
Shakker
80010a864b test: route subagent registry state env 2026-06-19 16:57:15 +01:00
Shakker
a536a0ddbc fix: isolate cli attempt home env 2026-06-19 16:54:05 +01:00
Vincent Koc
925d98d8e4 refactor(codex): drop unused prompt overlay wrapper 2026-06-19 23:51:44 +08:00
Vincent Koc
a42a1af942 fix(openrouter): bound oauth error bodies 2026-06-19 17:43:29 +02:00
Vincent Koc
b470b1e21a fix(mistral): sanitize realtime API key input 2026-06-19 17:37:09 +02:00
Vincent Koc
6fc0303ec0 fix(chutes): bound oauth token error bodies 2026-06-19 17:29:36 +02:00
Vincent Koc
6ef4684b89 fix(scripts): skip generated dist in legacy store guard 2026-06-19 17:22:14 +02:00
Vincent Koc
2005812dff fix(secrets): validate refs consistently at runtime 2026-06-19 17:22:14 +02:00
Vincent Koc
bf872b30cd test: remove unused mock alias exports 2026-06-19 23:19:46 +08:00
Vincent Koc
37962aac95 test(qqbot): keep stt temp helper on sdk surface 2026-06-19 17:03:16 +02:00
Vincent Koc
a876f8d073 fix(qqbot): bound chunked upload error bodies 2026-06-19 17:03:16 +02:00
Vincent Koc
0a3e0d081d test: remove no-op mock registrars 2026-06-19 22:55:38 +08:00
Vincent Koc
2c3b582c04 fix(scripts): avoid pnpm in parallels smoke wrappers 2026-06-19 16:47:03 +02:00
Vincent Koc
e0d58d994d fix(qqbot): bound stt error bodies 2026-06-19 16:44:51 +02:00
Vincent Koc
dc16aedd2e test(launcher): isolate bundled plugin env in fixtures 2026-06-19 16:42:44 +02:00
Vincent Koc
b16fd6bee7 test(qqbot): fix channel api bounded body assertion 2026-06-19 16:35:55 +02:00
Vincent Koc
51ebe87a09 fix(qqbot): guard channel api fetches 2026-06-19 16:30:53 +02:00
Vincent Koc
78b5618071 test(ui): isolate browser ownership in e2e fixtures 2026-06-19 16:24:38 +02:00
Vincent Koc
ed8ab712dc fix(qqbot): guard api client fetches 2026-06-19 16:14:19 +02:00
Vincent Koc
8594af21e9 fix(qqbot): bound token response bodies 2026-06-19 16:14:19 +02:00
Vincent Koc
2ddebf3897 refactor(config): drop duplicate account schema aliases 2026-06-19 22:12:44 +08:00
Vincent Koc
b9dadb9f66 test(ui): isolate sessions browser layout fixtures 2026-06-19 16:08:54 +02:00
Vincent Koc
f062171c54 test(ui): isolate mobile form control browser fixtures 2026-06-19 16:03:16 +02:00
pick-cat
b677ea6726 fix(agent): resolve compaction model alias to canonical model ref (#90885)
Merged via squash.

Prepared head SHA: 72d28dc385
Co-authored-by: Pick-cat <266665499+Pick-cat@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-19 15:03:06 +01:00
Vincent Koc
e74a7d2f14 fix(sdk): refresh api baseline hash 2026-06-19 15:57:57 +02:00
Vincent Koc
917a0f3052 refactor(extensions): drop private alias exports 2026-06-19 21:43:24 +08:00
Vincent Koc
b3dfa0f1b1 refactor(shared): drop unused internal format/import aliases 2026-06-19 21:36:52 +08:00
Vincent Koc
772158c716 fix(qqbot): bound api error bodies 2026-06-19 15:30:29 +02:00
Vincent Koc
940d33cf89 fix(scripts): clean package download temp files after stream abort 2026-06-19 15:22:55 +02:00
Vincent Koc
698efb23a6 fix(discord): bound api error bodies 2026-06-19 15:21:07 +02:00
Vincent Koc
d29c3a5d6f refactor(cron): drop duplicate active-job reset alias 2026-06-19 21:15:22 +08:00
Peter Steinberger
341ae21d03 feat(slack): handle global and message shortcuts (#94881)
Merged via squash.

Prepared head SHA: 32dea12d7a
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-19 14:12:30 +01:00
Andrew Stroup
378c4134f1 fix(slack): default member-info userId to inbound sender (#89236)
Merged via squash.

Prepared head SHA: c7a39e54f7
Co-authored-by: stroupaloop <2424551+stroupaloop@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-19 14:03:29 +01:00
Vincent Koc
cd2d837a1f fix(slack): preserve buffered thread stream replies (#78536)
Merged via squash.

Prepared head SHA: 0d8d75918d
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-19 14:02:50 +01:00
Vincent Koc
29e44f5eba refactor(tasks): drop duplicate maintenance stop alias 2026-06-19 20:56:56 +08:00
Vincent Koc
ce7f899165 fix(discord): bound voice upload error bodies 2026-06-19 14:50:29 +02:00
Vincent Koc
4c3b15bae6 fix(discord): bound webhook error bodies 2026-06-19 14:43:17 +02:00
Kendrick Ha
4723602e7e feat(channels): add Zalo ClawBot external channel entry and documenta… (#89586)
Merged via squash.

Prepared head SHA: 5ef4fe999a
Co-authored-by: ken-kuro <47441476+ken-kuro@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-19 13:42:38 +01:00
Peter Lee
430682e97a fix(xai): reject unsupported multi-agent model refs before runtime fallback (#93969)
Merged via squash.

Prepared head SHA: b58d798381
Co-authored-by: xialonglee <22994703+xialonglee@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-19 13:42:00 +01:00
Peter Lee
5c8761976c fix(whatsapp): restart listener on selfChatMode config change (#93873)
Merged via squash.

Prepared head SHA: d85f604f01
Co-authored-by: xialonglee <22994703+xialonglee@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-19 13:41:26 +01:00
Vincent Koc
7fafad8c49 refactor(plugins): drop duplicate memory reset alias 2026-06-19 20:38:03 +08:00
NIO
47545e04c4 fix(channels): stop duplicating inbound previews in system events (#94589)
Merged via squash.

Prepared head SHA: 981003591c
Co-authored-by: hugenshen <16300669+hugenshen@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-19 13:37:28 +01:00
Vincent Koc
aea208f0ac fix(discord): bound pluralkit error bodies 2026-06-19 14:29:11 +02:00
Vincent Koc
4d37f42df7 fix(github-copilot): bound embedding error bodies 2026-06-19 14:21:28 +02:00
Vincent Koc
56c5630107 refactor(agents): drop duplicate gateway allowlist test export 2026-06-19 20:19:11 +08:00
joshavant
99e69e16b7 remove ios identity migration 2026-06-19 14:16:48 +02:00
joshavant
f13dc76ba1 fix ios share extension device identity 2026-06-19 14:16:48 +02:00
Vincent Koc
0de3d47195 fix(google-meet): bound google api error bodies 2026-06-19 14:06:36 +02:00
Vincent Koc
f7c3775140 fix(test): prefer local bundled plugins in linked Vitest worktrees 2026-06-19 14:00:32 +02:00
Peter Steinberger
e2b52f29e4 test(plugins): separate activation-scoped web search ids
Exclude startup-lazy Codex and QA Lab entries from the loader-scoped baseline before asserting them as explicit activation-scoped contracts.
2026-06-19 07:58:44 -04:00
Vincent Koc
482d6d59ac refactor(plugin-state): drop duplicate close alias 2026-06-19 19:57:26 +08:00
Vincent Koc
ff35b29a06 fix(mattermost): stream guarded api responses 2026-06-19 13:51:37 +02:00
Peter Steinberger
5a00720de0 fix(ci): repair signing lint and test types
Use the canonical undefined comparison and preserve the gateway predicate mock signature so full release-gate lint and test-type checks pass.
2026-06-19 07:42:51 -04:00
Vincent Koc
817dd593bb test(commands): type gateway transport mock input 2026-06-19 13:34:04 +02:00
Vincent Koc
c218255815 test(plugins): pin activation-scoped web search contracts 2026-06-19 13:34:04 +02:00
Vincent Koc
3bc936b675 test(sdk): keep package e2e pnpm noninteractive 2026-06-19 13:34:04 +02:00
Vincent Koc
4799fe7df6 fix(msteams): stream graph success responses 2026-06-19 13:25:18 +02:00
Vincent Koc
f29af26326 fix(sms): bound twilio api response bodies 2026-06-19 13:24:16 +02:00
Vincent Koc
b0c1010fbf refactor(cron): drop duplicate isolated-agent test aliases 2026-06-19 19:19:08 +08:00
Vincent Koc
f14a2cb9c5 fix(clickclack): bound api error response bodies 2026-06-19 13:16:08 +02:00
wangmiao0668000666
27f702d68f fix(gateway): authorize plugin methods from attached registry (#94343)
Authorize plugin gateway methods against the exact registry attached to dispatch, preserving fallback behavior for dynamic methods and deleting one-off repro scripts.

Fixes #92044.

Co-authored-by: wangmiao0668000666 <wang.miao86@xydigit.com>
2026-06-19 11:56:24 +01:00
Super Zheng
0781dae620 fix(plugins): keep tool discovery request-local (#93276)
Keep plugin tool discovery request-local, preserve active provider/channel registries, and carry the prepared registry through MCP and catalog resolution.

Co-authored-by: 郑苏波 (Super Zheng) <superzheng@tencent.com>
2026-06-19 11:56:20 +01:00
Peter Lee
6256ad86c9 fix(gateway): classify probe reachability by validated transport (#93948)
Distinguish validated gateway reachability from pre-open and TLS-validation failures, and sanitize close diagnostics before terminal output.

Fixes #79099.

Co-authored-by: xialonglee <li.xialong@xydigit.com>
2026-06-19 11:56:16 +01:00
joshavant
f7f415f26b fix(ios): wire share extension app group signing 2026-06-19 12:53:45 +02:00
ZengWen-DT
2983edd5a2 docs(browser): clarify networkidle session support (#94020)
Clarify that `networkidle` is supported for managed and raw-CDP browser sessions but rejected for existing-session mode.

Fixes #80587.

Co-authored-by: ZengWen-DT <ceng.wen@xydigit.com>
2026-06-19 11:53:07 +01:00
Alix-007
4da36da605 feat(status): show session duration in footer (#88988)
Show elapsed session duration in the status footer using the canonical session lifecycle timestamps and compact formatter.

Fixes #68226.

Co-authored-by: Alix-007 <li.long15@xydigit.com>
2026-06-19 11:53:04 +01:00
Vincent Koc
92d1f04de3 refactor(agents): drop duplicate internal aliases 2026-06-19 18:33:56 +08:00
Vincent Koc
611ad1a097 fix(voice-call): bound provider api response bodies 2026-06-19 12:33:39 +02:00
Vincent Koc
6ef4970988 refactor(agents): drop unused harness registry wrappers 2026-06-19 18:22:26 +08:00
Peter Steinberger
8d9eba3f4f fix(ios): complete single-target watch migration
Use the watchOS application API for text input, remove simulator-only Debug architecture restrictions, and document the standard Watch bundle location. Refs #92477.

Co-authored-by: Sash Zats <sash@zats.io>
2026-06-19 06:18:43 -04:00
Vincent Koc
40dc8fd147 fix(plugins): cancel marketplace archive error bodies 2026-06-19 12:17:45 +02:00
Vincent Koc
2257a21b7e refactor(tests): drop duplicate helper aliases 2026-06-19 18:12:54 +08:00
David
d4833e27c7 fix(cron): refuse keyless implicit isolated cron delivery inherited from shared agent-main bucket (#91685)
Summary:
- The PR changes isolated cron delivery resolution to reject keyless implicit delivery inherited from the shar ...  targets into delivery context resolution, and cleans up direct cron sessions on unresolved delivery exits.
- PR surface: Source +57, Tests +496. Total +553 across 8 files.
- Reproducibility: yes. from source inspection: current resolver can inherit the shared agent-main last target ... ls or sends based on that resolved target; I did not run live Matrix reproduction in this read-only review.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(cron): clean up deleteAfterRun session when keyless cron delivery…
- PR branch already contained follow-up commit before automerge: Merge remote-tracking branch 'upstream/main' into fix/91613-isolated-…
- PR branch already contained follow-up commit before automerge: Merge upstream main into fix/91613-isolated-cron-delivery-identity
- PR branch already contained follow-up commit before automerge: chore: retrigger PR CI after upstream base fix

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

Prepared head SHA: f129375dd7
Review: https://github.com/openclaw/openclaw/pull/91685#issuecomment-4659309145

Co-authored-by: nxmxbbd <32288+nxmxbbd@users.noreply.github.com>
2026-06-19 10:05:07 +00:00
clawsweeper[bot]
d1bb2d5a12 fix(telegram): normalize all HTML tables before entity-escaping in rich messages (#94856)
Summary:
- The PR changes Telegram legacy HTML rendering so raw HTML table tags are converted to `<pre><code>` pipe-tab ... ks before unsupported-tag escaping, while preserving pre/code literals and rich-message table sanitization.
- PR surface: Source +38, Tests +31. Total +69 across 2 files.
- Reproducibility: yes. Source inspection shows current main's legacy HTML renderer sends raw tables directly  ... the linked issue describes that same escaped output; I did not run tests because this review was read-only.

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

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

Prepared head SHA: 5944f8e4d2
Review: https://github.com/openclaw/openclaw/pull/94856#issuecomment-4749452707

Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: zhangqueping <3436352+zhangqueping@users.noreply.github.com>
2026-06-19 10:04:22 +00:00
Vincent Koc
eb7da0a2e5 fix(plugins): cancel self-hosted probe error bodies 2026-06-19 12:03:31 +02:00
Vincent Koc
797865c9dc fix(cli): cancel camera URL error bodies 2026-06-19 11:57:43 +02:00
Vincent Koc
7fcbfa6971 refactor(plugins): drop unused web-channel send wrapper 2026-06-19 17:52:10 +08:00
Vincent Koc
3091c13713 refactor(acpx): drop unused codex trust wrapper 2026-06-19 17:49:50 +08:00
Vincent Koc
c159063c70 fix(plugins): bound embedding error bodies 2026-06-19 11:43:18 +02:00
Vincent Koc
dae37a4579 refactor(plugins): drop unused web-channel facade wrappers 2026-06-19 17:41:24 +08:00
clawsweeper[bot]
2e0dfda462 test(perf): compare saved CLI startup benchmarks (#94812)
Summary:
- Adds saved CLI startup benchmark report comparison flags to `scripts/bench-cli-startup.ts`, plus JSON output coverage and changed-target routing expectations for the new test-helper importer.
- PR surface: Tests +77, Other +109. Total +186 across 4 files.
- Reproducibility: not applicable. as a feature/tooling PR. The prior PR defects were source-proven in review comments and the current head addresses them; I did not run local tests because this review was read-only.

Automerge notes:
- Ran the ClawSweeper repair loop before final review.
- Included post-review commit in the final squash: test(perf): compare saved CLI startup benchmarks

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

Prepared head SHA: 1afa110f1b
Review: https://github.com/openclaw/openclaw/pull/94812#issuecomment-4748785428

Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: Felix Isaac Lim <38658663+FelixIsaac@users.noreply.github.com>
2026-06-19 09:37:47 +00:00
Vincent Koc
5b3d652c05 fix(sdk): cancel Copilot token error bodies 2026-06-19 11:34:54 +02:00
Sash Zats
b39a932112 fix: migrate watch app to single-target app (Xcode 27+ compat) (#92477)
* fix: migrate watch app to single-target app

* fix: build watch screenshots generically

* docs(ios): clarify watch embed invariant

* docs(ios): clarify watch embed invariant

---------

Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-06-19 11:34:34 +02:00
scotthuang
0c76a98f10 fix(outbound): keep direct-only targets out of group sessions (#94683)
Merged via squash.

Prepared head SHA: d2cb01b5ba
Co-authored-by: scotthuang <1670837+scotthuang@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-06-19 11:31:25 +02:00
Ayaan Zaidi
a8b5f5d551 fix(telegram): send progress previews as html text 2026-06-19 14:56:35 +05:30
Vincent Koc
bbe9669926 fix(agents): cancel tool download error bodies 2026-06-19 11:19:57 +02:00
Vincent Koc
7580c80f37 refactor(channels): drop unused test helpers 2026-06-19 17:17:14 +08:00
Vincent Koc
7f38b1a910 fix(sdk): cancel live catalog error bodies 2026-06-19 11:11:59 +02:00
Vincent Koc
8aaf937bc0 fix(auth): cancel WHAM probe error bodies 2026-06-19 11:02:59 +02:00
Vincent Koc
6467c1962a fix(chutes): cancel userinfo error bodies 2026-06-19 10:57:41 +02:00
Vincent Koc
0c565f3b0e fix(usage): cancel provider error bodies 2026-06-19 10:51:58 +02:00
Vincent Koc
7211d77553 refactor(channels): drop unused approval aliases 2026-06-19 16:41:19 +08:00
Vincent Koc
dba291ed35 fix(agents): cancel OpenRouter catalog error bodies 2026-06-19 10:38:00 +02:00
Vincent Koc
32c02e843a refactor(browser): drop unused cdp helpers 2026-06-19 16:36:49 +08:00
Vincent Koc
5e329f4065 fix(channels): preserve command progress detail (#94868)
Merged via squash.

Prepared head SHA: 3217f45e61
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
2026-06-19 16:36:36 +08:00
Vincent Koc
e6743eb783 fix(agents): cancel prompt cache error bodies 2026-06-19 10:30:04 +02:00
Vincent Koc
dbd5689ea1 fix(agents): cancel model scan error bodies 2026-06-19 10:19:23 +02:00
Vincent Koc
44b0644e88 fix(slack): cancel followed redirect bodies 2026-06-19 10:12:35 +02:00
Vincent Koc
6aa85dfaa1 refactor(memory): drop unused host-sdk helpers 2026-06-19 16:04:00 +08:00
Vincent Koc
86b24ac2b2 fix(gateway): cancel pricing fetch bodies 2026-06-19 10:03:13 +02:00
Vincent Koc
d236612cc0 fix(sdk): refresh plugin API baseline hash 2026-06-19 09:59:02 +02:00
Vincent Koc
c3390f0bc6 fix(qa): keep whatsapp lease exhaustion visible 2026-06-19 09:58:03 +02:00
Vincent Koc
a6ac8de523 fix(openai): cancel OAuth preflight bodies 2026-06-19 09:53:35 +02:00
snowzlmbot
ca527aad9d fix(reply): clarify provider internal error copy (#94737)
Summary:
- The PR adds provider-internal/server_error classification in reply failure handling and regression tests for classifier output plus pre-reply external-channel copy.
- PR surface: Source +21, Tests +58. Total +79 across 3 files.
- Reproducibility: yes. source-reproducible. Current main sanitizes generic provider internal errors to a stab ... and conversation-state branches, so pre-reply chat failures can fall through to generic session-reset copy.

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

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

Prepared head SHA: 8265fc71f3
Review: https://github.com/openclaw/openclaw/pull/94737#issuecomment-4747506983

Co-authored-by: snowzlm <snowzlm@noreply.codeberg.org>
Approved-by: vincentkoc
2026-06-19 07:52:51 +00:00
ZOOWH
3a435eebc0 fix(telegram): classify sendChatAction 401 by structured error_code, not bare substring match (#94810)
Summary:
- The PR changes Telegram sendChatAction 401 detection to trust structured Telegram `error_code` values before an unauthorized-text fallback and adds regression tests for false 401 suspension cases.
- PR surface: Source +14, Tests +90. Total +104 across 2 files.
- Reproducibility: yes. Source inspection shows current main and the latest release classify any rendered erro ...  before transient handling, matching the linked issue's structured 429 `retry_after=401` reproduction path.

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

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

Prepared head SHA: 0ffee85d17
Review: https://github.com/openclaw/openclaw/pull/94810#issuecomment-4748778567

Co-authored-by: 徐闻涵0668001344 <xu.wenhan1@xydigit.com>
Approved-by: vincentkoc
2026-06-19 07:51:40 +00:00
Vincent Koc
dfc5bd5fcc fix(crestodian): cancel gateway probe bodies 2026-06-19 09:32:50 +02:00
Vincent Koc
7cc66b5175 refactor(channels): drop unused bootstrap exports 2026-06-19 15:32:30 +08:00
Vincent Koc
fcec95ffd7 fix(signal): cancel status-only response bodies 2026-06-19 09:26:03 +02:00
Vincent Koc
e67f8ba459 fix(discord): cancel failed probe response bodies 2026-06-19 09:20:12 +02:00
Vincent Koc
33fa225f65 refactor(memory): drop unused host helpers 2026-06-19 15:13:27 +08:00
Vincent Koc
86a28636fa fix(update): cancel npm registry error bodies 2026-06-19 09:11:43 +02:00
Vincent Koc
90ba9fc864 fix(copilot): cancel model policy response bodies 2026-06-19 09:06:13 +02:00
Vincent Koc
f5419b5bb0 fix(openrouter): release music stream readers 2026-06-19 09:04:11 +02:00
Vincent Koc
14fd10f8f8 fix(qa): wait longer for live credential leases 2026-06-19 08:59:48 +02:00
Vincent Koc
38fefc5aaf refactor(runtime): drop unused exported helpers 2026-06-19 14:57:32 +08:00
Vincent Koc
ccdec2e294 test(tasks): pass temp root into registry helper 2026-06-19 08:55:56 +02:00
Vincent Koc
c79a5aa253 fix(onboard): cancel custom verification bodies 2026-06-19 08:55:56 +02:00
Vincent Koc
0dbac0d5f9 fix(tools): release bounded web response readers 2026-06-19 08:50:25 +02:00
Vincent Koc
b972feb3f7 fix(qa): honor telegram live ready timeout 2026-06-19 08:47:16 +02:00
Vincent Koc
3c01716c82 fix(agents): release proxy stream body readers 2026-06-19 08:43:51 +02:00
Vincent Koc
e802fb8a9f fix(agents): release provider error body readers 2026-06-19 08:36:39 +02:00
SpecialLeon
94b710ac00 test(cron): expand parseAbsoluteTimeMs test coverage to 39 cases (#91656)
Summary:
- The PR expands `src/cron/parse.test.ts` with grouped `parseAbsoluteTimeMs` coverage for epoch, ISO timezone/offset, precision, whitespace, invalid-format, and cron example cases.
- PR surface: Tests +233. Total +233 across 1 file.
- Reproducibility: not applicable. this is a test coverage PR, not a runtime bug report with user steps. Source inspection confirms the requested parser coverage is still added only by this open PR path.

Automerge notes:
- Ran the ClawSweeper repair loop before final review.
- Included post-review commit in the final squash: test(cron): expand parseAbsoluteTimeMs test coverage to 39 cases

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

Prepared head SHA: 69a49d9512
Review: https://github.com/openclaw/openclaw/pull/91656#issuecomment-4657254372

Co-authored-by: 刘江0668001123 <liu.jiang2@xydigit.com>
Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
2026-06-19 06:36:31 +00:00
Vincent Koc
ebee101d30 fix(qa): cancel Docker health probe bodies 2026-06-19 08:33:32 +02:00
JC
5697ab810e fix(tasks): deliver ACP completions to bound Discord threads (#89279)
Summary:
- The branch adds a bounded task-registry predicate and tests so successful delegated ACP parent-review comple ... with a Discord channel target and threadId send the parent-review terminal message directly to that thread.
- PR surface: Source +24, Tests +142. Total +166 across 2 files.
- Reproducibility: yes. at source level. Current main queues successful ACP parent-review completions through  ... annel/group owner keys, and the linked canonical issue includes matching Discord thread-bound ACP evidence.

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

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

Prepared head SHA: 04ad66b23d
Review: https://github.com/openclaw/openclaw/pull/89279#issuecomment-4597994374

Co-authored-by: anyech <anyech@gmail.com>
2026-06-19 06:31:42 +00:00
David
cd98f195a7 fix(gateway): ignore stale abort markers for fresh chat events (#91013)
Summary:
- The branch stamps Gateway chat run registrations and abort markers with ordering metadata, uses freshness checks for chat projection suppression, and updates abort/restart/maintenance tests and related types.
- PR surface: Source +79, Tests +103. Total +182 across 13 files.
- Reproducibility: yes. source-level: on current main, seed abortedRuns for a client run id, register a same-k ...  end; the presence-only checks suppress both projections. I did not execute tests in this read-only review.

Automerge notes:
- PR branch already contained follow-up commit before automerge: ci: re-trigger checks against current main
- PR branch already contained follow-up commit before automerge: Merge upstream/main into stale-abort marker fix
- PR branch already contained follow-up commit before automerge: Merge remote-tracking branch 'upstream/main' into nex/91013-conflict-…

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

Prepared head SHA: 6f13d6f7c2
Review: https://github.com/openclaw/openclaw/pull/91013#issuecomment-4640475472

Co-authored-by: nxmxbbd <32288+nxmxbbd@users.noreply.github.com>
2026-06-19 06:30:47 +00:00
Vincent Koc
be7d86ed80 fix(memory): abort remote response body reads 2026-06-19 08:25:53 +02:00
Vincent Koc
5d6ac23086 fix(agents): cancel local service probe bodies 2026-06-19 08:24:54 +02:00
Vincent Koc
f61ad70d3f fix(qa): surface live transport failure phases 2026-06-19 08:19:13 +02:00
Vincent Koc
05e70bd331 refactor(discord): drop unused setup account lister 2026-06-19 14:18:53 +08:00
Vincent Koc
8480ef3f86 fix(e2e): cancel readiness probe bodies 2026-06-19 08:18:01 +02:00
Vincent Koc
fc1bdecf08 fix(e2e): cancel ClickClack fixture bodies 2026-06-19 08:14:39 +02:00
Vincent Koc
a57e761f6b fix(e2e): cancel Open WebUI HTTP probe bodies 2026-06-19 08:11:11 +02:00
Vincent Koc
13be16d699 fix(openai): cancel oversized Codex image streams 2026-06-19 08:07:17 +02:00
Vincent Koc
257b533e85 fix(release): cancel Discord cleanup bodies 2026-06-19 08:07:00 +02:00
Vincent Koc
5939ab4c49 refactor(canvas): dedupe bundle hash inputs 2026-06-19 14:06:51 +08:00
Jesse Merhi
5db2f6c1fc Add stdout diagnostics OTEL log exporter
Adds stdout and both-mode diagnostics OTEL log export, with focused QA Lab smoke coverage and docs/config updates.

Prepared head SHA: efa2ef07ab
Verification: CI 27808480969 passed for the prepared head.
Reviewed-by: @jesse-merhi
2026-06-19 16:06:37 +10:00
Vincent Koc
afd9cb0c10 fix(github): cancel maintainer membership bodies 2026-06-19 08:02:41 +02:00
Vincent Koc
2e1e4167a9 fix(release): satisfy ClawHub retry lint 2026-06-19 13:57:56 +08:00
Vincent Koc
3bacf96ccc fix(codex): classify streamed subagent messages 2026-06-19 13:57:56 +08:00
Vincent Koc
433d8cbb2c fix(release): drain rate-limited ClawHub responses 2026-06-19 13:57:56 +08:00
Vincent Koc
37b2770071 fix(release): retry ClawHub release planning 2026-06-19 13:57:56 +08:00
Vincent Koc
e172f64f3f fix(codex): complete native subagent turns 2026-06-19 13:57:56 +08:00
Vincent Koc
8e66d7aad3 fix(release): cancel beta verifier status bodies 2026-06-19 07:57:19 +02:00
Vincent Koc
688ecb1655 fix(release): wrap bare Windows npm execpath 2026-06-19 07:55:33 +02:00
Vincent Koc
96dbd1c723 refactor(copilot): drop unused exported helpers 2026-06-19 13:55:08 +08:00
Glenn-Agent
2f12755498 fix: suggest close CLI commands (#91345)
Summary:
- The PR adds descriptor-backed CLI command suggestions for unknown root commands, wires them into Commander parse errors and early unowned-root diagnostics, and covers both paths with focused CLI tests.
- PR surface: Source +104, Tests +71. Total +175 across 5 files.
- Reproducibility: yes. for the behavior gap: current main's formatter and early unowned-root path emit generic diagnostics without closest-command hints, and the PR proof shows the after-fix CLI output.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix: suppress suggestions for plugin policy diagnostics
- PR branch already contained follow-up commit before automerge: Merge remote-tracking branch 'origin/main' into fix/83999-cli-command…
- PR branch already contained follow-up commit before automerge: test: align agent model expectations
- PR branch already contained follow-up commit before automerge: test: restore unrelated agent test fixture

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

Prepared head SHA: b98f5b59e6
Review: https://github.com/openclaw/openclaw/pull/91345#issuecomment-4646215016

Co-authored-by: Glenn-Agent <glenn_agent@163.com>
2026-06-19 05:54:14 +00:00
Vincent Koc
a37dd0210b fix(e2e): bound upgrade survivor probe retries 2026-06-19 07:52:44 +02:00
Sash Zats
17106b4844 fix(ios): clean up notification settings state (#91923)
Summary:
- The branch replaces iOS notification permission display-string state with a typed SettingsNotificationStatus ... n value, and opens the app notification Settings page with UIApplication.openNotificationSettingsURLString.
- PR surface: Other +51. Total +51 across 5 files.
- Reproducibility: yes. Current main has a source-level reproduction path where the Notifications settings act ... n display strings and opens the general app Settings URL instead of the notification-specific Settings URL.

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

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

Prepared head SHA: 1a2fdeeac5
Review: https://github.com/openclaw/openclaw/pull/91923#issuecomment-4669439195

Co-authored-by: Sash Zats <sash@zats.io>
2026-06-19 05:51:28 +00:00
cornna
93d0d2aedd fix(feishu): avoid axios interceptor internals (#89806)
Summary:
- The branch replaces Feishu's module-load Axios `handlers` reset with public request-interceptor registration and adds tests that throw on private handler access.
- PR surface: Source +7, Tests +48. Total +55 across 2 files.
- Reproducibility: yes. for the source/dependency boundary: current main still writes `interceptors.request.ha ... l on that access before the production change. No live authenticated Feishu request failure was reproduced.

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

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

Prepared head SHA: b87083193b
Review: https://github.com/openclaw/openclaw/pull/89806#issuecomment-4611809953

Co-authored-by: Cornna <96944678+ymylive@users.noreply.github.com>
2026-06-19 05:50:54 +00:00
Vincent Koc
82ae81f3bf fix(e2e): time out package url response bodies 2026-06-19 07:45:52 +02:00
Vincent Koc
2db37c2cd0 refactor(copilot): drop unused permission policy helpers 2026-06-19 13:43:27 +08:00
Vincent Koc
6370f2023a fix(release): cancel ClawHub probe bodies 2026-06-19 07:42:04 +02:00
Vincent Koc
2dbbef46bb fix(e2e): cancel Open WebUI probe body reads 2026-06-19 07:37:28 +02:00
bbblending
df261fabb3 fix(macos): open NSOpenPanel for embedded Control UI file inputs (#94468) (#94612)
Summary:
- The PR wires the macOS Dashboard and Canvas WKWebViews to WKUIDelegate and presents NSOpenPanel for HTML file inputs.
- PR surface: Other +61. Total +61 across 3 files.
- Reproducibility: yes. at source level: current main renders the affected file inputs while the macOS Dashboa ... fore-fix packaged macOS app in this read-only review, but the after-fix screenshots show the real app path.

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

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

Prepared head SHA: 4f477c4ed0
Review: https://github.com/openclaw/openclaw/pull/94612#issuecomment-4743165861

Co-authored-by: bbblending <li.mingkang@xydigit.com>
2026-06-19 05:32:45 +00:00
clawsweeper[bot]
c041a45ece chore: forward-port alpha release fixes
Forward-port Tideclaw alpha validation fixes from v2026.6.19-alpha.1 prep: Kova timeout evidence and Telegram QA command markers.
2026-06-19 05:32:10 +00:00
Vincent Koc
089f8c7fb5 fix(e2e): cancel plugin preflight body reads 2026-06-19 07:28:00 +02:00
Vincent Koc
712e69dd74 fix(e2e): honor gateway network client deadline 2026-06-19 07:23:24 +02:00
Vincent Koc
13aaece8b3 refactor(deepinfra): drop unused provider config helper 2026-06-19 13:23:09 +08:00
Vincent Koc
32ee308f55 fix(e2e): cancel RPC RTT probe bodies 2026-06-19 07:21:40 +02:00
Vincent Koc
5776b9b4e6 fix(e2e): cancel kitchen probe body reads on abort 2026-06-19 07:16:35 +02:00
Fabian.Xu
6368c1173c fix: guard tool event callbacks (AI-assisted) (#81696)
Summary:
- This PR wraps embedded-agent tool-handler onExecutionPhase and per-run onAgentEvent emissions in best-effort warning guards and adds regression tests for throwing and rejecting callbacks.
- PR surface: Source +31, Tests +44. Total +75 across 2 files.
- Reproducibility: yes. Current main directly invokes the relevant callbacks in the tool-start and tool-event  ... sync observer can leak unless guarded; I did not run a failing current-main repro in this read-only review.

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

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

Prepared head SHA: 65de17d9e0
Review: https://github.com/openclaw/openclaw/pull/81696#issuecomment-4448200659

Co-authored-by: xuyi1243 <maginaxwhz@gmail.com>
2026-06-19 05:13:35 +00:00
Vincent Koc
dc9b1d5159 fix(e2e): cancel chat-tools response reads on timeout 2026-06-19 07:11:07 +02:00
clawsweeper[bot]
308fb97f7a feat(slack): log INFO receipt for inbound app_mention events (#94790)
Summary:
- The branch adds a Slack subsystem INFO receipt formatter/logger for accepted non-DM app_mention events before dispatch, plus direct log tests and a test-harness team id.
- PR surface: Source +37, Tests +81. Total +118 across 3 files.
- Reproducibility: yes. from source inspection. Current main and v2026.6.8 route accepted Slack app_mention ev ... andleSlackMessage without a per-inbound INFO receipt, while Telegram emits an inbound line before dispatch.

Automerge notes:
- PR branch already contained follow-up commit before automerge: feat(slack): log INFO receipt for inbound app_mention events

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

Prepared head SHA: b174201e0a
Review: https://github.com/openclaw/openclaw/pull/94790#issuecomment-4748509343

Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: ZengWen-DT <290981215+ZengWen-DT@users.noreply.github.com>
2026-06-19 05:10:54 +00:00
Vincent Koc
e498fc8c3b fix(tooling): cancel labeler response bodies on timeout 2026-06-19 07:08:03 +02:00
Vincent Koc
b794d7fb58 fix(clickclack): reconnect after websocket errors 2026-06-19 07:07:25 +02:00
Vincent Koc
bc7c2baa5c fix(openai): suppress realtime startup close callbacks 2026-06-19 07:07:25 +02:00
Vincent Koc
46c42d4a0d refactor(providers): drop unused model defaults helpers 2026-06-19 13:05:48 +08:00
Vincent Koc
3a82bf5766 fix(tooling): cancel clawtributor avatar body reads 2026-06-19 07:02:27 +02:00
Vincent Koc
5e7a0b1558 refactor(providers): drop unused onboarding wrappers 2026-06-19 12:52:46 +08:00
Vincent Koc
38ebc24f77 fix(github): cancel gh-read bodies on timeout 2026-06-19 06:52:19 +02:00
Vincent Koc
af3c10626c test(plugin): align prerelease rpc lane contract 2026-06-19 06:49:46 +02:00
Vincent Koc
324ad548a8 fix(release): keep ClawHub verification bodies timed 2026-06-19 06:36:55 +02:00
Vincent Koc
e552f97866 refactor(irc): drop unused text splitter 2026-06-19 12:36:41 +08:00
Vincent Koc
259f071a93 fix(signal): report receive websocket pre-open closes 2026-06-19 06:34:46 +02:00
liuhao1024
a5190f7d4a fix(skills/trello): add curl to requires.bins to match body examples (fixes #94727) (#94729)
Summary:
- The PR adds `curl` to the bundled Trello skill's `metadata.openclaw.requires.bins` entry.
- PR surface: Docs 0. Total 0 across 1 file.
- Reproducibility: yes. at source level. Current main and v2026.6.8 declare only `jq` for Trello while the skill body uses `curl`, and the shared requirement evaluator checks only declared bins.

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

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

Prepared head SHA: 83ae5e8bef
Review: https://github.com/openclaw/openclaw/pull/94729#issuecomment-4747397470

Co-authored-by: liuhao1024 <sunsky.lau@gmail.com>
2026-06-19 04:33:38 +00:00
Vincent Koc
a619518ebe fix(e2e): keep cross-os response reads timed 2026-06-19 06:31:32 +02:00
Vincent Koc
ea0b0ad0a0 refactor(line): drop unused media kind detector 2026-06-19 12:30:06 +08:00
nas
fb25f29638 fix(codex): bound turn/start text when context budget is non-positive (#94756)
Summary:
- The PR updates Codex context projection fitting so non-positive context budgets still return turn/start text within the app-server input cap while preserving the current user request tail.
- PR surface: Source +23, Tests +87. Total +110 across 2 files.
- Reproducibility: yes. Current main is source-reproducible: when `beforeContext.length + afterContext.length  ... ll-over-limit text; the linked diagnostic also shows the real Codex app-server rejects that pre-fix string.

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

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

Prepared head SHA: 1510a3d13a
Review: https://github.com/openclaw/openclaw/pull/94756#issuecomment-4747889774

Co-authored-by: Anas <anaselghoudane@gmail.com>
2026-06-19 04:28:27 +00:00
nas
300f5e8590 fix(sessions): preserve Media* index alignment when reading user-turn fields (#94257)
Summary:
- Merged fix(sessions): preserve Media* index alignment when reading user-turn fields after ClawSweeper review.

Automerge notes:
- PR branch already contained follow-up commit before automerge: Merge branch 'main' into fix/media-types-index-alignment

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

Prepared head SHA: 62db437d10
Review: https://github.com/openclaw/openclaw/pull/94257#issuecomment-4748486369

Co-authored-by: Anas <anaselghoudane@gmail.com>
Co-authored-by: nas <156536069+Nas01010101@users.noreply.github.com>
Approved-by: vincentkoc
2026-06-19 04:28:14 +00:00
Vincent Koc
f144899219 test(diffs): format viewer client coverage 2026-06-19 06:25:04 +02:00
Vincent Koc
d095e2a4f5 refactor(mattermost): drop unused draft preview helpers 2026-06-19 12:24:35 +08:00
liuhao1024
8aaa4bf3ef test(diffs): add viewerState, toolbar toggle, shadow root, and hydrateProps tests (fixes #83915) (#92873)
Summary:
- Merged test(diffs): add viewerState, toolbar toggle, shadow root, and hydrateProps tests (fixes #83915) after ClawSweeper review.

Automerge notes:
- PR branch already contained follow-up commit before automerge: test(diffs): use real FileContents shape in hydrateProps fixtures

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

Prepared head SHA: a09ee50584
Review: https://github.com/openclaw/openclaw/pull/92873#issuecomment-4748453452

Co-authored-by: liuhao1024 <sunsky.lau@gmail.com>
Approved-by: vincentkoc
2026-06-19 04:19:54 +00:00
Vincent Koc
06b6f7055b fix(e2e): keep clickclack fixture body reads timed 2026-06-19 06:19:21 +02:00
Vincent Koc
de95726177 fix(googlechat): format approval capability import 2026-06-19 06:18:38 +02:00
Vincent Koc
e91c17947b test(cron): cover force run mode 2026-06-19 06:18:38 +02:00
Vincent Koc
a25c64a4e4 refactor(diffs): drop unused language hint filter 2026-06-19 12:17:46 +08:00
Stellar鱼
8888bca752 test(browser): cover action-input CLI request bodies (#92574)
Summary:
- The branch adds Vitest coverage for browser action-input CLI request bodies across element, navigation/resize, fill/evaluate, and upload paths, plus blank-ref validation.
- PR surface: Tests +278. Total +278 across 4 files.
- Reproducibility: yes. for a source-level coverage gap: current main exposes the browser action-input command ... isting tests still lack broad success-path request-body assertions. This is not a runtime bug reproduction.

Automerge notes:
- PR branch already contained follow-up commit before automerge: test(browser): cover click-coords action body

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

Prepared head SHA: c070a8d51b
Review: https://github.com/openclaw/openclaw/pull/92574#issuecomment-4697124920

Co-authored-by: Stellar鱼 <2182712990@qq.com>
Co-authored-by: yu-xin-c <2182712990@qq.com>
2026-06-19 04:17:29 +00:00
Hidetsugu55
50a4bb00e5 fix(gateway): never return an empty chat.history transcript (#92383)
Summary:
- The PR changes gateway chat-history byte-budget fallback behavior to return a small metadata-free unavailable sentinel instead of an empty transcript, with focused budget tests.
- PR surface: Source +20, Tests +73. Total +93 across 2 files.
- Reproducibility: yes. Source inspection shows current main reaches `messages: []` when the full history, las ... d copied oversized placeholder all exceed `maxBytes`; I did not run tests because this review is read-only.

Automerge notes:
- PR branch already contained follow-up commit before automerge: test: access __openclaw via bracket notation for no-underscore-dangle

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

Prepared head SHA: f2fa246ab7
Review: https://github.com/openclaw/openclaw/pull/92383#issuecomment-4688688923

Co-authored-by: Hidetsugu55 <183473679+Hidetsugu55@users.noreply.github.com>
2026-06-19 04:17:02 +00:00
Vincent Koc
de17d5b9ef fix(scripts): fail RPC RTT on websocket pre-open close 2026-06-19 06:16:22 +02:00
Voscko
6a0c3eaf78 fix(android): group settings by intent (#94539)
Summary:
- The PR reorganizes the Android Settings home rows into titled intent sections and adds ShellScreen logic tests for section title mapping and section ordering.
- PR surface: Other +106. Total +106 across 2 files.
- Reproducibility: not applicable. this is a UI organization cleanup rather than a bug report. The relevant ve ... ion path is the before/after Android emulator screenshot proof plus source comparison against current main.

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

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

Prepared head SHA: da9bf5c5b5
Review: https://github.com/openclaw/openclaw/pull/94539#issuecomment-4741795253

Co-authored-by: Tosko4 <tosko4@gmail.com>
2026-06-19 04:15:19 +00:00
mushuiyu_xydt
78f948f768 fix(tui): show 0 not ? for fresh-session context tokens in footer (#94337)
Summary:
- The PR extends TUI session info to carry `totalTokensFresh`, maps fresh missing totals to `0`, and adds a focused regression test for the footer merge path.
- PR surface: Source +15, Tests +38. Total +53 across 4 files.
- Reproducibility: yes. at source level: `chat.history` returns session info with `totalTokensFresh`, but curr ...  `null` before footer formatting. I did not run local tests or a live TUI session in this read-only review.

Automerge notes:
- PR branch already contained follow-up commit before automerge: Merge branch 'main' into fix/followup-93798

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

Prepared head SHA: 43657b52c8
Review: https://github.com/openclaw/openclaw/pull/94337#issuecomment-4737123127

Co-authored-by: 杨浩宇0668001029 <yang.haoyu@xydigit.com>
Co-authored-by: mushuiyu_xydt <yang.haoyu@xydigit.com>
2026-06-19 04:10:08 +00:00
Vincent Koc
975340fbd5 fix(audit): cancel stalled advisory body reads 2026-06-19 06:09:34 +02:00
Vincent Koc
7570831ee1 refactor(extensions): drop unused compatibility aliases 2026-06-19 12:08:51 +08:00
Vincent Koc
f2a83a7a71 fix(transcription): preserve websocket session errors 2026-06-19 06:05:24 +02:00
Vincent Koc
d9c66b9c6d fix(e2e): bound upgrade survivor probe body reads 2026-06-19 06:02:31 +02:00
Steven
39328ed692 fix(skills): retarget stale plugin skill symlinks (#86719)
Summary:
- The PR retargets stale generated plugin-skill symlinks when their old target disappeared and adds regression coverage for that case.
- PR surface: Source +11, Tests +17. Total +28 across 2 files.
- Reproducibility: no. high-confidence current-main failure was run in this read-only review. The linked issue ... ased-build filesystem state and source inspection confirms the runtime publisher path that this PR changes.

Automerge notes:
- PR branch already contained follow-up commit before automerge: Merge remote-tracking branch 'upstream/main' into fix/plugin-skill-st…
- PR branch already contained follow-up commit before automerge: fix(skills): unlink generated plugin skill symlinks

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

Prepared head SHA: 94a9765735
Review: https://github.com/openclaw/openclaw/pull/86719#issuecomment-4539047343

Co-authored-by: Steven Palmer <palmer.e.steven@gmail.com>
2026-06-19 04:02:05 +00:00
Josh Lehman
8662b9de54 refactor: route sdk session compatibility through accessor (#89904) 2026-06-18 21:00:05 -07:00
Dirk
6504237900 fix(note): prevent clack from re-breaking copy-sensitive tokens (#94746)
Summary:
- The PR widens the virtual Clack output columns for wrapped terminal notes and adds a rendered-output regression test for copy-sensitive session-lock paths.
- PR surface: Source +8, Tests +28. Total +36 across 2 files.
- Reproducibility: yes. Current source routes session lock paths through `note()`, and the pinned Clack note renderer hard-wraps final content from `getColumns(output) - 6` after OpenClaw's first wrapping pass.

Automerge notes:
- PR branch already contained follow-up commit before automerge: test(note): add rendered-output regression test for copy-sensitive to…

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

Prepared head SHA: b17a4ff571
Review: https://github.com/openclaw/openclaw/pull/94746#issuecomment-4747714518

Co-authored-by: Dirk <0668000837@xydigit.com>
2026-06-19 03:59:09 +00:00
Vincent Koc
fb69db6365 refactor(extensions): drop unused approval and tunnel helpers 2026-06-19 11:59:05 +08:00
Vincent Koc
845ad1cf71 fix(tooling): timeout transitive manifest packuments 2026-06-19 05:55:00 +02:00
jincheng-xydt
356a199bd4 fix: default cron runMode to "due" instead of "force" (#94270) (#94453)
* fix: default cron runMode to 'due' instead of 'force'

When the runMode parameter is omitted from a cron 'run' action,
the default value now respects schedule guards ('due') instead
of bypassing them ('force'). This prevents unintended execution
of scheduled jobs outside their configured time windows.

Fixes #94270

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

* test: update runMode expectations for default 'due' (#94270)

* ci: trigger re-evaluation of real behavior proof

* fix(cron): document due-by-default agent runs

Signed-off-by: sallyom <somalley@redhat.com>

---------

Signed-off-by: sallyom <somalley@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: sallyom <somalley@redhat.com>
2026-06-18 23:53:17 -04:00
Josh Lehman
f1cab04966 refactor: route SDK session compatibility through seam (#89203) 2026-06-18 20:52:46 -07:00
Yuval Dinodia
87854e841e fix(whatsapp): keep opening text chunk when first media fails on multi-chunk reply (#93823)
Summary:
- The PR changes the WhatsApp auto-reply first-media failure fallback to resend the saved leading caption chunk and adds a multi-chunk regression test for that failure path.
- PR surface: Source 0, Tests +26. Total +26 across 2 files.
- Reproducibility: yes. Source inspection of current main gives a deterministic path: the first chunk is shift ... fallback shifts `remainingText` again before checking `caption`; this read-only review did not rerun tests.

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

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

Prepared head SHA: b609e44654
Review: https://github.com/openclaw/openclaw/pull/93823#issuecomment-4724923171

Co-authored-by: yetval <yetvald@gmail.com>
2026-06-19 03:52:43 +00:00
lizeyu-xydt
508e3bf413 fix: #80507 show dry-run output for message send/poll (#94684)
Summary:
- The branch changes `formatMessageCliText` to render dry-run message output from `result.dryRun` instead of only `handledBy === "dry-run"`.
- PR surface: Source 0. Total 0 across 1 file.
- Reproducibility: yes. source-reproducible. The linked issue has captured CLI output, and current main shows  ... e the formatter still checks `handledBy === "dry-run"`; I did not execute the CLI in this read-only review.

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

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

Prepared head SHA: dce6d6a0d3
Review: https://github.com/openclaw/openclaw/pull/94684#issuecomment-4746101038

Co-authored-by: lizeyu-xydt <li.zeyu@xydigit.com>
2026-06-19 03:51:50 +00:00
Vincent Koc
825188cb6a refactor(xai): drop unused sync tool auth helper 2026-06-19 11:48:55 +08:00
Vincent Koc
36bfe77db1 fix(github): bound guard response bodies 2026-06-19 05:47:19 +02:00
Vincent Koc
def4c995ac fix(ui): isolate gateway callback errors 2026-06-19 05:43:45 +02:00
Vincent Koc
f91350485c refactor(voice-call): drop unused voice lookup helpers 2026-06-19 11:43:26 +08:00
Vincent Koc
d91766e5e1 fix(release): bound ClawHub trusted publisher reads 2026-06-19 05:38:59 +02:00
sutra
dae06a203f fix: add self-knowledge docs rule to system prompt (#90882)
Summary:
- This PR replaces the generated Documentation prompt wording with self-knowledge docs-authority guidance and updates prompt tests plus the system-prompt docs.
- PR surface: Source 0, Tests +27, Docs +6. Total +33 across 4 files.
- Reproducibility: yes. from source for the prompt gap: current main and v2026.6.8 have only broad docs-first  ... ledge failure example. I did not run a fresh current-main live model conversation in this read-only review.

Automerge notes:
- PR branch already contained follow-up commit before automerge: fix: strengthen self-knowledge docs prompt
- PR branch already contained follow-up commit before automerge: test: narrow cli prompt tool assertion
- PR branch already contained follow-up commit before automerge: fix: condense self-knowledge docs prompt
- PR branch already contained follow-up commit before automerge: fix: clarify self-knowledge docs authority
- PR branch already contained follow-up commit before automerge: Merge branch 'main' into sutrah/self-knowledge-docs-prompt

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

Prepared head SHA: 88a7db5d2a
Review: https://github.com/openclaw/openclaw/pull/90882#issuecomment-4637990339

Co-authored-by: Sutra Hsing <sutrahsing@163.com>
Co-authored-by: sutra <sutrahsing@163.com>
2026-06-19 03:38:36 +00:00
Vincent Koc
52948a1726 refactor(file-transfer): drop unused fs error classifier 2026-06-19 11:34:41 +08:00
Vincent Koc
88c92922e1 fix(release): bound npm registry verification bodies 2026-06-19 05:33:16 +02:00
Vincent Koc
ae6e1fa4d2 fix(sdk): settle connect after observer errors 2026-06-19 05:27:57 +02:00
Vincent Koc
f1c0d5f06f refactor(sessions): drop unused thread parent helper 2026-06-19 11:27:23 +08:00
Vincent Koc
b28fda9ef8 fix(tooling): bound RPC RTT readiness bodies 2026-06-19 05:20:17 +02:00
Vincent Koc
09427aa760 fix(gateway): contain lifecycle callback errors 2026-06-19 05:15:20 +02:00
Vincent Koc
107904c2c5 refactor(pairing): drop unused direct lookup helpers 2026-06-19 11:14:22 +08:00
Vincent Koc
6c82a9fb18 fix(release): bound GitHub API body reads 2026-06-19 05:13:48 +02:00
kevinlin-openai
741f7080a7 feat(codex): support app-server secret refs (#94324)
Co-authored-by: kevinlin-openai <kevin@dendron.so>
2026-06-18 20:10:11 -07:00
Vincent Koc
a1b7118d0f refactor(infra): drop unused apns store wrappers 2026-06-19 11:06:30 +08:00
Vincent Koc
ca6d52e0e8 fix(e2e): bound bundled readyz diagnostics 2026-06-19 05:00:38 +02:00
Vincent Koc
4c55e04e49 refactor(infra): drop unused web push id helpers 2026-06-19 10:59:13 +08:00
Vincent Koc
8d596aa651 fix(sdk): reject work after client close 2026-06-19 04:54:50 +02:00
Vincent Koc
1385da8d3f refactor(infra): drop unused clawhub endpoint clients 2026-06-19 10:53:19 +08:00
Vincent Koc
ad715dfdc9 fix(e2e): abort kitchen sink retry waits 2026-06-19 04:49:43 +02:00
Vincent Koc
d1ab308f5c refactor(infra): trim dead archive facade exports 2026-06-19 10:44:19 +08:00
Vincent Koc
aaceaf8e7c fix(e2e): clear streaming host command timers 2026-06-19 04:37:43 +02:00
Vincent Koc
1492f9906a fix(qa-lab): keep workspace skills in sandbox 2026-06-19 04:33:18 +02:00
Vincent Koc
9ceb970a06 fix(e2e): clear npm update spawn timers 2026-06-19 04:31:34 +02:00
Vincent Koc
c33007ef58 fix(release): scope MiniMax live model gate 2026-06-19 10:26:02 +08:00
Vincent Koc
09a159c913 refactor(config): remove dead quota maintenance facade 2026-06-19 10:25:50 +08:00
Vincent Koc
eedb6678f1 fix(e2e): extend kitchen rpc watchdog 2026-06-19 04:21:10 +02:00
Vincent Koc
459bcd6198 fix(e2e): clean npm update guest scripts 2026-06-19 04:20:42 +02:00
Vincent Koc
1bbc3b6cb6 refactor(config): remove dead async session target resolver 2026-06-19 10:19:41 +08:00
Vincent Koc
6325a8b5f4 fix(e2e): clean Parallels guest temp scripts 2026-06-19 04:13:03 +02:00
Vincent Koc
5805af9dc4 fix(e2e): defer codex live profile exports 2026-06-19 04:09:10 +02:00
Vincent Koc
42bcb3ecb0 refactor(config): remove dead session schema exports 2026-06-19 10:08:24 +08:00
Thomas Krohnfuß
b48238aa88 feat(commands): add /name to rename the current session from chat (#88581)
* Add /name chat command to rename the current session

Adds a `/name <title>` slash command so users can name or rename the
current session directly from any chat channel, instead of only through
the web/admin session manager. This keeps parallel sessions easy to tell
apart from within the chat flow.

Behaviour:
- `/name <title>` sets the session label, reusing the canonical
  `parseSessionLabel` validation (trim, non-empty, max 512 chars) and the
  same cross-store uniqueness rule enforced by the web `sessions.patch`
  path, so chat naming behaves identically to the session manager.
- `/name` with no argument shows the current name plus a locally derived
  `deriveSessionTitle` suggestion without mutating anything (no LLM).
- Only authorized senders can rename (rejectUnauthorizedCommand), matching
  /goal. The label surfaces everywhere sessions.list is shown (TUI, web,
  CLI, MCP).

The handler resolves the session via resolveSessionStoreEntry so renames
land on the canonical entry even when the store still holds a legacy or
case-folded key alias, and excludes those aliases from the uniqueness scan
to avoid false conflicts. Failed renames skip the store write.

Registers the command in commands-registry.shared.ts and the handler in
loadCommandHandlers, documents it in docs/tools/slash-commands.md, and adds
unit tests covering rename, no-arg suggestion, duplicate-label rejection,
unauthorized senders, disabled text commands, and persisted-name re-read.

Part of the chat-native session naming feature (follows the web in-chat
rename PR). Relates to openclaw#85502 and openclaw#54397.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(name): seed native sessions and persist renames via canonical key

Address Codex review on PR #88581:
- Fall back to the in-memory params.sessionEntry when the store has no row
  yet, so a brand-new native slash session can be named from its first
  /name command instead of failing with 'no active session to name'.
- Persist the rename through resolved.normalizedKey and drop legacy/
  case-folded alias keys (mirroring persistResolvedSessionEntry) so the
  canonical entry is updated and sessions.list stops surfacing the stale
  alias row.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(name): emit session metadata changes

Route successful /name renames through the shared command session metadata seam so subscribed session lists receive sessions.changed like /goal.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(commands): add /name to rename the current session from chat

* fix(docs): document the /name slash command

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Agent <agent@example.com>
Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
2026-06-19 10:04:28 +08:00
Vincent Koc
dc980273e3 fix(e2e): align live timeout wrappers 2026-06-19 04:01:43 +02:00
Vincent Koc
74f90885f3 fix(release): unblock beta validation 2026-06-19 10:01:28 +08:00
Vincent Koc
236a0c8fe0 test(auto-reply): inline dead group mention fixture 2026-06-19 10:01:17 +08:00
Vincent Koc
b306745f64 fix(e2e): forward plugin lifecycle poll interval 2026-06-19 03:56:03 +02:00
Stellar鱼
20dd2be0f2 fix(agents): skip auth gate for CLI-owned transport (#88551)
* fix(agents): skip fallback auth gate for CLI runtimes

* fix(agents): skip auth gate for CLI-owned transport

* fix(agents): skip auth gate for CLI-owned transport

---------

Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
2026-06-19 09:54:15 +08:00
Vincent Koc
5b030418c1 refactor(sessions): drop dead transcript tree wrappers 2026-06-19 09:47:31 +08:00
Vincent Koc
a74ce6f20c fix(e2e): forward plugin sweep runtime knobs 2026-06-19 03:46:19 +02:00
Vincent Koc
10f0588ee3 fix(e2e): align codex live turn timeouts 2026-06-19 03:43:44 +02:00
Vincent Koc
39297fb0ad refactor(config): drop dead baseline and node wrappers 2026-06-19 09:41:52 +08:00
Vincent Koc
3914a3638c fix(e2e): forward kitchen sink fixture wait knob 2026-06-19 03:39:04 +02:00
Vincent Koc
2ef0589b76 fix(e2e): forward onboard gateway wait knobs 2026-06-19 03:30:17 +02:00
Vincent Koc
01e562113b fix(e2e): forward doctor switch timeouts 2026-06-19 03:21:28 +02:00
Vincent Koc
410a95269a fix(e2e): pass posix update agent timeouts 2026-06-19 03:19:41 +02:00
Vincent Koc
2c6bf1a5d8 fix(e2e): forward codex media log limit 2026-06-19 03:12:33 +02:00
Vincent Koc
ccc1ad4c74 refactor(auto-reply): remove stale tool meta helpers 2026-06-19 09:11:42 +08:00
Vincent Koc
f19cae6d1d test(ci): cover chat tools helper routing 2026-06-19 09:10:35 +08:00
Vincent Koc
8c3185d55c fix(e2e): forward bundled upgrade limits 2026-06-19 03:06:36 +02:00
Vincent Koc
a0d9f9ea45 fix(e2e): forward bundled plugin runtime limits 2026-06-19 03:02:01 +02:00
Vincent Koc
dd5febe2aa fix(config): preserve raw snapshots after materialization 2026-06-19 09:01:54 +08:00
Vincent Koc
943511674c fix(config): preserve raw snapshots after materialization 2026-06-19 09:01:03 +08:00
Vincent Koc
c33cec04d9 refactor(agents): remove stale file transcript rewrite 2026-06-19 08:57:00 +08:00
Vincent Koc
c9605779ef fix(doctor): warn on volatile SQLite state (#94725)
* fix(doctor): warn on volatile SQLite state

* fix(doctor): resolve symlinked state paths
2026-06-19 08:56:37 +08:00
Vincent Koc
c79f1e5441 fix(e2e): forward package assertion limits 2026-06-19 02:49:47 +02:00
Vincent Koc
15e101137d fix(e2e): forward live plugin scan limit 2026-06-19 02:48:30 +02:00
Vincent Koc
900a834c60 refactor(daemon): remove stale launchd helpers 2026-06-19 08:45:50 +08:00
Vincent Koc
9765f7333a fix(e2e): forward live plugin dump limit 2026-06-19 02:42:10 +02:00
Vincent Koc
1fb11ab306 fix(e2e): forward browser cdp snapshot limit 2026-06-19 02:40:09 +02:00
Vincent Koc
01b919aeff refactor(doctor): remove stale legacy path listers 2026-06-19 08:39:31 +08:00
Vincent Koc
c654641e0c refactor(channels): remove stale setup adapter lookup 2026-06-19 08:33:04 +08:00
Vincent Koc
d04cedb9fe fix(e2e): forward gateway network client limits 2026-06-19 02:30:53 +02:00
Vincent Koc
ba69d4fb03 test(ci): cover installer timeout normalization 2026-06-19 08:29:07 +08:00
Hani Koshaji
73241d39f6 fix(skills/1password): stop forcing tmux for desktop app auth (#52540) (#81825)
* fix(skills/1password): stop forcing tmux for desktop app auth (#52540)

The bundled skill currently mandates that every `op` invocation run inside
a fresh tmux session. That guidance is wrong on every desktop-app-integration
setup (macOS/Windows/Linux) because the 1Password app exposes the CLI over
a per-user Unix domain socket the gateway exec env can reach but tmux
subshells generally cannot — wrapping in tmux produces "1Password CLI
couldn't connect to the 1Password desktop app" failures.

Rewrite the skill to detect auth mode first and only use tmux for the one
case where it actually helps:

- Service account (`OP_SERVICE_ACCOUNT_TOKEN`): direct exec, no signin.
- Desktop app integration: direct exec, never tmux. Note the macOS socket
  location (`~/Library/Group Containers/2BUA8C4S2C.com.1password/t/`) so
  agents can recognize the failure mode.
- Standalone interactive signin: tmux is the right tool because it
  preserves the per-shell session token written by `op signin`.

Update Guardrails and the get-started reference accordingly. Drop the
blanket 'do not run op outside tmux' rule.

Fixes #52540

* fix(skills/1password): correct desktop-app IPC wording and signin example

Address PR #75090 review:

- Replace the blanket 'per-user Unix domain socket' description with
  per-platform wording: XPC via the 1Password Browser Helper on macOS,
  a Unix domain socket on Linux, a named pipe on Windows. Keep the macOS
  group-container path as a symptom indicator only, not as a transport
  claim. Mirror the same correction in the get-started reference and the
  changelog entry.
- Fix the standalone-signin tmux example: `op signin` was being sent as
  a plain command, so its eval-style export was printed but never applied.
  Subsequent `op whoami` and `op vault list` calls would fail because
  the OP_SESSION_* env var was never set. Wrap the call in
  `eval "$(op signin ...)"` so the session token is exported into the
  tmux pane environment as the surrounding text describes.

Same direct-exec direction; tighter and more accurate.

* docs(1password): clarify Windows standalone signin

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix(skills/1password): repair auth-mode guidance

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
2026-06-19 08:26:56 +08:00
Vincent Koc
c770c7b084 refactor(channels): remove stale setup reload helpers 2026-06-19 08:25:38 +08:00
Dallin Romney
e12cf72b17 Standardize QA coverage IDs on dotted names (#94702)
* fix: standardize qa coverage ids

* test: avoid qa coverage id assertion spread
2026-06-18 17:25:26 -07:00
JackWu
e9e44bf83c fix(scripts): avoid mutating tracked auth-monitor template during setup (#53920)
* fix(scripts): render auth monitor unit before install

Render the auth monitor service into temporary files instead of editing the tracked template. Quote the generated ExecStart safely, including spaces and literal dollars, then atomically install the rendered unit.

* fix(scripts): avoid mutating tracked auth-monitor template during setup

* fix(scripts): avoid mutating tracked auth-monitor template during setup

* fix(scripts): avoid mutating tracked auth-monitor template during setup

---------

Co-authored-by: JackWuGlobal <JackWuGlobal@users.noreply.github.com>
Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
2026-06-19 08:24:16 +08:00
Vincent Koc
843b1d6fbb fix(e2e): forward install session limits 2026-06-19 02:21:10 +02:00
Vincent Koc
09b56592d2 fix(e2e): forward code-mode client limits 2026-06-19 02:19:27 +02:00
Vincent Koc
701687efa3 fix(e2e): validate install e2e wrapper knobs 2026-06-19 02:15:58 +02:00
Vincent Koc
d8cc16a3e2 Merge branch 'main' of https://github.com/openclaw/openclaw
* 'main' of https://github.com/openclaw/openclaw: (82 commits)
  fix(e2e): validate chat tools body limit
  fix(e2e): honor chat tools body limit
  fix(e2e): validate chat tools timeout
  fix(e2e): give cleanup smoke build heap headroom
  fix(e2e): validate plugin lifecycle limits
  refactor(auto-reply): add lifecycle storage seams (#93685)
  fix: preserve pending subagent completion announces (#94349)
  fix(e2e): validate plugin log limits before setup
  fix(e2e): validate codex media timeout
  fix(e2e): validate fixture log limits
  fix(e2e): validate cleanup log limits
  fix(e2e): validate docker log limits
  fix(live): validate docker pids limits
  fix(e2e): validate docker pids limits
  test: fold channel message flows into qa e2e (#93174)
  fix(e2e): validate docker build limits
  Prevent Codex thread rotation from losing next-step context (#94093)
  fix(e2e): validate fixture cleanup interval
  fix(agents): correct claw-score validation workflow
  fix(e2e): validate log tail limits
  ...
2026-06-19 08:14:35 +08:00
Vincent Koc
7f894ba2be fix(e2e): validate chat tools body limit 2026-06-19 02:07:32 +02:00
Vincent Koc
e5de09f96a fix(e2e): honor chat tools body limit 2026-06-19 01:59:12 +02:00
Vincent Koc
2a0e63d12b fix(e2e): validate chat tools timeout 2026-06-19 01:51:19 +02:00
Vincent Koc
e3bab80bda fix(e2e): give cleanup smoke build heap headroom 2026-06-19 01:50:54 +02:00
Vincent Koc
7207072436 fix(e2e): validate plugin lifecycle limits 2026-06-19 01:43:24 +02:00
Vincent Koc
8af89b097a docs(changelog): refresh 2026.6.9 notes 2026-06-19 02:34:19 +08:00
Vincent Koc
8bcb1e05b6 fix(release): generate npm shrinkwraps 2026-06-19 02:28:54 +08:00
2736 changed files with 98981 additions and 34778 deletions

View File

@@ -30,6 +30,9 @@ out of this repo. If a score needs private evidence, use the redacted
completeness-instruction paths.
- Feature `coverageIds` are ANDed proof targets, not aliases. A feature may
list multiple IDs when each ID proves part of one capability.
- Coverage IDs use dotted `namespace.behavior` form, with lowercase
alphanumeric/dash segments. Profile, surface, and category IDs may remain
dashed or dotted.
- Keep categories and feature names unique, product-shaped, and broader than raw
coverage IDs. Do not promote generic IDs into standalone feature names.
- Avoid duplicate coverage-ID bundles under different feature names in one

View File

@@ -4,6 +4,7 @@ import { execFileSync } from "node:child_process";
import { readFileSync, writeFileSync } from "node:fs";
const repo = "openclaw/openclaw";
const commitAssociationQueryBatchSize = 20;
const excludedHandles = new Set(["openclaw", "clawsweeper", "claude", "codex", "steipete"]);
const nonEditorialTypes = new Set([
"build",
@@ -618,13 +619,25 @@ function graphql(query) {
let lastError;
for (let attempt = 0; attempt < 5; attempt += 1) {
try {
return githubApi(["graphql", "-f", `query=${query}`]).data;
const response = githubApi(["graphql", "-f", `query=${query}`]);
if (response?.data && typeof response.data === "object") {
return response.data;
}
const errors = Array.isArray(response?.errors)
? response.errors.map((error) => error?.message).filter(Boolean)
: [];
const detail = [...errors, response?.message].filter(Boolean).join("\n");
throw new Error(
detail
? `GitHub GraphQL response did not include data:\n${detail}`
: "GitHub GraphQL response did not include data.",
);
} catch (error) {
lastError = error;
const message = [error?.message, error?.stdout, error?.stderr].filter(Boolean).join("\n");
// Historical ranges batch hundreds of objects; only retry transient transport failures.
if (
!/(?:operation timed out|ECONNRESET|ETIMEDOUT|EAI_AGAIN|TLS handshake timeout|stream error: .*CANCEL|unexpected end of JSON input|upstream connect error|connection termination|error connecting to api\.github\.com|Unexpected token '<')/i.test(
!/(?:operation timed out|ECONNRESET|ETIMEDOUT|EAI_AGAIN|TLS handshake timeout|stream error: .*CANCEL|unexpected end of JSON input|upstream connect error|connection termination|connection reset by peer|error connecting to api\.github\.com|Unexpected token '<'|something went wrong|temporarily unavailable|internal server error|rate limit)/i.test(
message,
)
) {
@@ -657,8 +670,8 @@ function resolveAssociatedPullRequests(commitHashes, targetTimestamp) {
pending.push({ commitHash, cursor: connection.pageInfo.endCursor });
}
}
for (let index = 0; index < commitHashes.length; index += 40) {
const chunk = commitHashes.slice(index, index + 40);
for (let index = 0; index < commitHashes.length; index += commitAssociationQueryBatchSize) {
const chunk = commitHashes.slice(index, index + commitAssociationQueryBatchSize);
const fields = chunk
.map(
(hash, offset) =>

View File

@@ -107,16 +107,9 @@ Reject:
## PR Body Proof
Use the repo PR template. Include these exact labels:
```text
Behavior addressed:
Real environment tested:
Exact steps or command run after this patch:
Evidence after fix:
Observed result after fix:
What was not tested:
```
Use the repo PR template. Include authored `## What Problem This Solves` and
`## Evidence` sections. Keep the body focused on intent and the most useful
validation evidence; inspect the code, tests, and CI before judging correctness.
## Existing PR Rules

View File

@@ -4,6 +4,14 @@ set -euo pipefail
repo="openclaw/openclaw"
months="12"
include_global="0"
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(git -C "$script_dir/../../../.." rev-parse --show-toplevel 2>/dev/null || true)"
if [ -z "$repo_root" ]; then
repo_root="$(cd "$script_dir/../../../.." && pwd)"
fi
# shellcheck disable=SC1091
source "$repo_root/scripts/lib/plain-gh.sh"
usage() {
printf 'Usage: %s [--repo owner/repo] [--months N] [--global] <github-login> [login...]\n' "$0"
@@ -18,6 +26,10 @@ need() {
command -v "$1" >/dev/null 2>&1 || die "missing required command: $1"
}
gh() {
gh_plain "$@"
}
date_utc_relative_months() {
local count="$1"
if date -u -v-"${count}"m +%Y-%m-%dT00:00:00Z >/dev/null 2>&1; then
@@ -131,7 +143,8 @@ done
exit 2
}
need gh
OPENCLAW_GH_BIN="$(resolve_plain_gh_bin)" || die "missing required command: gh"
export OPENCLAW_GH_BIN
need jq
since_ts=$(date_utc_relative_months "$months")

View File

@@ -4,12 +4,12 @@
* Usage: node secret-scanning.mjs <command> [options]
*/
import { spawnSync } from "node:child_process";
import crypto from "node:crypto";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { spawnPlainGh } from "../../../../scripts/lib/plain-gh.mjs";
const REPO = "openclaw/openclaw";
const REPO_URL = `https://github.com/${REPO}`;
@@ -29,7 +29,7 @@ function tmpFile(purpose) {
}
function gh(args, { json = true, allowFailure = false } = {}) {
const proc = spawnSync("gh", args, { encoding: "utf8", maxBuffer: 10 * 1024 * 1024 });
const proc = spawnPlainGh(args, { encoding: "utf8", maxBuffer: 10 * 1024 * 1024 });
if (proc.status !== 0 && !allowFailure) {
fail(`gh ${args.slice(0, 3).join(" ")} failed:\n${(proc.stderr || proc.stdout || "").trim()}`);
}

View File

@@ -5,6 +5,7 @@
*/
import { execFileSync } from "node:child_process";
import process from "node:process";
import { plainGhEnv, resolvePlainGhBin } from "../../../../scripts/lib/plain-gh.mjs";
const runId = process.argv[2];
const repo = process.env.OPENCLAW_RELEASE_REPO || "openclaw/openclaw";
@@ -15,8 +16,9 @@ if (!runId) {
}
function gh(args) {
return execFileSync("gh", args, {
return execFileSync(resolvePlainGhBin(), args, {
encoding: "utf8",
env: plainGhEnv(),
stdio: ["ignore", "pipe", "pipe"],
});
}
@@ -32,14 +34,15 @@ function githubRestJson(pathSuffix) {
"-lc",
[
"set -euo pipefail",
'token="$(gh auth token)"',
'token="$("$OPENCLAW_PLAIN_GH_BIN" auth token)"',
'curl -fsS -H "Authorization: Bearer ${token}" -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" "${OPENCLAW_GITHUB_REST_URL}"',
].join("\n"),
],
{
encoding: "utf8",
env: {
...process.env,
...plainGhEnv(),
OPENCLAW_PLAIN_GH_BIN: resolvePlainGhBin(),
OPENCLAW_GITHUB_REST_URL: `https://api.github.com/repos/${repo}/${pathSuffix}`,
},
maxBuffer: 16 * 1024 * 1024,

View File

@@ -22,7 +22,7 @@ paths:
- src/plugins/memory-*.ts
- src/gateway/server-startup-memory.ts
- src/commands/doctor-memory-search.ts
- src/commands/doctor-cron-dreaming-payload-migration.ts
- src/commands/doctor/cron/dreaming-payload-migration.ts
paths-ignore:
- "**/node_modules"

View File

@@ -19,7 +19,6 @@ paths:
- src/plugins/bundled-compat.ts
- src/plugins/bundled-dir.ts
- src/plugins/bundled-plugin-metadata.ts
- src/plugins/bundled-public-surface-runtime-root.ts
- src/plugins/plugin-sdk-dist-alias.ts
- src/plugins/captured-registration.ts
- src/plugins/config-activation-shared.ts
@@ -46,7 +45,6 @@ paths:
- src/plugins/runtime-state.ts
- src/plugins/runtime.ts
- src/plugins/sdk-alias.ts
- src/plugins/source-loader.ts
- src/plugins/types.ts
- src/plugins/validation-diagnostics.ts
- src/plugins/web-provider-public-artifacts*.ts

View File

@@ -51,7 +51,6 @@ paths:
- src/plugins/runtime
- src/plugins/runtime-state.ts
- src/plugins/runtime.ts
- src/plugins/source-loader.ts
- src/plugins/update.ts
- src/plugins/validation-diagnostics.ts
- src/plugin-sdk/*entry*.ts

21
.github/labeler.yml vendored
View File

@@ -41,12 +41,6 @@
- any-glob-to-any-file:
- "extensions/google-meet/**"
- "docs/plugins/google-meet.md"
"plugin: meeting-notes":
- changed-files:
- any-glob-to-any-file:
- "extensions/meeting-notes/**"
- "docs/plugins/meeting-notes.md"
- "src/meeting-notes/**"
"plugin: workboard":
- changed-files:
- any-glob-to-any-file:
@@ -109,6 +103,11 @@
- any-glob-to-any-file:
- "extensions/qqbot/**"
- "docs/channels/qqbot.md"
"channel: raft":
- changed-files:
- any-glob-to-any-file:
- "extensions/raft/**"
- "docs/channels/raft.md"
"channel: qa-channel":
- changed-files:
- any-glob-to-any-file:
@@ -171,6 +170,10 @@
- any-glob-to-any-file:
- "extensions/zalo/**"
- "docs/channels/zalo.md"
"channel: zaloclawbot":
- changed-files:
- any-glob-to-any-file:
- "docs/channels/zaloclawbot.md"
"channel: zalouser":
- changed-files:
- any-glob-to-any-file:
@@ -248,12 +251,12 @@
- "src/agents/sandbox*.ts"
- "src/commands/sandbox*.ts"
- "src/cli/sandbox-cli.ts"
- "src/docker-setup.test.ts"
- "src/docker-setup.e2e.test.ts"
- "src/config/**/*sandbox*"
- "docs/cli/sandbox.md"
- "docs/gateway/sandbox*.md"
- "docs/install/docker.md"
- "docs/multi-agent-sandbox-tools.md"
- "docs/tools/multi-agent-sandbox-tools.md"
"agents":
- changed-files:
@@ -266,7 +269,7 @@
- ".github/workflows/opengrep-*.yml"
- ".semgrepignore"
- "docs/cli/security.md"
- "docs/gateway/security.md"
- "docs/gateway/security/**"
- "security/**"
"extensions: admin-http-rpc":

View File

@@ -1,118 +1,57 @@
## Summary
<!--
Optional linked context:
Add a visible `Closes #<issue-number>` or `Related: #<issue-number>` line
below this comment.
What problem does this PR solve?
Required PR title:
type: user-facing description
Use a parenthesized scope only when it adds clarity:
fix(auth): login redirect loops when session cookie is expired
Why does this matter now?
Types: feat, fix, improve, refactor, docs, chore.
For fixes, describe the user-visible symptom and trigger:
fix: task list fails to load when user has no environments
Avoid implementation details such as:
fix: add null check to task query
-->
What is the intended outcome?
## What Problem This Solves
What is intentionally out of scope?
<!--
Describe the concrete user, product, or operational problem.
For fixes, begin with:
"Fixes an issue where users <do X> would <experience Y> when <condition>."
or:
"Resolves a problem where..."
What does success look like?
Name the affected UI surface or workflow. Do not describe the code-level cause here.
-->
What should reviewers focus on?
## Why This Change Was Made
<details>
<summary>Summary guidance</summary>
<!--
In one or two sentences, explain the complete shipped solution, key design
decisions, and relevant boundaries or non-goals. Include implementation detail
only when it helps reviewers understand user-visible behavior or risk.
Avoid file-by-file narration.
-->
This PR description is the contributor's durable explanation of the change. Write it for human maintainers first; ClawSweeper and Barnacle use the same text to understand intent, proof, risk, and current review state.
## User Impact
Describe the intent and outcome in 2-5 bullets. Avoid restating the diff; reviewers and bots can read the changed files.
<!--
State what users, operators, or developers can now do or expect. Lead with the
concrete benefit and use user-facing language. If there is no user-visible
impact, say so plainly.
-->
If this PR fixes a plugin beta-release blocker, title it `fix(<plugin-id>): beta blocker - <summary>` and link the matching `Beta blocker: <plugin-name> - <summary>` issue labeled `beta-blocker`. Contributors cannot label PRs, so the title is the PR-side signal for maintainers and automation.
## Evidence
</details>
<!--
Show the most useful proof that this change works. Screenshots, screencasts,
terminal output, focused tests, CI results, live observations, redacted logs,
and artifact links are all useful. Include before/after evidence for visual
changes when it clarifies the result.
## Linked context
Which issue does this close?
Closes #
Which issues, PRs, or discussions are related?
Related #
Was this requested by a maintainer or owner?
<details>
<summary>Linked context guidance</summary>
Link the issue, PR, discussion, maintainer request, or owner request that explains why this PR should exist. Maintainer context helps reviewers and automation distinguish intended work from drive-by churn.
</details>
## Real behavior proof (required for external PRs)
- Behavior or issue addressed:
- Real environment tested:
- Exact steps or command run after this patch:
- Evidence after fix (screenshot, recording, terminal capture, console output, redacted runtime log, linked artifact, or copied live output):
- Observed result after fix:
- What was not tested:
- Proof limitations or environment constraints:
- Before evidence (optional but encouraged):
<details>
<summary>Real behavior proof guidance</summary>
External contributors must show after-fix evidence from a real OpenClaw setup. Unit tests, mocks, lint, typechecks, snapshots, and CI are supplemental only.
Screenshots are encouraged even for CLI, console, text, or log changes. Terminal screenshots, copied live output, redacted runtime logs, recordings, and linked artifacts count.
If your environment cannot produce the ideal proof, explain that under `Proof limitations or environment constraints` so reviewers and ClawSweeper can direct the next step properly.
Be mindful of private information like IP addresses, API keys, phone numbers, non-public endpoints, or other private details when providing evidence.
</details>
## Tests and validation
Which commands did you run?
What regression coverage was added or updated?
What failed before this fix, if known?
If no test was added, why not?
<details>
<summary>Testing guidance</summary>
List focused commands, not every incidental check. CI is useful support, but external PRs still need real behavior proof above when behavior changes.
</details>
## Risk checklist
Did user-visible behavior change? (`Yes/No`)
Did config, environment, or migration behavior change? (`Yes/No`)
Did security, auth, secrets, network, or tool execution behavior change? (`Yes/No`)
What is the highest-risk area?
How is that risk mitigated?
<details>
<summary>Risk guidance</summary>
Use this for author judgment that is not obvious from the diff. ClawSweeper can see touched files, but it cannot know which behavior you think is risky, why the risk is acceptable, or what mitigation reviewers should verify.
</details>
## Current review state
What is the next action?
What is still waiting on author, maintainer, CI, or external proof?
Which bot or reviewer comments were addressed?
<details>
<summary>Review state guidance</summary>
Keep this as the durable state for review progress. If useful information appears in comments, fold the current next action or blocker back here so maintainers and ClawSweeper do not need to reconstruct state from comment history.
</details>
Reviewers will inspect the code, tests, and CI. Use this section to make the
validation easy to understand, not to restate the diff.
-->

View File

@@ -14,6 +14,10 @@ on:
permissions:
contents: read
concurrency:
group: ${{ github.event_name == 'pull_request' && format('{0}-pr-v1-{1}', github.workflow, github.event.pull_request.number) || format('{0}-manual-v1-{1}', github.workflow, github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
@@ -210,24 +214,49 @@ jobs:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
BYTEPLUS_API_KEY: ${{ secrets.BYTEPLUS_API_KEY }}
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }}
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
KIMI_API_KEY: ${{ secrets.KIMI_API_KEY }}
MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
MODELSTUDIO_API_KEY: ${{ secrets.MODELSTUDIO_API_KEY }}
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
MOONSHOT_API_KEY: ${{ secrets.MOONSHOT_API_KEY }}
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
OPENCODE_ZEN_API_KEY: ${{ secrets.OPENCODE_ZEN_API_KEY }}
OPENCLAW_LIVE_BROWSER_CDP_URL: ${{ secrets.OPENCLAW_LIVE_BROWSER_CDP_URL }}
OPENCLAW_LIVE_SETUP_TOKEN: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN }}
OPENCLAW_LIVE_SETUP_TOKEN_MODEL: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_MODEL }}
OPENCLAW_LIVE_SETUP_TOKEN_PROFILE: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_PROFILE }}
OPENCLAW_LIVE_SETUP_TOKEN_VALUE: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_VALUE }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
QWEN_API_KEY: ${{ secrets.QWEN_API_KEY }}
FAL_KEY: ${{ secrets.FAL_KEY }}
RUNWAY_API_KEY: ${{ secrets.RUNWAY_API_KEY }}
DEEPGRAM_API_KEY: ${{ secrets.DEEPGRAM_API_KEY }}
TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }}
VYDRA_API_KEY: ${{ secrets.VYDRA_API_KEY }}
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
ZAI_API_KEY: ${{ secrets.ZAI_API_KEY }}
Z_AI_API_KEY: ${{ secrets.Z_AI_API_KEY }}
BYTEPLUS_ACCESS_KEY_ID: ${{ secrets.BYTEPLUS_ACCESS_KEY_ID }}
BYTEPLUS_SECRET_ACCESS_KEY: ${{ secrets.BYTEPLUS_SECRET_ACCESS_KEY }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
OPENCLAW_CODEX_AUTH_JSON: ${{ secrets.OPENCLAW_CODEX_AUTH_JSON }}
OPENCLAW_CODEX_CONFIG_TOML: ${{ secrets.OPENCLAW_CODEX_CONFIG_TOML }}
OPENCLAW_CLAUDE_JSON: ${{ secrets.OPENCLAW_CLAUDE_JSON }}
OPENCLAW_CLAUDE_CREDENTIALS_JSON: ${{ secrets.OPENCLAW_CLAUDE_CREDENTIALS_JSON }}
OPENCLAW_CLAUDE_SETTINGS_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_JSON }}
OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON }}
OPENCLAW_GEMINI_SETTINGS_JSON: ${{ secrets.OPENCLAW_GEMINI_SETTINGS_JSON }}
run: bash scripts/ci-hydrate-testbox-env.sh
- name: Run Testbox

View File

@@ -13,6 +13,10 @@ on:
permissions:
contents: read
concurrency:
group: ${{ github.event_name == 'pull_request' && format('{0}-pr-v1-{1}', github.workflow, github.event.pull_request.number) || format('{0}-manual-v1-{1}', github.workflow, github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
PNPM_CONFIG_STORE_DIR: "/tmp/openclaw-pnpm-store"
@@ -128,8 +132,10 @@ jobs:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
BYTEPLUS_API_KEY: ${{ secrets.BYTEPLUS_API_KEY }}
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }}
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
@@ -137,16 +143,38 @@ jobs:
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
KIMI_API_KEY: ${{ secrets.KIMI_API_KEY }}
MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
MODELSTUDIO_API_KEY: ${{ secrets.MODELSTUDIO_API_KEY }}
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
MOONSHOT_API_KEY: ${{ secrets.MOONSHOT_API_KEY }}
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
OPENCODE_ZEN_API_KEY: ${{ secrets.OPENCODE_ZEN_API_KEY }}
OPENCLAW_LIVE_BROWSER_CDP_URL: ${{ secrets.OPENCLAW_LIVE_BROWSER_CDP_URL }}
OPENCLAW_LIVE_SETUP_TOKEN: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN }}
OPENCLAW_LIVE_SETUP_TOKEN_MODEL: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_MODEL }}
OPENCLAW_LIVE_SETUP_TOKEN_PROFILE: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_PROFILE }}
OPENCLAW_LIVE_SETUP_TOKEN_VALUE: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_VALUE }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
QWEN_API_KEY: ${{ secrets.QWEN_API_KEY }}
FAL_KEY: ${{ secrets.FAL_KEY }}
RUNWAY_API_KEY: ${{ secrets.RUNWAY_API_KEY }}
DEEPGRAM_API_KEY: ${{ secrets.DEEPGRAM_API_KEY }}
TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }}
VYDRA_API_KEY: ${{ secrets.VYDRA_API_KEY }}
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
ZAI_API_KEY: ${{ secrets.ZAI_API_KEY }}
Z_AI_API_KEY: ${{ secrets.Z_AI_API_KEY }}
BYTEPLUS_ACCESS_KEY_ID: ${{ secrets.BYTEPLUS_ACCESS_KEY_ID }}
BYTEPLUS_SECRET_ACCESS_KEY: ${{ secrets.BYTEPLUS_SECRET_ACCESS_KEY }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
OPENCLAW_CODEX_AUTH_JSON: ${{ secrets.OPENCLAW_CODEX_AUTH_JSON }}
OPENCLAW_CODEX_CONFIG_TOML: ${{ secrets.OPENCLAW_CODEX_CONFIG_TOML }}
OPENCLAW_CLAUDE_JSON: ${{ secrets.OPENCLAW_CLAUDE_JSON }}
OPENCLAW_CLAUDE_CREDENTIALS_JSON: ${{ secrets.OPENCLAW_CLAUDE_CREDENTIALS_JSON }}
OPENCLAW_CLAUDE_SETTINGS_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_JSON }}
OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON }}
OPENCLAW_GEMINI_SETTINGS_JSON: ${{ secrets.OPENCLAW_GEMINI_SETTINGS_JSON }}
run: bash scripts/ci-hydrate-testbox-env.sh
- name: Run Testbox

View File

@@ -17,6 +17,10 @@ on:
permissions:
contents: read
concurrency:
group: ${{ github.event_name == 'pull_request' && format('{0}-pr-v1-{1}', github.workflow, github.event.pull_request.number) || format('{0}-manual-v1-{1}', github.workflow, github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
PNPM_CONFIG_STORE_DIR: "/tmp/openclaw-pnpm-store"
@@ -117,8 +121,10 @@ jobs:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
BYTEPLUS_API_KEY: ${{ secrets.BYTEPLUS_API_KEY }}
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }}
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
@@ -126,16 +132,38 @@ jobs:
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
KIMI_API_KEY: ${{ secrets.KIMI_API_KEY }}
MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
MODELSTUDIO_API_KEY: ${{ secrets.MODELSTUDIO_API_KEY }}
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
MOONSHOT_API_KEY: ${{ secrets.MOONSHOT_API_KEY }}
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
OPENCODE_ZEN_API_KEY: ${{ secrets.OPENCODE_ZEN_API_KEY }}
OPENCLAW_LIVE_BROWSER_CDP_URL: ${{ secrets.OPENCLAW_LIVE_BROWSER_CDP_URL }}
OPENCLAW_LIVE_SETUP_TOKEN: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN }}
OPENCLAW_LIVE_SETUP_TOKEN_MODEL: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_MODEL }}
OPENCLAW_LIVE_SETUP_TOKEN_PROFILE: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_PROFILE }}
OPENCLAW_LIVE_SETUP_TOKEN_VALUE: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_VALUE }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
QWEN_API_KEY: ${{ secrets.QWEN_API_KEY }}
FAL_KEY: ${{ secrets.FAL_KEY }}
RUNWAY_API_KEY: ${{ secrets.RUNWAY_API_KEY }}
DEEPGRAM_API_KEY: ${{ secrets.DEEPGRAM_API_KEY }}
TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }}
VYDRA_API_KEY: ${{ secrets.VYDRA_API_KEY }}
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
ZAI_API_KEY: ${{ secrets.ZAI_API_KEY }}
Z_AI_API_KEY: ${{ secrets.Z_AI_API_KEY }}
BYTEPLUS_ACCESS_KEY_ID: ${{ secrets.BYTEPLUS_ACCESS_KEY_ID }}
BYTEPLUS_SECRET_ACCESS_KEY: ${{ secrets.BYTEPLUS_SECRET_ACCESS_KEY }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
OPENCLAW_CODEX_AUTH_JSON: ${{ secrets.OPENCLAW_CODEX_AUTH_JSON }}
OPENCLAW_CODEX_CONFIG_TOML: ${{ secrets.OPENCLAW_CODEX_CONFIG_TOML }}
OPENCLAW_CLAUDE_JSON: ${{ secrets.OPENCLAW_CLAUDE_JSON }}
OPENCLAW_CLAUDE_CREDENTIALS_JSON: ${{ secrets.OPENCLAW_CLAUDE_CREDENTIALS_JSON }}
OPENCLAW_CLAUDE_SETTINGS_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_JSON }}
OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON }}
OPENCLAW_GEMINI_SETTINGS_JSON: ${{ secrets.OPENCLAW_GEMINI_SETTINGS_JSON }}
run: bash scripts/ci-hydrate-testbox-env.sh
- name: Run Testbox

View File

@@ -41,11 +41,32 @@ env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
jobs:
# Keep the canonical main queue quiet long enough for a follow-up push to
# cancel this run before it registers the Blacksmith matrix.
runner-admission:
permissions:
contents: read
runs-on: ubuntu-24.04
timeout-minutes: 3
env:
OPENCLAW_MAIN_CI_DEBOUNCE_SECONDS: "90"
steps:
- name: Debounce canonical main pushes
if: github.event_name == 'push' && github.repository == 'openclaw/openclaw' && github.ref == 'refs/heads/main'
run: |
set -euo pipefail
echo "Waiting ${OPENCLAW_MAIN_CI_DEBOUNCE_SECONDS}s for a superseding main push before Blacksmith admission"
sleep "${OPENCLAW_MAIN_CI_DEBOUNCE_SECONDS}"
- name: Admit non-main CI runs immediately
if: github.event_name != 'push' || github.repository != 'openclaw/openclaw' || github.ref != 'refs/heads/main'
run: echo "No canonical main debounce required"
# Preflight: establish routing truth and job matrices once, then let real
# work fan out from a single source of truth.
preflight:
permissions:
contents: read
needs: [runner-admission]
if: github.event_name != 'pull_request' || !github.event.pull_request.draft
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-24.04') }}
timeout-minutes: 20
@@ -197,7 +218,7 @@ jobs:
node --input-type=module <<'EOF'
import { appendFileSync } from "node:fs";
import {
createNodeTestShards,
createNodeTestShardBundles,
} from "./scripts/lib/ci-node-test-plan.mjs";
import {
createChannelContractTestShards,
@@ -272,18 +293,22 @@ jobs:
}
}
const compactPullRequest = isCanonicalRepository && eventName === "pull_request";
const nodeTestShards = runNodeFull
? createNodeTestShards({
? createNodeTestShardBundles({
includeReleaseOnlyPluginShards: false,
compact: compactPullRequest,
}).map((shard) => ({
check_name: shard.checkName,
runtime: "node",
task: "test-shard",
shard_name: shard.shardName,
groups: shard.groups,
configs: shard.configs,
includePatterns: shard.includePatterns,
requires_dist: shard.requiresDist,
runner: shard.runner,
timeout_minutes: shard.timeoutMinutes,
}))
: [];
const nodeTestNonDistShards = nodeTestShards.filter((shard) => !shard.requires_dist);
@@ -320,7 +345,14 @@ jobs:
run_checks_windows: runWindows,
checks_windows_matrix: createMatrix(
runWindows
? [{ check_name: "checks-windows-node-test", runtime: "node", task: "test" }]
? [
{
check_name: "checks-windows-node-test",
runtime: "node",
task: "test",
runner: "blacksmith-8vcpu-windows-2025",
},
]
: [],
),
run_macos_node: runMacos,
@@ -354,6 +386,7 @@ jobs:
security-fast:
permissions:
contents: read
needs: [runner-admission]
if: github.event_name != 'pull_request' || !github.event.pull_request.draft
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-24.04') }}
timeout-minutes: 20
@@ -558,7 +591,7 @@ jobs:
contents: read
needs: [preflight]
if: needs.preflight.outputs.run_build_artifacts == 'true'
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && 'blacksmith-32vcpu-ubuntu-2404' || 'ubuntu-24.04') }}
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && 'blacksmith-16vcpu-ubuntu-2404' || 'ubuntu-24.04') }}
timeout-minutes: 20
outputs:
channels-result: ${{ steps.built_artifact_checks.outputs['channels-result'] }}
@@ -819,6 +852,7 @@ jobs:
timeout-minutes: 60
strategy:
fail-fast: false
max-parallel: 8
matrix: ${{ fromJson(needs.preflight.outputs.checks_fast_core_matrix) }}
steps:
- name: Checkout
@@ -908,6 +942,7 @@ jobs:
timeout-minutes: 60
strategy:
fail-fast: false
max-parallel: 8
matrix: ${{ fromJson(needs.preflight.outputs.plugin_contracts_matrix) }}
steps:
- name: Checkout
@@ -988,6 +1023,7 @@ jobs:
timeout-minutes: 60
strategy:
fail-fast: false
max-parallel: 8
matrix: ${{ fromJson(needs.preflight.outputs.channel_contracts_matrix) }}
steps:
- name: Checkout
@@ -1136,10 +1172,11 @@ jobs:
name: ${{ matrix.check_name }}
needs: [preflight]
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 || 'blacksmith-8vcpu-ubuntu-2404') || 'ubuntu-24.04') }}
timeout-minutes: 60
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: ${{ matrix.timeout_minutes || 60 }}
strategy:
fail-fast: false
max-parallel: 12
matrix: ${{ fromJson(needs.preflight.outputs.checks_node_core_nondist_matrix) }}
steps:
- name: Checkout
@@ -1198,6 +1235,7 @@ jobs:
- name: Run Node test shard
env:
NODE_OPTIONS: --max-old-space-size=8192
OPENCLAW_NODE_TEST_GROUPS_JSON: ${{ toJson(matrix.groups || null) }}
OPENCLAW_NODE_TEST_CONFIGS_JSON: ${{ toJson(matrix.configs) }}
OPENCLAW_NODE_TEST_INCLUDE_PATTERNS_JSON: ${{ toJson(matrix.includePatterns) }}
OPENCLAW_VITEST_SHARD_NAME: ${{ matrix.shard_name }}
@@ -1212,28 +1250,47 @@ jobs:
import { writeFileSync } from "node:fs";
import { join } from "node:path";
const configs = JSON.parse(process.env.OPENCLAW_NODE_TEST_CONFIGS_JSON ?? "[]");
if (!Array.isArray(configs) || configs.length === 0) {
console.error("Missing node test shard configs");
process.exit(1);
}
const includePatterns = JSON.parse(process.env.OPENCLAW_NODE_TEST_INCLUDE_PATTERNS_JSON ?? "null");
const childEnv = { ...process.env };
if (Array.isArray(includePatterns) && includePatterns.length > 0) {
const includeFile = join(
process.env.RUNNER_TEMP ?? ".",
`node-test-include-${process.env.GITHUB_JOB ?? "local"}-${Date.now()}.json`,
const groups = JSON.parse(process.env.OPENCLAW_NODE_TEST_GROUPS_JSON ?? "null");
const plans = Array.isArray(groups) && groups.length > 0
? groups
: [{
configs: JSON.parse(process.env.OPENCLAW_NODE_TEST_CONFIGS_JSON ?? "[]"),
includePatterns: JSON.parse(
process.env.OPENCLAW_NODE_TEST_INCLUDE_PATTERNS_JSON ?? "null",
),
shard_name: process.env.OPENCLAW_VITEST_SHARD_NAME,
}];
for (const plan of plans) {
const configs = plan.configs;
if (!Array.isArray(configs) || configs.length === 0) {
console.error("Missing node test shard configs");
process.exit(1);
}
const childEnv = {
...process.env,
...(plan.shard_name ? { OPENCLAW_VITEST_SHARD_NAME: plan.shard_name } : {}),
};
if (Array.isArray(plan.includePatterns) && plan.includePatterns.length > 0) {
const includeFile = join(
process.env.RUNNER_TEMP ?? ".",
`node-test-include-${process.env.GITHUB_JOB ?? "local"}-${Date.now()}.json`,
);
writeFileSync(includeFile, JSON.stringify(plan.includePatterns), "utf8");
childEnv.OPENCLAW_VITEST_INCLUDE_FILE = includeFile;
} else {
delete childEnv.OPENCLAW_VITEST_INCLUDE_FILE;
}
const result = spawnSync(
"pnpm",
["exec", "node", "scripts/test-projects.mjs", ...configs],
{
env: childEnv,
stdio: "inherit",
},
);
writeFileSync(includeFile, JSON.stringify(includePatterns), "utf8");
childEnv.OPENCLAW_VITEST_INCLUDE_FILE = includeFile;
}
const result = spawnSync("pnpm", ["exec", "node", "scripts/test-projects.mjs", ...configs], {
env: childEnv,
stdio: "inherit",
});
if ((result.status ?? 1) !== 0) {
process.exit(result.status ?? 1);
if ((result.status ?? 1) !== 0) {
process.exit(result.status ?? 1);
}
}
EOF
@@ -1248,6 +1305,7 @@ jobs:
timeout-minutes: 20
strategy:
fail-fast: false
max-parallel: 8
matrix:
include:
- check_name: check-guards
@@ -1264,7 +1322,7 @@ jobs:
runner: blacksmith-16vcpu-ubuntu-2404
- check_name: check-dependencies
task: dependencies
runner: blacksmith-8vcpu-ubuntu-2404
runner: blacksmith-4vcpu-ubuntu-2404
- check_name: check-test-types
task: test-types
runner: blacksmith-4vcpu-ubuntu-2404
@@ -1385,30 +1443,39 @@ jobs:
name: ${{ matrix.check_name }}
needs: [preflight]
if: ${{ !cancelled() && always() && needs.preflight.outputs.run_check_additional == 'true' }}
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'ubuntu-24.04' || (github.repository == 'openclaw/openclaw' && 'blacksmith-8vcpu-ubuntu-2404' || 'ubuntu-24.04') }}
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
max-parallel: 8
matrix:
include:
- check_name: check-additional-boundaries-a
group: boundaries
boundary_shard: 1/4
runner: blacksmith-8vcpu-ubuntu-2404
- check_name: check-additional-boundaries-bcd
group: boundaries
boundary_shard: 2/4,3/4,4/4
runner: blacksmith-8vcpu-ubuntu-2404
- check_name: check-session-accessor-boundary
group: session-accessor-boundary
runner: blacksmith-4vcpu-ubuntu-2404
- check_name: check-session-transcript-reader-boundary
group: session-transcript-reader-boundary
runner: blacksmith-4vcpu-ubuntu-2404
- check_name: check-additional-extension-channels
group: extension-channels
runner: blacksmith-8vcpu-ubuntu-2404
- check_name: check-additional-extension-bundled
group: extension-bundled
runner: blacksmith-8vcpu-ubuntu-2404
- check_name: check-additional-extension-package-boundary
group: extension-package-boundary
runner: blacksmith-8vcpu-ubuntu-2404
- check_name: check-additional-runtime-topology-architecture
group: runtime-topology-architecture
runner: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout
shell: bash
@@ -1751,7 +1818,7 @@ jobs:
name: ${{ matrix.check_name }}
needs: [preflight]
if: needs.preflight.outputs.run_checks_windows == 'true'
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'windows-2025' || (github.repository == 'openclaw/openclaw' && 'blacksmith-16vcpu-windows-2025' || 'windows-2025') }}
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'windows-2025' || (github.repository == 'openclaw/openclaw' && (matrix.runner || 'blacksmith-8vcpu-windows-2025') || 'windows-2025') }}
timeout-minutes: 60
env:
NODE_OPTIONS: --max-old-space-size=8192
@@ -1763,6 +1830,7 @@ jobs:
shell: bash
strategy:
fail-fast: false
max-parallel: 2
matrix: ${{ fromJson(needs.preflight.outputs.checks_windows_matrix) }}
steps:
- name: Checkout
@@ -2092,6 +2160,7 @@ jobs:
timeout-minutes: 20
strategy:
fail-fast: false
max-parallel: 2
matrix: ${{ fromJson(needs.preflight.outputs.android_matrix) }}
steps:
- name: Checkout

View File

@@ -18,15 +18,16 @@ permissions:
contents: read
concurrency:
group: clawsweeper-dispatch-${{ github.repository }}-${{ github.event.issue.number || github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ github.event.action == 'edited' || github.event.action == 'synchronize' || github.event.action == 'ready_for_review' }}
group: ${{ github.event_name == 'push' && format('clawsweeper-dispatch-{0}-{1}', github.repository, github.ref) || format('clawsweeper-dispatch-{0}-{1}', github.repository, github.event.issue.number || github.event.pull_request.number || github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'push' || github.event.action == 'edited' || github.event.action == 'synchronize' || github.event.action == 'ready_for_review' }}
jobs:
dispatch:
runs-on: ubuntu-latest
if: >-
${{
github.event_name == 'issue_comment' ||
(github.event_name != 'issue_comment' ||
(github.actor != 'clawsweeper[bot]' && github.actor != 'openclaw-clawsweeper[bot]')) &&
!(
endsWith(github.actor, '[bot]') &&
(github.event.action == 'labeled' || github.event.action == 'unlabeled')
@@ -41,6 +42,34 @@ jobs:
if: ${{ github.event.action == 'labeled' || github.event.action == 'unlabeled' }}
run: sleep 20
- name: Debounce main push dispatch
if: ${{ github.event_name == 'push' }}
run: sleep 45
- name: Install GitHub API backoff helper
run: |
cat > "$RUNNER_TEMP/github-api-backoff.sh" <<'BASH'
gh_api_with_retry() {
local attempt output status lower_output
for attempt in 1 2 3 4 5; do
if output="$(gh api "$@" 2>&1)"; then
printf '%s\n' "$output"
return 0
fi
status=$?
lower_output="${output,,}"
if [[ "$lower_output" != *"rate limit"* && "$output" != *"HTTP 429"* ]]; then
printf '%s\n' "$output" >&2
return "$status"
fi
echo "::warning::GitHub API throttled ClawSweeper dispatch on attempt ${attempt}; retrying after backoff." >&2
sleep $((attempt * attempt * 5))
done
printf '%s\n' "$output" >&2
return "$status"
}
BASH
- name: Create ClawSweeper dispatch token
id: token
if: ${{ env.HAS_CLAWSWEEPER_APP_PRIVATE_KEY == 'true' }}
@@ -52,9 +81,27 @@ jobs:
repositories: clawsweeper
permission-contents: write
- name: Pre-filter ClawSweeper comment
id: comment_filter
if: ${{ github.event_name == 'issue_comment' }}
env:
COMMENT_BODY: ${{ github.event.comment.body }}
run: |
set -euo pipefail
if grep -Eiq '(^|[[:space:]])@(clawsweeper|openclaw-clawsweeper)\b(\[bot\])?|(^|[[:space:]])/(clawsweeper|review|autoclose|auto([[:space:]]+|-)?merge)\b' <<< "$COMMENT_BODY"; then
echo "is_command=true" >> "$GITHUB_OUTPUT"
else
echo "is_command=false" >> "$GITHUB_OUTPUT"
fi
- name: Create target comment token
id: target_token
if: ${{ github.event_name == 'issue_comment' && env.HAS_CLAWSWEEPER_APP_PRIVATE_KEY == 'true' }}
if: >-
${{
github.event_name == 'issue_comment' &&
steps.comment_filter.outputs.is_command == 'true' &&
env.HAS_CLAWSWEEPER_APP_PRIVATE_KEY == 'true'
}}
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
with:
client-id: ${{ env.CLAWSWEEPER_APP_CLIENT_ID }}
@@ -77,6 +124,7 @@ jobs:
echo "::notice::Skipping GitHub activity dispatch because no ClawSweeper app token is configured."
exit 0
fi
. "$RUNNER_TEMP/github-api-backoff.sh"
activity="$(jq -c \
--arg target_repo "$TARGET_REPO" \
--arg event_name "$SOURCE_EVENT" \
@@ -143,7 +191,7 @@ jobs:
' "$GITHUB_EVENT_PATH")"
payload="$(jq -nc --argjson activity "$activity" \
'{event_type:"github_activity",client_payload:{activity:$activity}}')"
if gh api repos/openclaw/clawsweeper/dispatches \
if gh_api_with_retry repos/openclaw/clawsweeper/dispatches \
--method POST \
--input - <<< "$payload"; then
echo "Dispatched GitHub activity to ClawSweeper."
@@ -165,6 +213,7 @@ jobs:
echo "::notice::Skipping ClawSweeper dispatch because no ClawSweeper app token is configured. Not falling back to a maintainer token."
exit 0
fi
. "$RUNNER_TEMP/github-api-backoff.sh"
payload="$(jq -nc \
--arg target_repo "$TARGET_REPO" \
--argjson item_number "$ITEM_NUMBER" \
@@ -173,7 +222,7 @@ jobs:
--arg source_action "$SOURCE_ACTION" \
--argjson supersedes_in_progress "$SUPERSEDES_IN_PROGRESS" \
'{event_type:"clawsweeper_item",client_payload:{target_repo:$target_repo,item_number:$item_number,item_kind:$item_kind,source_event:$source_event,source_action:$source_action,supersedes_in_progress:$supersedes_in_progress}}')"
if gh api repos/openclaw/clawsweeper/dispatches \
if gh_api_with_retry repos/openclaw/clawsweeper/dispatches \
--method POST \
--input - <<< "$payload"; then
echo "Dispatched ClawSweeper review."
@@ -182,7 +231,11 @@ jobs:
fi
- name: Acknowledge and dispatch ClawSweeper comment
if: ${{ github.event_name == 'issue_comment' }}
if: >-
${{
github.event_name == 'issue_comment' &&
steps.comment_filter.outputs.is_command == 'true'
}}
env:
DISPATCH_TOKEN: ${{ steps.token.outputs.token }}
TARGET_TOKEN: ${{ steps.target_token.outputs.token }}
@@ -198,15 +251,12 @@ jobs:
echo "::notice::Skipping ClawSweeper comment dispatch because no ClawSweeper app token is configured."
exit 0
fi
. "$RUNNER_TEMP/github-api-backoff.sh"
body_file="$RUNNER_TEMP/clawsweeper-comment-body.txt"
printf '%s\n' "$COMMENT_BODY" > "$body_file"
if ! grep -Eiq '(^|[[:space:]])@(clawsweeper|openclaw-clawsweeper)\b(\[bot\])?|(^|[[:space:]])/(clawsweeper|review|automerge|autoclose)\b' "$body_file"; then
echo "No ClawSweeper command found in comment."
exit 0
fi
if [ -n "$TARGET_TOKEN" ]; then
err="$(mktemp)"
if GH_TOKEN="$TARGET_TOKEN" gh api -X POST \
if GH_TOKEN="$TARGET_TOKEN" gh_api_with_retry -X POST \
-H "Accept: application/vnd.github+json" \
"repos/$TARGET_REPO/issues/comments/$COMMENT_ID/reactions" \
-f content="eyes" 2>"$err" >/dev/null; then
@@ -233,7 +283,7 @@ jobs:
"Command router queued. I will update this comment with the next step.")"
status_payload="$(jq -nc --arg body "$status_body" '{body:$body}')"
status_err="$(mktemp)"
if status_response="$(GH_TOKEN="$TARGET_TOKEN" gh api \
if status_response="$(GH_TOKEN="$TARGET_TOKEN" gh_api_with_retry \
"repos/$TARGET_REPO/issues/$ITEM_NUMBER/comments" \
--method POST \
--input - <<< "$status_payload" 2>"$status_err")"; then
@@ -254,7 +304,7 @@ jobs:
--arg source_event "issue_comment" \
--arg source_action "$SOURCE_ACTION" \
'{event_type:"clawsweeper_comment",client_payload:({target_repo:$target_repo,item_number:$item_number,comment_id:$comment_id,source_event:$source_event,source_action:$source_action,max_comments:"1"} + (if $status_comment_id != "" then {status_comment_id:($status_comment_id|tonumber)} else {} end))}')"
if GH_TOKEN="$DISPATCH_TOKEN" gh api repos/openclaw/clawsweeper/dispatches \
if GH_TOKEN="$DISPATCH_TOKEN" gh_api_with_retry repos/openclaw/clawsweeper/dispatches \
--method POST \
--input - <<< "$payload"; then
echo "Dispatched ClawSweeper comment router."
@@ -276,6 +326,7 @@ jobs:
echo "::notice::Skipping ClawSweeper commit dispatch because no ClawSweeper app token is configured. Not falling back to a maintainer token."
exit 0
fi
. "$RUNNER_TEMP/github-api-backoff.sh"
case "$CREATE_CHECKS" in
true|TRUE|1|yes|YES|on|ON) create_checks=true ;;
*) create_checks=false ;;
@@ -287,7 +338,7 @@ jobs:
--arg ref "$SOURCE_REF" \
--argjson create_checks "$create_checks" \
'{event_type:"clawsweeper_commit_review",client_payload:{target_repo:$target_repo,before_sha:$before_sha,after_sha:$after_sha,ref:$ref,enabled:true,create_checks:$create_checks}}')"
if gh api repos/openclaw/clawsweeper/dispatches \
if gh_api_with_retry repos/openclaw/clawsweeper/dispatches \
--method POST \
--input - <<< "$payload"; then
echo "Dispatched ClawSweeper commit review."

View File

@@ -6,7 +6,7 @@ on:
- cron: "0 7 * * *"
concurrency:
group: codeql-android-critical-security-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.sha }}
group: codeql-android-critical-security-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && format('manual-{0}', github.run_id) || format('ref-{0}', github.ref) }}
cancel-in-progress: false
env:

View File

@@ -96,7 +96,7 @@ on:
- "src/auto-reply/reply/post-compaction-context.ts"
- "src/auto-reply/reply/queue/**"
- "src/auto-reply/reply/startup-context.ts"
- "src/commands/doctor-cron-dreaming-payload-migration.ts"
- "src/commands/doctor/cron/dreaming-payload-migration.ts"
- "src/commands/doctor-memory-search.ts"
- "src/commands/doctor-session-*.ts"
- "src/commands/session-store-targets.ts"
@@ -136,7 +136,7 @@ on:
- cron: "30 6 * * *"
concurrency:
group: codeql-critical-quality-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name == 'pull_request' && github.event.pull_request.number || github.sha }}
group: codeql-critical-quality-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && format('manual-{0}', github.run_id) || github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('ref-{0}', github.ref) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
@@ -257,7 +257,7 @@ jobs:
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/*)
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/*)
memory=true
;;
src/infra/outbound/base-session-key.ts|src/infra/outbound/delivery-queue*.ts|src/infra/outbound/outbound-session.ts|src/infra/outbound/session-binding*.ts|src/infra/outbound/session-context.ts|src/infra/outbound/targets-session.ts)
@@ -295,7 +295,7 @@ jobs:
src/model-catalog/*|src/plugins/*provider*.ts|src/plugins/capability-provider-runtime.ts|src/plugins/compaction-provider.ts|src/plugins/memory-embedding-provider*.ts|src/plugins/memory-embedding-providers*.ts|src/plugins/migration-provider-runtime.ts|src/plugins/synthetic-auth.runtime.ts|src/plugins/web-fetch-providers*.ts|src/plugins/web-search-providers*.ts)
provider=true
;;
src/plugins/activation-planner.ts|src/plugins/api-builder.ts|src/plugins/bundled-*.ts|src/plugins/captured-registration.ts|src/plugins/config-*.ts|src/plugins/discovery.ts|src/plugins/effective-plugin-ids.ts|src/plugins/externalized-bundled-plugins.ts|src/plugins/installed-plugin-index*.ts|src/plugins/loader*.ts|src/plugins/manifest*.ts|src/plugins/module-export.ts|src/plugins/package-entrypoints.ts|src/plugins/plugin-registry*.ts|src/plugins/public-surface*.ts|src/plugins/registry.ts|src/plugins/registry-types.ts|src/plugins/runtime|src/plugins/runtime/*|src/plugins/runtime-state.ts|src/plugins/runtime.ts|src/plugins/sdk-alias.ts|src/plugins/source-loader.ts|src/plugins/types.ts|src/plugins/validation-diagnostics.ts)
src/plugins/activation-planner.ts|src/plugins/api-builder.ts|src/plugins/bundled-*.ts|src/plugins/captured-registration.ts|src/plugins/config-*.ts|src/plugins/discovery.ts|src/plugins/effective-plugin-ids.ts|src/plugins/externalized-bundled-plugins.ts|src/plugins/installed-plugin-index*.ts|src/plugins/loader*.ts|src/plugins/manifest*.ts|src/plugins/module-export.ts|src/plugins/package-entrypoints.ts|src/plugins/plugin-registry*.ts|src/plugins/public-surface*.ts|src/plugins/registry.ts|src/plugins/registry-types.ts|src/plugins/runtime|src/plugins/runtime/*|src/plugins/runtime-state.ts|src/plugins/runtime.ts|src/plugins/sdk-alias.ts|src/plugins/types.ts|src/plugins/validation-diagnostics.ts)
plugin=true
;;
packages/plugin-package-contract/*|packages/plugin-sdk/*)

View File

@@ -6,7 +6,7 @@ on:
- cron: "0 8 * * 1"
concurrency:
group: codeql-macos-critical-security-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.sha }}
group: codeql-macos-critical-security-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && format('manual-{0}', github.run_id) || format('ref-{0}', github.ref) }}
cancel-in-progress: false
env:

View File

@@ -32,8 +32,8 @@ on:
- cron: "0 6 * * *"
concurrency:
group: codeql-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.event_name == 'pull_request' && github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
group: codeql-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && format('manual-{0}', github.run_id) || github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('ref-{0}', github.ref) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main') }}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"

View File

@@ -23,8 +23,8 @@ permissions:
contents: write
concurrency:
group: control-ui-locale-refresh
cancel-in-progress: false
group: control-ui-locale-refresh-${{ github.event_name == 'push' && github.ref || github.event_name == 'workflow_dispatch' && format('manual-{0}', github.run_id) || github.event_name == 'release' && format('release-{0}', github.event.release.tag_name) || format('{0}-{1}', github.event_name, github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
jobs:
plan:

View File

@@ -663,8 +663,10 @@ jobs:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
BYTEPLUS_API_KEY: ${{ secrets.BYTEPLUS_API_KEY }}
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }}
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
@@ -672,16 +674,38 @@ jobs:
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
KIMI_API_KEY: ${{ secrets.KIMI_API_KEY }}
MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
MODELSTUDIO_API_KEY: ${{ secrets.MODELSTUDIO_API_KEY }}
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
MOONSHOT_API_KEY: ${{ secrets.MOONSHOT_API_KEY }}
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
OPENCODE_ZEN_API_KEY: ${{ secrets.OPENCODE_ZEN_API_KEY }}
OPENCLAW_LIVE_BROWSER_CDP_URL: ${{ secrets.OPENCLAW_LIVE_BROWSER_CDP_URL }}
OPENCLAW_LIVE_SETUP_TOKEN: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN }}
OPENCLAW_LIVE_SETUP_TOKEN_MODEL: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_MODEL }}
OPENCLAW_LIVE_SETUP_TOKEN_PROFILE: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_PROFILE }}
OPENCLAW_LIVE_SETUP_TOKEN_VALUE: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_VALUE }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
QWEN_API_KEY: ${{ secrets.QWEN_API_KEY }}
FAL_KEY: ${{ secrets.FAL_KEY }}
RUNWAY_API_KEY: ${{ secrets.RUNWAY_API_KEY }}
DEEPGRAM_API_KEY: ${{ secrets.DEEPGRAM_API_KEY }}
TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }}
VYDRA_API_KEY: ${{ secrets.VYDRA_API_KEY }}
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
ZAI_API_KEY: ${{ secrets.ZAI_API_KEY }}
Z_AI_API_KEY: ${{ secrets.Z_AI_API_KEY }}
BYTEPLUS_ACCESS_KEY_ID: ${{ secrets.BYTEPLUS_ACCESS_KEY_ID }}
BYTEPLUS_SECRET_ACCESS_KEY: ${{ secrets.BYTEPLUS_SECRET_ACCESS_KEY }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
OPENCLAW_CODEX_AUTH_JSON: ${{ secrets.OPENCLAW_CODEX_AUTH_JSON }}
OPENCLAW_CODEX_CONFIG_TOML: ${{ secrets.OPENCLAW_CODEX_CONFIG_TOML }}
OPENCLAW_CLAUDE_JSON: ${{ secrets.OPENCLAW_CLAUDE_JSON }}
OPENCLAW_CLAUDE_CREDENTIALS_JSON: ${{ secrets.OPENCLAW_CLAUDE_CREDENTIALS_JSON }}
OPENCLAW_CLAUDE_SETTINGS_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_JSON }}
OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON }}
OPENCLAW_GEMINI_SETTINGS_JSON: ${{ secrets.OPENCLAW_GEMINI_SETTINGS_JSON }}
run: bash scripts/ci-hydrate-testbox-env.sh
- name: Mark Crabbox ready

View File

@@ -13,6 +13,10 @@ on:
permissions:
contents: read
concurrency:
group: docs-sync-publish-${{ github.event_name == 'workflow_dispatch' && format('manual-{0}', github.run_id) || github.ref }}
cancel-in-progress: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
jobs:
sync-publish-repo:
runs-on: ubuntu-latest

View File

@@ -70,7 +70,7 @@ on:
default: ""
type: string
npm_telegram_package_spec:
description: Optional published package spec for the package Telegram E2E lane
description: Optional published package spec for the focused package Telegram E2E rerun
required: false
default: ""
type: string
@@ -95,7 +95,7 @@ on:
default: ""
type: string
npm_telegram_provider_mode:
description: Provider mode for the package Telegram E2E lane
description: Provider mode for the focused package Telegram E2E rerun
required: false
default: mock-openai
type: choice
@@ -103,7 +103,7 @@ on:
- mock-openai
- live-frontier
npm_telegram_scenario:
description: Optional comma-separated Telegram scenario ids for the package Telegram lane
description: Optional comma-separated Telegram scenario ids for the focused package Telegram E2E rerun
required: false
default: ""
type: string
@@ -200,14 +200,16 @@ jobs:
if [[ -n "${RELEASE_PACKAGE_SPEC// }" ]]; then
echo "- Published release package: \`${RELEASE_PACKAGE_SPEC}\`"
fi
if [[ -n "${NPM_TELEGRAM_PACKAGE_SPEC// }" ]]; then
if [[ "$RERUN_GROUP" == "npm-telegram" && -n "${NPM_TELEGRAM_PACKAGE_SPEC// }" ]]; then
echo "- Published-package Telegram E2E: \`${NPM_TELEGRAM_PACKAGE_SPEC}\`"
elif [[ -n "${RELEASE_PACKAGE_SPEC// }" ]]; then
elif [[ "$RERUN_GROUP" == "npm-telegram" && -n "${RELEASE_PACKAGE_SPEC// }" ]]; then
echo "- Published-package Telegram E2E: \`${RELEASE_PACKAGE_SPEC}\`"
elif [[ "$RERUN_GROUP" == "all" && "$RELEASE_PROFILE" == "full" ]]; then
echo "- Package Telegram E2E: parent \`release-package-under-test\` artifact"
elif [[ "$RERUN_GROUP" == "npm-telegram" ]]; then
echo "- Package Telegram E2E: focused rerun requires \`release_package_spec\` or \`npm_telegram_package_spec\`"
elif [[ "$RERUN_GROUP" == "all" || "$RERUN_GROUP" == "release-checks" || "$RERUN_GROUP" == "package" ]]; then
echo "- Package Telegram E2E: OpenClaw Release Checks Package Acceptance"
else
echo "- Package Telegram E2E: skipped unless \`release_profile=full\`, \`release_package_spec\`, or \`npm_telegram_package_spec\` is provided"
echo "- Package Telegram E2E: skipped by rerun group"
fi
if [[ -n "${EVIDENCE_PACKAGE_SPEC// }" ]]; then
echo "- Private evidence package proof: \`${EVIDENCE_PACKAGE_SPEC}\`"
@@ -764,83 +766,13 @@ jobs:
dispatch_and_wait openclaw-release-checks.yml "${args[@]}"
prepare_release_package:
name: Prepare release package artifact
needs: [resolve_target, docker_runtime_assets_preflight]
if: ${{ always() && needs.resolve_target.result == 'success' && inputs.npm_telegram_package_spec == '' && inputs.release_package_spec == '' && inputs.rerun_group == 'all' && inputs.release_profile == 'full' && needs.docker_runtime_assets_preflight.result == 'success' }}
runs-on: ubuntu-24.04
timeout-minutes: 15
permissions:
contents: read
packages: write
outputs:
artifact_name: ${{ steps.artifact.outputs.name }}
package_sha256: ${{ steps.package.outputs.sha256 }}
package_version: ${{ steps.package.outputs.package_version }}
source_sha: ${{ steps.package.outputs.source_sha }}
steps:
- name: Checkout trusted workflow ref
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: true
ref: ${{ github.ref_name }}
fetch-depth: 0
- name: Set artifact metadata
id: artifact
run: echo "name=release-package-under-test" >> "$GITHUB_OUTPUT"
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
node-version: ${{ env.NODE_VERSION }}
install-bun: "true"
install-deps: "false"
- name: Resolve release package artifact
id: package
shell: bash
env:
PACKAGE_REF: ${{ needs.resolve_target.outputs.sha }}
run: |
set -euo pipefail
node scripts/resolve-openclaw-package-candidate.mjs \
--source ref \
--package-ref "$PACKAGE_REF" \
--output-dir .artifacts/docker-e2e-package \
--output-name openclaw-current.tgz \
--metadata .artifacts/docker-e2e-package/package-candidate.json \
--github-output "$GITHUB_OUTPUT"
digest="$(node -p "JSON.parse(require('fs').readFileSync('.artifacts/docker-e2e-package/package-candidate.json', 'utf8')).sha256")"
version="$(node -p "JSON.parse(require('fs').readFileSync('.artifacts/docker-e2e-package/package-candidate.json', 'utf8')).version")"
source_sha="$(node -p "JSON.parse(require('fs').readFileSync('.artifacts/docker-e2e-package/package-candidate.json', 'utf8')).packageSourceSha")"
echo "source_sha=$source_sha" >> "$GITHUB_OUTPUT"
{
echo "## Release package artifact"
echo
echo "- Artifact: \`release-package-under-test\`"
echo "- Package ref: \`$PACKAGE_REF\`"
echo "- SHA-256: \`$digest\`"
echo "- Version: \`$version\`"
echo "- Source SHA: \`$source_sha\`"
} >> "$GITHUB_STEP_SUMMARY"
- name: Upload release package artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: release-package-under-test
path: |
.artifacts/docker-e2e-package/openclaw-current.tgz
.artifacts/docker-e2e-package/package-candidate.json
if-no-files-found: error
npm_telegram:
name: Run package Telegram E2E
needs: [resolve_target, prepare_release_package]
if: ${{ always() && contains(fromJSON('["all","npm-telegram"]'), inputs.rerun_group) && (inputs.npm_telegram_package_spec != '' || inputs.release_package_spec != '' || (inputs.rerun_group == 'all' && inputs.release_profile == 'full')) }}
needs: [resolve_target]
if: ${{ always() && needs.resolve_target.result == 'success' && inputs.rerun_group == 'npm-telegram' && (inputs.npm_telegram_package_spec != '' || inputs.release_package_spec != '') }}
continue-on-error: ${{ startsWith(github.ref, 'refs/heads/tideclaw/alpha/') }}
runs-on: ubuntu-24.04
timeout-minutes: ${{ inputs.release_profile == 'full' && 120 || 60 }}
timeout-minutes: ${{ inputs.release_profile == 'full' && 360 || 60 }}
outputs:
run_id: ${{ steps.dispatch.outputs.run_id }}
url: ${{ steps.dispatch.outputs.url }}
@@ -853,8 +785,6 @@ jobs:
CHILD_WORKFLOW_REF: ${{ github.ref_name }}
TARGET_SHA: ${{ needs.resolve_target.outputs.sha }}
PACKAGE_SPEC: ${{ inputs.npm_telegram_package_spec || inputs.release_package_spec }}
PACKAGE_ARTIFACT_NAME: ${{ needs.prepare_release_package.outputs.artifact_name }}
PREPARE_PACKAGE_RESULT: ${{ needs.prepare_release_package.result }}
PROVIDER_MODE: ${{ inputs.npm_telegram_provider_mode }}
SCENARIO: ${{ inputs.npm_telegram_scenario }}
run: |
@@ -883,18 +813,7 @@ jobs:
return "$status"
}
args=(-f package_spec="${PACKAGE_SPEC:-openclaw@beta}" -f harness_ref="$TARGET_SHA" -f provider_mode="$PROVIDER_MODE")
if [[ -z "${PACKAGE_SPEC// }" ]]; then
if [[ "$PREPARE_PACKAGE_RESULT" != "success" || -z "${PACKAGE_ARTIFACT_NAME// }" ]]; then
echo "Full release Telegram requires either npm_telegram_package_spec or a prepared release-package-under-test artifact." >&2
exit 1
fi
args+=(
-f package_artifact_name="$PACKAGE_ARTIFACT_NAME"
-f package_artifact_run_id="${GITHUB_RUN_ID}"
-f package_label="full-release-${TARGET_SHA:0:12}"
)
fi
args=(-f package_spec="$PACKAGE_SPEC" -f harness_ref="$TARGET_SHA" -f provider_mode="$PROVIDER_MODE")
if [[ -n "${SCENARIO// }" ]]; then
args+=(-f scenario="$SCENARIO")
fi
@@ -971,7 +890,7 @@ jobs:
needs: [resolve_target, docker_runtime_assets_preflight]
if: ${{ always() && needs.resolve_target.result == 'success' && contains(fromJSON('["all","performance"]'), inputs.rerun_group) && (inputs.rerun_group != 'all' || needs.docker_runtime_assets_preflight.result == 'success') }}
runs-on: ubuntu-24.04
timeout-minutes: 120
timeout-minutes: ${{ inputs.release_profile == 'full' && 360 || 120 }}
outputs:
run_id: ${{ steps.dispatch.outputs.run_id }}
url: ${{ steps.dispatch.outputs.url }}

View File

@@ -1686,7 +1686,8 @@ jobs:
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
OPENCLAW_LIVE_PROVIDERS: ${{ matrix.providers }}
OPENCLAW_LIVE_IMAGE: ${{ needs.prepare_live_test_image.outputs.live_image }}
OPENCLAW_LIVE_MAX_MODELS: "6"
OPENCLAW_LIVE_MODELS: ${{ matrix.models || 'modern' }}
OPENCLAW_LIVE_MAX_MODELS: ${{ matrix.max_models || '6' }}
OPENCLAW_LIVE_MODEL_TIMEOUT_MS: "45000"
OPENCLAW_SKIP_DOCKER_BUILD: "1"
OPENCLAW_VITEST_MAX_WORKERS: "2"
@@ -2000,7 +2001,7 @@ jobs:
profiles: stable full
- suite_id: native-live-src-gateway-profiles-minimax
label: Native live gateway profiles MiniMax
command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=minimax,minimax-portal OPENCLAW_LIVE_GATEWAY_MODELS=minimax/MiniMax-M3,minimax-portal/MiniMax-M3 OPENCLAW_LIVE_GATEWAY_MAX_MODELS=2 node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles
command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=minimax,minimax-portal OPENCLAW_LIVE_GATEWAY_MODELS=minimax/MiniMax-M2.7,minimax-portal/MiniMax-M2.7 OPENCLAW_LIVE_GATEWAY_MAX_MODELS=2 node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles
timeout_minutes: 60
profile_env_only: false
profiles: stable full
@@ -2303,7 +2304,7 @@ jobs:
profiles: stable full
- suite_id: live-gateway-minimax-docker
label: Docker live gateway MiniMax
command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=minimax,minimax-portal OPENCLAW_LIVE_GATEWAY_MODELS=minimax/MiniMax-M3,minimax-portal/MiniMax-M3 OPENCLAW_LIVE_GATEWAY_MAX_MODELS=1 OPENCLAW_LIVE_GATEWAY_STEP_TIMEOUT_MS=90000 OPENCLAW_LIVE_GATEWAY_MODEL_TIMEOUT_MS=180000 OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" timeout --foreground --kill-after=30s 35m bash .release-harness/scripts/test-live-gateway-models-docker.sh
command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=minimax,minimax-portal OPENCLAW_LIVE_GATEWAY_MODELS=minimax/MiniMax-M2.7,minimax-portal/MiniMax-M2.7 OPENCLAW_LIVE_GATEWAY_MAX_MODELS=2 OPENCLAW_LIVE_GATEWAY_STEP_TIMEOUT_MS=90000 OPENCLAW_LIVE_GATEWAY_MODEL_TIMEOUT_MS=180000 OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" timeout --foreground --kill-after=30s 35m bash .release-harness/scripts/test-live-gateway-models-docker.sh
timeout_minutes: 40
profile_env_only: false
profiles: stable full

View File

@@ -45,7 +45,7 @@ on:
kova_ref:
description: openclaw/Kova Git ref to install
required: false
default: b63b6f9e20efb23641df00487e982230d81a90ac
default: 4f146016583018bad9e24f8e64a6af5f963bb7ee
type: string
dispatch_id:
description: Optional parent workflow dispatch identifier
@@ -66,6 +66,7 @@ env:
OCM_LINUX_X64_SHA256: b849b8de5d77e97e0df9319703254ae95e29d7f26a7552ea79bf173ff110ea0a
KOVA_REPOSITORY: openclaw/Kova
PERFORMANCE_MODEL_ID: gpt-5.5
KOVA_SCENARIO_TIMEOUT_MS: "300000"
jobs:
kova:
@@ -98,7 +99,7 @@ jobs:
live: "true"
include_filters: "scenario:agent-cold-warm-message"
env:
KOVA_REF: ${{ inputs.kova_ref || 'b63b6f9e20efb23641df00487e982230d81a90ac' }}
KOVA_REF: ${{ inputs.kova_ref || '4f146016583018bad9e24f8e64a6af5f963bb7ee' }}
KOVA_HOME: ${{ github.workspace }}/.artifacts/kova/home/${{ matrix.lane }}
PERFORMANCE_HELPER_DIR: ${{ github.workspace }}/.artifacts/performance-workflow
REPORT_DIR: ${{ github.workspace }}/.artifacts/kova/reports/${{ matrix.lane }}
@@ -291,6 +292,7 @@ jobs:
--auth "$AUTH_MODE"
--parallel 1
--repeat "$repeat"
--timeout-ms "$KOVA_SCENARIO_TIMEOUT_MS"
--report-dir "$REPORT_DIR"
--execute
--json
@@ -361,6 +363,7 @@ jobs:
- Kova repository: ${KOVA_REPOSITORY}
- Kova ref: ${KOVA_REF}
- Kova profile: ${PROFILE}
- Kova scenario timeout: ${KOVA_SCENARIO_TIMEOUT_MS}ms
- Lane auth: ${AUTH_MODE}
- Lane model: ${PERFORMANCE_MODEL_ID}
- Lane repeat: ${repeat}

View File

@@ -717,7 +717,6 @@ jobs:
published_upgrade_survivor_baselines: ${{ needs.resolve_target.outputs.run_release_soak == 'true' && 'last-stable-4 2026.4.23 2026.5.2 2026.4.15' || '' }}
published_upgrade_survivor_scenarios: ${{ needs.resolve_target.outputs.run_release_soak == 'true' && 'reported-issues' || '' }}
telegram_mode: mock-openai
telegram_scenarios: telegram-help-command,telegram-commands-command,telegram-tools-compact-command,telegram-whoami-command,telegram-status-command,telegram-other-bot-command-gating,telegram-context-command,telegram-mentioned-message-reply,telegram-long-final-reuses-preview,telegram-mention-gating
secrets:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}

View File

@@ -519,12 +519,7 @@ jobs:
local workflow="$1"
shift
local before_json dispatch_output run_id
before_json="$(gh api -X GET "repos/${GITHUB_REPOSITORY}/actions/workflows/${workflow}/runs" \
-F event=workflow_dispatch \
-F per_page=100 \
--jq '[.workflow_runs[].id]')"
local dispatch_output run_id
dispatch_output="$(gh workflow run --repo "$GITHUB_REPOSITORY" "$workflow" --ref "$workflow_ref" "$@" 2>&1)"
printf '%s\n' "$dispatch_output" >&2
run_id="$(
@@ -534,22 +529,7 @@ jobs:
)"
if [[ -z "$run_id" ]]; then
for _ in $(seq 1 60); do
run_id="$(
BEFORE_IDS="$before_json" gh api -X GET "repos/${GITHUB_REPOSITORY}/actions/workflows/${workflow}/runs" \
-F event=workflow_dispatch \
-F per_page=50 \
--jq '.workflow_runs | map({databaseId:.id, createdAt:.created_at}) | map(select(.databaseId as $id | (env.BEFORE_IDS | fromjson | index($id) | not))) | sort_by(.createdAt) | reverse | .[0].databaseId // empty'
)"
if [[ -n "$run_id" ]]; then
break
fi
sleep 5
done
fi
if [[ -z "${run_id:-}" ]]; then
echo "Could not find dispatched run for ${workflow}." >&2
echo "gh workflow run ${workflow} did not return an Actions run URL; refusing to guess from recent workflow_dispatch runs." >&2
exit 1
fi

View File

@@ -23,8 +23,8 @@ permissions:
contents: write
concurrency:
group: openclaw-stable-main-closeout
cancel-in-progress: false
group: openclaw-stable-main-closeout-${{ github.event_name == 'workflow_dispatch' && (inputs.tag || github.run_id) || github.ref }}
cancel-in-progress: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
jobs:
resolve:
@@ -43,6 +43,30 @@ jobs:
should_closeout: ${{ steps.inputs.outputs.should_closeout }}
tag: ${{ steps.inputs.outputs.tag }}
steps:
- name: Install GitHub API backoff helper
run: |
cat > "$RUNNER_TEMP/github-api-backoff.sh" <<'BASH'
gh_with_retry() {
local attempt output status lower_output
for attempt in 1 2 3 4 5; do
if output="$(gh "$@" 2>&1)"; then
printf '%s\n' "$output"
return 0
fi
status=$?
lower_output="${output,,}"
if [[ "$lower_output" != *"rate limit"* && "$output" != *"HTTP 429"* ]]; then
printf '%s\n' "$output" >&2
return "$status"
fi
echo "::warning::GitHub API throttled stable closeout on attempt ${attempt}; retrying after backoff." >&2
sleep $((attempt * attempt * 5))
done
printf '%s\n' "$output" >&2
return "$status"
}
BASH
- name: Checkout pushed main
if: ${{ github.event_name == 'push' }}
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
@@ -62,9 +86,13 @@ jobs:
TRIGGER_SHA: ${{ github.sha }}
run: |
set -euo pipefail
if [[ "$EVENT_NAME" == "push" ]]; then
sleep 45
fi
. "$RUNNER_TEMP/github-api-backoff.sh"
if [[ "$EVENT_NAME" == "push" ]]; then
main_ref="$TRIGGER_SHA"
tag="$(gh release list --repo "$GITHUB_REPOSITORY" --exclude-drafts --limit 100 \
tag="$(gh_with_retry release list --repo "$GITHUB_REPOSITORY" --exclude-drafts --limit 100 \
--json tagName,isPrerelease,publishedAt \
--jq '[.[] | select(.isPrerelease | not) | select(.tagName | test("^v[0-9]{4}\\.[0-9]+\\.[0-9]+(-[0-9]+)?$"))] | sort_by(.publishedAt) | last | .tagName // empty')"
if [[ -z "$tag" ]]; then
@@ -88,8 +116,27 @@ jobs:
if [[ "$release_package_version" =~ ^(.+)-[0-9]+$ ]]; then
fallback_package_version="${BASH_REMATCH[1]}"
fi
tag_package_version="$(gh api "repos/$GITHUB_REPOSITORY/contents/package.json?ref=$tag" \
--jq '.content' | tr -d '\n' | base64 --decode | jq -r '.version // empty')"
tag_package_content="$RUNNER_TEMP/tag-package-content.b64"
tag_package_read=false
for attempt in 1 2 3; do
if gh_with_retry api "repos/$GITHUB_REPOSITORY/contents/package.json?ref=$tag" \
--jq '.content' > "$tag_package_content"; then
tag_package_read=true
break
fi
if [[ "$attempt" != "3" ]]; then
sleep $((attempt * 5))
fi
done
if [[ "$tag_package_read" != "true" ]]; then
echo "Stable closeout could not read package.json for $tag from GitHub API." >&2
exit 1
fi
if ! tag_package_json="$(tr -d '\n' < "$tag_package_content" | base64 --decode)"; then
echo "Stable closeout package.json content for $tag was not valid base64." >&2
exit 1
fi
tag_package_version="$(jq -r '.version // empty' <<<"$tag_package_json")"
fallback_correction=false
evidence_source_tag="$tag"
if [[ "$release_package_version" != "$fallback_package_version" &&
@@ -107,7 +154,7 @@ jobs:
closeout_checksum_asset="${closeout_asset}.sha256"
closeout_dir="$RUNNER_TEMP/release-closeout-evidence"
mkdir -p "$closeout_dir"
gh release download "$tag" --repo "$GITHUB_REPOSITORY" \
gh_with_retry release download "$tag" --repo "$GITHUB_REPOSITORY" \
--pattern "$closeout_asset" --pattern "$closeout_checksum_asset" --dir "$closeout_dir" || true
closeout_json_path="$closeout_dir/$closeout_asset"
closeout_checksum_path="$closeout_dir/$closeout_checksum_asset"
@@ -163,8 +210,11 @@ jobs:
fi
evidence_dir="$RUNNER_TEMP/release-postpublish-evidence"
mkdir -p "$evidence_dir"
if ! gh release download "$evidence_source_tag" --repo "$GITHUB_REPOSITORY" \
--pattern "$evidence_asset" --pattern "$evidence_checksum_asset" --dir "$evidence_dir"; then
gh_with_retry release download "$evidence_source_tag" --repo "$GITHUB_REPOSITORY" \
--pattern "$evidence_asset" --pattern "$evidence_checksum_asset" --dir "$evidence_dir" || true
evidence_path="$evidence_dir/$evidence_asset"
evidence_checksum_path="$evidence_dir/$evidence_checksum_asset"
if [[ ! -f "$evidence_path" || ! -f "$evidence_checksum_path" ]]; then
if [[ "$EVENT_NAME" == "push" ]]; then
echo "Stable closeout skipped: $evidence_source_tag predates immutable postpublish evidence." >&2
echo "should_closeout=false" >> "$GITHUB_OUTPUT"
@@ -173,7 +223,6 @@ jobs:
echo "Stable closeout is required for $tag, but immutable postpublish evidence from $evidence_source_tag is missing." >&2
exit 1
fi
evidence_path="$evidence_dir/$evidence_asset"
if ! (
cd "$evidence_dir"
sha256sum --strict --status -c "$evidence_checksum_asset"
@@ -195,6 +244,11 @@ jobs:
exit 1
fi
if [[ -z "$ROLLBACK_DRILL_ID" || -z "$ROLLBACK_DRILL_DATE" ]]; then
if [[ "$EVENT_NAME" == "push" ]]; then
echo "::warning::Stable closeout skipped: rollback drill repository variables are missing; manual dispatch remains required to complete closeout."
echo "should_closeout=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "Stable closeout requires repository variables RELEASE_ROLLBACK_DRILL_ID and RELEASE_ROLLBACK_DRILL_DATE, or explicit manual overrides." >&2
exit 1
fi
@@ -253,6 +307,30 @@ jobs:
exit 1
fi
- name: Install GitHub API backoff helper
run: |
cat > "$RUNNER_TEMP/github-api-backoff.sh" <<'BASH'
gh_with_retry() {
local attempt output status lower_output
for attempt in 1 2 3 4 5; do
if output="$(gh "$@" 2>&1)"; then
printf '%s\n' "$output"
return 0
fi
status=$?
lower_output="${output,,}"
if [[ "$lower_output" != *"rate limit"* && "$output" != *"HTTP 429"* ]]; then
printf '%s\n' "$output" >&2
return "$status"
fi
echo "::warning::GitHub API throttled stable closeout on attempt ${attempt}; retrying after backoff." >&2
sleep $((attempt * attempt * 5))
done
printf '%s\n' "$output" >&2
return "$status"
}
BASH
- name: Verify release workflow evidence
env:
GH_TOKEN: ${{ github.token }}
@@ -260,7 +338,8 @@ jobs:
RELEASE_PUBLISH_RUN_ID: ${{ needs.resolve.outputs.release_publish_run_id }}
run: |
set -euo pipefail
gh run view "$FULL_RELEASE_VALIDATION_RUN_ID" --repo "$GITHUB_REPOSITORY" \
. "$RUNNER_TEMP/github-api-backoff.sh"
gh_with_retry run view "$FULL_RELEASE_VALIDATION_RUN_ID" --repo "$GITHUB_REPOSITORY" \
--json workflowName,event,status,conclusion \
> "$RUNNER_TEMP/full-release-validation-run.json"
node --input-type=module - "$RUNNER_TEMP/full-release-validation-run.json" <<'NODE'
@@ -277,7 +356,7 @@ jobs:
}
}
NODE
gh run view "$RELEASE_PUBLISH_RUN_ID" --repo "$GITHUB_REPOSITORY" \
gh_with_retry run view "$RELEASE_PUBLISH_RUN_ID" --repo "$GITHUB_REPOSITORY" \
--json workflowName,event,status,conclusion \
> "$RUNNER_TEMP/release-publish-run.json"
node --input-type=module - "$RUNNER_TEMP/release-publish-run.json" <<'NODE'
@@ -298,7 +377,7 @@ jobs:
manifest_dir="$RUNNER_TEMP/full-release-validation-manifest"
rm -rf "$manifest_dir"
mkdir -p "$manifest_dir"
gh run download "$FULL_RELEASE_VALIDATION_RUN_ID" --repo "$GITHUB_REPOSITORY" \
gh_with_retry run download "$FULL_RELEASE_VALIDATION_RUN_ID" --repo "$GITHUB_REPOSITORY" \
--name "full-release-validation-${FULL_RELEASE_VALIDATION_RUN_ID}" \
--dir "$manifest_dir"
tag_sha="$(git -C "$GITHUB_WORKSPACE/release-tag" rev-parse HEAD)"
@@ -327,7 +406,8 @@ jobs:
run: |
set -euo pipefail
mkdir -p "$CLOSEOUT_DIR"
gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" \
. "$RUNNER_TEMP/github-api-backoff.sh"
gh_with_retry release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" \
--json tagName,isDraft,isPrerelease,assets \
> "$CLOSEOUT_DIR/github-release.json"
node scripts/verify-stable-main-closeout.mjs \
@@ -353,21 +433,23 @@ jobs:
CLOSEOUT_DIR: ${{ runner.temp }}/openclaw-stable-main-closeout
run: |
set -euo pipefail
. "$RUNNER_TEMP/github-api-backoff.sh"
release_version="${RELEASE_TAG#v}"
attach_or_verify() {
local source_path="$1"
local asset_name="$2"
local existing_dir="$CLOSEOUT_DIR/existing-${asset_name}"
mkdir -p "$existing_dir"
if gh release download "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" \
--pattern "$asset_name" --dir "$existing_dir"; then
gh_with_retry release download "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" \
--pattern "$asset_name" --dir "$existing_dir" || true
if [[ -f "$existing_dir/$asset_name" ]]; then
cmp --silent "$source_path" "$existing_dir/$asset_name" || {
echo "Existing release asset $asset_name differs from closeout evidence." >&2
exit 1
}
return
fi
gh release upload "$RELEASE_TAG" "$source_path#$asset_name" --repo "$GITHUB_REPOSITORY"
gh_with_retry release upload "$RELEASE_TAG" "$source_path#$asset_name" --repo "$GITHUB_REPOSITORY"
}
attach_or_verify \
"$CLOSEOUT_DIR/stable-main-closeout.json" \

View File

@@ -38,8 +38,8 @@ on:
type: string
concurrency:
group: plugin-npm-release-${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.sha }}
cancel-in-progress: false
group: plugin-npm-release-${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.ref }}
cancel-in-progress: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"

View File

@@ -532,6 +532,7 @@ jobs:
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_TRANSPORT_READY_TIMEOUT_MS: "180000"
INPUT_SCENARIO: ${{ github.event_name == 'workflow_dispatch' && inputs.scenario || '' }}
run: |
set -euo pipefail
@@ -624,6 +625,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_DISCORD_CAPTURE_CONTENT: "1"
INPUT_SCENARIO: ${{ github.event_name == 'workflow_dispatch' && inputs.discord_scenario || '' }}
@@ -721,6 +723,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_WHATSAPP_CAPTURE_CONTENT: "1"
INPUT_SCENARIO: ${{ github.event_name == 'workflow_dispatch' && inputs.whatsapp_scenario || '' }}
@@ -815,6 +818,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_SLACK_CAPTURE_CONTENT: "1"
OPENCLAW_QA_TRANSPORT_READY_TIMEOUT_MS: "180000"

View File

@@ -19,7 +19,7 @@ permissions:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
cancel-in-progress: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main') }}
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"

View File

@@ -57,6 +57,10 @@ jobs:
echo "could not read required Blacksmith metadata" >&2
exit 1
fi
if ! jq -e 'type == "number"' <<<"$installation_model_id" >/dev/null; then
echo "invalid Blacksmith installation model id: ${installation_model_id}" >&2
exit 1
fi
if [ -n "${BLACKSMITH_HOSTNAME:-}" ]; then
runner_host="$BLACKSMITH_HOSTNAME"
@@ -65,21 +69,32 @@ jobs:
fi
runner_ssh_port="${BLACKSMITH_SSH_PORT:-22}"
hydrating_body="$RUNNER_TEMP/testbox-hydrating.json"
hydrating_response="$RUNNER_TEMP/testbox-hydrating.response"
jq -n \
--arg testbox_id "$TESTBOX_ID" \
--argjson installation_model_id "$installation_model_id" \
--arg status "hydrating" \
--arg ip_address "$runner_host" \
--arg ssh_port "$runner_ssh_port" \
--arg working_directory "$GITHUB_WORKSPACE" \
--arg adopted_run_id "$GITHUB_RUN_ID" \
'{
testbox_id: $testbox_id,
installation_model_id: $installation_model_id,
status: $status,
ip_address: $ip_address,
ssh_port: $ssh_port,
working_directory: $working_directory,
adopted_run_id: $adopted_run_id,
metadata: {}
}' > "$hydrating_body"
hydrating_http_code="$(curl -sS -L --post302 --post303 -o "$hydrating_response" -w '%{http_code}' \
-X POST "${api_url}/api/testbox/phone-home" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${auth_token}" \
-d "{
\"testbox_id\": \"${TESTBOX_ID}\",
\"installation_model_id\": ${installation_model_id},
\"status\": \"hydrating\",
\"ip_address\": \"${runner_host}\",
\"ssh_port\": \"${runner_ssh_port}\",
\"working_directory\": \"${GITHUB_WORKSPACE}\",
\"adopted_run_id\": \"${GITHUB_RUN_ID}\",
\"metadata\": {}
}" || true)"
--data-binary @"$hydrating_body" || true)"
echo "phone_home_hydrating_http=${hydrating_http_code}"
if [[ ! "$hydrating_http_code" =~ ^2 ]]; then
@@ -152,20 +167,30 @@ jobs:
runner_ssh_port="$(cat "$state/runner_ssh_port")"
working_directory="$(cat "$state/working_directory")"
adopted_run_id="$(cat "$state/adopted_run_id")"
if ! jq -e 'type == "number"' <<<"$installation_model_id" >/dev/null; then
echo "invalid Blacksmith installation model id: ${installation_model_id}" >&2
exit 1
fi
ready_body="$RUNNER_TEMP/testbox-ready.json"
cat > "$ready_body" <<JSON
{
"testbox_id": "${testbox_id}",
"installation_model_id": ${installation_model_id},
"status": "ready",
"ip_address": "${runner_host}",
"ssh_port": "${runner_ssh_port}",
"working_directory": "${working_directory}",
"adopted_run_id": "${adopted_run_id}",
"metadata": {}
}
JSON
jq -n \
--arg testbox_id "$testbox_id" \
--argjson installation_model_id "$installation_model_id" \
--arg status "ready" \
--arg ip_address "$runner_host" \
--arg ssh_port "$runner_ssh_port" \
--arg working_directory "$working_directory" \
--arg adopted_run_id "$adopted_run_id" \
'{
testbox_id: $testbox_id,
installation_model_id: $installation_model_id,
status: $status,
ip_address: $ip_address,
ssh_port: $ssh_port,
working_directory: $working_directory,
adopted_run_id: $adopted_run_id,
metadata: {}
}' > "$ready_body"
http_code="$(curl -sS -L --post302 --post303 -o "$RUNNER_TEMP/testbox-ready.response" -w '%{http_code}' \
-X POST "${api_url}/api/testbox/phone-home" \

View File

@@ -129,11 +129,28 @@ jobs:
trusted_config="$RUNNER_TEMP/pre-commit-base.yaml"
trusted_zizmor_config="$RUNNER_TEMP/zizmor-base.yml"
if ! git cat-file -e "${BASE_SHA}^{commit}" 2>/dev/null; then
timeout --signal=TERM --kill-after=10s 30s git fetch --no-tags --depth=1 origin \
"+${BASE_SHA}:refs/remotes/origin/security-base" ||
fetch_base_ref() {
local ref="$1"
local target="$2"
local fetch_status
for attempt in 1 2 3; do
timeout --signal=TERM --kill-after=10s 30s git fetch --no-tags --depth=1 origin \
"+refs/heads/${BASE_REF}:refs/remotes/origin/${BASE_REF}"
"+${ref}:${target}" && 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::trusted base fetch for '$ref' timed out on attempt $attempt; retrying"
sleep 5
done
}
if ! git cat-file -e "${BASE_SHA}^{commit}" 2>/dev/null; then
fetch_base_ref "$BASE_SHA" "refs/remotes/origin/security-base" ||
fetch_base_ref "refs/heads/${BASE_REF}" "refs/remotes/origin/${BASE_REF}"
fi
if git cat-file -e "${BASE_SHA}:.pre-commit-config.yaml" 2>/dev/null; then

View File

@@ -35,7 +35,7 @@ Skills own workflows; root owns hard policy and routing.
- One-sided fixes need sibling-surface proof, an explanation for why siblings are unaffected, or explicit follow-up work.
- Changelog findings: see Docs / Changelog.
- Public ClawSweeper comments prefer `https://docs.openclaw.ai/...` when a public docs page exists; structured evidence still cites repo files, lines, SHAs.
- Findings need current source, shipped/current behavior, tests/CI evidence, and dependency contract proof when dependency-backed behavior is involved. Validation is judged against touched and sibling surfaces plus this file's commands; real behavior proof matters for user-visible changes, with Telegram/Desktop proof for Telegram-visible behavior when feasible.
- Findings need current source, shipped/current behavior, tests/CI evidence, and dependency contract proof when dependency-backed behavior is involved. Validation is judged against touched and sibling surfaces plus this file's commands; clear evidence matters for user-visible changes, with Telegram/Desktop proof for Telegram-visible behavior when feasible.
- Prefer findings for concrete behavior regressions, missing changed-surface proof, owner-boundary violations, security/API contract issues, or docs/config mismatches.
- Do not file findings for repo policy preference when changed code follows the relevant scoped guide and no user-visible, runtime, security, or maintainer-risk impact is shown.
@@ -117,11 +117,11 @@ Skills own workflows; root owns hard policy and routing.
- Tests in a normal source checkout: `pnpm test <path-or-filter> [vitest args...]`, `pnpm test:changed`, `pnpm test:serial`, `pnpm test:coverage`; never raw `vitest`.
- If raw Vitest is unavoidable, use `vitest run ...`; bare `vitest ...` starts local watch mode and will not exit on its own.
- Tests in a Codex worktree or linked/sparse checkout: avoid direct local `pnpm test*`; use `node scripts/run-vitest.mjs <path-or-filter>` for tiny explicit-file proof, or Crabbox/Testbox for anything broader.
- Checks/lint in a normal source checkout: `pnpm check:changed` delegates to Crabbox/Testbox; lanes: `pnpm changed:lanes --json`; staged/path-scoped: `pnpm check:changed --staged` or `pnpm check:changed -- <files...>`; full `pnpm check`/`pnpm lint` only when required.
- Checks in a normal source checkout: `pnpm check:changed` delegates to Crabbox/Testbox; lanes: `pnpm changed:lanes --json`; staged: `pnpm check:changed --staged`; full: `pnpm check`.
- Checks in a Codex worktree or linked/sparse checkout: avoid direct local `pnpm check*`; use `node scripts/crabbox-wrapper.mjs run ... -- env OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1 OPENCLAW_CHANGED_LANES_RAW_SYNC=1 corepack pnpm check:changed` so pnpm runs inside Testbox, not locally.
- Extension tests: `pnpm test:extensions`, `pnpm test extensions`, `pnpm test extensions/<id>`.
- Typecheck: `tsgo` lanes only (`pnpm tsgo*`, `pnpm check:test-types`); never add `tsc --noEmit`, `typecheck`, `check:types`.
- Formatting: `oxfmt`, not Prettier. Use repo wrappers (`pnpm format:*`, `scripts/run-oxlint.mjs`; full `pnpm lint:*` only when scope requires).
- Formatting: `oxfmt`, not Prettier. Use repo wrappers (`pnpm format:*`, `pnpm lint:*`, `scripts/run-oxlint.mjs`).
- Build before push when build output, packaging, lazy/module boundaries, dynamic imports, or published surfaces can change.
## Validation
@@ -165,13 +165,12 @@ Skills own workflows; root owns hard policy and routing.
- Representing user: if user already has a comment/thread for the point, update/reply there when possible; avoid duplicate PR/issue comments.
- No surprise GH writes: chat must mention every posted/updated public comment with URL.
- GH comments with backticks, `$`, or shell snippets: use heredoc/body file, not inline double-quoted `--body`.
- PR create: real body required. Include Summary + Verification; mention refs, behavior, and proof.
- PR create: real body required. Use the current template: `What Problem This Solves`, `Why This Change Was Made`, `User Impact`, and `Evidence`; include visible refs, behavior, and validation.
- PR create/refresh: keep PR branches takeover-ready. Use a branch maintainers can push to, or for fork PRs ensure `maintainer_can_modify` / GitHub's `Allow edits by maintainers` is enabled unless explicitly told otherwise or GitHub's Actions/secrets warning makes that unsafe.
- GitHub issue/PR create: read `$agent-transcript`; ask about sanitized transcript logs when available.
- Contributor PRs: parsed `Real behavior proof` uses exact `field: value` labels: `Behavior addressed`, `Real environment tested`, `Exact steps or command run after this patch`, `Evidence after fix`, `Observed result after fix`, `What was not tested`.
- Contributor PRs: parsed context requires authored `What Problem This Solves` and `Evidence` sections. Do not require field-level proof forms; reviewers inspect code, tests, and CI for correctness.
- PR artifacts/screenshots: attach to PR/comment/external artifact store. Never push screenshots, videos, proof images, or proof assets to OpenClaw or any product repo branch, including temp artifact branches. Use Crabbox artifact publishing plus the manifest URL. Do not commit `.github/pr-assets`.
- CI polling: exact SHA, relevant checks only, minimal fields. Skip routine noise (`Auto response`, `Labeler`, docs agents, performance/stale). Logs only after failure/completion or concrete need.
- OpenClaw write-access maintainers may skip `Real behavior proof` when local tests or Crabbox verified behavior; record proof in PR verification.
- Agent PR landing to `main`: use only the repo-native `scripts/pr` wrapper: run `scripts/pr review-init <PR>`, follow its emitted checkout/guard guidance, initialize and complete review artifacts with `scripts/pr review-artifacts-init <PR>`, validate them with `scripts/pr review-validate-artifacts <PR>`, then run `scripts/pr prepare-run <PR>` and `scripts/pr merge-run <PR>`; do not idle on `auto-response` or `check-docs`.
## Code

View File

@@ -6,34 +6,34 @@ Docs: https://docs.openclaw.ai
### Highlights
- **Richer Telegram delivery:** Telegram now sends rich HTML, preserves rich markdown and sticker paths, renders progress drafts and command output more faithfully, and keeps mentions and spooled handlers on the right delivery path. (#93286, #93164, #93124, #93364, #93130, #93088, #93281) Thanks @obviyus, @vincentkoc, @goutamadwant, @kesslerio, @NianJiuZst, @SweetSophia, @Marvinthebored, and @aaajiao.
- **Richer Telegram delivery:** Telegram now sends rich HTML, preserves rich markdown and sticker paths, renders progress drafts and command output more faithfully, normalizes HTML tables safely, and keeps mentions and spooled handlers on the right delivery path. (#93286, #93164, #93124, #93364, #93130, #93002, #93088, #93281, #94891, #94856) Thanks @obviyus, @vincentkoc, @goutamadwant, @kesslerio, @NianJiuZst, @SweetSophia, @Marvinthebored, @aaajiao, @zhangguiping-xydt, @zhangqueping, and @jairrab.
- **More dependable agent recovery:** retries, terminal outcomes, usage after compaction, session history repair, and reply reconciliation now keep more interrupted or partial turns moving toward a visible final result. (#92191, #93073, #93228, #93084, #93469, #93291, #90943) Thanks @ai-hpc, @lml2468, @fuller-stack-dev, @Hollychou924, @leno23, @de1tydev, @425072024, @wuwahe3, @drvoss, @yetval, @sandieman2, and @vincentkoc.
- **A stronger Codex integration:** Codex gains automatic plugin approvals, GPT-5.3 Spark OAuth routing, remote-node `exec` as a dynamic tool, and more reliable app-server teardown and terminal outcomes. (#92625, #89133, #93654, #91767, #93287) Thanks @kevinslin, @VACInc, @vincentkoc, @JPKay-AI, and @aliahnaf2013-max.
- **Standalone official provider plugins:** external provider packages are now first-class npm releases, externally installed channel plugins load at Gateway startup, and StepFun is intentionally npm-only because its ClawHub package name is unavailable. (#93470) Thanks @sunlit-deng, @cxdnicole, and @vincentkoc.
- **Standalone official provider plugins:** external provider packages are now first-class npm releases, externally installed channel plugins load at Gateway startup, and StepFun is available from npm and ClawHub. (#93470) Thanks @sunlit-deng, @cxdnicole, and @vincentkoc.
- **More capable web and native clients:** the Control UI adds a session workspace rail and extension health, iOS adds Watch controls, and Android shows chat context. (#92856, #91952, #93387, #92837) Thanks @Solvely-Colin, @jalehman, @joshavant, and @Tosko4.
- **More useful search and skills:** Codex Hosted Search is available, key-free search providers remain deliberate opt-ins, and ClawHub skill installs retain verified source provenance. (#93446, #93616, #93283, #93506) Thanks @fuller-stack-dev, @davemorin, @momothemage, @nmccready-tars, and @vincentkoc.
### Changes
- Providers and auth: add Codex Hosted Search, improve Gemini CLI OAuth behind proxies, and keep external provider onboarding on current choices and package metadata. (#93446, #92815) Thanks @fuller-stack-dev, @yetval, @EvetteYoung, and @vincentkoc.
- Plugins and installs: externalized official providers publish as independent npm packages, Gateway discovers installed channel plugins at startup, and StepFun installs exclusively from npm. (#93470) Thanks @sunlit-deng, @cxdnicole, and @vincentkoc.
- Plugins and installs: externalized official providers publish as independent npm packages, Gateway discovers installed channel plugins at startup, and StepFun installs from npm or ClawHub. (#93470) Thanks @sunlit-deng, @cxdnicole, and @vincentkoc.
- Dashboard and mobile: add a session workspace rail, plugin health in status, compact cron lists, and iOS Watch controls. (#92856, #91952, #93395, #93387) Thanks @Solvely-Colin, @jalehman, @yu-xin-c, @centralpc, @joshavant, and @vincentkoc.
- Codex and skills: add automatic plugin approvals, preserve ClawHub skill provenance, and expose remote-node execution to Codex when a node is connected. (#92625, #93283, #93654) Thanks @kevinslin, @momothemage, @nmccready-tars, @vincentkoc, and @JPKay-AI.
- QA and release engineering: QA scenarios now use YAML, with broader profile evidence and release coverage for the plugin and channel matrix.
- Codex, observability, and skills: add automatic plugin approvals and SecretRefs, preserve ClawHub skill provenance, add OpenTelemetry log export, and expose remote-node execution to Codex when a node is connected. (#92625, #94324, #93283, #94561, #93654) Thanks @kevinslin, @kevinlin-openai, @momothemage, @nmccready-tars, @jesse-merhi, @vincentkoc, and @JPKay-AI.
- QA and release engineering: QA scenarios now use YAML, with broader profile evidence and release coverage for the plugin and channel matrix. Thanks @vincentkoc.
### Fixes
- Security and privacy: redact secrets from debug/config output, block internal HTTP session overrides, audit open-DM tool exposure, and retain plugin write ownership checks. (#93333, #88496, #93443, #92883, #93353) Thanks @Alix-007, @jason-allen-oneal, @coygeek, @RichardCao, @yu-xin-c, @cjg20ss, @eleqtrizit, and @vincentkoc.
- Agent and session runtime: retry thinking-only and empty post-tool turns, prevent duplicate hook execution, preserve fresh usage through compaction, and repair partial JSON/history artifacts. (#92191, #93073, #93009, #93084, #93469) Thanks @ai-hpc, @lml2468, @fuller-stack-dev, @zenglingbiao, @dertbv, @Hollychou924, @leno23, @de1tydev, @425072024, @wuwahe3, @drvoss, and @vincentkoc.
- Channels and replies: fix Telegram rich delivery and ingress recovery, preserve WhatsApp auth and media error reporting, keep Mattermost thread replies intact, and harden Discord action handling. (#93286, #93364, #93281, #93076, #93334, #93424, #93488) Thanks @obviyus, @NianJiuZst, @mcaxtr, @rushindrasinha, @amknight, @lzyyzznl, @darealgege, and @vincentkoc.
- Agent and session runtime: retry thinking-only and empty post-tool turns, prevent duplicate hook execution, preserve pending subagent delivery, preserve fresh usage through compaction, and repair partial JSON/history artifacts. (#92191, #93073, #93009, #93084, #93469, #94349, #92383, #94257) Thanks @ai-hpc, @lml2468, @fuller-stack-dev, @zenglingbiao, @dertbv, @Hollychou924, @leno23, @de1tydev, @425072024, @wuwahe3, @drvoss, @vincentkoc, @sallyom, @oiGaDio, @Hidetsugu55, and @Nas01010101.
- Channels and replies: fix Telegram rich delivery, table rendering, action-error handling, progress draft cleanup before visible tool output, and ingress recovery; preserve command progress detail across channel adapters; retain WhatsApp opening text after a media failure; keep Mattermost thread replies intact; and harden Discord action handling. (#93286, #93364, #93281, #93002, #93076, #93334, #93424, #93488, #94868, #94891, #94856, #94810, #93823) Thanks @obviyus, @NianJiuZst, @mcaxtr, @zhangguiping-xydt, @rushindrasinha, @amknight, @lzyyzznl, @darealgege, @vincentkoc, @zhangqueping, @jairrab, @ZOOWH, @parveshsaini, and @yetval.
- Storage and migrations: avoid SQLite WAL on network filesystems, clean reindex artifacts, keep setup state out of workspace dot-directories, and import default-agent auth profiles into SQLite. (#93454, #92891, #93182, #93295, #93520, #93156) Thanks @vincentkoc, @ZengWen-DT, @Zeng-wen, @potterdigital, @Alix-007, @Pick-cat, @sallyom, @1qh, and @Tazio7.
- Provider and model behavior: fix Gemini CLI proxy OAuth, restore Codex Spark OAuth routing, correct Bedrock embedding model IDs, and preserve configured defaults in embedded runs. (#92815, #89133, #93452, #93428) Thanks @yetval, @EvetteYoung, @VACInc, @LiuwqGit, @aleck31, @zenglingbiao, @danielgerlag, and @vincentkoc.
- CLI, TUI, and apps: accept global flags after subcommands, keep terminal output and activity indicators visible, preserve CJK IME composition, and refresh stale UI state. (#93455, #93460, #93006, #93427, #93498, #93606) Thanks @ooiuuii, @Alix-007, @ZengWen-DT, @Zeng-wen, @AlethiaQuizForge, @Zhaoqj2016, @liuhao1024, @BrianClaw1955, @vincentkoc, and @NicoBoom13.
- Operations and updates: harden official plugin recovery, restart managed Gateways after failed update handoff, avoid Node-specific npm prefixes, and keep package validation paths reliable. (#93325, #92111, #93650) Thanks @vincentkoc, @yetval, @ofan, and @yaanfpv.
- Operations and updates: harden official plugin recovery, restart managed Gateways after failed update handoff, keep safe cron delivery defaults, avoid Node-specific npm prefixes, and keep package validation paths reliable. (#93325, #92111, #93650, #94453, #91685) Thanks @vincentkoc, @yetval, @ofan, @yaanfpv, @jincheng-xydt, @sallyom, @davectr, and @nxmxbbd.
### Complete contribution record
This audited record covers the complete v2026.6.8..HEAD history: 373 merged PRs. The generation manifest also supplies direct commits as editorial input; the grouped notes above prioritize user impact.
This audited record covers the complete v2026.6.8..HEAD history: 423 merged PRs. The generation manifest also supplies direct commits as editorial input; the grouped notes above prioritize user impact.
#### Pull requests
@@ -57,6 +57,7 @@ This audited record covers the complete v2026.6.8..HEAD history: 373 merged PRs.
- **PR #88792** fix(state): harden sqlite path caching. Thanks @vincentkoc.
- **PR #93022** fix(gateway): repair usage cost aggregation across agents. Thanks @luke-skywalker-open-claw and @stablegenius49.
- **PR #93020** fix(telegram): cool down transient sendChatAction failures. Related #56096. Thanks @Boulea7 and @sumaiazaman and @Pick-cat and @cal-rufus.
- **PR #93002** fix(telegram): clear progress drafts before visible tool output. Thanks @zhangguiping-xydt.
- **PR #89160** fix(agents): detect truncated API responses to prevent silent session hang. Related #89051. Thanks @joelnishanth and @ArthurusDent.
- **PR #93009** fix(agents): make wrapToolWithBeforeToolCallHook idempotent to prevent double hook execution (fixes #92973). Thanks @zenglingbiao and @dertbv.
- **PR #92991** fix(agents): tolerate missing attribution baseUrl. Related #92974. Thanks @samrusani and @Haderach-Ram.
@@ -175,7 +176,7 @@ This audited record covers the complete v2026.6.8..HEAD history: 373 merged PRs.
- **PR #90003** feat(policy): cover exec approvals artifact. Thanks @giodl73-repo.
- **PR #93448** fix(guards): allow auth profile sqlite reader. Thanks @amknight.
- **PR #93424** fix(mattermost): keep message tool replies in threads. Thanks @amknight and @vincentkoc.
- **PR #93418** fix(telegram): forward Bot API 10.1 rich_message content to agent. Related #93410. Thanks @xzh-xydt and @vincentkoc and @0pen7ech.
- **PR #93418** fix(telegram): forward Bot API 10.1 rich_message content to agent. Related #93410. Thanks @xzh-icenter and @vincentkoc and @0pen7ech.
- **PR #93175** test(qa): taxonomy profiles: includeAllCategories for release profile, update some coverage. Thanks @RomneyDa.
- **PR #93456** fix(agents): handle string assistant message content. Thanks @vincentkoc.
- **PR #93441** fix(outbound): ignore schema-padded poll metadata on send. Related #43015. Thanks @weichengdeng and @charzhou.
@@ -410,6 +411,55 @@ This audited record covers the complete v2026.6.8..HEAD history: 373 merged PRs.
- **PR #94118** [codex] Fix Telegram rich local Markdown link hrefs. Related #94117. Thanks @dankarization and @obviyus.
- **PR #94646** refactor(sqlite): land database-first memory and proxy alignment. Thanks @vincentkoc.
- **PR #94658** test(sqlite): use shared temp directory helper. Thanks @vincentkoc.
- **PR #92135** fix(openai-embedding): preserve openai/ prefix for non-native base URLs. Related #92124. Thanks @xialonglee and @Kambrian.
- **PR #93737** refactor: add session maintenance transaction seam. Thanks @jalehman.
- **PR #93685** refactor(auto-reply): add lifecycle storage seams. Thanks @jalehman.
- **PR #94349** fix(agents): preserve pending subagent completion announces. Related #93323. Thanks @sallyom and @oiGaDio.
- **PR #93174** test: fold channel message flows into qa e2e. Thanks @RomneyDa.
- **PR #94093** Prevent Codex thread rotation from losing next-step context. Thanks @VACInc.
- **PR #53920** fix(scripts): avoid mutating tracked auth-monitor template during setup. Thanks @JackWuGlobal.
- **PR #94702** Standardize QA coverage IDs on dotted names. Thanks @RomneyDa.
- **PR #81825** fix(skills/1password): stop forcing tmux for desktop app auth (#52540). Thanks @koshaji and @tylerbittner.
- **PR #94725** fix(doctor): warn on volatile SQLite state. Thanks @vincentkoc.
- **PR #88551** fix(agents): skip auth gate for CLI-owned transport. Thanks @yu-xin-c.
- **PR #88581** feat(commands): add /name to rename the current session from chat. Thanks @BSG2000.
- **PR #94324** feat(codex): support app-server SecretRefs. Thanks @kevinlin-openai and @kevinslin.
- **PR #90882** fix: add self-knowledge docs rule to system prompt. Related #90713. Thanks @SutraHsing.
- **PR #94684** fix: #80507 show dry-run output for message send/poll. Thanks @lzyyzznl and @YB0y.
- **PR #93823** fix(whatsapp): keep opening text chunk when first media fails on multi-chunk reply. Thanks @yetval.
- **PR #89203** refactor: route SDK session compatibility through seam. Thanks @jalehman.
- **PR #94453** fix: default cron runMode to "due" instead of "force" (#94270). Thanks @jincheng-xydt and @sallyom and @davectr.
- **PR #94746** fix(note): prevent clack from re-breaking copy-sensitive tokens. Related #94730. Thanks @xzh-icenter and @berkgungor.
- **PR #89904** refactor: route sdk session compatibility through accessor. Thanks @jalehman.
- **PR #86719** fix(skills): retarget stale plugin skill symlinks. Related #85925. Thanks @stevenepalmer and @shakkernerd.
- **PR #94337** fix(tui): show 0 not ? for fresh-session context tokens in footer. Thanks @mushuiyu886.
- **PR #94539** fix(android): group settings by intent. Thanks @Tosko4.
- **PR #92383** fix(gateway): never return an empty chat.history transcript. Thanks @Hidetsugu55.
- **PR #92574** test(browser): cover action-input CLI request bodies. Related #83877. Thanks @yu-xin-c and @davinci282828.
- **PR #92873** test(diffs): add viewerState, toolbar toggle, shadow root, and hydrateProps tests (fixes #83915). Thanks @liuhao1024 and @davinci282828.
- **PR #94257** fix(sessions): preserve Media\* index alignment when reading user-turn fields. Thanks @Nas01010101.
- **PR #94756** fix(codex): bound turn/start text when context budget is non-positive. Related #94748. Thanks @Nas01010101.
- **PR #94729** fix(skills/trello): add curl to requires.bins to match body examples (fixes #94727). Thanks @liuhao1024 and @berkgungor.
- **PR #94790** feat(slack): log INFO receipt for inbound app_mention events. Related #94691. Thanks @ZengWen-DT and @BryceMurray.
- **PR #81696** fix: guard tool event callbacks (AI-assisted). Thanks @enjoylife1243.
- **PR #94809** chore: forward-port alpha release fixes.
- **PR #94612** fix(macos): open NSOpenPanel for embedded Control UI file inputs (#94468). Thanks @bbblending and @DINGDANGMAOUP.
- **PR #89806** fix(feishu): avoid axios interceptor internals. Related #83913. Thanks @sweetcornna and @davinci282828.
- **PR #91923** fix(ios): clean up notification settings state. Thanks @zats.
- **PR #91345** fix: suggest close CLI commands. Related #83999. Thanks @glenn-agent and @HannesOberreiter.
- **PR #94561** Add stdout diagnostics OTEL log exporter. Thanks @jesse-merhi.
- **PR #91013** fix(gateway): ignore stale abort markers for fresh chat events. Related #91012. Thanks @nxmxbbd.
- **PR #89279** fix(tasks): deliver ACP completions to bound Discord threads. Related #84022. Thanks @anyech and @h-mascot.
- **PR #91656** test(cron): expand parseAbsoluteTimeMs test coverage to 39 cases. Related #91654. Thanks @SpecialLeon.
- **PR #94810** fix(telegram): classify sendChatAction 401 by structured error_code, not bare substring match. Related #94787. Thanks @ZOOWH and @parveshsaini.
- **PR #94737** fix(reply): clarify provider internal error copy. Thanks @snowzlmbot.
- **PR #94868** fix(channels): preserve command progress detail. Thanks @vincentkoc.
- **PR #94891** fix(telegram): send progress previews as html text. Thanks @obviyus.
- **PR #94683** fix(outbound): keep direct-only targets out of group sessions. Related #92384. Thanks @scotthuang and @haiwei01.
- **PR #92477** fix: migrate watch app to single-target app (Xcode 27+ compat). Thanks @zats and @joshavant.
- **PR #94812** test(perf): compare saved CLI startup benchmarks. Thanks @FelixIsaac.
- **PR #94856** fix(telegram): normalize all HTML tables before entity-escaping in rich messages. Related #94317. Thanks @zhangqueping and @jairrab.
- **PR #91685** fix(cron): refuse keyless implicit isolated cron delivery inherited from shared agent-main bucket. Thanks @nxmxbbd.
## 2026.6.8

View File

@@ -106,7 +106,8 @@ For coordinated change sets that genuinely need more than 20 PRs, join the **#cl
## Before You PR
- Test locally with your OpenClaw instance
- External PRs must include a filled **Real behavior proof** section in the PR body. Show the real setup you tested, the exact command or steps you ran after the patch, after-fix evidence, the observed result, and anything you did not test. Screenshots, recordings, terminal screenshots, console output, copied live output, linked artifacts, and redacted runtime logs all count. Unit tests, mocks, snapshots, lint, typechecks, and CI are useful but do not satisfy this requirement by themselves. Maintainers may apply `proof: override` only when the proof gate should not apply.
- External PRs must describe the user, product, or operational problem in **What Problem This Solves** and include useful validation in **Evidence**. Focused tests, CI results, screenshots, recordings, terminal output, live observations, redacted logs, and artifact links all count. Reviewers will inspect the code, tests, and CI; use the PR body to explain intent and make validation easy to understand.
- When ClawSweeper, Codex, Barnacle, or a maintainer asks for more context or evidence, edit the PR description instead of only replying in a new comment. Keep **What Problem This Solves**, **Why This Change Was Made**, **User Impact**, and **Evidence** current; a short comment can point reviewers to the update, but the PR body should remain the durable explanation for maintainers and bots.
- Keep PRs takeover-ready: open them from a branch maintainers can push to. For fork PRs, leave GitHub's **Allow edits by maintainers** option enabled so maintainers can finish urgent fixes, changelog entries, or merge prep when needed. If GitHub shows **Allow edits and access to secrets by maintainers**, enable it only when that workflow/secrets access is acceptable and say so in the PR.
- Do not edit `CHANGELOG.md` in contributor PRs. Maintainers or ClawSweeper add the changelog entry when landing user-facing changes.
- Run tests: `pnpm build && pnpm check && pnpm test`
@@ -169,7 +170,7 @@ Built with Codex, Claude, or other AI tools? **Awesome - just mark it!**
Please include in your PR:
- [ ] Mark as AI-assisted in the PR title or description
- [ ] Include human-run real behavior proof from your own setup. AI-generated tests, mocks, lint, typechecks, and CI output are supplemental only; they do not prove the fix works for users.
- [ ] Include a concise **Evidence** section with the most useful validation. Reviewers will inspect the code, tests, and CI rather than relying on the PR body alone.
- [ ] Include prompts or session logs if possible (super helpful!)
- [ ] Confirm you understand what the code does
- [ ] If you have access to Codex, run `codex review --base origin/main` locally and address the findings before asking for review

View File

@@ -2,5 +2,5 @@
# Source of truth: apps/android/version.json
# Generated by scripts/android-sync-versioning.ts.
OPENCLAW_ANDROID_VERSION_NAME=2026.6.2
OPENCLAW_ANDROID_VERSION_CODE=2026060201
OPENCLAW_ANDROID_VERSION_NAME=2026.6.9
OPENCLAW_ANDROID_VERSION_CODE=2026060901

View File

@@ -898,32 +898,38 @@ private fun SettingsShellScreen(
ProfilePanel(displayName = displayName.ifBlank { "OpenClaw" }, onClick = { onRouteChange(SettingsRoute.Profile) })
}
item {
SettingsGroup(
rows =
listOf(
SettingsRow("Profile", displayName.ifBlank { "Local device" }, Icons.Default.Person, route = SettingsRoute.Profile),
SettingsRow("Voice", if (speakerEnabled) "Speaker on" else "Speaker muted", Icons.Default.Mic, route = SettingsRoute.Voice),
SettingsRow("Agents", if (agents.isEmpty()) "Load from gateway" else "${agents.size} available", Icons.Default.Person, status = agents.isNotEmpty(), route = SettingsRoute.Agents),
SettingsRow("Approvals", approvalsSummary(pendingToolCalls.size), Icons.Default.Lock, status = approvalsStatus(pendingToolCalls.size), route = SettingsRoute.Approvals),
SettingsRow("Cron Jobs", cronJobsSummary(cronStatus.jobs), Icons.Outlined.AccessTime, status = if (cronStatus.jobs > 0) cronStatus.enabled else null, route = SettingsRoute.CronJobs),
SettingsRow("Usage", usageSummaryText(usageSummary.providers.size), Icons.Default.Storage, status = if (usageSummary.providers.isNotEmpty()) true else null, route = SettingsRoute.Usage),
SettingsRow("Skills", skillsSummaryText(skillsSummary.skills), Icons.Default.Settings, status = skillsStatus(skillsSummary.skills), route = SettingsRoute.Skills),
SettingsRow("Nodes & Devices", nodesDevicesSummaryText(nodesDevicesSummary), Icons.Default.Cloud, status = nodesDevicesStatus(nodesDevicesSummary), route = SettingsRoute.NodesDevices),
SettingsRow("Channels", channelsSummaryText(channelsSummary), Icons.Default.Notifications, status = channelsStatus(channelsSummary), route = SettingsRoute.Channels),
SettingsRow("Dreaming", dreamingSummaryText(dreamingSummary), Icons.Default.Storage, status = dreamingStatus(dreamingSummary), route = SettingsRoute.Dreaming),
SettingsRow("Canvas", "Screen surface", Icons.AutoMirrored.Filled.ScreenShare, status = isConnected, route = SettingsRoute.Canvas),
SettingsRow("Notifications", if (notificationForwardingEnabled) "Smart delivery" else "Off", Icons.Default.Notifications, route = SettingsRoute.Notifications),
SettingsRow("Phone Capabilities", if (cameraEnabled) "Camera enabled" else "Locked", Icons.Default.Lock, status = !cameraEnabled, route = SettingsRoute.PhoneCapabilities),
SettingsRow("Gateway", gatewaySummary(statusText, isConnected), Icons.Default.Cloud, status = isConnected, route = SettingsRoute.Gateway),
SettingsRow("Appearance", appearanceThemeSummary(appearanceThemeMode), Icons.Default.Palette, route = SettingsRoute.Appearance),
SettingsRow("Health", "Diagnostics", Icons.Default.Settings, status = isConnected, route = SettingsRoute.Health),
SettingsRow("About", "Version and update", Icons.Default.Storage, route = SettingsRoute.About),
),
onOpen = onRouteChange,
val settingsRows =
listOf(
SettingsRow("Gateway", gatewaySummary(statusText, isConnected), Icons.Default.Cloud, status = isConnected, route = SettingsRoute.Gateway),
SettingsRow("Nodes & Devices", nodesDevicesSummaryText(nodesDevicesSummary), Icons.Default.Cloud, status = nodesDevicesStatus(nodesDevicesSummary), route = SettingsRoute.NodesDevices),
SettingsRow("Channels", channelsSummaryText(channelsSummary), Icons.Default.Notifications, status = channelsStatus(channelsSummary), route = SettingsRoute.Channels),
SettingsRow("Agents", if (agents.isEmpty()) "Load from gateway" else "${agents.size} available", Icons.Default.Person, status = agents.isNotEmpty(), route = SettingsRoute.Agents),
SettingsRow("Approvals", approvalsSummary(pendingToolCalls.size), Icons.Default.Lock, status = approvalsStatus(pendingToolCalls.size), route = SettingsRoute.Approvals),
SettingsRow("Cron Jobs", cronJobsSummary(cronStatus.jobs), Icons.Outlined.AccessTime, status = if (cronStatus.jobs > 0) cronStatus.enabled else null, route = SettingsRoute.CronJobs),
SettingsRow("Usage", usageSummaryText(usageSummary.providers.size), Icons.Default.Storage, status = if (usageSummary.providers.isNotEmpty()) true else null, route = SettingsRoute.Usage),
SettingsRow("Skills", skillsSummaryText(skillsSummary.skills), Icons.Default.Settings, status = skillsStatus(skillsSummary.skills), route = SettingsRoute.Skills),
SettingsRow("Dreaming", dreamingSummaryText(dreamingSummary), Icons.Default.Storage, status = dreamingStatus(dreamingSummary), route = SettingsRoute.Dreaming),
SettingsRow("Voice", if (speakerEnabled) "Speaker on" else "Speaker muted", Icons.Default.Mic, route = SettingsRoute.Voice),
SettingsRow("Canvas", "Screen surface", Icons.AutoMirrored.Filled.ScreenShare, status = isConnected, route = SettingsRoute.Canvas),
SettingsRow("Notifications", if (notificationForwardingEnabled) "Smart delivery" else "Off", Icons.Default.Notifications, route = SettingsRoute.Notifications),
SettingsRow("Phone Capabilities", if (cameraEnabled) "Camera enabled" else "Locked", Icons.Default.Lock, status = !cameraEnabled, route = SettingsRoute.PhoneCapabilities),
SettingsRow("Appearance", appearanceThemeSummary(appearanceThemeMode), Icons.Default.Palette, route = SettingsRoute.Appearance),
SettingsRow("About", "Version and update", Icons.Default.Storage, route = SettingsRoute.About),
SettingsRow("Health", "Diagnostics", Icons.Default.Settings, status = isConnected, route = SettingsRoute.Health),
)
settingsSections(settingsRows).forEach { section ->
item {
SettingsSectionTitle(section.title)
}
item {
SettingsGroup(rows = section.rows, onOpen = onRouteChange)
}
}
item {
SettingsSectionTitle("Account")
}
item {
SettingsGroup(
rows = listOf(SettingsRow("Sign Out", "Disconnect", Icons.AutoMirrored.Filled.ExitToApp)),
@@ -1057,7 +1063,7 @@ private fun dreamingStatus(summary: GatewayDreamingSummary): Boolean? =
else -> null
}
private data class SettingsRow(
internal data class SettingsRow(
val title: String,
val value: String,
val icon: ImageVector,
@@ -1065,6 +1071,65 @@ private data class SettingsRow(
val route: SettingsRoute? = null,
)
internal data class SettingsSection(
val title: String,
val rows: List<SettingsRow>,
)
internal fun settingsSections(rows: List<SettingsRow>): List<SettingsSection> =
settingsSectionOrder.mapNotNull { title ->
val sectionRows = rows.filter { row -> row.route?.let(::settingsSectionTitleForRoute) == title }
if (sectionRows.isEmpty()) null else SettingsSection(title = title, rows = sectionRows)
}
private val settingsSectionOrder =
listOf(
"Connection",
"Agents & automation",
"Phone context & privacy",
"Profile & device",
"Diagnostics",
)
internal fun settingsSectionTitleForRoute(route: SettingsRoute): String =
when (route) {
SettingsRoute.Gateway,
SettingsRoute.NodesDevices,
SettingsRoute.Channels,
-> "Connection"
SettingsRoute.Agents,
SettingsRoute.Approvals,
SettingsRoute.CronJobs,
SettingsRoute.Usage,
SettingsRoute.Skills,
SettingsRoute.Dreaming,
-> "Agents & automation"
SettingsRoute.Voice,
SettingsRoute.Canvas,
SettingsRoute.Notifications,
SettingsRoute.PhoneCapabilities,
-> "Phone context & privacy"
SettingsRoute.Profile,
SettingsRoute.Appearance,
SettingsRoute.About,
-> "Profile & device"
SettingsRoute.Health -> "Diagnostics"
SettingsRoute.Home -> "Diagnostics"
}
@Composable
private fun SettingsSectionTitle(title: String) {
Text(
text = title.uppercase(),
style = ClawTheme.type.caption.copy(fontSize = 12.sp, lineHeight = 16.sp),
color = ClawTheme.colors.textMuted,
)
}
@Composable
private fun ProfilePanel(
displayName: String,

View File

@@ -7,6 +7,8 @@ import ai.openclaw.app.GatewayNodeApprovalState
import ai.openclaw.app.GatewayNodeSummary
import ai.openclaw.app.GatewayNodesDevicesSummary
import ai.openclaw.app.GatewayPendingDeviceSummary
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Settings
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -155,7 +157,46 @@ class ShellScreenLogicTest {
assertEquals("Node approval pending", rows.single().subtitle)
}
@Test
fun settingsSectionTitlesGroupPowerSettingsByMeaning() {
assertEquals("Connection", settingsSectionTitleForRoute(SettingsRoute.Gateway))
assertEquals("Connection", settingsSectionTitleForRoute(SettingsRoute.NodesDevices))
assertEquals("Agents & automation", settingsSectionTitleForRoute(SettingsRoute.Approvals))
assertEquals("Agents & automation", settingsSectionTitleForRoute(SettingsRoute.CronJobs))
assertEquals("Phone context & privacy", settingsSectionTitleForRoute(SettingsRoute.PhoneCapabilities))
assertEquals("Phone context & privacy", settingsSectionTitleForRoute(SettingsRoute.Notifications))
assertEquals("Profile & device", settingsSectionTitleForRoute(SettingsRoute.Appearance))
assertEquals("Diagnostics", settingsSectionTitleForRoute(SettingsRoute.Health))
}
@Test
fun settingsSectionsPreserveMeaningfulOrder() {
val sections =
settingsSections(
listOf(
settingsRow(SettingsRoute.Voice),
settingsRow(SettingsRoute.Agents),
settingsRow(SettingsRoute.Gateway),
settingsRow(SettingsRoute.Appearance),
settingsRow(SettingsRoute.Health),
),
)
assertEquals(
listOf(
"Connection",
"Agents & automation",
"Phone context & privacy",
"Profile & device",
"Diagnostics",
),
sections.map { it.title },
)
}
private fun emptyChannels(): GatewayChannelsSummary = GatewayChannelsSummary(channels = emptyList())
private fun emptyNodesDevices(): GatewayNodesDevicesSummary = GatewayNodesDevicesSummary(nodes = emptyList(), pendingDevices = emptyList(), pairedDevices = emptyList())
private fun settingsRow(route: SettingsRoute): SettingsRow = SettingsRow(route.name, "Value", Icons.Default.Settings, route = route)
}

View File

@@ -1,3 +1 @@
OpenClaw is now available on Android.
Connect to your OpenClaw Gateway to chat with your assistant, use realtime Talk mode, review approvals, and bring Android device capabilities like camera, location, screen, and notifications into your private automation workflows.
Maintenance update for the current OpenClaw Android release.

View File

@@ -1,4 +1,4 @@
{
"version": "2026.6.2",
"versionCode": 2026060201
"version": "2026.6.9",
"versionCode": 2026060901
}

View File

@@ -12,7 +12,7 @@ report_include:
- Sources/**
- ShareExtension/**
- ActivityWidget/**
- WatchExtension/Sources/**
- WatchApp/Sources/**
build_arguments:
- -destination
- generic/platform=iOS Simulator

View File

@@ -1,5 +1,13 @@
# OpenClaw iOS Changelog
## 2026.6.9 - 2026-06-20
Maintenance update for the current OpenClaw release.
- Added Apple Watch controls for common agent actions.
- Improved Gateway setup, notification settings, and share-extension identity handling.
- Updated the Watch app integration for current Xcode compatibility.
## 2026.6.2 - 2026-06-02
OpenClaw is now available on iPhone.

View File

@@ -3,6 +3,7 @@
"signingRepo": "git@github.com:openclaw/apps-signing.git",
"signingBranch": "main",
"profileType": "appstore",
"appGroupId": "group.ai.openclawfoundation.app.shared",
"targets": [
{
"target": "OpenClaw",
@@ -11,7 +12,8 @@
"platform": "IOS",
"profileKey": "OPENCLAW_APP_PROFILE",
"profileName": "OpenClaw App Store ai.openclawfoundation.app",
"capabilities": ["PUSH_NOTIFICATIONS"]
"capabilities": ["PUSH_NOTIFICATIONS", "APP_GROUPS"],
"appGroups": ["group.ai.openclawfoundation.app.shared"]
},
{
"target": "OpenClawShareExtension",
@@ -20,7 +22,8 @@
"platform": "IOS",
"profileKey": "OPENCLAW_SHARE_PROFILE",
"profileName": "OpenClaw App Store ai.openclawfoundation.app.share",
"capabilities": []
"capabilities": ["APP_GROUPS"],
"appGroups": ["group.ai.openclawfoundation.app.shared"]
},
{
"target": "OpenClawActivityWidget",
@@ -39,15 +42,6 @@
"profileKey": "OPENCLAW_WATCH_APP_PROFILE",
"profileName": "OpenClaw App Store ai.openclawfoundation.app.watchkitapp",
"capabilities": []
},
{
"target": "OpenClawWatchExtension",
"displayName": "OpenClaw Watch Extension",
"bundleId": "ai.openclawfoundation.app.watchkitapp.extension",
"platform": "IOS",
"profileKey": "OPENCLAW_WATCH_EXTENSION_PROFILE",
"profileName": "OpenClaw App Store ai.openclawfoundation.app.watchkitapp.extension",
"capabilities": []
}
]
}

View File

@@ -7,12 +7,11 @@ OPENCLAW_DEVELOPMENT_TEAM = $(OPENCLAW_IOS_SELECTED_TEAM)
OPENCLAW_CODE_SIGN_STYLE = Automatic
OPENCLAW_CODE_SIGN_IDENTITY = Apple Development
OPENCLAW_APP_BUNDLE_ID = ai.openclawfoundation.app
OPENCLAW_APP_GROUP_ID = group.ai.openclawfoundation.app.shared
OPENCLAW_WATCH_APP_BUNDLE_ID = ai.openclawfoundation.app.watchkitapp
OPENCLAW_WATCH_EXTENSION_BUNDLE_ID = ai.openclawfoundation.app.watchkitapp.extension
OPENCLAW_ACTIVITY_WIDGET_BUNDLE_ID = ai.openclawfoundation.app.activitywidget
OPENCLAW_ACTIVITY_WIDGET_PROFILE =
OPENCLAW_WATCH_APP_PROFILE =
OPENCLAW_WATCH_EXTENSION_PROFILE =
// Local contributors can override this by running scripts/ios-configure-signing.sh.
// Keep include after defaults: xcconfig is evaluated top-to-bottom.

View File

@@ -2,8 +2,8 @@
// Source of truth: apps/ios/version.json
// Generated by scripts/ios-sync-versioning.ts.
OPENCLAW_IOS_VERSION = 2026.6.2
OPENCLAW_MARKETING_VERSION = 2026.6.2
OPENCLAW_IOS_VERSION = 2026.6.9
OPENCLAW_MARKETING_VERSION = 2026.6.9
OPENCLAW_BUILD_VERSION = 1
#include? "../build/Version.xcconfig"

View File

@@ -7,13 +7,12 @@ OPENCLAW_DEVELOPMENT_TEAM = YOUR_TEAM_ID
OPENCLAW_APP_BUNDLE_ID = ai.openclawfoundation.app
OPENCLAW_SHARE_BUNDLE_ID = ai.openclawfoundation.app.share
OPENCLAW_APP_GROUP_ID = group.ai.openclawfoundation.app.shared
OPENCLAW_ACTIVITY_WIDGET_BUNDLE_ID = ai.openclawfoundation.app.activitywidget
OPENCLAW_WATCH_APP_BUNDLE_ID = ai.openclawfoundation.app.watchkitapp
OPENCLAW_WATCH_EXTENSION_BUNDLE_ID = ai.openclawfoundation.app.watchkitapp.extension
// Leave empty with automatic signing.
OPENCLAW_APP_PROFILE =
OPENCLAW_SHARE_PROFILE =
OPENCLAW_ACTIVITY_WIDGET_PROFILE =
OPENCLAW_WATCH_APP_PROFILE =
OPENCLAW_WATCH_EXTENSION_PROFILE =

View File

@@ -101,6 +101,7 @@ Release-owner secrets:
- App Store Connect API auth uses Keychain for private key material plus non-secret `apps/ios/fastlane/.env` variables.
- The encrypted signing repo password lives outside this repo in the release-owner vault and is exposed locally as `MATCH_PASSWORD`.
- The share sheet requires the Apple Developer App Group in `apps/ios/Config/AppStoreSigning.json` to be associated with both the app and share-extension bundle IDs before App Store profiles are regenerated.
- Apple Distribution private keys, certificates, provisioning profiles, and decrypted signing sync output stay under `apps/ios/build/` or Keychain and are gitignored.
- Rotating release signing means refreshing Fastlane `match` assets and pushing a fresh encrypted sync state.
@@ -155,7 +156,8 @@ This should create `apps/ios/fastlane/.env` with non-secret App Store Connect va
- `ai.openclawfoundation.app.share`
- `ai.openclawfoundation.app.activitywidget`
- `ai.openclawfoundation.app.watchkitapp`
- `ai.openclawfoundation.app.watchkitapp.extension`
The main app and share extension must both be associated with the App Group pinned in `apps/ios/Config/AppStoreSigning.json`.
Use `pnpm ios:release:signing:setup` for the initial portal setup, then `MATCH_PASSWORD=... pnpm ios:release:signing:sync:push` to publish encrypted Fastlane match assets to the shared private repo.

View File

@@ -41,5 +41,7 @@
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).ShareViewController</string>
</dict>
<key>OpenClawAppGroupIdentifier</key>
<string>$(OPENCLAW_APP_GROUP_ID)</string>
</dict>
</plist>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>$(OPENCLAW_APP_GROUP_ID)</string>
</array>
</dict>
</plist>

View File

@@ -184,7 +184,8 @@ final class ShareViewController: UIViewController {
clientId: clientId,
clientMode: "node",
clientDisplayName: "OpenClaw Share",
includeDeviceIdentity: false)
deviceIdentityProfile: .shareExtension,
includeDeviceIdentity: true)
}
do {

View File

@@ -10,8 +10,8 @@ OPENCLAW_DEVELOPMENT_TEAM = FWJYW4S8P8
OPENCLAW_APP_BUNDLE_ID = ai.openclawfoundation.app
OPENCLAW_SHARE_BUNDLE_ID = ai.openclawfoundation.app.share
OPENCLAW_APP_GROUP_ID = group.ai.openclawfoundation.app.shared
OPENCLAW_WATCH_APP_BUNDLE_ID = ai.openclawfoundation.app.watchkitapp
OPENCLAW_WATCH_EXTENSION_BUNDLE_ID = ai.openclawfoundation.app.watchkitapp.extension
OPENCLAW_ACTIVITY_WIDGET_BUNDLE_ID = ai.openclawfoundation.app.activitywidget
OPENCLAW_APNS_ENTITLEMENT_ENVIRONMENT = development
@@ -19,7 +19,6 @@ OPENCLAW_APP_PROFILE = ai.openclawfoundation.app Development
OPENCLAW_SHARE_PROFILE = ai.openclawfoundation.app.share Development
OPENCLAW_ACTIVITY_WIDGET_PROFILE =
OPENCLAW_WATCH_APP_PROFILE =
OPENCLAW_WATCH_EXTENSION_PROFILE =
// Keep local includes after defaults: xcconfig is evaluated top-to-bottom,
// so later assignments in local files override the defaults above.

View File

@@ -53,8 +53,9 @@ struct SettingsProTab: View {
@State var suppressCredentialPersist = false
@State var locationStatusText: String?
@State var previousLocationModeRaw: String = OpenClawLocationMode.off.rawValue
@State var notificationStatusText = "Checking"
@State var notificationActionText = "Request Access"
@State var notificationStatus: SettingsNotificationStatus = .checking
@State var isRequestingNotificationAuthorization = false
@State var showNotificationRelayDisclosure = false
@State var diagnosticsLastRunText = "Not run"
@State var diagnosticsIssueCount: Int?
@State var showTalkIssueDetails = false
@@ -62,15 +63,18 @@ struct SettingsProTab: View {
let initialRoute: SettingsRoute?
let directRoute: SettingsRoute?
let headerLeadingAction: OpenClawSidebarHeaderAction?
let onRouteChange: ((SettingsRoute?) -> Void)?
init(
initialRoute: SettingsRoute? = nil,
directRoute: SettingsRoute? = nil,
headerLeadingAction: OpenClawSidebarHeaderAction? = nil)
headerLeadingAction: OpenClawSidebarHeaderAction? = nil,
onRouteChange: ((SettingsRoute?) -> Void)? = nil)
{
self.initialRoute = initialRoute
self.directRoute = directRoute
self.headerLeadingAction = headerLeadingAction
self.onRouteChange = onRouteChange
}
var body: some View {
@@ -118,6 +122,7 @@ struct SettingsProTab: View {
self.refreshNotificationSettings()
self.applyPendingGatewaySetupLinkIfNeeded()
self.applyInitialRouteIfNeeded()
self.notifyRouteChange()
}
.onChange(of: self.scenePhase) { _, phase in
if phase == .active {
@@ -154,6 +159,9 @@ struct SettingsProTab: View {
.onChange(of: self.appModel.gatewaySetupRequestID) { _, _ in
self.applyPendingGatewaySetupLinkIfNeeded()
}
.onChange(of: self.navigationPath) { _, _ in
self.notifyRouteChange()
}
}
private func settingsModalPresentation(_ content: some View) -> some View {
@@ -218,6 +226,19 @@ struct SettingsProTab: View {
} message: {
Text(self.scannerError ?? "")
}
.alert("Enable OpenClaw Hosted Push Relay?", isPresented: self.$showNotificationRelayDisclosure) {
Button("Continue") {
self.requestNotificationAuthorizationFromSettings()
}
Button("Not Now", role: .cancel) {}
} message: {
Text(self.notificationRelayDisclosureMessage)
}
}
func openNotificationsRouteFromApprovals() {
guard self.directRoute == nil else { return }
self.navigationPath = [.notifications]
}
private func applyInitialRouteIfNeeded() {
@@ -226,4 +247,12 @@ struct SettingsProTab: View {
guard self.navigationPath != [initialRoute] else { return }
self.navigationPath = [initialRoute]
}
private func notifyRouteChange() {
if let directRoute {
self.onRouteChange?(directRoute)
return
}
self.onRouteChange?(self.navigationPath.last)
}
}

View File

@@ -65,7 +65,7 @@ extension SettingsProTab {
title: "Notifications",
detail: "Approval and event alert channel",
value: self.notificationStatusText,
color: self.notificationStatusText == "Allowed" ? OpenClawBrand.ok : .secondary)
color: self.notificationStatus.color)
Divider().padding(.leading, 60)
self.diagnosticCheckRow(
icon: "rectangle.on.rectangle",
@@ -157,7 +157,7 @@ extension SettingsProTab {
gatewayConnected: self.gatewayDiagnosticConnected,
discoveredGatewayCount: self.gatewayController.gateways.count,
talkConfigLoaded: self.gatewayDiagnosticTalkConfigLoaded,
notificationStatusText: self.notificationStatusText)
notificationsAllowed: self.notificationStatus == .allowed)
self.diagnosticsIssueCount = issueCount
self.diagnosticsLastRunText = SettingsDiagnostics.timestamp(Date())
}
@@ -422,40 +422,41 @@ extension SettingsProTab {
}
func handleNotificationAction() {
if self.notificationStatusText == "Allowed" || self.notificationStatusText == "Not Allowed" {
self.openSystemSettings()
if self.notificationStatus.shouldOpenNotificationSettings {
self.openNotificationSettings()
return
}
guard self.notificationStatus == .notSet else { return }
if PushBuildConfig.current.usesOpenClawHostedRelay {
self.showNotificationRelayDisclosure = true
return
}
self.requestNotificationAuthorizationFromSettings()
}
func requestNotificationAuthorizationFromSettings() {
guard !self.isRequestingNotificationAuthorization else { return }
self.isRequestingNotificationAuthorization = true
Task {
let granted = await (try? UNUserNotificationCenter.current().requestAuthorization(options: [
.alert,
.badge,
.sound,
])) ?? false
let settings = await UNUserNotificationCenter.current().notificationSettings()
await MainActor.run {
self.notificationStatusText = granted ? "Allowed" : "Not Allowed"
self.notificationActionText = granted ? "Open System Settings" : "Open System Settings"
self.isRequestingNotificationAuthorization = false
self.notificationStatus = SettingsNotificationStatus(settings.authorizationStatus)
guard granted, self.notificationStatus.allowsNotifications else { return }
UIApplication.shared.registerForRemoteNotifications()
}
}
}
@MainActor
func applyNotificationStatus(_ status: UNAuthorizationStatus) {
switch status {
case .authorized, .provisional, .ephemeral:
self.notificationStatusText = "Allowed"
self.notificationActionText = "Open System Settings"
case .denied:
self.notificationStatusText = "Not Allowed"
self.notificationActionText = "Open System Settings"
case .notDetermined:
self.notificationStatusText = "Not Set"
self.notificationActionText = "Request Access"
@unknown default:
self.notificationStatusText = "Unknown"
self.notificationActionText = "Open System Settings"
}
self.notificationStatus = SettingsNotificationStatus(status)
}
func persistGatewayToken(_ value: String) {
@@ -476,8 +477,8 @@ extension SettingsProTab {
instanceId: instanceId)
}
func openSystemSettings() {
guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
func openNotificationSettings() {
guard let url = URL(string: UIApplication.openNotificationSettingsURLString) else { return }
UIApplication.shared.open(url)
}
@@ -675,6 +676,9 @@ extension SettingsProTab {
if self.appModel.isAppleReviewDemoModeEnabled {
return "Live gateway requests are disabled in demo mode."
}
if self.notificationsNeedAttention {
return "Foreground approvals still appear while OpenClaw is connected."
}
return self.gatewayConnected ? "Gateway requests will appear here." : "Connect to the gateway."
}
@@ -714,7 +718,19 @@ extension SettingsProTab {
}
var approvalsDetail: String {
self.pendingApproval == nil ? "No approvals waiting" : "1 request waiting"
if self.notificationsNeedAttention {
return self.pendingApproval == nil ? "Notifications off" : "1 waiting, notifications off"
}
return self.pendingApproval == nil ? "No approvals waiting" : "1 request waiting"
}
var notificationsNeedAttention: Bool {
switch self.notificationStatus {
case .allowed, .checking:
false
case .notAllowed, .notSet, .unknown:
true
}
}
var approvalItems: [SettingsApprovalItem] {
@@ -777,4 +793,41 @@ extension SettingsProTab {
case .always: "Always"
}
}
var notificationStatusText: String {
self.notificationStatus.text
}
var notificationActionText: String {
self.notificationStatus.actionTitle
}
var notificationStatusDetail: String {
switch self.notificationStatus {
case .checking:
"Checking iOS notification permission."
case .allowed:
"OpenClaw can show approval prompts and event alerts when the app is not active."
case .notAllowed:
"Notifications have been denied. Enable them in iOS Settings."
case .notSet:
"Enable notifications to receive approval prompts and event alerts outside the app."
case .unknown:
"OpenClaw cannot determine the current notification permission state."
}
}
var notificationRelayDetail: String {
if PushBuildConfig.current.usesOpenClawHostedRelay {
return """
This build uses OpenClaw's hosted push relay at ios-push-relay.openclaw.ai for notification \
delivery data.
"""
}
return "This build is not configured to use OpenClaw's hosted push relay."
}
var notificationRelayDisclosureMessage: String {
"Enabling this sends delivery data through OpenClaw's hosted push relay."
}
}

View File

@@ -308,15 +308,57 @@ extension SettingsProTab {
self.detailStatusCard(
icon: "checkmark.shield.fill",
title: "Approvals",
detail: self.pendingApproval == nil ? "No gateway actions are waiting for review." :
"Review the pending gateway action.",
value: self.pendingApproval == nil ? "clear" : "1 waiting",
color: self.pendingApproval == nil ? OpenClawBrand.ok : OpenClawBrand.warn)
detail: self.notificationsNeedAttention
? "Out-of-app approval alerts need notification permission."
: (self.pendingApproval == nil ? "No gateway actions are waiting for review." :
"Review the pending gateway action."),
value: self.notificationsNeedAttention
? "Alerts Off"
: (self.pendingApproval == nil ? "clear" : "1 waiting"),
color: self.notificationsNeedAttention ? OpenClawBrand.warn :
(self.pendingApproval == nil ? OpenClawBrand.ok : OpenClawBrand.warn))
if self.notificationsNeedAttention {
self.approvalNotificationsWarningCard
}
self.approvalsReviewCard
}
}
var approvalNotificationsWarningCard: some View {
ProCard(radius: SettingsLayout.cardRadius) {
VStack(alignment: .leading, spacing: 12) {
HStack(alignment: .top, spacing: 12) {
ProIconBadge(systemName: "bell.slash.fill", color: OpenClawBrand.warn)
VStack(alignment: .leading, spacing: 4) {
Text("Notifications are off")
.font(.subheadline.weight(.semibold))
Text(
"""
Enable Notifications to receive approval notifications while OpenClaw is not open.
""")
.font(.caption)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
}
if self.directRoute == nil {
Button {
self.openNotificationsRouteFromApprovals()
} label: {
Label("Open Notifications", systemImage: "bell.badge")
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
.controlSize(.small)
}
}
}
.padding(.horizontal, OpenClawProMetric.pagePadding)
}
var approvalsReviewCard: some View {
ProCard(radius: SettingsLayout.cardRadius) {
VStack(alignment: .leading, spacing: 12) {
@@ -490,9 +532,9 @@ extension SettingsProTab {
self.detailStatusCard(
icon: "bell",
title: "Notifications",
detail: "Approvals and event alerts from OpenClaw.",
detail: self.notificationStatusDetail,
value: self.notificationStatusText,
color: self.notificationStatusText == "Allowed" ? OpenClawBrand.ok : .secondary)
color: self.notificationStatus.color)
ProCard(radius: SettingsLayout.cardRadius) {
VStack(alignment: .leading, spacing: 12) {
@@ -501,15 +543,30 @@ extension SettingsProTab {
} label: {
Label(
self.notificationActionText,
systemImage: self.notificationStatusText == "Allowed" ? "gear" : "bell.badge")
systemImage: self.notificationStatus.actionIcon)
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.controlSize(.small)
.disabled(self.notificationStatus == .checking || self.isRequestingNotificationAuthorization)
Text("OpenClaw uses notifications for approval prompts and mirrored event alerts.")
Text(self.notificationStatusDetail)
.font(.caption)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
Divider()
HStack(alignment: .top, spacing: 10) {
Image(systemName: "network")
.font(.caption.weight(.semibold))
.foregroundStyle(OpenClawBrand.accent)
.frame(width: 22, height: 22)
Text(self.notificationRelayDetail)
.font(.caption)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
}
}
.padding(.horizontal, OpenClawProMetric.pagePadding)

View File

@@ -1,6 +1,7 @@
import Darwin
import OpenClawKit
import SwiftUI
import UserNotifications
enum SettingsRoute: Hashable {
case gateway
@@ -65,6 +66,87 @@ struct SettingsApprovalRow: View {
}
}
enum SettingsNotificationStatus: Equatable {
case checking
case allowed
case notAllowed
case notSet
case unknown
init(_ status: UNAuthorizationStatus) {
switch status {
case .authorized, .provisional, .ephemeral:
self = .allowed
case .denied:
self = .notAllowed
case .notDetermined:
self = .notSet
@unknown default:
self = .unknown
}
}
var text: String {
switch self {
case .checking: "Checking"
case .allowed: "Enabled"
case .notAllowed: "Denied"
case .notSet: "Not Enabled"
case .unknown: "Unknown"
}
}
var actionTitle: String {
switch self {
case .notSet:
"Enable Notifications"
case .checking:
"Checking"
case .allowed:
"Manage in iOS Settings"
case .notAllowed, .unknown:
"Open iOS Settings"
}
}
var actionIcon: String {
switch self {
case .allowed:
"gear"
case .notAllowed, .unknown:
"gear.badge"
case .checking:
"hourglass"
case .notSet:
"bell.badge"
}
}
var color: Color {
switch self {
case .allowed:
OpenClawBrand.ok
case .notAllowed, .unknown:
OpenClawBrand.warn
case .checking, .notSet:
.secondary
}
}
var shouldOpenNotificationSettings: Bool {
switch self {
case .allowed, .notAllowed, .unknown:
true
case .checking, .notSet:
false
}
}
var allowsNotifications: Bool {
self == .allowed
}
}
enum SettingsDiagnosticIssue: String, Equatable, CaseIterable {
case gatewayOffline
case discoveryUnavailable
@@ -77,13 +159,13 @@ enum SettingsDiagnostics {
gatewayConnected: Bool,
discoveredGatewayCount: Int,
talkConfigLoaded: Bool,
notificationStatusText: String) -> [SettingsDiagnosticIssue]
notificationsAllowed: Bool) -> [SettingsDiagnosticIssue]
{
var issues: [SettingsDiagnosticIssue] = []
if !gatewayConnected { issues.append(.gatewayOffline) }
if discoveredGatewayCount == 0 { issues.append(.discoveryUnavailable) }
if gatewayConnected, !talkConfigLoaded { issues.append(.talkConfigMissing) }
if notificationStatusText != "Allowed" { issues.append(.notificationsUnavailable) }
if !notificationsAllowed { issues.append(.notificationsUnavailable) }
return issues
}
@@ -91,13 +173,13 @@ enum SettingsDiagnostics {
gatewayConnected: Bool,
discoveredGatewayCount: Int,
talkConfigLoaded: Bool,
notificationStatusText: String) -> Int
notificationsAllowed: Bool) -> Int
{
self.issues(
gatewayConnected: gatewayConnected,
discoveredGatewayCount: discoveredGatewayCount,
talkConfigLoaded: talkConfigLoaded,
notificationStatusText: notificationStatusText).count
notificationsAllowed: notificationsAllowed).count
}
static func timestamp(_ date: Date) -> String {

View File

@@ -2,11 +2,14 @@ import SwiftUI
private struct ExecApprovalPromptDialogModifier: ViewModifier {
@Environment(NodeAppModel.self) private var appModel: NodeAppModel
let suppressedApprovalID: String?
func body(content: Content) -> some View {
content
.overlay {
if let prompt = self.appModel.pendingExecApprovalPrompt {
if let prompt = self.appModel.pendingExecApprovalPrompt,
prompt.id != self.suppressedApprovalID
{
ZStack {
Color.black.opacity(0.38)
.ignoresSafeArea()
@@ -58,7 +61,7 @@ private struct ExecApprovalPromptCard: View {
VStack(alignment: .leading, spacing: 6) {
Text("Exec approval required")
.font(.headline)
Text("OpenClaw opened from a notification. Review this exec request before continuing.")
Text("Review this exec request before continuing. Your decision will be sent back to the gateway.")
.font(.subheadline)
.foregroundStyle(.secondary)
}
@@ -188,7 +191,7 @@ private struct ExecApprovalPromptMetadataRow: View {
}
extension View {
func execApprovalPromptDialog() -> some View {
self.modifier(ExecApprovalPromptDialogModifier())
func execApprovalPromptDialog(suppressedApprovalID: String? = nil) -> some View {
self.modifier(ExecApprovalPromptDialogModifier(suppressedApprovalID: suppressedApprovalID))
}
}

View File

@@ -62,6 +62,7 @@ struct GatewayConnectConfig {
lhs.clientId == rhs.clientId &&
lhs.clientMode == rhs.clientMode &&
lhs.clientDisplayName == rhs.clientDisplayName &&
lhs.deviceIdentityProfile == rhs.deviceIdentityProfile &&
lhs.includeDeviceIdentity == rhs.includeDeviceIdentity &&
lhsScopes == rhsScopes &&
lhsCaps == rhsCaps &&

View File

@@ -0,0 +1,102 @@
import SwiftUI
private struct NotificationPermissionGuidanceDialogModifier: ViewModifier {
@Environment(NodeAppModel.self) private var appModel: NodeAppModel
let openNotifications: (String) -> Void
func body(content: Content) -> some View {
content
.overlay {
if let prompt = self.appModel.pendingNotificationPermissionGuidancePrompt {
ZStack {
Color.black.opacity(0.38)
.ignoresSafeArea()
NotificationPermissionGuidanceCard(
onOpenNotifications: {
let approvalId = prompt.approvalId
self.appModel.dismissNotificationPermissionGuidancePrompt(
suppressFuture: false)
self.openNotifications(approvalId)
},
onDismiss: {
self.appModel.dismissNotificationPermissionGuidancePrompt(
suppressFuture: false)
},
onSuppressFuture: {
self.appModel.dismissNotificationPermissionGuidancePrompt(
suppressFuture: true)
})
.padding(.horizontal, 20)
.frame(maxWidth: 460)
.transition(.scale(scale: 0.98).combined(with: .opacity))
}
.zIndex(2)
.id(prompt.id)
}
}
.animation(
.easeInOut(duration: 0.18),
value: self.appModel.pendingNotificationPermissionGuidancePrompt?.id)
}
}
private struct NotificationPermissionGuidanceCard: View {
let onOpenNotifications: () -> Void
let onDismiss: () -> Void
let onSuppressFuture: () -> Void
var body: some View {
VStack(alignment: .leading, spacing: 14) {
VStack(alignment: .leading, spacing: 6) {
Text("Notifications are off")
.font(.headline)
Text(
"""
Exec approvals can only be reviewed while OpenClaw is open and connected.
Enable Notifications to receive approval notifications while OpenClaw is not open.
""")
.font(.subheadline)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
VStack(spacing: 10) {
Button {
self.onOpenNotifications()
} label: {
Text("Open Notifications Settings")
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
Button(role: .cancel) {
self.onDismiss()
} label: {
Text("Not Now")
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
Button {
self.onSuppressFuture()
} label: {
Text("Don't show again")
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
}
.controlSize(.large)
.frame(maxWidth: .infinity)
}
.padding(18)
.proPanelSurface(tint: OpenClawBrand.warn, radius: 20, isProminent: true)
}
}
extension View {
func notificationPermissionGuidanceDialog(openNotifications: @escaping (String) -> Void) -> some View {
self.modifier(NotificationPermissionGuidanceDialogModifier(openNotifications: openNotifications))
}
}

View File

@@ -78,6 +78,8 @@
<string>OpenClaw uses on-device speech recognition for talk mode and voice wake.</string>
<key>NSSupportsLiveActivities</key>
<true/>
<key>OpenClawAppGroupIdentifier</key>
<string>$(OPENCLAW_APP_GROUP_ID)</string>
<key>OpenClawCanonicalVersion</key>
<string>$(OPENCLAW_IOS_VERSION)</string>
<key>OpenClawPushAPNsEnvironment</key>

View File

@@ -23,6 +23,10 @@ private struct WatchChatPreview {
var statusText: String?
}
private struct ExecApprovalGatewayEventPayload: Decodable {
var id: String
}
/// Ensures notification requests return promptly even if the system prompt blocks.
private final class NotificationInvokeLatch<T: Sendable>: @unchecked Sendable {
private let lock = NSLock()
@@ -83,6 +87,11 @@ final class NodeAppModel {
}
}
struct NotificationPermissionGuidancePrompt: Identifiable, Equatable {
let id = UUID()
let approvalId: String
}
private enum ExecApprovalResolutionOutcome {
case resolved
case stale
@@ -96,6 +105,8 @@ final class NodeAppModel {
}
private let deepLinkLogger = Logger(subsystem: "ai.openclawfoundation.app", category: "DeepLink")
private nonisolated static let execApprovalNotificationGuidanceSuppressedKey =
"notifications.execApprovalGuidance.suppressed"
private let pushWakeLogger = Logger(subsystem: "ai.openclawfoundation.app", category: "PushWake")
private let pendingActionLogger = Logger(subsystem: "ai.openclawfoundation.app", category: "PendingAction")
private let locationWakeLogger = Logger(subsystem: "ai.openclawfoundation.app", category: "LocationWake")
@@ -156,6 +167,7 @@ final class NodeAppModel {
private(set) var pendingExecApprovalPromptResolving: Bool = false
private(set) var pendingExecApprovalPromptErrorText: String?
private var pendingExecApprovalPromptRequestGeneration: Int = 0
private(set) var pendingNotificationPermissionGuidancePrompt: NotificationPermissionGuidancePrompt?
private var queuedAgentDeepLinkPrompt: AgentDeepLinkPrompt?
private var lastAgentDeepLinkPromptAt: Date = .distantPast
@ObservationIgnored private var queuedAgentDeepLinkPromptTask: Task<Void, Never>?
@@ -895,26 +907,50 @@ final class NodeAppModel {
for await evt in stream {
if Task.isCancelled { return }
guard let payload = evt.payload else { continue }
switch evt.event {
case "voicewake.changed":
struct Payload: Decodable { var triggers: [String] }
guard let decoded = try? GatewayPayloadDecoding.decode(payload, as: Payload.self) else { continue }
let triggers = VoiceWakePreferences.sanitizeTriggerWords(decoded.triggers)
VoiceWakePreferences.saveTriggerWords(triggers)
case "talk.mode":
struct Payload: Decodable {
var enabled: Bool
var phase: String?
}
guard let decoded = try? GatewayPayloadDecoding.decode(payload, as: Payload.self) else { continue }
self.applyTalkModeSync(enabled: decoded.enabled, phase: decoded.phase)
default:
continue
}
await self.handleOperatorGatewayServerEvent(evt)
}
}
}
private func handleOperatorGatewayServerEvent(_ evt: EventFrame) async {
guard let payload = evt.payload else { return }
switch evt.event {
case "voicewake.changed":
struct Payload: Decodable { var triggers: [String] }
guard let decoded = try? GatewayPayloadDecoding.decode(payload, as: Payload.self) else { return }
let triggers = VoiceWakePreferences.sanitizeTriggerWords(decoded.triggers)
VoiceWakePreferences.saveTriggerWords(triggers)
case "talk.mode":
struct Payload: Decodable {
var enabled: Bool
var phase: String?
}
guard let decoded = try? GatewayPayloadDecoding.decode(payload, as: Payload.self) else { return }
self.applyTalkModeSync(enabled: decoded.enabled, phase: decoded.phase)
case ExecApprovalNotificationBridge.requestedKind:
guard let approvalId = Self.execApprovalEventID(from: payload) else { return }
await self.presentNotificationPermissionGuidanceForExecApprovalIfNeeded(approvalId: approvalId)
await self.presentExecApprovalNotificationPrompt(
ExecApprovalNotificationPrompt(approvalId: approvalId))
case ExecApprovalNotificationBridge.resolvedKind:
guard let approvalId = Self.execApprovalEventID(from: payload) else { return }
await self.handleExecApprovalResolvedRemotePush(approvalId: approvalId)
default:
return
}
}
private nonisolated static func execApprovalEventID(from payload: AnyCodable) -> String? {
guard let decoded = try? GatewayPayloadDecoding.decode(
payload,
as: ExecApprovalGatewayEventPayload.self)
else {
return nil
}
let approvalId = decoded.id.trimmingCharacters(in: .whitespacesAndNewlines)
return approvalId.isEmpty ? nil : approvalId
}
private func applyTalkModeSync(enabled: Bool, phase: String?) {
_ = phase
guard self.talkMode.isEnabled != enabled else { return }
@@ -1332,8 +1368,8 @@ final class NodeAppModel {
error: OpenClawNodeError(code: .invalidRequest, message: "INVALID_REQUEST: empty notification"))
}
let finalStatus = await self.requestNotificationAuthorizationIfNeeded()
guard finalStatus == .authorized || finalStatus == .provisional || finalStatus == .ephemeral else {
let status = await self.notificationAuthorizationStatus()
guard Self.isNotificationAuthorizationAllowed(status) else {
return BridgeInvokeResponse(
id: req.id,
ok: false,
@@ -1385,9 +1421,18 @@ final class NodeAppModel {
error: OpenClawNodeError(code: .invalidRequest, message: "INVALID_REQUEST: empty chat.push text"))
}
let finalStatus = await self.requestNotificationAuthorizationIfNeeded()
let shouldSpeak = params.speak ?? true
let status = await self.notificationAuthorizationStatus()
let notificationsAllowed = Self.isNotificationAuthorizationAllowed(status)
if !notificationsAllowed, !shouldSpeak {
return BridgeInvokeResponse(
id: req.id,
ok: false,
error: OpenClawNodeError(code: .unavailable, message: "NOT_AUTHORIZED: notifications"))
}
let messageId = UUID().uuidString
if finalStatus == .authorized || finalStatus == .provisional || finalStatus == .ephemeral {
if notificationsAllowed {
let addResult = await self.runNotificationCall(timeoutSeconds: 2.0) { [notificationCenter] in
let content = UNMutableNotificationContent()
content.title = "OpenClaw"
@@ -1408,7 +1453,7 @@ final class NodeAppModel {
}
}
if params.speak ?? true {
if shouldSpeak {
let toSpeak = text
Task { @MainActor in
try? await TalkSystemSpeechSynthesizer.shared.speak(text: toSpeak)
@@ -1420,26 +1465,6 @@ final class NodeAppModel {
return BridgeInvokeResponse(id: req.id, ok: true, payloadJSON: json)
}
private func requestNotificationAuthorizationIfNeeded() async -> NotificationAuthorizationStatus {
let status = await self.notificationAuthorizationStatus()
guard status == .notDetermined else { return status }
// Avoid hanging invoke requests if the permission prompt is never answered.
_ = await self.runNotificationCall(timeoutSeconds: 2.0) { [notificationCenter] in
_ = try await notificationCenter.requestAuthorization(options: [.alert, .sound, .badge])
}
let updatedStatus = await self.notificationAuthorizationStatus()
if Self.isNotificationAuthorizationAllowed(updatedStatus) {
// Refresh APNs registration immediately after the first permission grant so the
// gateway can receive a push registration without requiring an app relaunch.
await MainActor.run {
UIApplication.shared.registerForRemoteNotifications()
}
}
return updatedStatus
}
private func notificationAuthorizationStatus() async -> NotificationAuthorizationStatus {
let result = await self.runNotificationCall(timeoutSeconds: 1.5) { [notificationCenter] in
await notificationCenter.authorizationStatus()
@@ -1463,6 +1488,29 @@ final class NodeAppModel {
}
}
private func presentNotificationPermissionGuidanceForExecApprovalIfNeeded(approvalId: String) async {
guard !self.execApprovalNotificationGuidanceSuppressed else { return }
let status = await self.notificationAuthorizationStatus()
guard !Self.isNotificationAuthorizationAllowed(status) else { return }
self.pendingNotificationPermissionGuidancePrompt =
NotificationPermissionGuidancePrompt(approvalId: approvalId)
}
var execApprovalNotificationGuidanceSuppressed: Bool {
UserDefaults.standard.bool(forKey: Self.execApprovalNotificationGuidanceSuppressedKey)
}
func dismissNotificationPermissionGuidancePrompt(suppressFuture: Bool) {
if suppressFuture {
UserDefaults.standard.set(true, forKey: Self.execApprovalNotificationGuidanceSuppressedKey)
}
self.pendingNotificationPermissionGuidancePrompt = nil
}
func resetExecApprovalNotificationGuidanceSuppression() {
UserDefaults.standard.removeObject(forKey: Self.execApprovalNotificationGuidanceSuppressedKey)
}
private func runNotificationCall<T: Sendable>(
timeoutSeconds: Double,
operation: @escaping @Sendable () async throws -> T) async -> Result<T, NotificationCallError>
@@ -2335,10 +2383,6 @@ extension NodeAppModel {
nodeOptions: nodeOptions,
sessionBox: sessionBox)
}
// QR bootstrap onboarding should surface the system notification permission
// prompt immediately so visible APNs alerts work without a second manual step.
_ = await self.requestNotificationAuthorizationIfNeeded()
}
private func refreshBackgroundReconnectSuppressionIfNeeded(source: String) {
@@ -3916,11 +3960,15 @@ extension NodeAppModel {
let hadWatchPrompt = self.watchExecApprovalPromptsByID[normalizedApprovalID] != nil
let hadPendingPrompt = self.pendingExecApprovalPrompt?.id == normalizedApprovalID
let hadPendingRecoveryID = self.pendingWatchExecApprovalRecoveryIDs.contains(normalizedApprovalID)
guard hadWatchPrompt || hadPendingPrompt || hadPendingRecoveryID else {
let hadGuidancePrompt = self.pendingNotificationPermissionGuidancePrompt?.approvalId == normalizedApprovalID
let hadApprovalSurface = hadWatchPrompt || hadPendingPrompt || hadPendingRecoveryID
guard hadApprovalSurface || hadGuidancePrompt else {
return
}
await self.publishWatchExecApprovalExpired(approvalId: normalizedApprovalID, reason: .resolved)
if hadApprovalSurface {
await self.publishWatchExecApprovalExpired(approvalId: normalizedApprovalID, reason: .resolved)
}
self.clearPendingExecApprovalPromptIfMatches(normalizedApprovalID)
}
@@ -4401,10 +4449,17 @@ extension NodeAppModel {
private func clearPendingExecApprovalPromptIfMatches(_ approvalId: String) {
let normalizedApprovalID = approvalId.trimmingCharacters(in: .whitespacesAndNewlines)
self.clearNotificationPermissionGuidancePromptIfMatches(normalizedApprovalID)
guard self.pendingExecApprovalPrompt?.id == normalizedApprovalID else { return }
self.dismissPendingExecApprovalPrompt()
}
private func clearNotificationPermissionGuidancePromptIfMatches(_ approvalId: String) {
let normalizedApprovalID = approvalId.trimmingCharacters(in: .whitespacesAndNewlines)
guard self.pendingNotificationPermissionGuidancePrompt?.approvalId == normalizedApprovalID else { return }
self.pendingNotificationPermissionGuidancePrompt = nil
}
private nonisolated static func isApprovalNotificationStaleError(_ error: Error) -> Bool {
guard let gatewayError = error as? GatewayResponseError else { return false }
if gatewayError.code != "INVALID_REQUEST" {
@@ -5110,6 +5165,20 @@ extension NodeAppModel {
self.pendingExecApprovalPrompt
}
func _test_pendingNotificationPermissionGuidancePrompt() -> NotificationPermissionGuidancePrompt? {
self.pendingNotificationPermissionGuidancePrompt
}
func _debug_presentNotificationPermissionGuidancePromptForScreenshot() {
self.resetExecApprovalNotificationGuidanceSuppression()
self.pendingNotificationPermissionGuidancePrompt =
NotificationPermissionGuidancePrompt(approvalId: "screenshot-exec-approval")
}
func _test_resetExecApprovalNotificationGuidanceSuppression() {
self.resetExecApprovalNotificationGuidanceSuppression()
}
func _test_recordPendingWatchExecApprovalRecoveryID(_ approvalId: String) {
self.appendPendingWatchExecApprovalRecoveryID(approvalId)
}
@@ -5139,6 +5208,14 @@ extension NodeAppModel {
isBackgrounded: isBackgrounded)
}
nonisolated static func _test_execApprovalEventID(from payload: AnyCodable) -> String? {
self.execApprovalEventID(from: payload)
}
func _test_handleOperatorGatewayServerEvent(_ event: EventFrame) async {
await self.handleOperatorGatewayServerEvent(event)
}
nonisolated static func _test_watchExecApprovalIDsNeedingFetch(
candidateIDs: [String],
cachedApprovalIDs: [String]) -> [String]
@@ -5235,24 +5312,6 @@ extension NodeAppModel {
func _test_restartGatewaySessionsAfterForegroundStaleConnection() async {
await self.restartGatewaySessionsAfterForegroundStaleConnection()
}
func _test_handleSuccessfulBootstrapGatewayOnboarding() async {
await self.handleSuccessfulBootstrapGatewayOnboarding(
url: URL(string: "wss://gateway.example")!,
stableID: "test-gateway",
token: nil,
password: nil,
nodeOptions: GatewayConnectOptions(
role: "node",
scopes: [],
caps: [],
commands: [],
permissions: [:],
clientId: "openclaw-ios",
clientMode: "node",
clientDisplayName: nil),
sessionBox: nil)
}
}
#endif
// swiftlint:enable type_body_length file_length

View File

@@ -18,6 +18,7 @@ enum GatewayOnboardingReset {
let deviceId = DeviceIdentityStore.loadOrCreate().deviceId
DeviceAuthStore.clearToken(deviceId: deviceId, role: "node")
DeviceAuthStore.clearToken(deviceId: deviceId, role: "operator")
DeviceAuthStore.clearAll(profile: .shareExtension)
GatewaySettingsStore.clearLastGatewayConnection(defaults: defaults)
GatewaySettingsStore.clearPreferredGatewayStableID(defaults: defaults)

View File

@@ -4,5 +4,9 @@
<dict>
<key>aps-environment</key>
<string>$(OPENCLAW_APNS_ENTITLEMENT_ENVIRONMENT)</string>
<key>com.apple.security.application-groups</key>
<array>
<string>$(OPENCLAW_APP_GROUP_ID)</string>
</array>
</dict>
</plist>

View File

@@ -409,7 +409,7 @@ enum WatchPromptNotificationBridge {
let title = params.title.trimmingCharacters(in: .whitespacesAndNewlines)
let body = params.body.trimmingCharacters(in: .whitespacesAndNewlines)
guard !title.isEmpty || !body.isEmpty else { return }
guard await self.requestNotificationAuthorizationIfNeeded() else { return }
guard await self.isNotificationAuthorizationAllowed() else { return }
let normalizedActions = (params.actions ?? []).compactMap { action -> OpenClawWatchAction? in
let id = action.id.trimmingCharacters(in: .whitespacesAndNewlines)
@@ -516,29 +516,10 @@ enum WatchPromptNotificationBridge {
}
}
private static func requestNotificationAuthorizationIfNeeded() async -> Bool {
private static func isNotificationAuthorizationAllowed() async -> Bool {
let center = UNUserNotificationCenter.current()
let status = await self.notificationAuthorizationStatus(center: center)
switch status {
case .authorized, .provisional, .ephemeral:
return true
case .notDetermined:
let granted = await (try? center.requestAuthorization(options: [.alert, .sound, .badge])) ?? false
if !granted { return false }
let updatedStatus = await self.notificationAuthorizationStatus(center: center)
if self.isAuthorizationStatusAllowed(updatedStatus) {
// Refresh APNs registration immediately after the first permission grant so the
// gateway can receive a push registration without requiring an app relaunch.
await MainActor.run {
UIApplication.shared.registerForRemoteNotifications()
}
}
return self.isAuthorizationStatusAllowed(updatedStatus)
case .denied:
return false
@unknown default:
return false
}
return self.isAuthorizationStatusAllowed(status)
}
private static func isAuthorizationStatusAllowed(_ status: UNAuthorizationStatus) -> Bool {
@@ -635,6 +616,9 @@ struct OpenClawApp: App {
UserDefaults.standard.set(true, forKey: "gateway.hasConnectedOnce")
UserDefaults.standard.set(true, forKey: "onboarding.quickSetupDismissed")
appModel.enterScreenshotFixtureMode()
if Self.screenshotNotificationGuidanceEnabled {
appModel._debug_presentNotificationPermissionGuidancePromptForScreenshot()
}
}
#endif
OpenClawAppModelRegistry.appModel = appModel
@@ -686,6 +670,14 @@ struct OpenClawApp: App {
#endif
}
private static var screenshotNotificationGuidanceEnabled: Bool {
#if DEBUG
ProcessInfo.processInfo.arguments.contains("--openclaw-screenshot-notification-guidance")
#else
false
#endif
}
@MainActor
private func applyAppearancePreference() {
let style = self.appearancePreference.userInterfaceStyle

View File

@@ -22,6 +22,20 @@ struct PushBuildConfig {
let apnsEnvironment: PushAPNsEnvironment
static let current = PushBuildConfig()
static let openClawHostedRelayHost = "ios-push-relay.openclaw.ai"
var usesOpenClawHostedRelay: Bool {
guard self.transport == .relay, self.distribution == .official else { return false }
guard let relayBaseURL = self.relayBaseURL,
let components = URLComponents(url: relayBaseURL, resolvingAgainstBaseURL: false)
else {
return false
}
return components.scheme?.lowercased() == "https"
&& components.host?.lowercased() == Self.openClawHostedRelayHost
&& components.user == nil
&& components.password == nil
}
init(bundle: Bundle = .main) {
self.transport = Self.readEnum(

View File

@@ -24,6 +24,8 @@ struct RootTabs: View {
AppAppearancePreference.system.rawValue
@State private var selectedTab: AppTab = Self.initialTab
@State private var selectedSidebarDestination: SidebarDestination = Self.initialSidebarDestination
@State private var selectedSettingsRoute: SettingsRoute? = Self.initialSidebarDestination.settingsRoute
@State private var selectedSettingsRouteRequestID: Int = 0
@State private var isSidebarVisible: Bool = Self.initialSidebarVisibility ?? false
@State private var sidebarVisibilityUserOverridden: Bool = Self.initialSidebarVisibility != nil
@State private var isSidebarDrawerLayout: Bool = false
@@ -39,6 +41,7 @@ struct RootTabs: View {
@State private var didApplyInitialAppearance: Bool = false
@State private var didApplyInitialChatSession: Bool = false
@State private var handledGatewaySetupRequestID: Int = 0
@State private var suppressedExecApprovalPromptIDForNotificationSettings: String?
private static var initialTab: AppTab {
let arguments = ProcessInfo.processInfo.arguments
@@ -161,8 +164,10 @@ struct RootTabs: View {
.tabItem { Label("Agent", systemImage: "person.2.fill") }
.tag(AppTab.agent)
SettingsProTab(initialRoute: self.selectedSidebarDestination.settingsRoute)
.id(self.selectedSidebarDestination.settingsRoute.map { "\($0)" } ?? "settings")
SettingsProTab(
initialRoute: self.selectedSettingsRoute,
onRouteChange: self.handleSettingsRouteChange)
.id(self.settingsTabViewID)
.tabItem { Label("Settings", systemImage: "gearshape.fill") }
.tag(AppTab.settings)
}
@@ -235,7 +240,7 @@ struct RootTabs: View {
private var sidebarDetailShell: some View {
self.sidebarDetail
.id(self.selectedSidebarDestination.id)
.id(self.sidebarDetailShellID)
}
private var sidebarColumn: some View {
@@ -463,11 +468,21 @@ struct RootTabs: View {
headerLeadingAction: self.sidebarHeaderLeadingAction,
gatewayAction: { self.selectSidebarDestination(.gateway) })
case .settings:
SettingsProTab(headerLeadingAction: self.sidebarHeaderLeadingAction)
if let selectedSettingsRoute {
SettingsProTab(
directRoute: selectedSettingsRoute,
headerLeadingAction: self.sidebarHeaderLeadingAction,
onRouteChange: self.handleSettingsRouteChange)
} else {
SettingsProTab(
headerLeadingAction: self.sidebarHeaderLeadingAction,
onRouteChange: self.handleSettingsRouteChange)
}
case .gateway:
SettingsProTab(
directRoute: self.selectedSidebarDestination.settingsRoute ?? .gateway,
headerLeadingAction: self.sidebarHeaderLeadingAction)
directRoute: self.selectedSettingsRoute ?? self.selectedSidebarDestination.settingsRoute ?? .gateway,
headerLeadingAction: self.sidebarHeaderLeadingAction,
onRouteChange: self.handleSettingsRouteChange)
}
}
@@ -492,6 +507,21 @@ struct RootTabs: View {
return UIDevice.current.userInterfaceIdiom
}
private var sidebarDetailShellID: String {
let routeID = self.selectedSettingsRoute.map { "\($0)" } ?? "root"
return "\(self.selectedSidebarDestination.id):\(routeID):\(self.selectedSettingsRouteRequestID)"
}
private var settingsTabViewID: String {
let routeID = self.selectedSettingsRoute.map { "\($0)" } ?? "settings"
return "\(routeID):\(self.selectedSettingsRouteRequestID)"
}
private var activeExecApprovalPromptSuppressionID: String? {
guard self.selectedTab == .settings, self.selectedSettingsRoute == .notifications else { return nil }
return self.suppressedExecApprovalPromptIDForNotificationSettings
}
private var shouldCollapseSidebarAfterSelection: Bool {
Self.shouldCollapseSidebarAfterSelection(
layoutMode: self.isSidebarDrawerLayout ? .drawer : .split)
@@ -705,6 +735,11 @@ struct RootTabs: View {
.onChange(of: self.appModel.gatewaySetupRequestID) { _, _ in
self.maybeOpenSettingsForGatewaySetup()
}
.onChange(of: self.appModel.pendingExecApprovalPrompt?.id) { _, newValue in
if newValue != self.suppressedExecApprovalPromptIDForNotificationSettings {
self.suppressedExecApprovalPromptIDForNotificationSettings = nil
}
}
}
private func rootPresentation(_ content: some View) -> some View {
@@ -742,7 +777,12 @@ struct RootTabs: View {
}
.gatewayTrustPromptAlert()
.deepLinkAgentPromptAlert()
.execApprovalPromptDialog()
.execApprovalPromptDialog(
suppressedApprovalID: self.activeExecApprovalPromptSuppressionID)
.notificationPermissionGuidanceDialog(openNotifications: { approvalId in
self.suppressedExecApprovalPromptIDForNotificationSettings = approvalId
self.selectSettingsRoute(.notifications)
})
}
private var appearancePreference: AppAppearancePreference {
@@ -874,9 +914,15 @@ struct RootTabs: View {
private func homeCanvasName(for agent: AgentSummary) -> String {
self.normalized(agent.name) ?? agent.id
}
}
extension RootTabs {
private func selectSidebarDestination(_ destination: SidebarDestination) {
if destination.settingsRoute != .notifications {
self.suppressedExecApprovalPromptIDForNotificationSettings = nil
}
self.selectedSidebarDestination = destination
self.selectedSettingsRoute = destination.settingsRoute
self.selectedTab = destination.appTab
guard self.usesSidebarTabs, self.shouldCollapseSidebarAfterSelection else { return }
withAnimation(.easeInOut(duration: 0.22)) {
@@ -884,6 +930,31 @@ struct RootTabs: View {
}
}
private func selectSettingsRoute(_ route: SettingsRoute) {
if route != .notifications {
self.suppressedExecApprovalPromptIDForNotificationSettings = nil
}
self.selectedSettingsRoute = route
self.selectedSettingsRouteRequestID &+= 1
self.selectedSidebarDestination = .settings
self.selectedTab = .settings
guard self.usesSidebarTabs, self.shouldCollapseSidebarAfterSelection else { return }
withAnimation(.easeInOut(duration: 0.22)) {
self.setSidebarVisible(false)
}
}
private func handleSettingsRouteChange(_ route: SettingsRoute?) {
guard route != .notifications else { return }
if route == nil {
self.selectedSettingsRoute = nil
if self.selectedTab == .settings {
self.selectedSidebarDestination = .settings
}
}
self.suppressedExecApprovalPromptIDForNotificationSettings = nil
}
private func showSidebar() {
self.sidebarVisibilityUserOverridden = true
withAnimation(.easeInOut(duration: 0.22)) {

View File

@@ -16,7 +16,6 @@ enum NotificationAuthorizationStatus {
protocol NotificationCentering: Sendable {
func authorizationStatus() async -> NotificationAuthorizationStatus
func requestAuthorization(options: UNAuthorizationOptions) async throws -> Bool
func add(_ request: UNNotificationRequest) async throws
func removePendingNotificationRequests(withIdentifiers identifiers: [String]) async
func removeDeliveredNotifications(withIdentifiers identifiers: [String]) async
@@ -48,10 +47,6 @@ struct LiveNotificationCenter: NotificationCentering, @unchecked Sendable {
}
}
func requestAuthorization(options: UNAuthorizationOptions) async throws -> Bool {
try await self.center.requestAuthorization(options: options)
}
func add(_ request: UNNotificationRequest) async throws {
try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in
self.center.add(request) { error in

View File

@@ -50,6 +50,7 @@ Sources/Gateway/GatewayDiscoveryModel.swift
Sources/Gateway/GatewayHealthMonitor.swift
Sources/Gateway/GatewayProblemView.swift
Sources/Gateway/GatewayQuickSetupSheet.swift
Sources/Gateway/NotificationPermissionGuidanceDialog.swift
Sources/Gateway/GatewayServiceResolver.swift
Sources/Gateway/GatewaySettingsStore.swift
Sources/Gateway/GatewayTrustPromptAlert.swift
@@ -109,10 +110,10 @@ Sources/Voice/VoiceWakePreferences.swift
ShareExtension/ShareViewController.swift
ActivityWidget/OpenClawActivityWidgetBundle.swift
ActivityWidget/OpenClawLiveActivity.swift
WatchExtension/Sources/OpenClawWatchApp.swift
WatchExtension/Sources/WatchConnectivityReceiver.swift
WatchExtension/Sources/WatchInboxStore.swift
WatchExtension/Sources/WatchInboxView.swift
WatchApp/Sources/OpenClawWatchApp.swift
WatchApp/Sources/WatchConnectivityReceiver.swift
WatchApp/Sources/WatchInboxStore.swift
WatchApp/Sources/WatchInboxView.swift
../shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift
../shared/OpenClawKit/Sources/OpenClawChatUI/ChatMarkdownRenderer.swift
../shared/OpenClawKit/Sources/OpenClawChatUI/ChatMarkdownPreprocessor.swift

View File

@@ -14,10 +14,6 @@ private final class MockNotificationCenter: NotificationCentering, @unchecked Se
self.authorization
}
func requestAuthorization(options _: UNAuthorizationOptions) async throws -> Bool {
true
}
func add(_ request: UNNotificationRequest) async throws {
self.addedRequests.append(request)
}

View File

@@ -200,25 +200,16 @@ private final class MockWatchMessagingService: @preconcurrency WatchMessagingSer
private final class MockBootstrapNotificationCenter: NotificationCentering, @unchecked Sendable {
var status: NotificationAuthorizationStatus = .notDetermined
var requestAuthorizationResult = false
var requestAuthorizationCalls = 0
var addCalls = 0
func authorizationStatus() async -> NotificationAuthorizationStatus {
self.status
}
func requestAuthorization(options _: UNAuthorizationOptions) async throws -> Bool {
self.requestAuthorizationCalls += 1
if self.requestAuthorizationResult {
self.status = .authorized
} else {
self.status = .denied
}
return self.requestAuthorizationResult
func add(_: UNNotificationRequest) async throws {
self.addCalls += 1
}
func add(_: UNNotificationRequest) async throws {}
func removePendingNotificationRequests(withIdentifiers _: [String]) async {}
func removeDeliveredNotifications(withIdentifiers _: [String]) async {}
@@ -1160,6 +1151,35 @@ private final class MockBootstrapNotificationCenter: NotificationCentering, @unc
isBackgrounded: false))
}
@Test func execApprovalEventIDDecodesGatewayPayload() {
#expect(NodeAppModel._test_execApprovalEventID(from: AnyCodable(["id": " approval-1 "])) == "approval-1")
#expect(NodeAppModel._test_execApprovalEventID(from: AnyCodable(["id": " "])) == nil)
#expect(NodeAppModel._test_execApprovalEventID(from: AnyCodable(["other": "approval-1"])) == nil)
}
@Test @MainActor func operatorGatewayResolvedEventClearsPendingApprovalPrompt() async throws {
let appModel = NodeAppModel()
try appModel._test_presentExecApprovalPrompt(
#require(
NodeAppModel._test_makeExecApprovalPrompt(
id: "approval-event-resolved",
commandText: "echo clear",
allowedDecisions: ["allow-once", "deny"],
host: "gateway",
nodeId: nil,
agentId: nil,
expiresAtMs: Int(Date().timeIntervalSince1970 * 1000) + 60000)))
await appModel._test_handleOperatorGatewayServerEvent(EventFrame(
type: "event",
event: ExecApprovalNotificationBridge.resolvedKind,
payload: AnyCodable(["id": "approval-event-resolved"]),
seq: nil,
stateversion: nil))
#expect(appModel._test_pendingExecApprovalPrompt() == nil)
}
@Test func watchExecApprovalHydrateFetchesOnlyMissingIDs() {
let idsToFetch = NodeAppModel._test_watchExecApprovalIDsNeedingFetch(
candidateIDs: ["cached", "pending", "cached", "other", "", " pending "],
@@ -1201,13 +1221,65 @@ private final class MockBootstrapNotificationCenter: NotificationCentering, @unc
hasStoredOperatorToken: false))
}
@Test @MainActor func successfulBootstrapOnboardingRequestsNotificationAuthorization() async {
@Test @MainActor func operatorGatewayRequestedEventShowsNotificationGuidanceWhenNotificationsOff() async throws {
let center = MockBootstrapNotificationCenter()
center.status = .notDetermined
let appModel = NodeAppModel(notificationCenter: center)
appModel._test_resetExecApprovalNotificationGuidanceSuppression()
defer { appModel._test_resetExecApprovalNotificationGuidanceSuppression() }
await appModel._test_handleSuccessfulBootstrapGatewayOnboarding()
await appModel._test_handleOperatorGatewayServerEvent(EventFrame(
type: "event",
event: ExecApprovalNotificationBridge.requestedKind,
payload: AnyCodable(["id": "approval-notifications-off"]),
seq: nil,
stateversion: nil))
#expect(center.requestAuthorizationCalls == 1)
let prompt = try #require(appModel._test_pendingNotificationPermissionGuidancePrompt())
#expect(prompt.approvalId == "approval-notifications-off")
}
@Test @MainActor func suppressedOperatorGatewayRequestedEventDoesNotShowNotificationGuidance() async {
let center = MockBootstrapNotificationCenter()
center.status = .denied
let appModel = NodeAppModel(notificationCenter: center)
appModel._test_resetExecApprovalNotificationGuidanceSuppression()
defer { appModel._test_resetExecApprovalNotificationGuidanceSuppression() }
appModel.dismissNotificationPermissionGuidancePrompt(suppressFuture: true)
await appModel._test_handleOperatorGatewayServerEvent(EventFrame(
type: "event",
event: ExecApprovalNotificationBridge.requestedKind,
payload: AnyCodable(["id": "approval-suppressed"]),
seq: nil,
stateversion: nil))
#expect(appModel._test_pendingNotificationPermissionGuidancePrompt() == nil)
}
@Test @MainActor func operatorGatewayResolvedEventClearsNotificationGuidancePrompt() async throws {
let center = MockBootstrapNotificationCenter()
center.status = .denied
let appModel = NodeAppModel(notificationCenter: center)
appModel._test_resetExecApprovalNotificationGuidanceSuppression()
defer { appModel._test_resetExecApprovalNotificationGuidanceSuppression() }
await appModel._test_handleOperatorGatewayServerEvent(EventFrame(
type: "event",
event: ExecApprovalNotificationBridge.requestedKind,
payload: AnyCodable(["id": "approval-guidance-resolved"]),
seq: nil,
stateversion: nil))
_ = try #require(appModel._test_pendingNotificationPermissionGuidancePrompt())
await appModel._test_handleOperatorGatewayServerEvent(EventFrame(
type: "event",
event: ExecApprovalNotificationBridge.resolvedKind,
payload: AnyCodable(["id": "approval-guidance-resolved"]),
seq: nil,
stateversion: nil))
#expect(appModel._test_pendingNotificationPermissionGuidancePrompt() == nil)
}
@Test func clearingBootstrapTokenStripsReconnectConfigEvenWithoutPersistence() throws {
@@ -1269,6 +1341,78 @@ private final class MockBootstrapNotificationCenter: NotificationCentering, @unc
#expect(res.error?.message.contains("CAMERA_DISABLED") == true)
}
@Test @MainActor func systemNotifyReturnsUnavailableWhenNotificationsOff() async throws {
let center = MockBootstrapNotificationCenter()
center.status = .notDetermined
let appModel = NodeAppModel(notificationCenter: center)
let params = OpenClawSystemNotifyParams(title: "Approval", body: "Review request")
let paramsData = try JSONEncoder().encode(params)
let req = BridgeInvokeRequest(
id: "notify-off",
command: OpenClawSystemCommand.notify.rawValue,
paramsJSON: String(decoding: paramsData, as: UTF8.self))
let res = await appModel._test_handleInvoke(req)
#expect(res.ok == false)
#expect(res.error?.code == .unavailable)
#expect(res.error?.message == "NOT_AUTHORIZED: notifications")
#expect(center.addCalls == 0)
}
@Test @MainActor func systemNotifySchedulesWhenNotificationsAreAlreadyAllowed() async throws {
let center = MockBootstrapNotificationCenter()
center.status = .authorized
let appModel = NodeAppModel(notificationCenter: center)
let params = OpenClawSystemNotifyParams(title: "Approval", body: "Review request")
let paramsData = try JSONEncoder().encode(params)
let req = BridgeInvokeRequest(
id: "notify-on",
command: OpenClawSystemCommand.notify.rawValue,
paramsJSON: String(decoding: paramsData, as: UTF8.self))
let res = await appModel._test_handleInvoke(req)
#expect(res.ok)
#expect(center.addCalls == 1)
}
@Test @MainActor func chatPushWithoutSpeechReturnsUnavailableWhenNotificationsOff() async throws {
let center = MockBootstrapNotificationCenter()
center.status = .notDetermined
let appModel = NodeAppModel(notificationCenter: center)
let params = OpenClawChatPushParams(text: "Build finished", speak: false)
let paramsData = try JSONEncoder().encode(params)
let req = BridgeInvokeRequest(
id: "chat-push-off",
command: OpenClawChatCommand.push.rawValue,
paramsJSON: String(decoding: paramsData, as: UTF8.self))
let res = await appModel._test_handleInvoke(req)
#expect(res.ok == false)
#expect(res.error?.code == .unavailable)
#expect(res.error?.message == "NOT_AUTHORIZED: notifications")
#expect(center.addCalls == 0)
}
@Test @MainActor func chatPushSchedulesWhenNotificationsAreAlreadyAllowed() async throws {
let center = MockBootstrapNotificationCenter()
center.status = .authorized
let appModel = NodeAppModel(notificationCenter: center)
let params = OpenClawChatPushParams(text: "Build finished", speak: false)
let paramsData = try JSONEncoder().encode(params)
let req = BridgeInvokeRequest(
id: "chat-push-on",
command: OpenClawChatCommand.push.rawValue,
paramsJSON: String(decoding: paramsData, as: UTF8.self))
let res = await appModel._test_handleInvoke(req)
#expect(res.ok)
#expect(center.addCalls == 1)
}
@Test @MainActor func handleInvokeRejectsInvalidScreenFormat() async {
let appModel = NodeAppModel()
let params = OpenClawScreenRecordParams(format: "gif")

View File

@@ -1,8 +1,8 @@
import Foundation
import Testing
@Suite struct RootTabsSourceGuardTests {
@Test func hiddenSidebarRevealUsesDestinationHeaderWithoutReservedRail() throws {
struct RootTabsSourceGuardTests {
@Test func `hidden sidebar reveal uses destination header without reserved rail`() throws {
let source = try String(contentsOf: Self.rootTabsSourceURL(), encoding: .utf8)
let componentSource = try String(contentsOf: Self.proComponentsSourceURL(), encoding: .utf8)
@@ -38,7 +38,7 @@ import Testing
#expect(!source.contains("shouldShowOverviewHeaderSidebarReveal"))
}
@Test func iPadSplitUsesSlidingSidebarWhilePortraitKeepsDrawerOverlay() throws {
@Test func `i pad split uses sliding sidebar while portrait keeps drawer overlay`() throws {
let source = try String(contentsOf: Self.rootTabsSourceURL(), encoding: .utf8)
let splitContent = try Self.extract(
source,
@@ -67,7 +67,7 @@ import Testing
#expect(!drawerContent.contains("NavigationSplitView"))
}
@Test func sidebarKeepsNavigationModelDestinationOnly() throws {
@Test func `sidebar keeps navigation model destination only`() throws {
let source = try String(contentsOf: Self.rootTabsSourceURL(), encoding: .utf8)
let navigationSource = try String(contentsOf: Self.rootTabsNavigationSourceURL(), encoding: .utf8)
let sidebarColumn = try Self.extract(
@@ -114,7 +114,7 @@ import Testing
#expect(navigationSource.contains("SidebarGroup(title: \"REFERENCE\", destinations: [.docs])"))
}
@Test func sidebarRoutesUseDestinationHeadersInsteadOfRepeatedProductBranding() throws {
@Test func `sidebar routes use destination headers instead of repeated product branding`() throws {
let rootSource = try String(contentsOf: Self.rootTabsSourceURL(), encoding: .utf8)
let agentOverviewSource = try String(contentsOf: Self.agentProTabOverviewSourceURL(), encoding: .utf8)
let docsSource = try String(contentsOf: Self.docsSourceURL(), encoding: .utf8)
@@ -148,7 +148,7 @@ import Testing
#expect(!docsSource.contains("Text(\"OpenClaw Docs\")"))
}
@Test func agentsDirectRouteKeepsSingleSidebarControl() throws {
@Test func `agents direct route keeps single sidebar control`() throws {
let source = try String(contentsOf: Self.agentProTabSourceURL(), encoding: .utf8)
let destinationsSource = try String(contentsOf: Self.agentProTabDestinationsSourceURL(), encoding: .utf8)
let nodesSource = try String(contentsOf: Self.agentProNodesDestinationSourceURL(), encoding: .utf8)
@@ -165,7 +165,7 @@ import Testing
#expect(dreamingSource.contains("OpenClawSidebarHeaderLeadingSlot(action: headerLeadingAction)"))
}
@Test func routedHeadersUseSharedAdaptiveLayout() throws {
@Test func `routed headers use shared adaptive layout`() throws {
let componentsSource = try String(contentsOf: Self.proComponentsSourceURL(), encoding: .utf8)
let featureChromeSource = try String(contentsOf: Self.iPadSidebarScreenChromeSourceURL(), encoding: .utf8)
let docsSource = try String(contentsOf: Self.docsSourceURL(), encoding: .utf8)
@@ -187,7 +187,7 @@ import Testing
#expect(settingsSource.contains("OpenClawAdaptiveHeaderRow("))
}
@Test func phoneHubKeepsDocsAsDestinationOnly() throws {
@Test func `phone hub keeps docs as destination only`() throws {
let source = try String(contentsOf: Self.phoneHubSourceURL(), encoding: .utf8)
#expect(source.contains("case .docs:"))
@@ -198,7 +198,7 @@ import Testing
#expect(!source.contains("https://docs.openclaw.ai"))
}
@Test func rootShellPreviewMatrixCoversPhoneAndIPadStates() throws {
@Test func `root shell preview matrix covers phone and I pad states`() throws {
let source = try String(contentsOf: Self.rootTabsSourceURL(), encoding: .utf8)
#expect(source.contains("#Preview(\n \"Shell iPhone portrait\""))
@@ -211,7 +211,7 @@ import Testing
#expect(source.contains("#Preview(\n \"Shell iPad gateway error\""))
}
@Test func sharedChatPreviewMatrixCoversConnectionStates() throws {
@Test func `shared chat preview matrix covers connection states`() throws {
let source = try String(contentsOf: Self.sharedChatPreviewSourceURL(), encoding: .utf8)
#expect(source.contains("#Preview(\"Chat connected\")"))
@@ -226,7 +226,7 @@ import Testing
#expect(source.contains("Gateway not connected. Check Tailscale and retry."))
}
@Test func phoneHubKeepsContentAboveFloatingTabBar() throws {
@Test func `phone hub keeps content above floating tab bar`() throws {
let source = try String(contentsOf: Self.phoneHubSourceURL(), encoding: .utf8)
#expect(source.contains(".safeAreaPadding(.bottom, self.bottomScrollInset)"))
@@ -235,7 +235,7 @@ import Testing
#expect(!source.contains("bottomTabBarClearance"))
}
@Test func phoneHubHeaderStaysTaskFirst() throws {
@Test func `phone hub header stays task first`() throws {
let source = try String(contentsOf: Self.phoneHubSourceURL(), encoding: .utf8)
#expect(source.contains("private var gatewayActionRow: some View"))
@@ -253,7 +253,7 @@ import Testing
#expect(!source.contains("private func metric(label:"))
}
@Test func workboardUsesRealGatewayMethods() throws {
@Test func `workboard uses real gateway methods`() throws {
let source = try String(contentsOf: Self.iPadWorkboardScreenSourceURL(), encoding: .utf8)
#expect(source.contains("workboard.cards.list"))
@@ -268,7 +268,7 @@ import Testing
#expect(!source.contains("Multi-column queue control"))
}
@Test func workboardCreateActionSurfacesUnavailableReasons() throws {
@Test func `workboard create action surfaces unavailable reasons`() throws {
let source = try String(contentsOf: Self.iPadWorkboardScreenSourceURL(), encoding: .utf8)
let createFunction = try Self.extract(
source,
@@ -295,7 +295,7 @@ import Testing
#expect(createFunction.contains("return true"))
}
@Test func taskScopeControlsSendRealGatewayParams() throws {
@Test func `task scope controls send real gateway params`() throws {
let source = try Self.iPadTaskFeatureScreensSource()
#expect(source.contains("private var boardScopeMenu: some View"))
@@ -314,7 +314,7 @@ import Testing
"params: EmptyParams(),\n timeoutSeconds: 20)\n let response = try JSONDecoder().decode(IPadSkillProposalManifest.self"))
}
@Test func compactTaskRowsKeepPhoneNativeActions() throws {
@Test func `compact task rows keep phone native actions`() throws {
let source = try Self.iPadTaskFeatureScreensSource()
let compactControls = try Self.extract(
source,
@@ -348,7 +348,7 @@ import Testing
#expect(compactControls.contains("Label(\"Dispatch\""))
}
@Test func skillWorkshopUsesKanbanLanesOnWideIPad() throws {
@Test func `skill workshop uses kanban lanes on wide I pad`() throws {
let source = try String(contentsOf: Self.iPadSkillWorkshopScreenSourceURL(), encoding: .utf8)
let previewSource = try String(contentsOf: Self.iPadSidebarFeaturePreviewsSourceURL(), encoding: .utf8)
let content = try Self.extract(
@@ -376,7 +376,7 @@ import Testing
#expect(previewSource.contains("status: \"manual_QA\""))
}
@Test func compactTaskRowsHavePopulatedPhonePreviews() throws {
@Test func `compact task rows have populated phone previews`() throws {
let source = try String(contentsOf: Self.iPadSidebarFeaturePreviewsSourceURL(), encoding: .utf8)
#expect(source.contains("#Preview(\"Workboard phone queue rows\")"))
@@ -387,7 +387,7 @@ import Testing
#expect(source.contains("IPadSkillWorkshopPreviewFixtures.proposals"))
}
@Test func taskScreenPreviewMatricesCoverPrimaryStates() throws {
@Test func `task screen preview matrices cover primary states`() throws {
let source = try String(contentsOf: Self.iPadSidebarFeaturePreviewsSourceURL(), encoding: .utf8)
#expect(source.contains("#Preview(\"Workboard states\")"))
@@ -412,7 +412,7 @@ import Testing
#expect(source.contains("\"manual_QA\""))
}
@Test func activityPreviewMatrixCoversConnectionStates() throws {
@Test func `activity preview matrix covers connection states`() throws {
let source = try String(contentsOf: Self.iPadSidebarFeaturePreviewsSourceURL(), encoding: .utf8)
#expect(source.contains("#Preview(\"Activity states\")"))
@@ -426,7 +426,7 @@ import Testing
#expect(source.contains("title: \"Loading sessions\""))
}
@Test func routedFeatureScreensReuseSharedProComponents() throws {
@Test func `routed feature screens reuse shared pro components`() throws {
let source = try Self.iPadTaskFeatureScreensSource()
let componentsSource = try String(contentsOf: Self.proComponentsSourceURL(), encoding: .utf8)
let channelsSource = try String(contentsOf: Self.channelsSourceURL(), encoding: .utf8)
@@ -445,20 +445,22 @@ import Testing
#expect(componentsSource.contains("struct ProStatusRow"))
}
@Test func activityScreenStaysSplitFromTaskFeatureScreens() throws {
@Test func `activity screen stays split from task feature screens`() throws {
let taskSource = try Self.iPadTaskFeatureScreensSource()
let activitySource = try String(contentsOf: Self.iPadActivityScreenSourceURL(), encoding: .utf8)
let appModelSource = try String(contentsOf: Self.nodeAppModelSourceURL(), encoding: .utf8)
let projectSource = try String(contentsOf: Self.xcodeProjectSourceURL(), encoding: .utf8)
#expect(activitySource.contains("struct IPadActivityScreen: View"))
#expect(activitySource.contains("IOSGatewayChatTransport(gateway: self.appModel.operatorSession)"))
#expect(activitySource.contains("self.appModel.makeChatTransport()"))
#expect(appModelSource.contains("return IOSGatewayChatTransport(gateway: self.operatorSession)"))
#expect(activitySource.contains("IPadSidebarScreenChrome("))
#expect(!taskSource.contains("struct IPadActivityScreen"))
#expect(!taskSource.contains("import OpenClawChatUI"))
#expect(projectSource.contains("IPadActivityScreen.swift in Sources"))
}
@Test func routedFeatureChromeStaysSplitFromTaskFeatureScreens() throws {
@Test func `routed feature chrome stays split from task feature screens`() throws {
let taskSource = try Self.iPadTaskFeatureScreensSource()
let chromeSource = try String(contentsOf: Self.iPadSidebarScreenChromeSourceURL(), encoding: .utf8)
let projectSource = try String(contentsOf: Self.xcodeProjectSourceURL(), encoding: .utf8)
@@ -470,7 +472,7 @@ import Testing
#expect(projectSource.contains("IPadSidebarScreenChrome.swift in Sources"))
}
@Test func routedFeatureChromeKeepsGatewayPillActionable() throws {
@Test func `routed feature chrome keeps gateway pill actionable`() throws {
let chromeSource = try String(contentsOf: Self.iPadSidebarScreenChromeSourceURL(), encoding: .utf8)
let featureSource = try Self.iPadTaskFeatureScreensSource()
let rootSource = try String(contentsOf: Self.rootTabsSourceURL(), encoding: .utf8)
@@ -486,14 +488,18 @@ import Testing
.count == 1)
}
@Test func routedGatewayPillsOpenGatewaySettings() throws {
@Test func `routed gateway pills open gateway settings`() throws {
let rootSource = try String(contentsOf: Self.rootTabsSourceURL(), encoding: .utf8)
let agentSource = try String(contentsOf: Self.agentProTabSourceURL(), encoding: .utf8)
let agentOverviewSource = try String(contentsOf: Self.agentProTabOverviewSourceURL(), encoding: .utf8)
let overviewSource = try String(contentsOf: Self.commandCenterSourceURL(), encoding: .utf8)
let chatSource = try String(contentsOf: Self.chatProTabSourceURL(), encoding: .utf8)
let docsSource = try String(contentsOf: Self.docsSourceURL(), encoding: .utf8)
let settingsTabSource = try String(contentsOf: Self.settingsProTabSourceURL(), encoding: .utf8)
let settingsSource = try String(contentsOf: Self.settingsProTabSectionsSourceURL(), encoding: .utf8)
let notificationGuidanceSource = try String(
contentsOf: Self.notificationPermissionGuidanceDialogSourceURL(),
encoding: .utf8)
#expect(rootSource.matches(of: /openSettings: \{ self\.selectSidebarDestination\(\.gateway\) \}/).count >= 2)
#expect(rootSource.matches(of: /gatewayAction: \{ self\.selectSidebarDestination\(\.gateway\) \}/).count == 1)
@@ -512,15 +518,39 @@ import Testing
#expect(docsSource.contains("let gatewayAction: (() -> Void)?"))
#expect(settingsSource.contains("NavigationLink(value: SettingsRoute.gateway)"))
#expect(rootSource.contains("case .settings:"))
#expect(rootSource.contains("SettingsProTab(headerLeadingAction: self.sidebarHeaderLeadingAction)"))
#expect(rootSource.contains("directRoute: self.selectedSidebarDestination.settingsRoute ?? .gateway"))
#expect(rootSource.contains("SettingsProTab(initialRoute: self.selectedSidebarDestination.settingsRoute)"))
#expect(rootSource
.matches(of: /SettingsProTab\(\s*headerLeadingAction: self\.sidebarHeaderLeadingAction,/)
.count >= 1)
#expect(rootSource
.contains(
"directRoute: self.selectedSettingsRoute ?? self.selectedSidebarDestination.settingsRoute ?? .gateway"))
#expect(rootSource.matches(of: /SettingsProTab\(\s*initialRoute: self\.selectedSettingsRoute,/).count == 1)
#expect(rootSource.contains(".id(self.settingsTabViewID)"))
#expect(rootSource.contains("@State private var selectedSettingsRouteRequestID: Int = 0"))
#expect(rootSource.contains("self.selectedSettingsRouteRequestID &+= 1"))
#expect(rootSource.contains("@State private var suppressedExecApprovalPromptIDForNotificationSettings"))
#expect(rootSource.contains("private var activeExecApprovalPromptSuppressionID: String?"))
#expect(rootSource.contains("suppressedApprovalID: self.activeExecApprovalPromptSuppressionID"))
#expect(rootSource.contains("if destination.settingsRoute != .notifications"))
#expect(rootSource.contains("if route != .notifications"))
#expect(rootSource.contains("if route == nil"))
#expect(rootSource.contains("self.selectedSettingsRoute = nil"))
#expect(rootSource.contains("self.selectedSidebarDestination = .settings"))
#expect(rootSource.contains("self.suppressedExecApprovalPromptIDForNotificationSettings = approvalId"))
#expect(rootSource.contains("onRouteChange: self.handleSettingsRouteChange"))
#expect(rootSource.contains("private func handleSettingsRouteChange(_ route: SettingsRoute?)"))
#expect(settingsTabSource.contains("let onRouteChange: ((SettingsRoute?) -> Void)?"))
#expect(settingsTabSource.contains("self.onRouteChange?(self.navigationPath.last)"))
#expect(notificationGuidanceSource.contains("onSuppressFuture"))
#expect(notificationGuidanceSource.contains("suppressFuture: true"))
#expect(notificationGuidanceSource.contains("Text(\"Don't show again\")"))
#expect(rootSource.contains("private func selectSettingsRoute(_ route: SettingsRoute)"))
#expect(settingsSource.contains("title: \"Channels / Integrations\""))
#expect(settingsSource.contains("route: .channels"))
#expect(docsSource.contains(".accessibilityHint(\"Opens Settings / Gateway\")"))
}
@Test func gatewaySettingsKeepsPairingTrustDiagnosticsAndTailscaleActions() throws {
@Test func `gateway settings keeps pairing trust diagnostics and tailscale actions`() throws {
let settingsSource = try String(contentsOf: Self.settingsProTabSourceURL(), encoding: .utf8)
let sectionsSource = try String(contentsOf: Self.settingsProTabSectionsSourceURL(), encoding: .utf8)
let actionsSource = try String(contentsOf: Self.settingsProTabActionsSourceURL(), encoding: .utf8)
@@ -566,7 +596,7 @@ import Testing
#expect(controllerSource.contains("trustRotatedGatewayCertificate(from problem: GatewayConnectionProblem)"))
}
@Test func gatewaySettingsPreviewMatrixCoversPrimaryStates() throws {
@Test func `gateway settings preview matrix covers primary states`() throws {
let supportSource = try String(contentsOf: Self.settingsProTabSupportSourceURL(), encoding: .utf8)
#expect(supportSource.contains("#Preview(\"Gateway settings states\")"))
@@ -585,12 +615,15 @@ import Testing
#expect(supportSource.contains("self.previewButton(\"Diagnose\""))
}
@Test func nativeChatUsesGatewayTransport() throws {
@Test func `native chat uses gateway transport`() throws {
let chatSource = try String(contentsOf: Self.chatProTabSourceURL(), encoding: .utf8)
let channelsSource = try String(contentsOf: Self.channelsSourceURL(), encoding: .utf8)
let settingsSectionsSource = try String(contentsOf: Self.settingsProTabSectionsSourceURL(), encoding: .utf8)
let appModelSource = try String(contentsOf: Self.nodeAppModelSourceURL(), encoding: .utf8)
#expect(chatSource.contains("IOSGatewayChatTransport(gateway: self.appModel.operatorSession)"))
#expect(channelsSource.contains("Message routing and external channel clients."))
#expect(chatSource.matches(of: /self\.appModel\.makeChatTransport\(\)/).count == 2)
#expect(appModelSource.contains("return IOSGatewayChatTransport(gateway: self.operatorSession)"))
#expect(settingsSectionsSource.contains("Message routing and external channel clients."))
#expect(channelsSource.contains("\"clickclack\": SettingsChannelFallbackMetadata"))
#expect(channelsSource.contains("label: \"ClickClack\""))
#expect(channelsSource.contains("Self-hosted chat bot routing."))
@@ -603,6 +636,13 @@ import Testing
.appendingPathComponent("Sources/RootTabs.swift")
}
private static func nodeAppModelSourceURL() -> URL {
URL(fileURLWithPath: #filePath)
.deletingLastPathComponent()
.deletingLastPathComponent()
.appendingPathComponent("Sources/Model/NodeAppModel.swift")
}
private static func phoneHubSourceURL() -> URL {
URL(fileURLWithPath: #filePath)
.deletingLastPathComponent()
@@ -746,6 +786,13 @@ import Testing
.appendingPathComponent("Sources/Design/SettingsProTab.swift")
}
private static func notificationPermissionGuidanceDialogSourceURL() -> URL {
URL(fileURLWithPath: #filePath)
.deletingLastPathComponent()
.deletingLastPathComponent()
.appendingPathComponent("Sources/Gateway/NotificationPermissionGuidanceDialog.swift")
}
private static func settingsProTabActionsSourceURL() -> URL {
URL(fileURLWithPath: #filePath)
.deletingLastPathComponent()

View File

@@ -8,7 +8,7 @@ import Testing
gatewayConnected: false,
discoveredGatewayCount: 0,
talkConfigLoaded: false,
notificationStatusText: "Not Set") == [
notificationsAllowed: false) == [
.gatewayOffline,
.discoveryUnavailable,
.notificationsUnavailable,
@@ -21,12 +21,12 @@ import Testing
gatewayConnected: true,
discoveredGatewayCount: 1,
talkConfigLoaded: false,
notificationStatusText: "Allowed") == [.talkConfigMissing])
notificationsAllowed: true) == [.talkConfigMissing])
#expect(
SettingsDiagnostics.issueCount(
gatewayConnected: true,
discoveredGatewayCount: 1,
talkConfigLoaded: true,
notificationStatusText: "Allowed") == 0)
notificationsAllowed: true) == 0)
}
}

View File

@@ -3,6 +3,10 @@ import OpenClawKit
import Testing
@Suite struct ShareToAgentDeepLinkTests {
@Test func appGroupIdentifierUsesCanonicalOpenClawGroup() {
#expect(OpenClawAppGroup.canonicalIdentifier == "group.ai.openclawfoundation.app.shared")
}
@Test func buildMessageIncludesSharedFields() {
let payload = SharedContentPayload(
title: "Article",

View File

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -20,9 +20,9 @@
<string>$(OPENCLAW_MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(OPENCLAW_BUILD_VERSION)</string>
<key>WKApplication</key>
<true/>
<key>WKCompanionAppBundleIdentifier</key>
<string>$(OPENCLAW_APP_BUNDLE_ID)</string>
<key>WKWatchKitApp</key>
<true/>
</dict>
</plist>

View File

@@ -1146,7 +1146,7 @@ private enum WatchNativeTextInput {
suggestions: [String],
onSubmit: @escaping (String) -> Void)
{
WKExtension.shared().visibleInterfaceController?.presentTextInputController(
WKApplication.shared().visibleInterfaceController?.presentTextInputController(
withSuggestions: suggestions,
allowedInputMode: .allowEmoji)
{ results in

View File

@@ -1,6 +0,0 @@
{
"info": {
"author": "xcode",
"version": 1
}
}

View File

@@ -1,32 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>OpenClaw</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundleShortVersionString</key>
<string>$(OPENCLAW_MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(OPENCLAW_BUILD_VERSION)</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>WKAppBundleIdentifier</key>
<string>$(OPENCLAW_WATCH_APP_BUNDLE_ID)</string>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.watchkit</string>
</dict>
</dict>
</plist>

View File

@@ -293,6 +293,8 @@ def capture_watch_screenshot
Dir[File.join(output_dir, "Apple Watch*-*.png")].each { |path| FileUtils.rm_f(path) }
FileUtils.rm_rf(derived_data_path)
# Single-target watch apps only expose generic simulator build destinations in Xcode.
# Keep the selected UDID for install/launch/screenshot below.
sh(
xcodebuild_shell_join([
"xcodebuild",
@@ -303,7 +305,7 @@ def capture_watch_screenshot
"-configuration",
"Debug",
"-destination",
"platform=watchOS Simulator,id=#{udid}",
"generic/platform=watchOS Simulator",
"-derivedDataPath",
derived_data_path,
"build",
@@ -311,10 +313,8 @@ def capture_watch_screenshot
)
UI.user_error!("Watch screenshot build did not produce #{app_path}.") unless File.exist?(app_path)
extension_path = File.join(app_path, "PlugIns", "OpenClawWatchExtension.appex")
watch_app_identifier = bundle_identifier_for_product(app_path)
watch_extension_identifier = bundle_identifier_for_product(extension_path)
screenshot_mode_bundle_identifiers = [watch_app_identifier, watch_extension_identifier]
screenshot_mode_bundle_identifiers = [watch_app_identifier]
sh("#{shell_join(["xcrun", "simctl", "boot", udid])} >/dev/null 2>&1 || true")
sh(shell_join(["xcrun", "simctl", "bootstatus", udid, "-b"]))
@@ -492,6 +492,9 @@ def produce_services_for_target(target)
if target.fetch("capabilities").include?("PUSH_NOTIFICATIONS")
services[:push_notification] = "on"
end
if target.fetch("capabilities").include?("APP_GROUPS")
services[:app_group] = "on"
end
services
end
@@ -567,6 +570,15 @@ def profile_plist_value(profile_path, key_path)
end
end
def profile_plist_array_values(profile_path, key_path)
raw = profile_plist_value(profile_path, key_path)
return [] unless raw
raw.lines.map(&:strip).reject do |line|
line.empty? || line == "Array {" || line == "}"
end
end
def validate_match_profile_capabilities!(target)
capabilities = target.fetch("capabilities")
return if capabilities.empty?
@@ -582,6 +594,17 @@ def validate_match_profile_capabilities!(target)
)
end
end
if capabilities.include?("APP_GROUPS")
expected_app_groups = target.fetch("appGroups")
actual_app_groups = profile_plist_array_values(profile_path, "Entitlements:com.apple.security.application-groups")
missing = expected_app_groups - actual_app_groups
unless missing.empty?
UI.user_error!(
"Provisioning profile #{target.fetch("profileName")} for #{target.fetch("bundleId")} is missing App Groups #{missing.join(", ")}; actual groups: #{actual_app_groups.empty? ? "missing" : actual_app_groups.join(", ")}."
)
end
end
end
def sync_app_store_signing!(readonly:)

View File

@@ -65,7 +65,7 @@ pnpm ios:release:signing:check
pnpm ios:release:signing:setup
```
`signing:setup` uses Fastlane `produce` and `modify_services` to create Developer Portal bundle IDs and enable required services before running `match`. If Fastlane does not already have a valid Apple Developer Portal session, run `fastlane spaceauth` for a release-owner Apple ID and export the resulting `FASTLANE_SESSION`.
`signing:setup` uses Fastlane `produce` and `modify_services` to create Developer Portal bundle IDs and enable required services before running `match`. The main app and share extension also require the shared App Group from `apps/ios/Config/AppStoreSigning.json`; associate that group with both bundle IDs in the Apple Developer Portal before regenerating profiles. If Fastlane does not already have a valid Apple Developer Portal session, run `fastlane spaceauth` for a release-owner Apple ID and export the resulting `FASTLANE_SESSION`.
Shared encrypted signing storage:

View File

@@ -1,3 +1,5 @@
OpenClaw is now available on iPhone.
Maintenance update for the current OpenClaw release.
Connect to your OpenClaw Gateway to chat with your assistant, use realtime Talk mode, review approvals, share content from iOS, and bring device capabilities like camera, location, screen, and notifications into your private automation workflows.
- Added Apple Watch controls for common agent actions.
- Improved Gateway setup, notification settings, and share-extension identity handling.
- Updated the Watch app integration for current Xcode compatibility.

View File

@@ -65,6 +65,8 @@ targets:
embed: true
- target: OpenClawActivityWidget
embed: true
# A companion watch application belongs in the standard Watch bundle location.
# PlugIns is for extension products and breaks paired watch installation.
- target: OpenClawWatchApp
- package: OpenClawKit
- package: OpenClawKit
@@ -88,7 +90,7 @@ targets:
exit 1
fi
swiftformat --lint --config "$SRCROOT/../../config/swiftformat" \
--unexclude "$SRCROOT/Sources,$SRCROOT/ShareExtension,$SRCROOT/ActivityWidget,$SRCROOT/WatchExtension,$SRCROOT/../shared/OpenClawKit,$SRCROOT/../swabble" \
--unexclude "$SRCROOT/Sources,$SRCROOT/ShareExtension,$SRCROOT/ActivityWidget,$SRCROOT/WatchApp,$SRCROOT/../shared/OpenClawKit,$SRCROOT/../swabble" \
--filelist "$SRCROOT/SwiftSources.input.xcfilelist"
- name: SwiftLint
basedOnDependencyAnalysis: false
@@ -140,6 +142,7 @@ targets:
- openclaw
CFBundleShortVersionString: "$(OPENCLAW_MARKETING_VERSION)"
OpenClawCanonicalVersion: "$(OPENCLAW_IOS_VERSION)"
OpenClawAppGroupIdentifier: "$(OPENCLAW_APP_GROUP_ID)"
CFBundleVersion: "$(OPENCLAW_BUILD_VERSION)"
UILaunchScreen: {}
UIApplicationSceneManifest:
@@ -192,6 +195,7 @@ targets:
settings:
base:
CODE_SIGN_IDENTITY: "$(OPENCLAW_CODE_SIGN_IDENTITY)"
CODE_SIGN_ENTITLEMENTS: ShareExtension/OpenClawShareExtension.entitlements
CODE_SIGN_STYLE: "$(OPENCLAW_CODE_SIGN_STYLE)"
DEVELOPMENT_TEAM: "$(OPENCLAW_DEVELOPMENT_TEAM)"
ENABLE_APPINTENTS_METADATA: NO
@@ -206,6 +210,7 @@ targets:
properties:
CFBundleDisplayName: OpenClaw Share
CFBundleShortVersionString: "$(OPENCLAW_MARKETING_VERSION)"
OpenClawAppGroupIdentifier: "$(OPENCLAW_APP_GROUP_ID)"
CFBundleVersion: "$(OPENCLAW_BUILD_VERSION)"
NSExtension:
NSExtensionPointIdentifier: com.apple.share-services
@@ -251,13 +256,17 @@ targets:
NSExtensionPointIdentifier: com.apple.widgetkit-extension
OpenClawWatchApp:
type: application.watchapp2
type: application
platform: watchOS
deploymentTarget: "11.0"
sources:
- path: WatchApp
excludes:
- Info.plist
dependencies:
- target: OpenClawWatchExtension
- sdk: AppIntents.framework
- sdk: WatchConnectivity.framework
- sdk: UserNotifications.framework
configFiles:
Debug: Config/Signing.xcconfig
Release: Config/Signing.xcconfig
@@ -274,6 +283,8 @@ targets:
ENABLE_APP_INTENTS_METADATA_GENERATION: NO
PRODUCT_BUNDLE_IDENTIFIER: "$(OPENCLAW_WATCH_APP_BUNDLE_ID)"
PROVISIONING_PROFILE_SPECIFIER: "$(OPENCLAW_WATCH_APP_PROFILE)"
SWIFT_STRICT_CONCURRENCY: complete
SWIFT_VERSION: "6.0"
info:
path: WatchApp/Info.plist
properties:
@@ -281,42 +292,7 @@ targets:
CFBundleShortVersionString: "$(OPENCLAW_MARKETING_VERSION)"
CFBundleVersion: "$(OPENCLAW_BUILD_VERSION)"
WKCompanionAppBundleIdentifier: "$(OPENCLAW_APP_BUNDLE_ID)"
WKWatchKitApp: true
OpenClawWatchExtension:
type: watchkit2-extension
platform: watchOS
deploymentTarget: "11.0"
sources:
- path: WatchExtension/Sources
- path: WatchExtension/Assets.xcassets
dependencies:
- sdk: AppIntents.framework
- sdk: WatchConnectivity.framework
- sdk: UserNotifications.framework
configFiles:
Debug: Config/Signing.xcconfig
Release: Config/Signing.xcconfig
attributes:
DevelopmentTeam: "$(OPENCLAW_DEVELOPMENT_TEAM)"
ProvisioningStyle: "$(OPENCLAW_CODE_SIGN_STYLE)"
settings:
base:
CODE_SIGN_IDENTITY: "$(OPENCLAW_CODE_SIGN_IDENTITY)"
CODE_SIGN_STYLE: "$(OPENCLAW_CODE_SIGN_STYLE)"
DEVELOPMENT_TEAM: "$(OPENCLAW_DEVELOPMENT_TEAM)"
PRODUCT_BUNDLE_IDENTIFIER: "$(OPENCLAW_WATCH_EXTENSION_BUNDLE_ID)"
PROVISIONING_PROFILE_SPECIFIER: "$(OPENCLAW_WATCH_EXTENSION_PROFILE)"
info:
path: WatchExtension/Info.plist
properties:
CFBundleDisplayName: OpenClaw
CFBundleShortVersionString: "$(OPENCLAW_MARKETING_VERSION)"
CFBundleVersion: "$(OPENCLAW_BUILD_VERSION)"
NSExtension:
NSExtensionAttributes:
WKAppBundleIdentifier: "$(OPENCLAW_WATCH_APP_BUNDLE_ID)"
NSExtensionPointIdentifier: com.apple.watchkit
WKApplication: true
OpenClawTests:
type: bundle.unit-test

View File

@@ -1,3 +1,3 @@
{
"version": "2026.6.2"
"version": "2026.6.9"
}

View File

@@ -0,0 +1,32 @@
import AppKit
import WebKit
extension CanvasWindowController {
// MARK: - WKUIDelegate
/// Bridges `<input type="file">` clicks in canvas HTML to a native `NSOpenPanel`.
/// Without a `WKUIDelegate`, WebKit silently drops the request and file-picker
/// buttons in canvas pages do nothing.
@MainActor
func webView(
_ webView: WKWebView,
runOpenPanelWith parameters: WKOpenPanelParameters,
initiatedByFrame frame: WKFrameInfo,
completionHandler: @escaping @MainActor @Sendable ([URL]?) -> Void)
{
let panel = NSOpenPanel()
panel.canChooseFiles = true
panel.canChooseDirectories = parameters.allowsDirectories
panel.allowsMultipleSelection = parameters.allowsMultipleSelection
panel.resolvesAliases = true
if let window = self.window {
panel.beginSheetModal(for: window) { response in
completionHandler(response == .OK ? panel.urls : nil)
}
return
}
panel.begin { response in
completionHandler(response == .OK ? panel.urls : nil)
}
}
}

View File

@@ -5,7 +5,7 @@ import OpenClawKit
import WebKit
@MainActor
final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NSWindowDelegate {
final class CanvasWindowController: NSWindowController, WKNavigationDelegate, WKUIDelegate, NSWindowDelegate {
let sessionKey: String
private let root: URL
private let sessionDir: URL
@@ -159,6 +159,7 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS
}
self.webView.navigationDelegate = self
self.webView.uiDelegate = self
self.window?.delegate = self
self.container.onClose = { [weak self] in
self?.hideCanvas()

View File

@@ -19,7 +19,7 @@ private final class DashboardWindowDragRegionView: NSView {
}
@MainActor
final class DashboardWindowController: NSWindowController, WKNavigationDelegate, NSWindowDelegate {
final class DashboardWindowController: NSWindowController, WKNavigationDelegate, WKUIDelegate, NSWindowDelegate {
private let webView: WKWebView
private var currentURL: URL
private var auth: DashboardWindowAuth
@@ -44,9 +44,37 @@ final class DashboardWindowController: NSWindowController, WKNavigationDelegate,
super.init(window: window)
self.webView.navigationDelegate = self
self.webView.uiDelegate = self
self.window?.delegate = self
}
// MARK: - WKUIDelegate
/// Bridges `<input type="file">` clicks in the embedded Control UI to a native
/// `NSOpenPanel`; without a `WKUIDelegate`, WebKit silently drops the request
/// and "Choose image" / file-picker buttons do nothing.
func webView(
_ webView: WKWebView,
runOpenPanelWith parameters: WKOpenPanelParameters,
initiatedByFrame frame: WKFrameInfo,
completionHandler: @escaping @MainActor @Sendable ([URL]?) -> Void)
{
let panel = NSOpenPanel()
panel.canChooseFiles = true
panel.canChooseDirectories = parameters.allowsDirectories
panel.allowsMultipleSelection = parameters.allowsMultipleSelection
panel.resolvesAliases = true
if let window = self.window {
panel.beginSheetModal(for: window) { response in
completionHandler(response == .OK ? panel.urls : nil)
}
return
}
panel.begin { response in
completionHandler(response == .OK ? panel.urls : nil)
}
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) is not supported")

View File

@@ -15,9 +15,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2026.6.2</string>
<string>2026.6.9</string>
<key>CFBundleVersion</key>
<string>2026060200</string>
<string>2026060900</string>
<key>CFBundleIconFile</key>
<string>OpenClaw</string>
<key>CFBundleURLTypes</key>

View File

@@ -21,10 +21,12 @@ private struct DeviceAuthStoreFile: Codable {
}
public enum DeviceAuthStore {
private static let fileName = "device-auth.json"
public static func loadToken(deviceId: String, role: String) -> DeviceAuthEntry? {
guard let store = readStore(), store.deviceId == deviceId else { return nil }
public static func loadToken(
deviceId: String,
role: String,
profile: GatewayDeviceIdentityProfile = .primary) -> DeviceAuthEntry?
{
guard let store = readStore(profile: profile), store.deviceId == deviceId else { return nil }
let role = self.normalizeRole(role)
return store.tokens[role]
}
@@ -33,10 +35,11 @@ public enum DeviceAuthStore {
deviceId: String,
role: String,
token: String,
scopes: [String] = []) -> DeviceAuthEntry
scopes: [String] = [],
profile: GatewayDeviceIdentityProfile = .primary) -> DeviceAuthEntry
{
let normalizedRole = self.normalizeRole(role)
var next = self.readStore()
var next = self.readStore(profile: profile)
if next?.deviceId != deviceId {
next = DeviceAuthStoreFile(version: 1, deviceId: deviceId, tokens: [:])
}
@@ -50,17 +53,25 @@ public enum DeviceAuthStore {
}
next?.tokens[normalizedRole] = entry
if let store = next {
self.writeStore(store)
self.writeStore(store, profile: profile)
}
return entry
}
public static func clearToken(deviceId: String, role: String) {
guard var store = readStore(), store.deviceId == deviceId else { return }
public static func clearToken(
deviceId: String,
role: String,
profile: GatewayDeviceIdentityProfile = .primary)
{
guard var store = readStore(profile: profile), store.deviceId == deviceId else { return }
let normalizedRole = self.normalizeRole(role)
guard store.tokens[normalizedRole] != nil else { return }
store.tokens.removeValue(forKey: normalizedRole)
self.writeStore(store)
self.writeStore(store, profile: profile)
}
public static func clearAll(profile: GatewayDeviceIdentityProfile = .primary) {
try? FileManager.default.removeItem(at: self.fileURL(profile: profile))
}
private static func normalizeRole(_ role: String) -> String {
@@ -74,14 +85,14 @@ public enum DeviceAuthStore {
return Array(Set(trimmed)).sorted()
}
private static func fileURL() -> URL {
private static func fileURL(profile: GatewayDeviceIdentityProfile) -> URL {
DeviceIdentityPaths.stateDirURL()
.appendingPathComponent("identity", isDirectory: true)
.appendingPathComponent(self.fileName, isDirectory: false)
.appendingPathComponent(profile.authFileName, isDirectory: false)
}
private static func readStore() -> DeviceAuthStoreFile? {
let url = self.fileURL()
private static func readStore(profile: GatewayDeviceIdentityProfile) -> DeviceAuthStoreFile? {
let url = self.fileURL(profile: profile)
guard let data = try? Data(contentsOf: url) else { return nil }
guard let decoded = try? JSONDecoder().decode(DeviceAuthStoreFile.self, from: data) else {
return nil
@@ -90,8 +101,8 @@ public enum DeviceAuthStore {
return decoded
}
private static func writeStore(_ store: DeviceAuthStoreFile) {
let url = self.fileURL()
private static func writeStore(_ store: DeviceAuthStoreFile, profile: GatewayDeviceIdentityProfile) {
let url = self.fileURL(profile: profile)
do {
try FileManager.default.createDirectory(
at: url.deletingLastPathComponent(),

View File

@@ -1,6 +1,29 @@
import CryptoKit
import Foundation
public enum GatewayDeviceIdentityProfile: String, Sendable {
case primary
case shareExtension
var identityFileName: String {
switch self {
case .primary:
"device.json"
case .shareExtension:
"share-device.json"
}
}
var authFileName: String {
switch self {
case .primary:
"device-auth.json"
case .shareExtension:
"share-device-auth.json"
}
}
}
public struct DeviceIdentity: Codable, Sendable {
public var deviceId: String
public var publicKey: String
@@ -19,6 +42,32 @@ enum DeviceIdentityPaths {
private static let stateDirEnv = ["OPENCLAW_STATE_DIR"]
static func stateDirURL() -> URL {
self.stateDirURL(
overrideURL: self.stateDirOverrideURL(),
legacyStateDirURL: self.legacyStateDirURL(),
appGroupStateDirURL: self.appGroupStateDirURL(),
temporaryDirectory: FileManager.default.temporaryDirectory)
}
static func stateDirURL(
overrideURL: URL?,
legacyStateDirURL: URL?,
appGroupStateDirURL: URL?,
temporaryDirectory: URL) -> URL
{
if let overrideURL {
return overrideURL
}
if let appGroupStateDirURL {
return appGroupStateDirURL
}
if let legacyStateDirURL {
return legacyStateDirURL
}
return temporaryDirectory.appendingPathComponent("openclaw", isDirectory: true)
}
private static func stateDirOverrideURL() -> URL? {
for key in self.stateDirEnv {
if let raw = getenv(key) {
let value = String(cString: raw).trimmingCharacters(in: .whitespacesAndNewlines)
@@ -27,34 +76,49 @@ enum DeviceIdentityPaths {
}
}
}
return nil
}
private static func legacyStateDirURL() -> URL? {
if let appSupport = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first {
return appSupport.appendingPathComponent("OpenClaw", isDirectory: true)
}
return nil
}
return FileManager.default.temporaryDirectory.appendingPathComponent("openclaw", isDirectory: true)
private static func appGroupStateDirURL() -> URL? {
guard
let containerURL = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: OpenClawAppGroup.identifier)
else {
return nil
}
return containerURL.appendingPathComponent("OpenClaw", isDirectory: true)
}
}
public enum DeviceIdentityStore {
private static let fileName = "device.json"
private static let ed25519SPKIPrefix = Data([
0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65,
0x30, 0x2A, 0x30, 0x05, 0x06, 0x03, 0x2B, 0x65,
0x70, 0x03, 0x21, 0x00,
])
private static let ed25519PKCS8PrivatePrefix = Data([
0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06,
0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20,
0x30, 0x2E, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06,
0x03, 0x2B, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20,
])
public static func loadOrCreate() -> DeviceIdentity {
self.loadOrCreate(fileURL: self.fileURL())
self.loadOrCreate(profile: .primary)
}
public static func loadOrCreate(profile: GatewayDeviceIdentityProfile) -> DeviceIdentity {
self.loadOrCreate(fileURL: self.fileURL(profile: profile))
}
static func loadOrCreate(fileURL url: URL) -> DeviceIdentity {
if let data = try? Data(contentsOf: url) {
switch self.decodeStoredIdentity(data) {
case .identity(let decoded):
case let .identity(decoded):
return decoded
case .recognizedInvalid:
return self.generate()
@@ -143,7 +207,7 @@ public enum DeviceIdentityStore {
let privateKeyData = Data(base64Encoded: identity.privateKey)
else { return nil }
guard publicKeyData.count == 32 && privateKeyData.count == 32,
guard publicKeyData.count == 32, privateKeyData.count == 32,
self.keyPairMatches(publicKeyData: publicKeyData, privateKeyData: privateKeyData)
else { return nil }
return DeviceIdentity(
@@ -211,11 +275,11 @@ public enum DeviceIdentityStore {
}
}
private static func fileURL() -> URL {
private static func fileURL(profile: GatewayDeviceIdentityProfile) -> URL {
let base = DeviceIdentityPaths.stateDirURL()
return base
.appendingPathComponent("identity", isDirectory: true)
.appendingPathComponent(self.fileName, isDirectory: false)
.appendingPathComponent(profile.identityFileName, isDirectory: false)
}
}

View File

@@ -107,6 +107,7 @@ public struct GatewayConnectOptions: Sendable {
public var clientId: String
public var clientMode: String
public var clientDisplayName: String?
public var deviceIdentityProfile: GatewayDeviceIdentityProfile
/// When false, the connection omits the signed device identity payload and cannot use
/// device-scoped auth (role/scope upgrades will require pairing). Keep this true for
/// role/scoped sessions such as operator UI clients.
@@ -122,6 +123,7 @@ public struct GatewayConnectOptions: Sendable {
clientId: String,
clientMode: String,
clientDisplayName: String?,
deviceIdentityProfile: GatewayDeviceIdentityProfile = .primary,
includeDeviceIdentity: Bool = true)
{
self.role = role
@@ -133,6 +135,7 @@ public struct GatewayConnectOptions: Sendable {
self.clientId = clientId
self.clientMode = clientMode
self.clientDisplayName = clientDisplayName
self.deviceIdentityProfile = deviceIdentityProfile
self.includeDeviceIdentity = includeDeviceIdentity
}
}
@@ -436,13 +439,15 @@ public actor GatewayChannelActor {
let clientId = options.clientId
let clientMode = options.clientMode
let role = options.role
let deviceIdentityProfile = options.deviceIdentityProfile
let requestedScopes = options.scopes
let scopesAreExplicit = options.scopesAreExplicit
let includeDeviceIdentity = options.includeDeviceIdentity
let identity = includeDeviceIdentity ? DeviceIdentityStore.loadOrCreate() : nil
let identity = includeDeviceIdentity ? DeviceIdentityStore.loadOrCreate(profile: deviceIdentityProfile) : nil
let selectedAuth = self.selectConnectAuth(
role: role,
includeDeviceIdentity: includeDeviceIdentity,
deviceIdentityProfile: deviceIdentityProfile,
deviceId: identity?.deviceId,
requestedScopes: requestedScopes)
let scopes = self.resolveConnectScopes(
@@ -532,7 +537,11 @@ public actor GatewayChannelActor {
try await self.task?.send(.data(data))
do {
let response = try await self.waitForConnectResponse(reqId: reqId)
try await self.handleConnectResponse(response, identity: identity, role: role)
try await self.handleConnectResponse(
response,
identity: identity,
role: role,
deviceIdentityProfile: deviceIdentityProfile)
self.pendingDeviceTokenRetry = false
self.deviceTokenRetryBudgetUsed = false
} catch {
@@ -550,7 +559,10 @@ public actor GatewayChannelActor {
self.shouldClearStoredDeviceTokenAfterRetry(error)
{
// Retry failed with an explicit device-token mismatch; clear stale local token.
DeviceAuthStore.clearToken(deviceId: identity.deviceId, role: role)
DeviceAuthStore.clearToken(
deviceId: identity.deviceId,
role: role,
profile: deviceIdentityProfile)
}
throw error
}
@@ -559,6 +571,7 @@ public actor GatewayChannelActor {
private func selectConnectAuth(
role: String,
includeDeviceIdentity: Bool,
deviceIdentityProfile: GatewayDeviceIdentityProfile,
deviceId: String?,
requestedScopes: [String]) -> SelectedConnectAuth
{
@@ -568,7 +581,7 @@ public actor GatewayChannelActor {
let explicitPassword = self.password?.trimmingCharacters(in: .whitespacesAndNewlines).nilIfEmpty
let storedEntry =
(includeDeviceIdentity && deviceId != nil)
? DeviceAuthStore.loadToken(deviceId: deviceId!, role: role)
? DeviceAuthStore.loadToken(deviceId: deviceId!, role: role, profile: deviceIdentityProfile)
: nil
let storedToken = storedEntry?.token
let storedScopes = storedEntry?.scopes ?? []
@@ -756,7 +769,8 @@ public actor GatewayChannelActor {
deviceId: String,
role: String,
token: String,
scopes: [String])
scopes: [String],
deviceIdentityProfile: GatewayDeviceIdentityProfile)
{
guard let filteredScopes = self.filteredBootstrapHandoffScopes(role: role, scopes: scopes) else {
return
@@ -765,7 +779,8 @@ public actor GatewayChannelActor {
deviceId: deviceId,
role: role,
token: token,
scopes: filteredScopes)
scopes: filteredScopes,
profile: deviceIdentityProfile)
}
private func persistIssuedDeviceToken(
@@ -773,7 +788,8 @@ public actor GatewayChannelActor {
deviceId: String,
role: String,
token: String,
scopes: [String])
scopes: [String],
deviceIdentityProfile: GatewayDeviceIdentityProfile)
{
if authSource == .bootstrapToken {
guard self.shouldPersistBootstrapHandoffTokens() else {
@@ -783,20 +799,23 @@ public actor GatewayChannelActor {
deviceId: deviceId,
role: role,
token: token,
scopes: scopes)
scopes: scopes,
deviceIdentityProfile: deviceIdentityProfile)
return
}
_ = DeviceAuthStore.storeToken(
deviceId: deviceId,
role: role,
token: token,
scopes: scopes)
scopes: scopes,
profile: deviceIdentityProfile)
}
private func handleConnectResponse(
_ res: ResponseFrame,
identity: DeviceIdentity?,
role: String) async throws
role: String,
deviceIdentityProfile: GatewayDeviceIdentityProfile) async throws
{
if res.ok == false {
let error = res.error
@@ -855,7 +874,8 @@ public actor GatewayChannelActor {
deviceId: identity.deviceId,
role: authRole,
token: deviceToken,
scopes: scopes)
scopes: scopes,
deviceIdentityProfile: deviceIdentityProfile)
}
if self.shouldPersistBootstrapHandoffTokens(),
let tokenEntries = auth["deviceTokens"]?.value as? [ProtoAnyCodable]
@@ -873,7 +893,8 @@ public actor GatewayChannelActor {
deviceId: identity.deviceId,
role: authRole,
token: deviceToken,
scopes: scopes)
scopes: scopes,
deviceIdentityProfile: deviceIdentityProfile)
}
}
}

View File

@@ -162,6 +162,7 @@ public actor GatewayNodeSession {
let clientId = options.clientId.trimmingCharacters(in: .whitespacesAndNewlines)
let clientMode = options.clientMode.trimmingCharacters(in: .whitespacesAndNewlines)
let clientDisplayName = (options.clientDisplayName ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
let deviceIdentityProfile = options.deviceIdentityProfile.rawValue
let includeDeviceIdentity = options.includeDeviceIdentity ? "1" : "0"
let permissions = options.permissions
.map { key, value in
@@ -179,6 +180,7 @@ public actor GatewayNodeSession {
clientId,
clientMode,
clientDisplayName,
deviceIdentityProfile,
includeDeviceIdentity,
permissions,
].joined(separator: "|")

View File

@@ -0,0 +1,11 @@
import Foundation
public enum OpenClawAppGroup {
public static let canonicalIdentifier = "group.ai.openclawfoundation.app.shared"
public static var identifier: String {
let raw = Bundle.main.object(forInfoDictionaryKey: "OpenClawAppGroupIdentifier") as? String
let trimmed = raw?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
return trimmed.isEmpty ? self.canonicalIdentifier : trimmed
}
}

View File

@@ -69,6 +69,17 @@
"fileName"
]
},
"api": {
"emoji": "🌐",
"title": "API",
"detailKeys": [
"url",
"endpoint",
"path",
"method",
"name"
]
},
"browser": {
"emoji": "🌐",
"title": "Browser",

View File

@@ -26,7 +26,7 @@ public struct ShareGatewayRelayConfig: Codable, Sendable, Equatable {
}
public enum ShareGatewayRelaySettings {
private static let suiteName = "group.ai.openclaw.shared"
private static var suiteName: String { OpenClawAppGroup.identifier }
private static let relayConfigKey = "share.gatewayRelay.config.v1"
private static let lastEventKey = "share.gatewayRelay.event.v1"

View File

@@ -1,7 +1,7 @@
import Foundation
public enum ShareToAgentSettings {
private static let suiteName = "group.ai.openclaw.shared"
private static var suiteName: String { OpenClawAppGroup.identifier }
private static let defaultInstructionKey = "share.defaultInstruction"
private static var defaults: UserDefaults {

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