* fix(agents): route media task hints below the system-prompt cache boundary
Per-turn image/video/music generation task hints were injected into the
static prependSystemContext slot, landing above the cache boundary inside the
cacheable prefix. The hints are present only on user/manual turns and vary
with active media tasks, so the cacheable prefix shifted turn-to-turn and
defeated Anthropic/OpenAI prompt caching (#85203).
Split the per-turn media hints out of the prepend resolver into
resolveAttemptMediaTaskSystemPromptAddition and route them below the boundary
via the existing prependSystemPromptAddition helper, matching how subagent and
context-engine system-prompt additions are already routed. The static plugin
prependSystemContext / appendSystemContext hook fields are unchanged and
remain in the cacheable prefix. Applied at both consumers (embedded agent
runner and CLI runner).
* fix(agents): keep media task hints below the cache boundary for hook systemPrompt overrides
A before_prompt_build hook that returns a full systemPrompt override replaces
the base prompt with marker-free text. Per-turn media-generation task hints
were then front-prepended into that marker-free prompt, which providers cache
as a single block, so the cached prefix still shifted turn-to-turn on the
override path (#85203).
Wrap the base with ensureSystemPromptCacheBoundary at both media-routing sites
(embedded agent runner and CLI runner) so a marker-free override gets an
appended boundary and the hint routes into the uncached suffix. The helper is
idempotent, so marker-bearing prompts are unchanged. The shared
prependSystemPromptAddition wrapper and the static prependSystemContext /
appendSystemContext hook fields are untouched.
* fix(agents): keep marker-free idle prompts cacheable below the boundary
A marker-free hook systemPrompt override only had the cache boundary
ensured on turns with an active media task. On idle turns the later
appendModelIdentitySystemPrompt landed above the absent boundary, so the
idle cached system prefix diverged from active turns and prompt caching
broke across active/idle transitions. Ensure the boundary regardless of
media state in both the embedded and CLI runners, and extend the
regression to cover the model-identity append across active->idle.
* fix(agents): scope cache-boundary ensure to the model-identity append
Ensuring the boundary unconditionally on media-idle turns appended a
boundary marker to empty raw/gateway system prompts (turning "" into a
marker-only prompt) and to prompts with nothing below the boundary.
Instead ensure the boundary only when a model identity line is actually
appended to a non-empty prompt, in both the embedded and CLI runners.
This still keeps the identity below the boundary for marker-free hook
systemPrompt overrides (the #85203 idle-cache regression) while leaving
empty and identity-less prompts untouched.
* test: refresh stale type and lint expectations
* test: stabilize CI timeout checks
* test: satisfy channel entry lint
* fix(agents): skip cache boundary for blank prompts
* fix(channels): keep draft flush timer referenced
* test(agents): tolerate failed exec timeout setup
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Provider-scoped configured regex mention patterns for Discord, Matrix, Slack, Telegram, and WhatsApp.
Native platform mentions keep their existing behavior, and unsupported channels do not opt into the new regex policy path. The new policy supports per-channel allow/deny routing through mentionPatterns.mode with allowIn and denyIn so group auto-reply regexes can be limited without broad global blast radius.
Refs #70864.
Supersedes #87200.
Thanks @patrick-slimelab.
Adds Xiaomi MiMo voicedesign TTS support by registering the v2.5 voicedesign model and omitting audio.voice for that model's prompt-driven voice design flow.
Also accepts generic TTS aliases modelId, speakerVoice, and speakerVoiceId for Xiaomi provider config and request overrides.
Fixes exec timeout classification so a process that exits after a missed timeout callback is still reported as timed out, using monotonic deadlines to avoid wall-clock skew.
Verification:
- node scripts/run-vitest.mjs extensions/xiaomi/speech-provider.test.ts
- node scripts/run-vitest.mjs src/process/supervisor/supervisor.test.ts
- node scripts/run-vitest.mjs src/agents/bash-tools.exec-foreground-failures.test.ts
- git diff --check
- autoreview --mode local
- live Xiaomi MiMo voicedesign call returned wav RIFF/WAVE output, 169004 bytes
- GitHub CI success on fb3018ef31: CI 26708919072, CodeQL Critical Quality 26708919082, CodeQL 26708919091, OpenGrep PR Diff 26708919089, Workflow Sanity 26708919083, Dependency Guard 26708918574, Real behavior proof 26708921767
Thanks @GimingRao.
Co-authored-by: Raoyu <2425198313@qq.com>
Co-authored-by: giming <53329020+GimingRao@users.noreply.github.com>
getTimeZoneOffsetMs built localAsUtc via Date.UTC() without the millisecond
argument, so for a sub-second instant the computed timezone offset was wrong by
that fraction. That corrupts resolvedMs and fails the exact-millisecond
re-validation in matchesOffsetlessIsoDateTimeParts, so parseOffsetlessIsoDateTimeInTimeZone
returned null for valid fractional input.
User impact: openclaw cron --at "<ISO>.<ms>" --tz <zone> was silently rejected
even though the parser's regex explicitly accepts fractional seconds (\.\d+).
Pass parts.millisecond (carried from utcMs via getUTCMilliseconds) into Date.UTC
so the offset is exact. Add fractional-second regression rows.
Co-authored-by: coder999999999 <coder999999999@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(infra): guard against overwriting corrupt target session store during migration
migrateLegacySessions reads the target agents/{id}/sessions/sessions.json
and merges it with the legacy sessions dir. When the target file is
corrupt, readSessionStoreJson5 swallows the parse error and returns
{store:{}, ok:false}, so the merge becomes legacy-only. The save gate
(legacyParsed.ok || targetParsed.ok) passes on legacyParsed.ok alone and
never checks targetParsed.ok, so the corrupt target is atomically
overwritten with the legacy-only store. Target-only session records (keys
with no legacy counterpart) are lost permanently and the corrupt file can
no longer be recovered by hand. Legacy corruption is already guarded
(warn + skip delete); target corruption was asymmetrically unprotected.
Skip the save (and the legacy delete) when the target store exists but is
unreadable, leaving the corrupt file and the legacy store both in place,
and push a warning mirroring the legacy-unreadable path. saveSessionStore
and readSessionStoreJson5 signatures are untouched.
AI-assisted: drafted with claude code (claude-opus-4-8).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(infra): report direct-chat session migration only after target save commits
Addresses ClawSweeper review on #88018. The `Migrated latest direct-chat session`
result.changes entry was pushed before the targetReadable guard, so the
corrupt-target skip path (which intentionally does not save) still reported a
session migration in doctor/startup logs. Defer that report into the
save-committed block (keeping its existing position before `Merged sessions
store`) and assert its absence in the corrupt-target regression test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(infra): add explicit corrupt session recovery
* fix(infra): keep legacy sessions retryable
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Fix Control UI Talk consults so an empty final chat event no longer forces the no-text realtime tool result when a later source-reply or delivery-mirror final contains the answer displayed in the UI.
Also makes agent.wait use the chat-side terminal snapshot while a same-runId chat.send is active, so lifecycle completion cannot beat chat post-dispatch/source-reply delivery.
Adds regression coverage for delayed source replies, agent.wait failure/timeout handling, the wait-before-source-reply race, gateway wait ordering, and punctuation-only skill searches.
Fixes#85275.
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Add bounded timeouts for Crabbox wrapper sanity probes so a stale or hung selected binary cannot block the wrapper indefinitely. The wrapper now maps timed-out sanity probes to a deterministic failure and keeps provider/help parsing behavior intact.
Also add regression coverage for a binary whose `--version` probe hangs while `run --help` still responds.
Co-authored-by: Evan Newman <evanjames010101@gmail.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(auto-reply): redact config show secrets
* fix(auto-reply): use schema redaction for config show
* fix(auto-reply): redact config set acknowledgements
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
WebChat now stores/restores composer draft and queued sends across refresh, scoped by gateway/session/agent. It skips in-flight/steered sends, restores after agent scope hydration, waits for fresh idle session proof before draining restored sends, and backfills visible chat history when the raw tail contains silent/context entries.
Refs #83344
Co-authored-by: Zee Zheng <zheng.zuo0@gmail.com>
Fixes#86161.
Route Telegram media-message edits through the Telegram caption/reply-markup APIs instead of always calling `editMessageText`. Button-only edits now update reply markup, explicit captions use `editMessageCaption`, and text edits can fall back to caption edits when Telegram reports the message has no editable text.
Also documents the edit behavior, adds regression coverage, tightens timer-spy cleanup for the affected agents test lane, and removes a stale loader helper from the current base that broke core typecheck.
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Remove isolate: true from the channel Vitest config and fix the leaking fake-timer/mock tests so the lane runs under the shared non-isolated runner. Verified with focused scoped-config/channel tests, the full channel Vitest config, git diff --check, and branch-mode autoreview.
Deliver same-session channel replies directly while preserving stale-reply guards.
The fix bypasses the announce decider only when the requester and target are the same source channel, carries reply baselines into fire-and-forget follow-up delivery, and keeps history reads best-effort so timeout-zero sends still dispatch. It also includes focused regression coverage for delayed same-session replies, stale snapshots, retry timer caps, and the current strict-null/package-boundary blockers fixed while preparing the PR.
Move Telegram plugin-local state from JSON sidecars into plugin-state SQLite. Keep legacy JSON handling in startup and doctor migration plans, with runtime state now reading and writing SQLite directly. Stabilize the channel Vitest lane by cleaning up typing timers and isolating that lane.
Adds a default-enabled SwiftPM Talk trait for OpenClawKit so chat-only consumers can opt out with traits: [] and avoid resolving ElevenLabsKit. Default traits preserve existing talk/TTS API and bundled app behavior; macOS CI now verifies the trait-off dependency graph and build.
Verification:
- CI at 85f00ebc04 passed macos-swift and Real behavior proof.
- Local Swift 6.3.2: trait-off dependency graph omitted ElevenLabsKit; full swift build with default traits disabled built through OpenClawChatUI; default dependency graph still included ElevenLabsKit; trait-off OpenClawKit target build passed.
- merge-tree against latest origin/main 4eba3e5d7d was clean.
- Current main already fails plugin-SDK declaration gates in unrelated TS files; reproduced locally with node scripts/run-tsgo.mjs -p tsconfig.plugin-sdk.dts.json --declaration true.
Thanks @mochiexists.
Co-authored-by: mochiexists <259077624+mochiexists@users.noreply.github.com>
Co-authored-by: atlascodesai <76924051+atlascodesai@users.noreply.github.com>
Keep Slack direct-message sessions stable while tracking routed Slack thread ids on active reply operations. Different top-level Slack DM threads from the same sender no longer steer into or block each other, while ordinary same-thread follow-ups and non-Slack direct-message behavior keep their existing semantics.
Verification:
- `git diff --check origin/main...FETCH_HEAD`
- `/Users/steipete/Projects/agent-scripts/skills/autoreview/scripts/autoreview --mode branch --base origin/main --output /tmp/pr85904-autoreview.txt --json-output /tmp/pr85904-autoreview.json`
- GitHub CI green for head `6703e166545bcb96c1a50de93a42446212cca9a7`, including Real behavior proof and auto-reply reply routing/dispatch shards.
Co-authored-by: guanbear <123guan@gmail.com>
Fixes#88056.
Reload workspace skill commands for `/skill <name>` when directive resolution supplied only an empty placeholder list, so the generic skill wrapper can invoke the same command-visible skills as direct slash commands.
Keep stale-message cutoff and empty-config channel suppression ahead of skill discovery and tool dispatch so suppressed `/skill` messages cannot trigger side-effecting skill tools.
Co-authored-by: Ted Li <tl2493@columbia.edu>
Align Telegram proactive DM-topic outbound session routing with inbound reply routing.
The Telegram plugin now uses the chat-scoped DM-topic suffix for direct-topic outbound sessions, so cron/proactive sends and replies reuse the same session. Delivery metadata is kept as the numeric Telegram topic id so visible sends still target the correct private topic.
Refs #80212.
Thanks @brokemac79.
Verification:
- PR head d904115e4c
- GitHub CI/checks green on PR head; Real behavior proof passed; OpenGrep passed; CodeQL neutral/pass
- git diff --check origin/main...pr/88421 -- extensions/telegram/src/channel.ts extensions/telegram/src/session-route.test.ts
- git merge-tree $(git merge-base origin/main pr/88421) origin/main pr/88421
Fix stale heartbeat scheduler deferrals so disabled/non-retry skips and flood deferrals advance the due slot instead of rearming a 0 ms timer loop.
Fixes#79380.
Supersedes #79418.
Proof:
- pnpm test src/infra/heartbeat-runner.scheduler.test.ts -- --reporter=verbose
- pnpm check:changed via Testbox tbx_01ksxfavykc7qyve4ysnxg3smh
- autoreview clean
- GitHub CI green for 213003a854, including Real behavior proof
Add a bounded `chat.message.get` gateway method so Control UI can fetch one display-normalized transcript message by id when an assistant history preview was truncated. Keep `chat.history` lightweight, reject oversized/hidden/missing rows with explicit unavailable reasons, and wire the WebChat side reader to request full content only for visible truncated assistant messages.
Also refresh the generated Swift gateway protocol models and document the new assistant-message side-reader behavior.
Closes#84651.
Related #53242.
Co-authored-by: NianJiuZst <3235467914@qq.com>
Extract shared normalization/coercion helpers into private @openclaw/normalization-core workspace package while preserving existing plugin SDK helper subpaths.\n\nAlso keeps direct normalization-core imports internal, wires UI/build/loader resolution, and replaces the slow PR network CodeQL lane with a fast added-line boundary scan while retaining full CodeQL for scheduled/manual runs.\n\nVerification: local moved tests, plugin SDK boundary tests, extension loader tests, agents-support shard, UI build/test, build artifacts, lint, workflow guards, autoreview, and GitHub CI passed on PR head 963d893715.
Move Workboard durable data into a relational SQLite database and add extension doctor migration for .28 plugin-state rows. Preserve attachment lifecycle behavior, SQLite permissions/WAL settings, and scoped plugin migration access.
* feat(browser): add optional vision understanding to screenshot tool
* fix(browser): wrap vision output as external content, enforce maxBytes, forward auth profiles
* fix(browser): remove no-op scope/attachments config, drop profile pass-through lacking runtime support
* feat(media-understanding): add profile/preferredProfile to DescribeImageFileWithModelParams and forward to describeImage
* style(browser): add curly braces to satisfy eslint curly rule
* fix(browser): correct tools.browser.enabled help text to match actual behavior
* fix(browser): thread agentDir/workspaceDir from plugin tool context into browser vision
* refactor(browser): move vision config from tools.browser to browser.models
The browser plugin's vision configuration now lives on the top-level
`browser` config namespace (browser.models, browser.visionEnabled,
browser.visionPrompt, etc.) instead of `tools.browser`. This aligns
with the plugin's existing config location and avoids confusion between
tool-level and plugin-level settings.
- Remove tools.browser from ToolsSchema and ToolsConfig
- Add models/vision* fields to BrowserConfig and its zod schema
- Update getBrowserVisionConfig to read from cfg.browser
- Update schema help, labels, and quality test
- Update vision.test.ts to use new config shape
* docs(browser): add screenshot vision configuration section
Document the new browser.models config for automatic screenshot
description via vision models, enabling text-only main models to
reason about web page content.
* fix(browser): remove deliverable media markers from vision result, drop unused import
P1: Vision-success path no longer exposes the raw screenshot as
deliverable media (removes MEDIA: line and details.media.mediaUrl).
This prevents channel delivery from auto-sending sensitive page content
when the intended output is a text description.
P2: Remove unused ToolsMediaUnderstandingSchema import that would fail
noUnusedLocals typecheck.
* fix(browser): add command/args fields to browser models schema
The browser vision model schema uses .strict(), so CLI-type entries
with command/args were rejected by TypeScript. Add these fields to
align with MediaUnderstandingModelSchema.
* chore(browser): remove debug console.log statements
* fix(browser): harden screenshot vision result against MEDIA: directive injection and restore image sanitization on failure fallback
ClawSweeper #84247 review round 2:
P1 (security, high): neutralize line-start MEDIA: directives in vision descriptions
before wrapping with wrapExternalContent. The agent media extractor scans every
browser tool-result text block via splitMediaFromOutput which treats line-start
MEDIA: as a trusted local-media delivery directive, and browser is on the
trusted-media allowlist. Without neutralization, page or vision-provider output
containing 'MEDIA:/tmp/secret.png' could synthesize a channel-deliverable media
artifact from untrusted content. wrapExternalContent itself does not strip
line-start directives. Introduce neutralizeMediaDirectives in vision.ts that
prepends '[neutralized] ' to any line whose trimStart() begins with MEDIA:
(case-insensitive), defanging the parser anchor while keeping the original
text human-readable.
P2 (compatibility): pass resolveRuntimeImageSanitization() to imageResultFromFile
in the vision-failure catch fallback. The non-vision screenshot path already
forwards this option (d5cc0d53b7) so configured agents.defaults.imageMaxDimensionPx
takes effect. Without this fix, any provider timeout/error silently bypasses the
sanitization guard and returns a raw full-resolution screenshot.
Regression coverage:
- vision.test.ts: 6 unit cases for neutralizeMediaDirectives (no-op fast path,
mid-line MEDIA: untouched, line-start defanged, leading-whitespace defanged,
case-insensitive, multiple directives per blob).
- browser-tool.test.ts: 2 integration cases that drive the full screenshot
tool execute path:
- 'neutralizes MEDIA: directives in vision text and does not attach media'
asserts no line matches /^\s*MEDIA:/i in returned text, secret path text
is preserved verbatim, details.media is absent, and imageResultFromFile
is not called on the success path.
- 'preserves screenshot image sanitization on vision failure fallback'
mocks describeImageFileWithModel to reject and asserts the fallback
imageResultFromFile call receives imageSanitization: {maxDimensionPx:1600}
plus the 'browser screenshot vision failed' extraText.
* fix(browser): apply clawsweeper fallback media fix from PR #84247
* refactor: reuse media image understanding for browser screenshots
* refactor: use structured media delivery
* test: update music completion media instruction expectation
* fix: trim buffered reply directive padding
* test: refresh codex prompt snapshots for message media aliases
---------
Co-authored-by: scotthuang <scotthuang@tencent.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Add MCP server add/configure/login/reload flows plus config/runtime support for enablement, filters, timeouts, OAuth, TLS, and parallel execution hints. Update docs and tests for the expanded MCP operator surface.
Treat OpenAI-compatible streaming tool deltas as executable only when the final finish reason is `tool_calls`. This prevents malformed provider streams from triggering spurious tool execution while preserving normal tool-call responses.
Fixes#85161.
Verification:
- Local OpenAI-compatible SSE replay: spurious stop stream `finalToolCalls: 0`; valid tool-call stream `finalToolCalls: 1`.
- `pnpm test src/agents/openai-transport-stream.test.ts src/llm/providers/openai-completions.test.ts -- --reporter=verbose`
- PR CI green on `cdc2fc34753492c862cae99b37f8cf3761d9bbed`.
Co-authored-by: 忻役 <xinyi@mininglamp.com>
Co-authored-by: Jerry-Xin <jerryxin0@gmail.com>
Preserve plugin-resolved cron delivery targets after target resolution so provider-looking canonical target prefixes are not stripped before outbound delivery.
Adds regression coverage for plugin canonical targets returned directly and via aliases, plus a guard that generic normalized fallback targets still strip the selected prefix.
Fixes#87905
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Surface gateway chat failures as visible assistant messages in the Control UI, with regression coverage and Crabbox/WebVNC proof.
(cherry picked from commit 31a46638ad)
Fixes#63558.
Adds a Dreaming-tab agent selector and propagates the selected agent through Dreaming status, diary, and diary actions while preserving default-agent fallback when agentId is omitted. Also keeps report Memory Palace cards in the Control UI wiki-preview flow and documents the optional Dreaming agentId gateway parameters.
Verification:
- GitHub CI run 26693682975 passed on 43a2b17243.
- CodeQL Critical Quality run 26693682971 passed.
- CodeQL / Security High run 26693682957 passed.
- Workflow Sanity run 26693682949 passed.
- OpenGrep PR Diff run 26693682947 passed.
- Dependency Guard run 26693682003 passed.
- Real behavior proof run 26693860539 passed.
- git diff --check origin/main...refs/remotes/origin/pr/78748 passed.
- git merge-tree --write-tree origin/main refs/remotes/origin/pr/78748 passed.
Thanks @stevenepalmer.
Co-authored-by: Steven Palmer <6134396+stevenepalmer@users.noreply.github.com>
Extracts serialized plaintext tool-call parsing, scrubbing, stream normalization, and standalone promotion into the private internal @openclaw/tool-call-repair package.
Provider wrappers and the embedded runner now share one repair path for standalone serialized tool calls, including adjacent text-block splits, while preserving exact argument bytes when already valid. The public plugin SDK payload module remains as the compatibility facade.
Verification:
- pnpm test src/plugin-sdk/provider-stream-shared.test.ts src/plugin-sdk/tool-payload.test.ts src/agents/embedded-agent-runner/run/attempt.tool-call-normalization.test.ts -- --reporter=verbose
- env -u OPENCLAW_TESTBOX pnpm check:changed
- PR CI: all reported checks green/skipped/neutral on ff0b3c0a5c
Refs #86924
Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Preserve Slack Agents & Assistants DM root thread context for tool and subagent replies even when Slack omits or misreports `channel_type`, while leaving non-DM self-thread roots top-level.
Fixes#63659.
Thanks @zozo123.
Fixes#88214.
Control UI dashboard Recent sessions now follows the selected agent, preserves legacy main sessions under stale identity, keeps unknown sessions unscoped, and scopes agent/default session refreshes before the session-list limit. Completed run refreshes now use the run's original session/agent target, global New Chat creates under the selected agent, and the agent switcher preserves last known target sessions across scoped refreshes without resurrecting deleted or archived sessions while accepting newer out-of-scope live rows into the switch cache. Also fixes a current-main lint issue around trusted approval params.
Co-authored-by: 张贵萍0668001030 <zhang.guiping@xydigit.com>
Classify release dependency ownership metadata so release evidence no longer reports current root dependencies as missing ownership metadata. Also recognizes command-explainer package-file lookups for tree-sitter-bash.
Verification: jq empty scripts/lib/dependency-ownership.json; node scripts/dependency-ownership-surface-report.mjs --check; node scripts/root-dependency-ownership-audit.mjs --check; targeted Vitest for root dependency ownership and ownership surface reports; git diff --check; autoreview clean; PR CI green including Real behavior proof.
* fix(responses): drop orphaned assistant msg_* id when reasoning is dropped (#88019)
When an Azure/OpenAI Responses session falls back to a non-Responses model
and later resumes a Responses model, sanitizeSessionHistory drops the
replayable reasoning (rs_*) item via downgradeOpenAIReasoningBlocks. The
paired assistant text block still carried its textSignature (the msg_* id),
so the transport replayed an assistant message item referencing msg_* with
no accompanying rs_* reasoning item. Azure Responses then rejected the next
turn with:
400 Item 'msg_...' provided without its required 'reasoning' item: 'rs_...'
permanently poisoning the session.
Fix:
- downgradeOpenAIReasoningBlocks now strips the textSignature from a turn's
text blocks whenever it drops a replayable reasoning item, so the msg_* id
and its rs_* reasoning are removed together. The transport then falls back
to a synthetic, unpaired id that Azure accepts.
- Because the synthetic fallback id is derived from the per-message msgIndex,
multiple id-less text blocks in one assistant turn (e.g. commentary +
final_answer) would collide on the same id. Make the fallback unique per
text block in both Responses conversion sites
(openai-transport-stream.ts and the shared llm provider
openai-responses-shared.ts).
Tests:
- sanitize-session-history: model-switch path drops the paired msg_* id.
- embedded-agent-helpers: downgrade strips paired text signature(s).
- reasoning-replay: multiple id-less text blocks get distinct item ids.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(responses): preserve phase metadata and guard malformed blocks (#88019)
Address PR review feedback on the orphaned msg_* replay fix:
- Preserve Responses phase metadata: dropping the paired msg_* id when its
rs_* reasoning is removed previously stripped the entire textSignature,
which also discarded the phase (commentary/final_answer). Phased text now
keeps a phase-only signature ({v:1,phase}) so commentary is not replayed
as user-visible output. Both parseTextSignature copies (shared provider and
embedded transport) now accept id-less phase-only signatures and fall back
to a synthetic id while preserving the phase.
- Guard malformed content blocks: the post-drop map no longer dereferences
contentBlock.type unconditionally, so a corrupted transcript with a
null/primitive block can still sanitize through a model switch.
Tests:
- sanitize-session-history: phase metadata is preserved while the paired id
is dropped on a model switch.
- reasoning-replay: id-less phase-only signatures get distinct synthetic ids
and retain their phase.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Payload-less session write-lock files now get a 30s grace for default/long acquire timeouts and cleanup sweeps, while short acquire timeouts keep 5s recovery. This avoids reclaiming a lock while the owner is suspended between exclusive create and metadata write.
Verified with:
- git diff --check origin/main...HEAD
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main --parallel-tests "node scripts/run-vitest.mjs src/agents/session-write-lock.test.ts"
- gh pr checks 80686 --repo openclaw/openclaw --watch=false
Thanks @wAngByg.
Extract web-content shared runtime helpers into packages/web-content-core, move the focused tests with the new package, and split quiet CI shards so the node matrix no longer stalls past the no-output watchdog.\n\nVerification: node scripts/run-vitest.mjs test/scripts/ci-node-test-plan.test.ts test/scripts/run-vitest.test.ts src/infra/restart.test.ts src/infra/os-summary.test.ts src/infra/gateway-processes.test.ts src/infra/inline-option-token.test.ts src/infra/map-size.test.ts src/infra/machine-name.test.ts src/commands/doctor-whatsapp-responsiveness.test.ts; autoreview clean; manual CI https://github.com/openclaw/openclaw/actions/runs/26693962844; dependency guard https://github.com/openclaw/openclaw/actions/runs/26693959937. Admin merge used because optional Mantis Telegram Desktop proof was cancelled after blocking merge outside this PR's required proof.
Bound MCP channel bridge pending Claude permission and approval maps with TTL sweep and close cleanup.
Also sweep before listing pending approvals so expired requests are not exposed between periodic ticks.
Fixes#71646.
Thanks @Feelw00.
Adds `openclaw sessions tail` as an operator-facing progress view over session trajectory events, with conservative redaction for prompt text, tool arguments, and tool result bodies. The command supports explicit session keys, store/agent scope, follow mode, relocated trajectory pointer files, and cursor-safe follow across bounded trajectory window rewrites.
Documents the new sessions tail CLI surface in `docs/cli/sessions.md`.
Fixes#83441.
Co-authored-by: zhengzuo0-ai <zheng.zuo0@gmail.com>
Skip browser lifecycle cleanup when root browser support or the browser plugin entry is disabled, and make the browser maintenance facade respect activation before cached surface use.
Also stabilize the resource-only MCP runtime test by waiting for the async rejection log that CI can observe late.
Verification:
- pnpm test src/plugin-sdk/browser-maintenance.test.ts src/browser-lifecycle-cleanup.test.ts src/auto-reply/reply/session.test.ts src/gateway/server.sessions.reset-cleanup.test.ts src/agents/auth-profiles/usage.test.ts
- pnpm test src/agents/agent-bundle-mcp-runtime.test.ts
- git diff --check
- pnpm build
- autoreview local: no accepted/actionable findings
- GitHub Actions: CI 26693713166, CodeQL 26693713159, CodeQL Critical Quality 26693713157, OpenGrep PR Diff 26693713125, Workflow Sanity 26693713149, Dependency Guard 26693712478
Co-authored-by: Nicolas Van Eenaeme <nicolas@poison.be>
Move MSTeams conversation and poll plugin-local stores to plugin-state SQLite. Legacy JSON stores import once without overwriting existing SQLite state; conversation and poll IDs are hashed for plugin-state keys; poll votes are sharded with bounded row-cap headroom and prune cleanup; MSTeams docs now describe SQLite storage. SSO and delegated token stores are unchanged. Verified with focused MSTeams tests, docs sanity, autoreview, Testbox check:changed, and green PR CI.
Deduplicate the browser lifecycle cleanup wrapper for embedded subagent completions while preserving retire and announce finalization for duplicate callers.\n\nAdds regression coverage for parallel completion callers and the held-first-cleanup duplicate-tail path.\n\nFixes #68668.\n\nCo-authored-by: Feelw00 <dhrtn1006@naver.com>
Repair invalid \u escapes during streaming JSON parsing without changing valid Unicode escapes. Split oversized node CI doctor/infra shards and fix the restart test mock deadlock so PR CI stays under the no-output threshold.\n\nCo-authored-by: Coder <83845889+coder999999999@users.noreply.github.com>
Move model catalog normalization and package-owned catalog schema/types into model-catalog-core while keeping public plugin SDK model catalog declarations on the existing SDK surface. Verified focused tests, package-boundary compile, full build, changed gate, declaration leak grep, CI, and autoreview.
Keep Codex app-server continuation turns alive after post-tool, raw assistant, and progress notifications, and reschedule continuation idle watches when shorter progress timeouts apply.
Add regression coverage for the plugin-sdk child_process mock helper deadlock that blocked CI shards on this PR.
Co-authored-by: abnershang <abner.shang@gmail.com>
Stop minimal cliStartup and gatewayWatch builds from copying generated plugin static assets they intentionally do not build.\n\nVerified with focused Vitest, autoreview, AWS Crabbox startup-memory proof, and AWS Crabbox changed gate run_bd9ea01e6a12 plus rebased changed gate run_bd9ea01e6a12.
* fix(export-html): guard all msg.content and result.content filter/iteration paths
Three call sites in the export HTML template called `.filter()` or iterated
with `for...of` directly on `msg.content` or `result.content` without first
checking `Array.isArray`. When a transcript message row carries a non-array
content value (null, undefined, or any scalar), those paths throw:
TypeError: msg.content.filter is not a function
Fix: normalize with `Array.isArray(x) ? x : []` before every unguarded
filter and iteration on `msg.content` (computeStats stats path and the
renderEntry assistant render loops) and `result.content` (renderToolCall
text/image accessors).
Regression test added: renderTemplate resolves without throwing for assistant
messages with null, undefined, string, and numeric content values.
Closes#88255
* fix(export-html): guard user message text extraction path against non-array content
The user-message render path in the export HTML template extracted text with
`content.filter(...)` without checking whether `content` is an array. A
persisted user message row with null, undefined, or any non-string scalar
content crashed during export with the same TypeError class as the assistant
path.
Fix: normalize the ternary so a non-string, non-array value falls through to
an empty string rather than calling `.filter` on it.
Regression test added for null, undefined, and numeric user message content.
Addresses feedback from ClawSweeper review on #88271.
* fix(export-html): preserve string content guards
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Move model catalog ref helpers into @openclaw/model-catalog-core/model-catalog-refs and update internal callers/package-boundary aliases. Also fix the timestamp predicate typing that blocked prod type checks on current main.
Fixes#37748.
Sort skill package archive entries by relative POSIX archive name so generated `.skill` bundles are reproducible regardless of filesystem traversal order.
Verification:
- `PYTHONDONTWRITEBYTECODE=1 python3 skills/skill-creator/scripts/test_package_skill.py`
- `git diff --check origin/main...HEAD`
- GitHub CI run 26690938925 on `43a0fdf7175f33a5c74bc7ff92723ebf5efc4df9`: all checks passed except repeated unrelated no-output timeouts in `checks-node-agentic-commands-doctor` and `checks-node-core-runtime-infra-state` after visible tests passed.
Forward OpenAI-compatible stop sequences from gateway chat completions through the agent runner into provider transports.
The gateway now normalizes stop into sampling extras, agent transports pass it into the shared stream options, and OpenAI, Anthropic, Mistral, Google, and Vertex-backed simple providers map it to their native request fields. Provider/gateway/agent coverage plus Crabbox live gateway proof verify valid stop dispatch and invalid stop rejection.
Refs #87920
Show the remote node name in exec tool transparency details when an exec call targets `host=node`, while ignoring stray `node` values for gateway, sandbox, and auto-host calls.
Covers node-only, cwd+node, absent-node, and non-node-host regression cases in the tool display tests.
Fixes#77719.
Co-authored-by: JiataiWang <wangjiatai@proton.me>
Feishu `channels.feishu.streaming=true` now streams ordinary assistant replies through CardKit in auto mode, while keeping tool-summary delivery on the existing message path.
Also discards stale partial previews when final delivery intentionally suppresses text for voice media or duplicate final text, and preserves streamed partial text for regular media-only finals.
Verification:
- `node scripts/run-vitest.mjs run extensions/feishu/src/reply-dispatcher.test.ts`
- `pnpm tsgo:extensions`
- `pnpm test:extensions:package-boundary:compile`
- `pnpm exec oxfmt --check extensions/feishu/src/reply-dispatcher.ts extensions/feishu/src/reply-dispatcher.test.ts extensions/feishu/src/streaming-card.ts`
- `git diff --check`
- `.agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main`
- GitHub PR checks on run 26689677607 passed except repeated unrelated broad Vitest no-output timeouts in `checks-node-agentic-commands-doctor` and `checks-node-core-runtime-infra-state`.
Co-authored-by: 传妈 <chuanmother@chuanMac-Mini.local>
Fixes#88218.
Preserves exact configured provider/model defaults before bare alias target reverse matches, while retaining slash-form aliases and auth-profile alias behavior.
Co-authored-by: Steven Palmer <palmer.e.steven@gmail.com>
Carry Discord reply typing feedback through preflight, queued dispatch, and cleanup so delayed accepted replies keep typing alive at the actual dispatch target without duplicate keepalives. Adds focused Discord queue/process policy coverage and stronger lifecycle invariant comments.
Prunes undefined Discord component and modal registry metadata before persisting it so SQLite-backed plugin state never receives JSON-incompatible undefined values. Adds direct regression coverage for undefined own properties on component, modal, and nested field entries.
Add actionable operator guidance when an unauthorized SIGUSR1 gateway restart is ignored because unmanaged restart is disabled.
The change is log-only: restart authorization and scheduling semantics are unchanged, and the existing run-loop test now asserts both the reason warning and the recovery hint.
Refs #79577
Refs #78110
Refs #82433
Co-authored-by: wAngByg <281221101+wAngByg@users.noreply.github.com>
Dedupe prompt-side inbound media note suffixes when sanitized MediaPath and MediaUrl render to the same value, while preserving genuinely distinct remote URLs.\n\nFixes #47587.\nThanks @MoerAI for the patch and @yzjJosh for the report.
Show the same Installing OpenClaw package progress line in the no-gum npm install fallback before redirecting npm output to the temp log.
Fixes#82305
Co-authored-by: Sebastien Tardif <sebtardif@ncf.ca>
Adds focused coverage for task-domain view mapper DTO contracts, including summary cloning, task run/detail mapping, flow view/detail mapping, and implicit summary computation.
Test-only PR. Verified with git diff --check and PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN=false pnpm test src/tasks/task-domain-views.test.ts on the current-main merge result.
Thanks @leno23.
Co-authored-by: wuyangfan <yangfan.wu@succaiss.com>
Fixes#49517.
Updates the TUI command catalog so /new describes spawning an isolated session while /reset describes resetting the current session. Adds a focused regression test for the two descriptions.
Co-authored-by: KhanCold <119404710+KhanCold@users.noreply.github.com>
Adds a persisted collapse state for the Control UI Recent sessions sidebar group, including storage and browser coverage.
Also narrows gateway run miss cache expiry typing so the rebased branch stays clean against current main.
Closes#85510
Co-authored-by: NianJiuZst <3235467914@qq.com>
Route internal model catalog imports to the extracted @openclaw/model-catalog-core package and delete obsolete internal facades.
Keep public SDK declarations self-contained by wrapping core helpers at public boundaries instead of leaking private package imports.
Verification:
- pnpm test src/plugins/contracts/model-catalog-core-imports.test.ts src/plugins/sdk-alias.test.ts packages/model-catalog-core/src/configured-model-refs.test.ts packages/model-catalog-core/src/provider-model-id-normalize.test.ts packages/model-catalog-core/src/provider-model-id-normalization.test.ts src/config/config.model-ref-validation.test.ts src/agents/model-selection.test.ts src/plugin-sdk/provider-model-shared.test.ts -- --reporter=verbose
- pnpm check:test-types
- pnpm test:extensions:package-boundary:compile
- pnpm build
- rg "@openclaw/model-catalog-core" dist/plugin-sdk packages/plugin-sdk/dist -n --glob '*.d.ts' || true
- git diff --check
- autoreview clean after fix
CI note: merged with admin override because checks-node-agentic-commands-doctor and checks-node-core-runtime-infra-state failed twice with exit 143/no-output watchdog termination after prior passing test output, while relevant local proof and the rest of CI were green.
Fixes#88198.
Ignore top-level helper scripts in auto-discovered global/workspace extension roots so they do not become manifestless plugin candidates during config validation. Standalone plugin files remain supported when explicitly configured through `plugins.load.paths`, and docs now call out the supported path.
Verification:
- `node scripts/run-vitest.mjs src/plugins/discovery.test.ts src/config/config.plugin-validation.test.ts`
- `node scripts/run-oxlint.mjs src/plugins/discovery.ts src/plugins/discovery.test.ts src/config/config.plugin-validation.test.ts`
- `git diff --check`
- GitHub CI green at `93073bfa85ee294e644c623881ba59ba71d90975`
- `.agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main`
Thanks @mushuiyu886 for the fix and @mmhzlrj for the report.
Pack newline-mode outbound paragraphs up to the configured text limit instead of sending one message per blank-line-separated paragraph. Preserves markdown fence guardrails and adds focused chunking plus outbound delivery regressions.\n\nVerified: autoreview clean; node scripts/run-vitest.mjs src/auto-reply/chunk.test.ts src/infra/outbound/deliver.test.ts; git diff --check origin/main...HEAD.\n\nThanks @kesslerio.
Fixes#66509.
QQBot now sends text-only tool progress immediately when partial streaming is enabled instead of buffering it until a fallback timer that is cleared by the final block. Immediate progress uses QQ plain-text sends so markdown-enabled accounts do not reinterpret media-like progress text, while streaming-off behavior remains final-only.
Thanks @gabrielduartesignart for the report.
Co-authored-by: samzong <samzong.lu@gmail.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* refactor: extract model catalog core package
* refactor: route model catalog imports through package boundary
* build: include model catalog in plugin sdk package dts
* fix: preserve static fallback model metadata
Classify the exact `ws` pre-handshake close-before-open error as a benign uncaught network exception so transient Feishu WebSocket cleanup does not crash the gateway process.
The classifier now keeps the upstream `ws` message as an exact contract and rejects broader prefixed WebSocket messages, with regression coverage for direct, wrapped, and non-exact cases.
Fixes#88257.
Thanks @akrimm702.
Co-authored-by: AI-HUB <144416483+akrimm702@users.noreply.github.com>
Completed WebChat stream segment bubbles now render without the active streaming animation after live output has moved on. The UI chat item contract now marks completed stream segments as non-streaming and the active stream as streaming, so the renderer applies the pulsing class only to live output.
Verified with:
- node scripts/run-vitest.mjs ui/src/ui/chat/build-chat-items.test.ts ui/src/ui/chat/grouped-render.test.ts ui/src/ui/views/chat.test.ts
- node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.test.ui.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/test-ui-stream-artifacts.tsbuildinfo
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
PR: #88225
Credit: @scotthuang
Refactor OpenAI provider identity so OpenAI remains the canonical provider for API-key and OAuth-backed flows while legacy openai-codex state is doctor/migration-only.
Keeps OpenAI Codex Responses as an API/transport class rather than a provider identity, moves auth aliases through providerAuthAliases, updates doctor repair sequencing for old auth/profile state, and refreshes tests/docs around the canonical OpenAI behavior.
Resolve raw plugin config environment references before plugin discovery and validation, while preserving the existing single-pass behavior for configs already loaded through config IO.
The loader now resolves raw config opt-ins with config.env vars included, bypasses active/cache reuse for that mode, and redacts plugin entry config from raw-mode cache keys so resolved secrets do not enter registry keys or reentry errors.
Verification:
- OPENCLAW_VITEST_MAX_WORKERS=1 node scripts/run-vitest.mjs src/plugins/loader.test.ts src/plugins/loader.runtime-registry.test.ts
- autoreview --mode branch --base origin/main
- pnpm check:changed on Blacksmith Testbox tbx_01ksw36bp7zygwxgq3jcsvjv3b / GitHub Actions run 26680322889
- PR CI green on facb77634e
Co-authored-by: Peter Lindsey <peter@lindsey.jp>
Adds first-class Xiaomi Token Plan provider support with regional onboarding/configuration, token-plan key prefix validation, runtime pricing/catalog metadata, and docs/test coverage.
Keeps Token Plan model catalog discovery runtime-owned so region-specific base URLs are required and the provider cannot silently fall back to the static SGP manifest catalog.
Fixes#86169.
Verification:
- node scripts/run-vitest.mjs src/plugins/provider-discovery.runtime.test.ts extensions/xiaomi/index.test.ts src/plugins/manifest-model-catalog.test.ts src/model-catalog/manifest-planner.test.ts
- git diff --check
- autoreview --mode local: clean, no accepted/actionable findings
- CI run 26678998539: all relevant checks passed; check-prod-types failed on unrelated browser unused-function issue already present on origin/main
Co-authored-by: NianJiuZst <3235467914@qq.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Keep Codex post-tool assistant/commentary progress and patch snapshot updates on the post-tool completion guard so long generated edits do not fall back to terminal idle handling. Enable Codex patch streaming events for native code mode and refresh exact prompt/config expectations.
Verification:
- pnpm prompt:snapshots:check
- pnpm test extensions/codex/src/app-server/run-attempt.turn-watches.test.ts extensions/codex/src/app-server/thread-lifecycle.test.ts extensions/codex/src/app-server/thread-lifecycle.binding.test.ts extensions/codex/src/app-server/side-question.test.ts
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- git diff --check origin/main...HEAD
- exact-head CI run 26677938955
- Real behavior proof override run 26678097960
Thanks @keshavbotagent.
Co-authored-by: Kelaw - Keshav's Agent <keshavbotagent@gmail.com>
Stop Codex app-server turns from projecting mirrored OpenClaw transcript history into prompt/model-input surfaces by default. Keep context-engine output on the rendered prompt/developer-instruction path and preserve mirrored history only for OpenClaw-side snapshots.
Move OpenClaw skills and the routed workspace-memory pointer out of native Codex turn user input and into turn-scoped collaboration developer instructions.
Preserve full MEMORY.md fallback prompt injection, delivery-hint rewrapping, lightweight cron exact prompts, and trajectory reporting for the rendered developer surface.
Co-authored-by: Beru <beru@lastguru.lv>
Fix Codex app-server native thread overflow recovery and CLI compaction fallback.
- rotate Codex native startup bindings when rollout token pressure leaves too little headroom
- keep byte-size rollout fuses ahead of rollout content reads
- clear stale resumed context-engine bindings only when the stored thread id still matches
- fall back to context-engine compaction when Codex owns/skips native compaction
Verification:
- node scripts/run-vitest.mjs run --config test/vitest/vitest.extension-codex.config.ts extensions/codex/src/app-server/startup-binding.test.ts extensions/codex/src/app-server/run-attempt.context-engine.test.ts extensions/codex/src/app-server/session-binding.test.ts --reporter=verbose
- node scripts/run-vitest.mjs run --config test/vitest/vitest.agents.config.ts src/agents/command/cli-compaction.test.ts --reporter=verbose
- git diff --check origin/main...HEAD
- autoreview --mode branch --base origin/main: clean
- GitHub CI for 466bfbe78c: green
Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
A claude-cli session whose JSONL transcript ends with an assistant
`tool_use` content block that was never answered by a `tool_result` user
message cannot resume — claude-cli will sit waiting for the missing
`tool_result`, hit its no-output watchdog, and the runtime kills it
with `reason=abort`. The dispatcher then sees an empty payload and emits
NO_REPLY, which to the user looks like the agent silently ignored their
message — same end-user symptom as the binding-flush amnesia bug, but a
different root cause.
The orphan can be left behind when:
- Gateway restarts mid-tool (brew upgrade, manual kickstart, OOM,
crash) — claude was waiting on a tool result that never arrived.
- `claude-live-session.ts` no-output watchdog fires while a tool is
actively running and OC kills the subprocess.
- The tool itself crashed or hung past its own deadline.
In all cases the resumed session is dead until the binding gets cleared,
because every subsequent resume hits the same trailing tool_use and the
same kill cycle. Observed in production on a personal OpenClaw gateway
(3d-engineer agent, 50-message-deep transcript ending in a Bash
`tool_use`; every Telegram message after the orphan landed silently
aborted at the 180s no-output mark).
Add `claudeCliSessionTranscriptHasOrphanedToolUse` to the helpers that
walks the JSONL, finds the last assistant message, and returns true if
any of its `tool_use` ids has no matching `tool_result` later in the
file. Wire into `prepareCliRunContext` as a second invalidator gate
alongside `missing-transcript`. The new `invalidatedReason:
"orphaned-tool-use"` follows the same path as missing-transcript: the
binding is dropped, this turn starts a fresh session, and the prior
context is reseeded into the new session via `RAW_TRANSCRIPT_RESEED`.
Detection only considers TRAILING orphans — an unanswered tool_use
deeper in history is inert because a later assistant message already
moved past it. Only the most recent assistant message's tool_use ids
matter for forward progress.
Probe runs only for claude-cli providers and only when the transcript-
content gate already passed, so we add no I/O on already-invalidated
sessions and no behavior change for non-claude providers.
AI-assisted: yes. Tooling: Claude Opus + claude-cli.
When a claude-cli turn produces a session id but the underlying claude
subprocess fails to flush an assistant-role record to its
~/.claude/projects/<cwd>/<sid>.jsonl transcript (e.g. mid-turn kill from
a concurrent fingerprint-mismatched turn, supervisor restart, internal
failure), buildCliRunResult was still persisting that session id into
cliSessionBinding. The next turn ran claudeCliSessionTranscriptHasContent,
didn't find the file, logged 'cli session reset: reason=missing-transcript',
and started a brand-new claude session with empty memory.
End-user symptom: agent forgets prior conversation between turns.
Gate the cliSessionBinding spread on the same predicate the next-turn
invalidator uses, evaluated at write time. Also clear agentMeta.sessionId
in the same case so the session-store fallback at command/session-store.ts
(which reads agentMeta.sessionId via setCliSessionId when the binding is
absent) doesn't re-persist the unflushed sid through a different field
path. The fallback is what makes the binding-only gate insufficient on
its own; both writes must drop together.
The gate only fires for claude-cli providers — other CLI providers don't
write to ~/.claude/projects, so probing them would always return false
and incorrectly strip valid binding metadata. isCliBindingFlushed now
takes the provider id and returns true unconditionally for non-claude-cli
sessions.
A bounded retry (0 / 50 / 150 ms) tolerates the brief gap between
claude-cli's stdio close and the OS making the JSONL line visible to
readers (cooperative fsync semantics on APFS, but not guaranteed under
stress).
The transcript-probe is exposed as an injectable dep
(setCliRunnerTestDeps / restoreCliRunnerTestDeps) mirroring the existing
pattern in src/agents/cli-runner/prepare.ts so isCliBindingFlushed is
testable without touching ~/.claude/projects.
AI-assisted: yes. Tooling: Claude Opus + claude-cli. Codex review caught
the fallback path and the missing provider gate before this hit upstream.
Real-Behavior-Proof: dist-side patch on M5 gateway; branch-build
follow-up pending — see PR body.
Move task run, delivery, and flow registry persistence onto the shared OpenClaw state SQLite database.
Summary:
- Store task runs, delivery state, and flow runs in state/openclaw.sqlite via the generated Kysely schema.
- Migrate shipped task sidecars into the shared state DB and archive old sidecars, including invalid-config/read-only CLI paths.
- Keep startup migration lightweight for read-only status/tasks paths while still detecting known legacy state markers and custom session stores.
Verification:
- .agents/skills/autoreview/scripts/autoreview --mode local: clean after final fix
- pnpm test src/tasks/task-registry.store.test.ts src/tasks/task-flow-registry.store.test.ts src/commands/doctor-state-migrations.test.ts -- --reporter=verbose
- pnpm test src/commands/doctor-state-migrations.test.ts src/cli/program/config-guard.test.ts src/cli/route.test.ts src/cli/command-path-policy.test.ts -- --reporter=verbose
- pnpm test src/cli/program/config-guard.test.ts src/cli/route.test.ts src/cli/command-startup-policy.test.ts src/cli/command-path-policy.test.ts src/cli/command-execution-startup.test.ts -- --reporter=verbose
- pnpm test src/cli/program/config-guard.test.ts src/cli/argv.test.ts src/cli/route.test.ts src/commands/doctor-config-preflight.state-migration.test.ts -- --reporter=verbose
- pnpm test src/tasks/task-flow-registry.store.test.ts -- --reporter=verbose
- pnpm test test/scripts/lint-suppressions.test.ts -- --reporter=verbose
- pnpm db:kysely:check
- pnpm lint:kysely
- git diff --check HEAD
- pnpm test:startup:memory
- PR CI green on 2f7d76f0d5
Preserve iMessage SMS reply routes for approval replies so a direct SMS /approve response can acknowledge and return results to the same SMS conversation.
Verification: gateway-only build, extension type checks, CI build-artifacts/check-prod-types/check-test-types/check-lint/check-additional-extension-package-boundary, and live prod iMessage SMS approval proof. checks-node-core-fast was waived by maintainer request after unrelated flaky failures in non-iMessage tests.
Adds Workboard orchestration statuses, dependency links, idempotent child creation, dispatch, and complete/block lifecycle operations backed by the plugin SQLite keyed store.
Persists tenant, skills, workspace, schedule, runtime, retry, dispatch, and handoff metadata in card records, with claim scoping and token redaction. Surfaces the new states and metadata in the Control UI, horizontal board layout, localized strings, and Workboard docs.
Verification:
- pnpm test extensions/workboard/src/store.test.ts extensions/workboard/src/tools.test.ts extensions/workboard/src/gateway.test.ts ui/src/ui/controllers/workboard.test.ts ui/src/styles/workboard.test.ts ui/src/ui/views/workboard.test.ts -- --reporter=verbose
- pnpm ui:i18n:check
- /Users/steipete/Projects/agent-scripts/skills/autoreview/scripts/autoreview --mode branch --base origin/main, followed by focused clean local autoreview loops for final fixes
- env -u OPENCLAW_TESTBOX pnpm check:changed
- git diff --check
Summary:
- The PR classifies selected embedded agent provider-denial error payloads through the shared failover matcher ... 1/current-ak auth matching, preserves guarded non-fallback cases, and covers fallback progression in tests.
- PR surface: Source +34, Tests +166. Total +200 across 5 files.
- Reproducibility: yes. Current main is source-reproducible: a non-GPT embedded result whose only signal is CE ... returns null from the classifier, and the fallback wrapper treats null classification as candidate success.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(agents): classify embedded provider business denials for fallback
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8304…
Validation:
- ClawSweeper review passed for head e266beac93.
- Required merge gates passed before the squash merge.
Prepared head SHA: e266beac93
Review: https://github.com/openclaw/openclaw/pull/84814#issuecomment-4505010446
Co-authored-by: Stellar鱼 <2182712990@qq.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Fixes#86820.
Preserve Codex OAuth-backed compaction by selecting and loading the Codex harness before resolving direct or queued compaction models, while keeping OpenAI-compatible custom base URLs on the OpenAI context config path. Also preserves persisted concrete harness pins so compaction does not hot-switch existing sessions just because an explicit Codex fallback exists.
Verification:
- node scripts/run-vitest.mjs src/agents/embedded-agent-runner/compact.hooks.test.ts src/agents/harness/selection.test.ts src/agents/harness/runtime-plugin.test.ts
- pnpm tsgo:prod
- pnpm check:test-types
- pnpm lint --threads=8
- git diff --check origin/main...HEAD
- git diff --check
- autoreview clean: no accepted/actionable findings reported; overall patch is correct (0.82)
- GitHub PR checks green on ac6f93de4a
Fix Codex app-server completion-stall recovery so replay-safe stdio completion-idle failures retry once, while progress/terminal turn-watch timeouts only surface timeout payloads.
Also preserve post-tool completion guards for scoped native response deltas and stabilize the oversized CONNECT timeout regression test picked up from latest main.
Co-authored-by: Kelaw - Keshav's Agent <keshavbotagent@gmail.com>
Adds the shared SQLite state database base, moves plugin keyed state into it with doctor migration coverage, and keeps generated Kysely guardrails aligned. Proof: focused SQLite/plugin-state tests, db:kysely:check, lint:kysely, architecture/dependency guards, autoreview, and PR CI all clean.
Move compaction planning work to a bounded worker-thread path so large transcript planning no longer monopolizes the agent event loop. Extract pure planning helpers, sanitize worker inputs before structured clone, package the worker entrypoint, and keep synchronous fallback only for worker-unavailable cases.
Fixes#86358.
Fixes#87438.
Bound unset heartbeat run timeouts so background heartbeat turns no longer inherit the built-in 48-hour interactive agent default. Timeout precedence is explicit heartbeat timeout, explicit global agent timeout, then heartbeat cadence capped at 600 seconds.
Verification:
- git diff --check
- Testbox tbx_01kstna69zvznn4fq7zrqr04a1: corepack pnpm test src/infra/heartbeat-runner.model-override.test.ts -- --reporter=verbose passed 13 tests
- Direct node --import tsx runtime probe verified 300s, 600s, 60s, and 45s timeout precedence cases
- Autoreview clean
Known CI state:
- PR CI run 26661465248 has failures matching latest main CI run 26661386468 at a7820b2f54; failures are outside this six-file heartbeat/docs diff.
Keep session lock cleanup from removing live OpenClaw-owned locks solely because they are old. Cleanup now reports age-only stale locks without deleting them, while still removing dead, orphaned, recycled, malformed-old, and non-OpenClaw-owned locks.
Update doctor docs and regression coverage for the cleanup/repair contract.
Refs #87779
Notte exposes a CDP-compatible WebSocket gateway at
wss://us-prod.notte.cc/sessions/connect?token=<NOTTE_API_KEY> that
auto-creates a session on connect — the same shape OpenClaw's existing
"Direct WebSocket CDP providers" section was generically framed for
(per #31085).
Real behaviour proof (against wss://us-prod.notte.cc/sessions/connect):
$ openclaw browser --browser-profile notte open https://example.com
opened: https://example.com/
tab: t4
id: 7FE04AC44931A6E1C799DE4ABF0DC807
A screenshot captured against the same session is a 1254x1111 PNG of
the rendered example.com page.
Playwright connectOverCDP flow against the same URL (today):
connectOverCDP 695ms
context.newCDPSession(page) 169ms
session.send('Target.getTargetInfo') → targetId 87ms
page.goto('https://example.com') 631ms
total 1.8s
AI-assisted (Claude Opus 4.7). codex review --base origin/main returned
clean. See PR description for the full pre-flight checklist.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Forward initial image/file attachments when spawning ACP subagents through the existing sessions_spawn attachment opt-in. Remove the PR-only acpEnabled config split so ACP uses the same attachment gate as other runtimes.
Also fix the PR branch CI fallout: type the browser element CLI request mock and use Vitest env stubs in the Azure speech test to satisfy the changed-path security scan.
Verification:
- GitHub CI passed on f6ca26b160.
- Autoreview clean.
- Crabbox AWS live OpenAI proof passed: cbx_a576d49493fe / run_081dcc6c6a1b.
Thanks @zhangguiping-xydt.
Fix `sessions.json` persistence after compaction transcript rotation.
When the agent runtime rotates from the pre-compaction session transcript to the post-compaction transcript, post-run consumers now receive the effective OpenClaw session id and session file. Backend CLI session ids remain backend metadata and no longer overwrite the top-level OpenClaw session identity.
Refs #88040.
Thanks @1052326311.
Verification:
- `node scripts/run-vitest.mjs src/agents/agent-command.compaction-rotation.test.ts src/agents/agent-command.live-model-switch.test.ts src/agents/command/session-store.test.ts`
- Autoreview clean
- GitHub CI green on PR head `c3d3c77ddf675bbba0b9ba6681b030a2f69a898c`
Fix claude-cli transcript resume so session-id rotation and transcript flush timing do not drop valid resume state.
- Capture the latest claude-cli session_id from JSONL output.
- Resolve Claude project transcript paths through the shared canonical project-dir resolver.
- Probe transcript content from the actual CLI process cwd.
- Thanks @benjamin1492!
Persist GitHub Copilot SDK session ids in the plugin-state SQLite store so separate OpenClaw process turns can resume the same Copilot-side session when the compatibility fingerprint still matches.
The fingerprint covers provider/model/cwd, resolved agent id, resolved Copilot home, and auth identity. Plugin-state lookup/register/delete failures are non-fatal, stale rows are invalidated, and reset delete failures use an in-process tombstone so reset does not accidentally reuse a durable binding.
Also routes the QQBot token POST through the plugin SDK SSRF guard with capture disabled for the secret-bearing request, preserving the current token lifetime validation from main.
Verification: focused Copilot and QQBot Vitest suites, raw channel fetch guard, autoreview clean, Blacksmith Testbox pnpm check:changed tbx_01kst9fwjmsfzwaxqatszcbf40, live local Copilot two-turn smoke with the same SDK session id persisted in SQLite.
Refs #88064
Summary:
- The PR changes plugin auto-enable materialization so an explicit empty `plugins.allow` stays empty while non-empty restrictive allowlists are still extended, and adds a regression test.
- PR surface: Source +3, Tests +17. Total +20 across 2 files.
- Reproducibility: yes. Source inspection of current main shows an empty array reaches `ensurePluginAllowlisted`, and the linked report gives a concrete `doctor --fix` config path that matches that code.
Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.
Validation:
- ClawSweeper review passed for head c06837f5dd.
- Required merge gates passed before the squash merge.
Prepared head SHA: c06837f5dd
Review: https://github.com/openclaw/openclaw/pull/87883#issuecomment-4570537738
Co-authored-by: 张贵萍0668001030 <zhang.guiping@xydigit.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Active Memory recall now runs on its own queue lane instead of sharing the parent prompt-build lane.\n\nValidation:\n- git diff --check\n- node scripts/run-vitest.mjs extensions/active-memory/index.test.ts -t "runs recall on a dedicated active-memory lane"\n- fresh local gateway smoke with Active Memory + Memory Core + loopback OpenAI-compatible model: HTTP 200, active-memory start/done, recall elapsedMs=209\n\nFixes #79026.\nRelated: #72015.
Summary:
- The PR updates Codex doctor route repair to preserve explicit non-default `agentRuntime` pins across agent model maps and provider policies, adds regression coverage, and tightens a live-gateway test helper type guard.
- PR surface: Source +240, Tests +574. Total +814 across 3 files.
- Reproducibility: yes. The source path is clear from current main's model-map merge behavior and the PR's bef ... beRepairCodexRoutes` with the reported config, though this read-only review did not execute the test suite.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(doctor): preserve explicit non-default agentRuntime pin during le…
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8414…
Validation:
- ClawSweeper review passed for head c142ec1ef8.
- Required merge gates passed before the squash merge.
Prepared head SHA: c142ec1ef8
Review: https://github.com/openclaw/openclaw/pull/84362#issuecomment-4493152445
Co-authored-by: David Huang <nxmxbbd@gmail.com>
Co-authored-by: Nex <nex@dbitstec.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Add a general typed tool-progress contract so long-running non-exec tools can emit public channel progress without overloading model-facing tool content.
`web_fetch` now uses the generic delayed progress helper: it shows `Fetching page content...` only when the fetch is still pending after five seconds, clears the timer on completion/abort, passes the abort signal into guarded fetch, and avoids provider fallback or cached success after cancellation. The subscriber path accepts only explicit `visibility: "channel"` and `privacy: "public"` progress metadata, while untyped tool partials and exec output keep their existing behavior.
Docs now explain typed progress, delayed producer examples, and the `web_fetch` timing behavior.
Proof: `pnpm test src/agents/tools/web-tools.fetch.test.ts src/agents/embedded-agent-subscribe.handlers.tools.test.ts -- --run`; `pnpm docs:check-mdx`; changed-file `pnpm exec oxlint ...`; `git diff --check`; autoreview clean.
Match text slash command names case-insensitively across the reset/new fallback paths and the shared registry/control detection contract while preserving command argument casing.
Add regression coverage for uppercase and mixed-case reset/new commands plus registered non-reset commands such as `/STATUS`, `/Model`, `/T`, and `/COMPACT`.
Co-authored-by: zhangtong26 <zhangtong26@xiaomi.com>
Co-authored-by: Lanzhi <lizhan3@xiaomi.com>
Remove stale Telegram-only wording from the reasoning stream acknowledgement and docs so channel-neutral behavior is reflected.
Fixes#68305.
Co-authored-by: Lanzhi <lizhan3@xiaomi.com>
Load `docker-compose.override.yml` when ClawDock builds its explicit Docker Compose file list, preserving standard Compose override behavior while keeping `docker-compose.extra.yml` as the final OpenClaw overlay.
Update Docker docs so manual Compose users include the same override order, and keep the regression test for the generated `_clawdock_compose` arguments.
Fixes#49909.
Thanks @spacegeologist.
Co-authored-by: zhengzuo0-ai <zheng.zuo0@gmail.com>
Preserve npm install selectors while recording resolved npm provenance for plugin and hook install/update records. Active `record.spec` stays the requested selector unless explicitly pinned, while resolved npm fields remain available for audit and diagnostics.
Adds focused coverage for hook-pack npm fallback provenance after the maintainer review found that path worth pinning down.
Co-authored-by: Phil <99397913+GitHoubi@users.noreply.github.com>
Forward Zalo quote-reply metadata from zca-js data.quote into the existing ReplyToId, ReplyToBody, and ReplyToIsQuote context keys so agents can correlate quoted replies with prior bot messages.
Adds parser and monitor regression coverage for quote extraction and context projection.
Fixes#86851.
Thanks @tanshanshan.
Remove unreachable optional chaining from four Discord message-handler-family runtime error calls.
This aligns the code with the required RuntimeEnv.error contract while leaving production behavior unchanged for valid runtimes. Maintainer-updated PR proof clarifies that shared queue reporter hooks still treat malformed runtime reporter failures as best-effort.
Clean up completed exec tool-call abort listeners so normal foreground completion and background-yield no longer retain the exec run/session context through AbortSignal listener state.
The listener cleanup now lives beside the exec listener registration and runs when the foreground process settles, rejects, or the tool returns a background running result. Existing abort/timeout/background behavior remains owned by the process supervisor and process registry.
Verification:
- gh pr checks 83022
- gh api repos/openclaw/openclaw/commits/fe86528ecb2043b6febef5c2eec53f9124be5543/check-runs
- git merge-tree --write-tree origin/main refs/remotes/pr/83022
- git diff --check origin/main...refs/remotes/pr/83022
- node AbortSignal add/remove listener probe
Thanks @c19354837.
Co-authored-by: Ninty <c19354837@hotmail.com>
Fixes#66479.
Workspace skills whose SKILL.md starts with a UTF-8 BOM now keep their shared markdown frontmatter metadata, so they remain discoverable through skills list. The fix strips one leading BOM at the parser boundary and adds parser plus workspace discovery regression coverage.
Thanks @jbetala7 for the fix.
Co-authored-by: Jayesh Betala <jayesh.betala7@gmail.com>
Route Microsoft Teams attachment downloads through the shared SSRF guarded fetch path so DNS validation is pinned into the dispatcher used for the actual request.
Keep Teams auth fallback and allowlisted HTTPS Authorization redirect behavior while failing closed for custom fetch hooks that cannot accept dispatcher injection.
Verification:
- CI=1 OPENCLAW_VITEST_MAX_WORKERS=1 timeout 300 node scripts/run-vitest.mjs run extensions/msteams/src/attachments/shared.test.ts extensions/msteams/src/attachments/bot-framework.test.ts src/infra/net/fetch-guard.ssrf.test.ts
- gh pr checks 87567 --repo openclaw/openclaw --watch=false
PR: #87567
Fix cron local-model preflight fallback handling so scheduled runs try configured fallback candidates before skipping when the local primary is unavailable.
Verification:
- GitHub CI on PR head fe884dab90: passing required CI checks.
- Local focused cron/model fallback tests passed earlier for the touched surface.
- Local merge-wrapper build and check passed on the prepared candidate.
- Local full pnpm test reported unrelated failures outside this PR's touched files; touched files are limited to cron docs, src/agents/model-fallback.ts, and src/cron/isolated-agent/*.
Co-authored-by: chen-zhang-cs-code <chenzhangcode@163.com>
Co-authored-by: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
Remove the chatType === 'direct' guard from
shouldAllowQuietChannelOwnedProgressCallbacks so that channel-owned native
progress callbacks (onToolStart, onItemEvent, onPlanUpdate,
onApprovalEvent, onCommandOutput, onPatchSummary, onCompactionStart/End)
are forwarded in group and group-channel sessions when verbose is off.
Previously the guard required chatType === 'direct', which meant that
/verbose off would suppress all progress callbacks in group sessions
while direct sessions continued to relay them. Message-level tool
summary suppression is handled separately; native channel relay hooks
should not be gated on chat type.
Closes#87612
Cache single-row gateway session child indexes without hiding live subagent registry changes.
Summary:
- Reuses store-derived child-session candidates for repeated single-row session loads.
- Keeps runtime subagent registry reads live per row so moved child sessions do not stay attached to stale parents.
- Versions the session-store cache and includes that version in the single-row cache key so same-object store rewrites cannot reuse stale child candidates.
- Adds focused regression coverage for cache reuse, live registry refresh, and same-object session-store writes.
Verification:
- git diff --check
- pnpm tsgo:prod
- pnpm test src/gateway/session-utils.single-row-cache.test.ts src/gateway/session-utils.subagent.test.ts -- --reporter=verbose
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- GitHub CI run 26620265206: passing
- Azure Crabbox cbx_a58389e50f49: single-row-loads 13.622240 ms before vs 1.869456 ms after, 7.29x speedup, 86.3% reduction
* feat: add Claude Opus 4.8 support
* fix: omit Vertex Opus sampling overrides
* fix: preserve Opus adaptive thinking levels
* fix: clamp Anthropic max effort support
* fix: use sha256 for QA mock call ids
* fix: type Anthropic transport test model metadata
* test: update PDF model default for Opus 4.8
Summary:
- This PR adds an internal gateway active-run projection flag, clears it during terminal lifecycle handling be ... ons.list on that flag, adds gateway regression coverage, and tightens memory-wiki confidence normalization.
- PR surface: Source +29, Tests +131. Total +160 across 7 files.
- Reproducibility: yes. Source inspection shows current main can broadcast terminal sessions.changed before ch ... the abort-controller entry, and the before/after recording supports the visible stuck In progress symptom.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(gateway): preserve chat retry guard after terminal state
- PR branch already contained follow-up commit before automerge: fix(gateway): clear completed session active runs
Validation:
- ClawSweeper review passed for head 9b132bdc2b.
- Required merge gates passed before the squash merge.
Prepared head SHA: 9b132bdc2b
Review: https://github.com/openclaw/openclaw/pull/87810#issuecomment-4569094800
Co-authored-by: scotthuang <scotthuang@tencent.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Adds opt-in Discord progress-draft commentary for assistant preambles while keeping commentary hidden by default and final delivery unchanged.
Keeps commentary config Discord-specific, strips directive tags/NO_REPLY, and clears stale commentary rows without stopping the active draft stream.
Thanks @bryanpearson.
Co-authored-by: bryanpearson <bryanmpearson@gmail.com>
Preserve OpenClaw-owned embedded system prompts after active tool selection in both normal embedded attempts and compaction. Adds an exact base prompt path on AgentSession that keeps active tool prompt metadata current for extension hooks.
Fixes#87807.
Verification:
- mise exec node@24.16.0 -- node scripts/run-vitest.mjs src/agents/sessions/sdk.test.ts src/agents/embedded-agent-runner/system-prompt.test.ts src/agents/embedded-agent-runner/run/attempt.spawn-workspace.context-engine.test.ts src/agents/embedded-agent-runner/compact.hooks.test.ts --reporter=dot
- mise exec node@24.16.0 -- pnpm tsgo:core
- git diff --check
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
Thanks @shakkernerd.
Handle exec-backed Gateway SecretRefs in doctor, lint, and health probing without executing providers by default.
- Add `openclaw doctor --allow-exec` for explicit SecretRef execution during lint/doctor checks.
- Skip only the active exec-backed gateway probe path and avoid local service diagnostics for remote-only skipped health.
- Keep env-winning and dormant fallback credentials probeable, stabilize related tests, and remove a stale live-shard fixture left by the moving base.
Verification:
- `node scripts/run-vitest.mjs src/commands/doctor-gateway-auth-token.test.ts src/commands/doctor.warns-state-directory-is-missing.e2e.test.ts src/gateway/credentials.test.ts src/gateway/probe-auth.test.ts src/commands/doctor-gateway-daemon-flow.test.ts test/scripts/test-live-shard.test.ts --reporter=verbose`
- `mise x node@24.13.0 -- pnpm prompt:snapshots:check`
- `pnpm tsgo:prod`
- `pnpm build`
- `.agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main`
- Crabbox AWS live config proof: `run_f44a4d9dae4e`
- GitHub CI: green on final head `88d24abdbf9529a59d75d1d5e04eac74bbbbc267` after rerunning a stale in-progress Security High workflow.
Co-authored-by: Merlin <258679497+funmerlin@users.noreply.github.com>
Fixes#87132.
Default Usage now requests all configured agents with `agentScope: "all"`, while selecting a specific agent sends `agentId` consistently to both session usage and cost usage calls. The gateway now supports explicit all-agent session usage, aggregates all-agent cost summaries across configured agents, and keeps scoped cache entries separate. Legacy gateway fallbacks remain for older `agentId` / `agentScope` support, with protocol docs/schema and Swift generated models updated.
Verification:
- `node scripts/run-vitest.mjs ui/src/ui/controllers/usage.node.test.ts ui/src/ui/app-render-usage-tab.test.ts ui/src/ui/views/usage.test.ts --reporter=dot`
- `node scripts/run-vitest.mjs run --config test/vitest/vitest.gateway-methods.config.ts src/gateway/server-methods/usage.test.ts src/gateway/server-methods/usage.cost-usage-cache.test.ts src/gateway/server-methods/usage.sessions-usage.test.ts --reporter=dot`
- `pnpm check:test-types`
- `pnpm protocol:check`
- targeted `node scripts/run-oxlint.mjs ...`
- `git diff --check`
- autoreview clean after Swift compatibility fix
- PR CI green at head `d67156a3c552c4f9c8b6edf8516b6242bf5cdd26`
Co-authored-by: Alix-007 <267018309+Alix-007@users.noreply.github.com>
Keep Codex reasoning updates as accumulated snapshots and mark the stream payload so channel consumers can distinguish snapshots from deltas.
This prevents Discord and Teams progress previews from duplicating accumulated reasoning text while preserving delta-style reasoning for legacy producers.
Refs #86708
Thanks @SebTardif.
Co-authored-by: OpenAI Codex <codex@openai.com>
Scope jiti filesystem transform caches for OpenClaw plugin loaders by package version and package.json install metadata so stale transforms cannot survive upgrades or package reinstalls.
Covers the central plugin module loader and the plugin SDK root alias CJS loader, while preserving jiti filesystem-cache env opt-outs and the TMPDIR cwd guard.
Verification: CI run 26601117143 passed; Real behavior proof run 26601445285 passed; CodeQL selected checks passed in run 26601117126; CodeQL Critical Quality plugin-boundary and plugin-sdk-package-contract passed in run 26601117074; OpenGrep PR diff passed in run 26601117137.
Refs: https://github.com/openclaw/openclaw/pull/87745
Thanks @fuller-stack-dev.
Route Codex app-server report-mode PreToolUse plugin approval requirements through the matching app-server approval request instead of failing closed. Shares duplicate in-flight approvals, preserves block/rewrite fail-closed behavior, and keeps generic plugin allow-always scoped to one Codex request. Supersedes #86978; thanks @clawSean for the original docs clarification.
Compact promoted short-term memory snippets before writing them into MEMORY.md, while keeping the full rehydrated snippet in recall state for ranking/provenance. Adds the deep-dreaming config surface and docs, with the default promoted snippet cap set to 160 estimated tokens.
Verification:
- git diff --check
- fnm exec --using v24.13.0 node scripts/run-vitest.mjs run extensions/memory-core/src/short-term-promotion.test.ts extensions/memory-core/src/dreaming.test.ts src/memory-host-sdk/dreaming.test.ts
- GitHub CI run 26605272497
- CodeQL security run 26605272404
Co-authored-by: AMARA <amara@eyeinthesky.pl>
Server-side cron job list filtering now applies schedule-kind and last-run-status filters before pagination, and the UI only sends table filters for the cron table view.
Fixes#9455.
Co-authored-by: Alix-007 <267018309+Alix-007@users.noreply.github.com>
* fix(msteams): rebase SDK migration onto current main
Reapply the msteams SDK migration (originally on feat/msteams-sdk-migration)
on top of upstream/main, resolving conflicts with parallel msteams work that
landed upstream during our session.
What got applied vs decisions made:
CLEANLY APPLIED (3-way patch):
- monitor.ts, monitor-handler.ts, polls.ts, reply-stream-controller.ts/.test.ts,
reply-dispatcher.ts, attachments/download.ts, monitor.lifecycle.test.ts,
monitor-handler/message-handler.ts, monitor-handler.types.ts, etc.
- streaming-message.ts + .test.ts deletions
WHOLESALE TAKE FROM ORIGINAL BRANCH (partial 3-way left broken cross-refs):
- sdk.ts, sdk.test.ts, messenger.ts, feedback-reflection.ts,
send-context.ts, send.test.ts
KEPT UPSTREAM (deferred for separate cleanup):
- extensions/msteams/package.json (still has jsonwebtoken/jwks-rsa per
Peter's b3bc60ae25 incremental approach)
- src/plugins/contracts/package-manifest.contract.test.ts (consistent with
package.json)
- pnpm-lock.yaml (avoids lockfile churn; pnpm install --frozen-lockfile clean)
ADAPTED:
- Dockerfile matrix-sdk-crypto check now wraps upstream's new retry-loop in
the if-matrix-bundled gate
KNOWN TEST FAILURES (need eyes — see PR comment):
- attachments.test.ts: 1 fail (pre-existing — warn meta arg shape changed in
our migration but test wasn't updated)
- reply-dispatcher.test.ts: 6 fails (pre-existing — tests mock old
TeamsHttpStream, not updated for our ctx.stream rewrite)
- send.test.ts: 4 fails (NEW from merge — upstream's send.ts changed media
loading; our mocks need updating or take upstream's send.test.ts wholesale)
UPSTREAM COMMITS POTENTIALLY MISSED (in wholesale-take files):
- 08c4af0ddf fix(msteams): accept conversation id allowlists
- e1840b8581 fix(msteams): bind global audience tokens to app id
- Channels turn-kernel refactor (ffe67e9cdc / 1ead1b2d18 / 9a9cd0c0ab) —
may be partially preserved in cleanly-patched files
Static checks pass: pnpm check:changed is green (typecheck, lint, contract
tests, import cycles, etc.). Manual testing required before merge.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(msteams): preserve thread routing for channel and group-chat replies
- monitor.ts: adaptSdkContext now uses ctx.reply() for channel and groupChat
conversations (so the SDK threads outbound activities to the inbound's
replyToId/serviceUrl) and ctx.send() only for personal DMs (where
reply()'s blockquote-prepend is ugly).
- messenger.ts: sendProactively passes resolvedThreadId on the non-thread
fallback path so channel @mentions that fall through outbound.ts -> send.ts
still land in the original thread instead of top-level.
Live-validated: channel @mention -> bot replies in thread, threaded reply
-> bot replies in same thread, no top-level leakage.
* fix(msteams): tag outbound SDK calls with OpenClaw User-Agent
- user-agent.ts: add buildOpenClawUserAgentFragment() that returns just
'OpenClaw/<version>'. The SDK's Client.clone merges this with its own
'teams.ts[apps]/<sdk-version>' identifier — passing the full buildUserAgent()
here would double-print the SDK token.
- sdk.ts: pass the fragment via AppOptions.client.headers['User-Agent'] so
the Teams backend can identify OpenClaw traffic for usage telemetry.
Final UA looks like 'OpenClaw/<openclaw-version> teams.ts[apps]/<sdk-version>'.
* fix(msteams): handle StreamCancelledError when user presses Stop mid-stream
The new SDK throws StreamCancelledError synchronously from stream.emit/update
when the user pressed Stop in Teams: Teams replies 403 to the next chunk
update, the SDK flips _canceled, and any subsequent emit() throws. The old
custom TeamsHttpStream either swallowed cancel or didn't expose this exception
type, so the migration inherited an SDK behavior the original code didn't have
to handle.
Symptom on 2026-05-05: pressing Stop during a streaming reply caused an
unhandled promise rejection that crashed the Node 24 process. Docker restarted
the gateway about two minutes after each Stop click. Two related bugs surfaced
once the crash was caught: the would-be block fallback re-delivered the full
text as a second message (duplicate after Stop), and the typing-keepalive kept
pulsing in Teams for the rest of the agent run because nothing told it to
stop.
reply-stream-controller.ts:
- Wrap stream.update / stream.emit / stream.close in try/catch that swallows
StreamCancelledError (matched by .name to dodge tsgo's SDK re-export
resolution quirk). Latch a wasCanceled flag so subsequent calls
short-circuit even if stream.canceled is stale.
- preparePayload() returns undefined when the stream was canceled — the
streamed prefix is already visible to the user, so dropping the payload
prevents a duplicate block message from overriding the cancel intent.
reply-dispatcher.ts:
- Typing-keepalive gate now also checks streamController.wasCanceled() so
typing pulses stop firing once Stop is observed. Otherwise the bot keeps
pulsing for the rest of the (uncancellable) agent run.
reply-stream-controller.test.ts:
- 6 new regression tests cover: cancel-during-emit (the crash scenario),
cancel-during-update, cancel-during-finalize, non-cancel error propagation,
post-cancel inactivity, and dropped-payload-on-cancel.
Live-validated: long streaming reply + Stop mid-stream -> stream freezes,
no duplicate message, no zombie typing, container stays healthy.
* fix(msteams): allow Bearer-token retry on Skype CDN attachment downloads
Teams puts inline DM images and clipboard-pasted images on
*.asm.skype.com URLs (e.g. us-api.asm.skype.com/v1/objects/<id>/views/imgo).
The download path in attachments/download.ts already does a plain GET first
and falls back to a Bearer-token retry on 401/403 — but the retry was gated
on the URL being in DEFAULT_MEDIA_AUTH_HOST_ALLOWLIST. asm.skype.com hosts
were in DEFAULT_MEDIA_HOST_ALLOWLIST (download permitted) but not in the
auth-host list, so a 401 plain-GET response skipped the retry and surfaced
as a missing image to the agent.
Add asm.skype.com and ams.skype.com to the auth allowlist so openclaw
attempts the Bearer-token retry consistently, matching how it treats the
other CDN/Bot-Framework hosts already in the list.
Note: this does not unblock all clipboard-pasted DM images — for at least
some tenants asm.skype.com rejects the Bot Framework token (returns 401
even with auth). Routing those URLs through <serviceUrl>/v3/attachments/...
the way #62219 already handles HTML-wrapped attachments is a separate
follow-up. The +button 'Upload from this device' path works today because
Teams generates an attachment with an HTML wrapper that triggers the
existing BF v3 attachments fallback in monitor-handler/inbound-media.ts.
* fix(msteams): align docker-compose msteams port default with plugin default
The plugin defaults webhook.port to 3978 (the Bot Framework standard used in
Microsoft samples) and listens on whatever the operator sets there. The
docker-compose.yml port mapping was exposing ${OPENCLAW_MSTEAMS_PORT:-3000}:3000
which only works for operators who explicitly set webhook.port to 3000.
Default-config users would have the plugin listening on 3978 inside the
container while compose forwarded 3000, causing connection refused.
Realign to ${OPENCLAW_MSTEAMS_PORT:-3978}:3978 so a default-config docker
compose up Just Works with Teams. Operators wanting a custom port override
both webhook.port in openclaw.json and OPENCLAW_MSTEAMS_PORT env var.
* fix(msteams): post-rebase reconciliation with main
Three follow-ups after rebasing the SDK migration onto current main:
- reply-dispatcher.ts: rename createChannelReplyPipeline to its post-rebase
identifier createChannelMessageReplyPipeline (the plugin-sdk barrel renamed
it during the 1454-commit rebase window).
- reply-dispatcher.ts: tighten the typing-keepalive onStartError signature to
(err: unknown) to satisfy upstream's stricter type checks.
- messenger.ts: drop the unconditional thread suffix on the bottom proactive
fallback. The previous behavior threaded all top-level proactive sends when
the stored ref had a threadId, which contradicts replyStyle='top-level'
semantics (and breaks the new upstream test). Threading on the proactive
path is preserved where it matters — the onRevoked branch within
replyStyle==='thread' still passes resolvedThreadId, which is the original
#55198 fix path.
- attachments.test.ts: update the warn-call assertion to match the migration's
inline message format (host=... error=...) — the structured meta object was
being dropped by the logger formatter pre-migration.
* feat(msteams): port streaming preview/progress features to ctx.stream
While the SDK migration was open, upstream landed preview/progress/draft
streaming features built on the OLD custom TeamsHttpStream class (which the
migration deletes). This commit ports the user-visible parts of those
features onto the new ctx.stream substrate so the migration doesn't lose
ground:
- pickInformativeStatusText: reads custom labels from
msteams.streaming.progressDraft config via resolveChannelProgressDraftLabel.
Falls back to the plugin-sdk default rotation. Pre-rebase used a hardcoded
4-string array.
- streamMode resolution: "partial" (default, per-token streaming),
"progress" (no tokens; preview card carries informative label that updates
as tools run), or "block" (no native streaming). Mode is read from
cfg.channels.msteams.streaming.preview.
- progress-draft gate: createChannelProgressDraftGate gates informative
updates so the rotating label only starts firing once meaningful work has
begun (avoids flicker before the first tool call).
- noteProgressWork() / pushProgressLine(): public methods on the controller
for callers (typing keepalive ticks, tool-event callbacks) to signal work.
pushProgressLine appends tool names as bullets above the rotating label
when streaming.previewToolProgress is enabled. Wiring these into actual
tool events is a separate follow-up.
- preparePayload progress-mode path: when stream is active but no tokens
streamed (progress mode) and a final text payload arrives, emit the text
into the stream so the preview card transitions in place to the final
reply on close().
reply-dispatcher: pass log + msteamsConfig + a stable progressSeed
(${accountId}:${conversation.id}) to createTeamsReplyStreamController so the
informative-label rotation is consistent across reconnects.
What's NOT ported and why:
- Live-edit-via-replaceInformativeWithFinal: the SDK's HttpStream natively
accumulates emitted text + entities + channelData and flushes ONE final
activity at close() using the same activity id as the preview. So the
separate "replace informative with final" call from upstream is
unnecessary — we get live-finalization for free via the SDK's design.
- pushProgressLine triggers from tool events: needs reply-pipeline-side
callbacks the new SDK migration didn't surface yet. Follow-up.
Tests: existing 22 reply-stream-controller tests still pass (the new
behaviors are additive).
* feat(msteams): wire pipeline tool events to streaming progress + fix test debt
Two follow-ups from yesterday's stopping point:
1. Wire pipeline events into the stream controller's progress-draft surface.
reply-dispatcher's replyOptions now exposes onReasoningStream, onToolStart,
onItemEvent, onPlanUpdate, onApprovalEvent, onCommandOutput callbacks that
format each event via the channel-streaming helpers and route through
streamController.pushProgressLine(). Mirrors the discord adapter's wiring.
Also:
- resolveChannelStreamingPreviewToolProgress + ...SuppressDefaultTool... so
the dispatcher exposes suppressDefaultToolProgressMessages on its
replyOptions when progress mode is on.
- Switch disableBlockStreaming resolution to the channel-streaming helpers
(resolveChannelPreviewStreamMode + resolveChannelStreamingBlockEnabled)
so streaming.mode='block' and streaming.block.enabled=true are honored
alongside the legacy blockStreaming boolean.
2. Fix the test debt that the rebase exposed:
- reply-dispatcher.test.ts: drop the streamInstances + TeamsHttpStream
mock pattern (file deleted by migration); replace with a streamMock
provided via context.stream that mirrors the SDK's IStreamer shape
(update/emit/close/canceled). Update assertions on sendInformativeUpdate
-> stream.update, stream.update -> stream.emit. Drop the
resumes-typing-between-segments test (no equivalent in the new
ctx.stream model — the SDK's HttpStream doesn't have a 'between
segments' notion; close ends the stream).
- send.test.ts: fix two stale mock targets — loadOutboundMediaFromUrl
comes from openclaw/plugin-sdk/outbound-media (not /msteams), and
resolveMarkdownTableMode comes from openclaw/plugin-sdk/markdown-table-runtime
(not /config-runtime). The previous mock paths were no-ops post-migration.
All 854 msteams tests now pass (was 17 failing in 4 files yesterday).
* fix(msteams): SDK streaming delta + use app.reply for proactive thread sends
Two narrow regressions exposed by the @microsoft/teams.apps migration:
- The SDK's HttpStream.emit appends each chunk to its internal buffer
(`this.text += activity.text`), but the channel reply pipeline emits
cumulative text on each chunk. Forwarding cumulative text into an
appending sink produced "chunk1 + chunk1chunk2 + chunk1chunk2chunk3..."
duplication for streamed (DM) replies. Track the emitted prefix length
in the stream controller and only forward the new tail.
- Replace the manual `${convId};messageid=${msgId}` URL construction in
the proactive thread fallback with `app.reply()`, which builds the
threaded conversation id via the SDK's own toThreadedConversationId
helper. Mechanically equivalent today; removes coupling to Teams' URL
format and tracks any future SDK changes.
Also adds the `reply` method to the structural MSTeamsApp type so the
refactor typechecks without casts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(msteams): bump @microsoft/teams.api and teams.apps to 2.0.10
2.0.10 adds support for the AAD v1 token issuer that the Bot Framework
JWT validator needs. The minor version bump pulls teams.cards / common /
graph along to 2.0.10 too.
Add `@microsoft/teams.*` to `minimumReleaseAgeExclude` in
pnpm-workspace.yaml because 2.0.10 was published <48h ago and the default
`minimumReleaseAge: 2880` (~2 days) would otherwise reject it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* revert(msteams): remove asm.skype.com auth-host allowlist additions
These hosts were added in dfc169d31d for inline DM image auth-retry, but
the commit's own footnote acknowledges it doesn't actually unblock
clipboard-pasted images (asm.skype.com rejects Bot Framework tokens in
at least some tenants). The change is unrelated to the SDK migration and
the user-visible bug it claimed to fix isn't fixed; lifting it out keeps
this PR focused on the migration. Will land as a separate PR if the
auth-allowlist consistency improvement is wanted on its own.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(msteams): typed ExpressAdapter helper, drop unknown-cast pyramid
The monitor's SDK bootstrap had an awkward chain:
httpServerAdapter: new (
(await import("@microsoft/teams.apps")) as unknown as {
ExpressAdapter: new (app: unknown) => unknown;
}
).ExpressAdapter(expressApp) as never,
Three casts (`unknown`, structural shape literal, `never`) were a
defensive workaround from when the SDK's hashed d.ts files tripped up
tsgo. With the SDK's exports now resolving cleanly, the same import can
be done with full types.
- Extend the lazy `loadSdkModules()` cache to include `ExpressAdapter`
alongside `App` so the dynamic import is shared.
- Add `createMSTeamsExpressAdapter(serverOrApp)` helper in `sdk.ts` that
encapsulates the lazy import and returns a properly-typed adapter
instance.
- Replace `httpServerAdapter`'s structural shape on `CreateMSTeamsAppOptions`
with the SDK's own `IHttpServerAdapter` interface (re-exported from
`@microsoft/teams.apps`).
The call site in `monitor.ts` becomes a single typed call with no `any`,
no `unknown`, no `as never`. The lazy-load behavior is preserved: nothing
imports `@microsoft/teams.apps` at module load time.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(msteams): unbreak tsgo:extensions on the ExpressAdapter helper
CI's check-prod-types failed because the previous commit's typed helper
used `typeof import("@microsoft/teams.apps").ExpressAdapter`, which
tsc/tsgo's NodeNext resolution can't follow through the SDK's chained
`export *` barrel:
@microsoft/teams.apps/dist/index.d.ts:
export * from "./http"; // folder with index.d.ts
export * from "./app"; // single .d.ts file
The folder re-export drops `ExpressAdapter` and `IHttpServerAdapter` from
the namespace shape under `tsconfig.extensions.json` (passes under the
per-extension `tsconfig.json` because of inherited `paths`). Same root
cause as why we already model `MSTeamsApp` structurally (line 47 comment).
Switch the ExpressAdapter side to the same structural-shape pattern:
- Define `MSTeamsHttpServerAdapter` and `MSTeamsExpressAdapterCtor` locally.
- Cast `m.ExpressAdapter` once inside `loadSdkModules` (the runtime export
is fine; only the type surface is hidden).
- `httpServerAdapter` on `CreateMSTeamsAppOptions` and the return type of
`createMSTeamsExpressAdapter` use the local structural type.
Net result: the call site in `monitor.ts` stays the cast-free single line
the previous commit landed; the one remaining cast is confined to the
SDK-loading helper with an explanatory comment.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(msteams): drop unused jsonwebtoken/jwks-rsa deps
The SDK migration removed all `import "jsonwebtoken"` / `import "jwks-rsa"`
from source code (the SDK does JWT validation internally now), but the
package.json entries and the matching `package-manifest.contract.test.ts`
expectation were left orphaned. Drop both:
- `extensions/msteams/package.json`: remove `jsonwebtoken` (^9), `jwks-rsa`
(^4) from `dependencies` and `@types/jsonwebtoken` from `devDependencies`.
- `src/plugins/contracts/package-manifest.contract.test.ts`: remove the
two entries from msteams's `pluginLocalRuntimeDeps` expectation.
- `monitor.lifecycle.test.ts`: extend the `./sdk.js` mock with the
`createMSTeamsExpressAdapter` export added in the typed-helper cleanup,
so the lifecycle suite still mounts after the deps drop.
Lockfile regenerates accordingly. All msteams tests (865) pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(msteams): drop unused @microsoft/teams.api direct dep
CI's deadcode:dependencies (knip) flagged @microsoft/teams.api as
unused in extensions/msteams. The plugin source uses structural type
aliases (MSTeamsActivityParams, MSTeamsActivityLike, etc.) to dodge
tsgo resolution bugs with teams.api's hashed d.ts files, so it never
imports teams.api directly. The package is brought in transitively
via @microsoft/teams.apps; the only other reference is
probe.test.ts's vi.mock("@microsoft/teams.api"), which works on the
import-path string and doesn't require a direct dep declaration.
Lockfile regenerates accordingly. tsgo:extensions, knip, and all
865 msteams tests pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(msteams): clear three CI gate failures (lint, contract, deprecated config API)
Three CI checks flagged on the latest run; all three are msteams-local
and unrelated to one another:
- **check-lint** / **check-additional-extension-bundled**:
`oxlint` flagged a redundant `as string[]` assertion in
`reply-dispatcher.ts:431`. The preceding `every((s: unknown) => typeof
s === "string")` already narrows the array type, so the cast does
nothing. Drop it.
- **checks-fast-contracts-plugins-c**: the
`package-manifest.contract.test.ts` `pluginLocalRuntimeDeps` for
msteams still expected `@microsoft/teams.api`, but the deadcode
cleanup commit (8f4050f51a) dropped it from
`extensions/msteams/package.json`. Remove it from the contract test
too — `teams.api` is only present transitively via `teams.apps`,
which is the reason knip flagged it.
- **check-additional-runtime-topology-architecture**: the deprecated
internal config API guard caught `messenger.ts:223` calling
`getMSTeamsRuntime().config.loadConfig()`. Switch to
`config.current()` to match the pattern used by phone-control,
synology-chat, and matrix.
Pre-existing failures on this run that are NOT msteams-related and not
caused by this PR: `check-test-types` (errors in
`src/agents/openai-transport-stream.test.ts` and
`pi-embedded-runner/openai-stream-wrappers.test.ts`) and `macos-swift`
(`hoistAwait` in `MacNodeRuntime.swift`). Leaving those for upstream.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(msteams): cast config.current() return to OpenClawConfig
The previous commit switched `messenger.ts:223` from the deprecated
`config.loadConfig()` to `config.current()` to satisfy the architecture
guard, but `config.current()` returns a deeply-readonly type that's not
assignable to the `Partial<OpenClawConfig>` parameter
`resolveMarkdownTableMode` expects (a mutable type from the SDK
contract). Phone-control, synology-chat, and matrix all cast at this
seam — adopt the same pattern.
Verified locally: tsgo:core, tsgo:extensions, check:architecture, and
test:extensions:package-boundary:compile all pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(msteams): address PR review — pre-auth body limit, allowlist log level, /api/messages forwarder, narrow release-age exclude
Four narrow fixes from the PR review (BradGroux + clawsweeper bot +
galiniliev's plan), each its own concern:
- **pre-auth-body-limit** (monitor.ts) — install
`express.json({ limit: DEFAULT_WEBHOOK_MAX_BODY_BYTES })` before the
bearer-presence gate and SDK route. Express memoizes the parsed body
on the request, so the SDK's later `json()` becomes a no-op and our
limit applies before any handler parses bodies. Closes the gap where
a `Bearer garbage`-shaped attacker could force unbounded JSON parsing
before token validation.
- **allowlist-error-logging** (monitor.ts) — restore main's `runtime.error`
level for the `msteams resolve failed` catch (was downgraded to
`runtime.log` mid-merge). Graph allowlist resolution failures are
security-relevant; they need to surface to operators.
- **legacy-messages-route** (monitor.ts) — when `webhook.path` is set
to a custom value, also accept POSTs on the legacy `/api/messages`
path with a one-time deprecation warning, then re-enter the Express
middleware chain on the configured path. Keeps existing Azure Bot
registrations working through the transition. Cast-free
(`expressApp(req, res, next)` works because `Application extends
IRouter extends RequestHandler`).
- **release-age-scope** (pnpm-workspace.yaml) — narrow
`@microsoft/teams.*` glob to the single direct dep
`@microsoft/teams.apps`. Future scoped packages no longer get a
freshness-guard pass.
Tests + checks: msteams suite (867), tsgo:core, tsgo:extensions,
tsgo:test, lint:extensions, check:architecture, knip --dependencies,
package-manifest contract, all green.
Still pending from the review (separate commits):
- auth-coverage-tests (Brad #1 + comment) — tests proving the SDK accepts
`aud=<bot app id>` and rejects `aud=api.botframework.com`.
- invoke-response-handling (Brad #2, codex P2) — file-consent invoke ack
must return through the SDK invoke handler, not `ctx.sendActivity`.
- stream-failure-fallback (codex P2, galin F5) — `streamFailed` latch so
partial streams fall back to block delivery on non-cancel errors.
- serviceurl-routing (Brad #4, codex P2) — proposed rebuttal pending
empirical confirmation that `smba.trafficmanager.net/teams` routes to
non-default-region conversations.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(msteams): lock SDK auth contract — aud + v1/v2 issuer coverage
Adds extensions/msteams/src/auth-coverage.test.ts driving ServiceTokenValidator
and createEntraTokenValidator directly with jose-minted RS256 tokens against an
in-memory JWKS (via JwksClient.prototype patch). Locks in the three contract
cases @BradGroux flagged on #76262: aud=<bot app id> accepted, aud=api.botframework.com
rejected even when appid/azp match, and v1/v2 issuers accepted for allowed tenant
(disallowed tenant rejected).
Drops a stale ambient module declaration in src/types/microsoft-teams-sdk.d.ts
that was shadowing the SDK's real jwt-validator types with a long-renamed
createServiceTokenValidator surface.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(msteams): route file-consent invokes through typed app.on, drop broken invokeResponse send
Brad #2 / codex #4 on PR #76262 — `ctx.sendActivity({ type: "invokeResponse", ... })`
no longer reaches Teams as an HTTP InvokeResponse on the new SDK; it becomes
an outbound Bot Framework activity instead. Move file-consent accept/decline
to typed `app.on("file.consent.accept|decline", ...)` handlers. The SDK's
typed-route layer wraps a void return into `{ status: 200 }`
(`app.process.js:130`), so the manual ack disappears.
While in here, type `MSTeamsApp.on` properly. Borrowing the SDK's `App.on`
directly fails because that function carries a `this: App<TPlugin>`
constraint our structural alias can't satisfy, so we model an equivalent
generic over `IRoutes` with route-specific overloads (`card.action`,
`file.consent.*`, `activity`). The overloads work around a tsgo bug — the
`@microsoft/teams.api` `Activity` discriminated union collapses to `any`,
turning `ActivityRoutes` into a `[string]: RouteHandler<X, void>` index
signature that swallows every typed `Out` not already void-compatible
(card.action returns `AdaptiveCardActionResponse`; the others happen to
include `void`). Real tsc resolves cleanly. Linked upstream:
https://github.com/microsoft/typescript-go/issues/1057.
Other cleanups:
- Cast-free call sites for `adaptSdkContext` (now returns
`MSTeamsTurnContext` instead of `unknown`).
- card.action error responses include `innerHttpError` per the SDK's
`HttpError` shape requirement.
- Activity catch-all also skips `fileConsent/invoke` now that it's
typed-routed (parallel to the existing `adaptiveCard/action` skip).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(msteams): route SSO sign-in invokes through typed app.on, drop broken invokeResponse send
Brad #2 / codex #4 on PR #76262, SSO half. Continue the typed-route migration:
`signin/tokenExchange` and `signin/verifyState` now register via
`app.on("signin.token-exchange" | "signin.verify-state", ...)`. Per the
SDK's router, registering a user route with the same name as a system
route removes the system default — so the SDK's built-in handlers (which
would call `api.users.token.exchange` themselves and emit a `signin` event
nobody currently subscribes to) are silenced, and only ours runs. The SDK
wraps a void return into the HTTP 200 InvokeResponse, so the legacy
`ctx.sendActivity({ type: "invokeResponse", ... })` ack — broken on the new
SDK because it becomes an outbound BF activity instead of the HTTP
response — is gone.
The handler body is extracted from the activity-catch-all dispatch in
`monitor-handler.ts` to a new `signin-invoke.ts`, parallel to
`file-consent-invoke.ts`. `isSigninInvokeAuthorized` is now exported from
`monitor-handler.ts` so the new handler can reuse it. The activity
catch-all skips the SSO invoke names alongside the existing skips for
`adaptiveCard/action` and `fileConsent/invoke`.
`MSTeamsAppOn` overloads now cover the two SSO routes with their typed
ctx (`ISignInTokenExchangeInvokeActivity` / `ISignInVerifyStateInvokeActivity`).
Tests in `monitor-handler.sso.test.ts` were rewritten to call the
extracted handler directly — the `registered.run(ctx)` shape no longer
covers SSO, and the `expect(ctx.sendActivity).toHaveBeenCalledWith({ type:
"invokeResponse" })` assertions were dropped to match the new contract
(the SDK ack happens via the typed-route return value).
Note on overlap with #77784 (Stefan Stüben, Microsoft): that PR is doing
a much bigger SSO rework (sign-in card / sign-in-link / six-digit-code
fallbacks plus a `ctx.auth` plumbed to plugin tools). This change is
the small migration-correctness fix and is structured so #77784's SSO
body changes drop into the typed-route registrations cleanly on rebase.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(msteams): route message-submit (feedback) invokes through typed app.on
Last invoke off the activity catch-all dispatch. `message/submitAction`
(thumbs up/down on AI-generated messages) now registers via
`app.on("message.submit", ...)`. Same shape as file-consent and SSO:
handler body extracted to a new `feedback-invoke.ts`, the SDK wraps a
void return into the HTTP 200 InvokeResponse, the broken
`ctx.sendActivity({ type: "invokeResponse", ... })` line is gone, and
the activity catch-all skips this invoke name alongside the others.
`isFeedbackInvokeAuthorized` is exported from `monitor-handler.ts` so
`feedback-invoke.ts` can reuse it. Tests in
`monitor-handler.feedback-authz.test.ts` were rewritten to call the
extracted handler directly — the old `handler.run(ctx)` shape no longer
intercepts feedback, and `originalRun` was removed because the typed
route is the dispatch point now.
`MSTeamsAppOn` overload added with the typed
`IMessageSubmitActionInvokeActivity` ctx, slotted between the SSO
overloads and the `activity` catch-all so `activity` stays last.
This leaves only `message`, `conversationUpdate`, and `messageReaction`
flowing through `app.on("activity", ...)` → `handler.run`. Promoting
those is the path to deleting the catch-all entirely.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(msteams): fall back to block delivery when partial-mode stream fails mid-flight
codex #5 / Galin F5 on PR #76262. `reply-stream-controller.ts` previously
re-threw any non-cancel error from `stream.emit` during partial streaming
and from `stream.emit`/`stream.close` during finalize. Combined with
`preparePayload` suppressing block delivery once `tokensEmitted` was
true, that meant a network blip or API error mid-stream produced a
truncated reply with no recovery — the user saw the prefix that made it
through and nothing else.
Add a `streamFailed` latch parallel to `canceledLocally` / `tokensEmitted`:
- `onPartialReply`: catch non-cancel errors, set `streamFailed = true`,
log a warn, don't propagate (the pipeline must keep running so
`preparePayload` can decide).
- `preparePayload`: when `tokensEmitted && streamFailed`, fall through to
block delivery instead of suppressing. The user may see a duplicate
(streamed prefix + full block reply); intentional — matches the
pre-migration `TeamsHttpStream.hasContent` recovery and is better than
truncated-only.
- `finalize`: same latch + warn on non-cancel close failure, swallow
rather than throw. The streamed content already reached the user; the
closing activity (AI-Generated marker, feedback channelData) is the
only loss, not worth blowing up the dispatcher.
- `isStreamActive` returns false once the stream has failed.
New tests cover crash-mid-stream after tokens were emitted (assert block
delivery payload is returned), happy-path no-duplicate behavior (assert
`preparePayload` still suppresses when nothing failed), and finalize
close-failure (assert no throw). The pre-existing "re-throws non-cancel"
test was inverted to assert non-throwing latch behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(msteams): declare @microsoft/teams.api as a runtime dependency
Type-only `import("@microsoft/teams.api/dist/...").TypeName` references
in `sdk.ts` (added when typed `MSTeamsApp.on` overloads were introduced)
are picked up by the `extension-runtime-dependencies` contract test as
genuine runtime imports. Declaring `@microsoft/teams.api` as a direct
dep makes the contract pass; the package was already coming in
transitively via `@microsoft/teams.apps`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(msteams): keep SSO on SDK signin routes
* test(msteams): avoid redundant signin handler assertion
* docs(msteams): clarify Teams cloud support
* fix(msteams): use current SDK string helper
* fix(msteams): gate SDK invoke side effects
* test(msteams): avoid implicit any in lifecycle tests
* fix(msteams): preserve SDK user agent and matrix check
* fix(msteams): expose SDK common dependency
* fix(msteams): use SDK user agent merge
* fix(msteams): fall back when stream close no-ops
* chore(msteams): drop unrelated merge artifacts
* chore(msteams): restore unrelated main files
* chore(msteams): restore unrelated main files
* chore(msteams): restore unrelated main files
* test(msteams): type stream close mock result
* fix(msteams): configure Teams cloud service URL
* chore(msteams): refresh shrinkwrap
* chore(deps): refresh shrinkwrap locks
* chore(ci): rerun guards after main sync
* chore(deps): refresh shrinkwrap for node 24
* chore(config): refresh docs baseline
* fix(msteams): preserve Teams SDK proactive references
* fix(msteams): harden SDK proactive sends
* fix(msteams): align service url contract
* test: fix bonjour beacon type narrowing
* fix(msteams): ignore ambient service url
* fix(msteams): fall through submit invokes
* test: align shrinkwrap override policy with Teams SDK deps
* fix(msteams): ack invoke routes promptly
* fix(msteams): support china cloud boundaries
* test: sync PR with current CI gates
* test: isolate channel setup registry metadata
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Recover Codex compaction paths when a stale app-server thread binding returns an unstructured `thread not found` failure. The raw missing-thread response now shares the same recovery behavior as structured missing/stale binding failures for preflight, queued compaction, and CLI fallback.
Fixes#87736.
Co-authored-by: Paul Frederiksen <paul@paulfrederiksen.com>
Summary:
- The PR reorders embedded attempt cleanup to release the session write lock before session/MCP/LSP teardown, treats sessions_yield cleanup as abort-like for flush timing, and adds focused regression tests.
- PR surface: Source +14, Tests +71. Total +85 across 3 files.
- Reproducibility: yes. Source inspection shows current main releases the cleanup lock only after runtime tear ... R body’s terminal proof exercises the same ordering with production cleanup and filesystem lock primitives.
Automerge notes:
- PR branch already contained follow-up commit before automerge: Merge branch 'main' into fix/session-lock-release-before-teardown
Validation:
- ClawSweeper review passed for head 178192fa0e.
- Required merge gates passed before the squash merge.
Prepared head SHA: 178192fa0e
Review: https://github.com/openclaw/openclaw/pull/87747#issuecomment-4566994280
Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: Jason (Json) <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Generated-media completions now use the existing idempotent direct-media fallback when active requester wake has already failed and the requester-agent handoff hits a session write-lock-shaped no-response error. Generic requester-agent handoff errors still fail visibly instead of direct-sending after an unknown side effect.
Release-note context: fixes a message-delivery loss path for generated images, music, and video where the artifact had been created but the final handoff could be reported as failed after a session write lock.
Verification:
- GitHub CI run 26601111985 passed at b0be994332.
- Blacksmith Testbox through Crabbox tbx_01ksr2jtt3fnz0zqvwmqq513h7 covered the exact lock fallback and qa-channel generated-media smoke.
- git diff --check origin/main...refs/remotes/pull/87741/head passed before merge.
Co-authored-by: Jason (Json) <263060202+fuller-stack-dev@users.noreply.github.com>
Reuses the current plugin metadata snapshot in facade activation checks when the resolved boundary config matches, avoiding repeated manifest registry loads on the facade path.
Falls back to manifest registry loading when the current snapshot is missing or belongs to a different config/environment. Adds regression coverage for snapshot mismatch, snapshot reuse, and Windows path normalization.
Co-authored-by: 郑苏波 (Super Zheng) <superzheng@tencent.com>
Bound aggregate tool-result history at the provider prompt boundary without rewriting persisted session entries.
Provider-visible prompt history now trims older aggregate tool results before newer evidence, while canonical session history, slash/extension command handlers, and context-engine afterTurn snapshots stay unmodified.
Co-authored-by: luyifan <al3060388206@gmail.com>
* fix(nvidia): load featured model catalog
Co-authored-by: CaptainTimon <CaptainTimon@users.noreply.github.com>
* fix(nvidia): widen catalog fetch timeout
* fix(nvidia): cover catalog registration
* fix(picker): include provider catalog loader
* fix(nvidia): guard featured catalog fetch
* fix(nvidia): sync bundled catalog with live API
Replace minimaxai/minimax-m2.5 (MiniMax M2.5) with minimaxai/minimax-m2.7 (Minimax M2.7) and z-ai/glm5 (GLM-5) with z-ai/glm-5.1 (GLM 5.1) in the bundled fallback catalog to match NVIDIA's public featured-models endpoint.
Update docs table and all extension test expectations.
* fix(nvidia): retain shipped catalog refs
* fix(picker): keep alias catalog rows
* fix(nvidia): restore live catalog priority
---------
Co-authored-by: CaptainTimon <CaptainTimon@users.noreply.github.com>
Summary:
- The PR changes three TUI final chat-event early returns to call `tui.requestRender(true)` and adds focused event-handler assertions for those branches.
- PR surface: Source 0, Tests +25. Total +25 across 2 files.
- Reproducibility: yes. Current main and the latest release still have the three unforced final-event repaint calls, and the linked source PR includes PTY terminal proof showing the changed behavior after the patch.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(tui): force repaint final chat events
Validation:
- ClawSweeper review passed for head 570dc3af86.
- Required merge gates passed before the squash merge.
Prepared head SHA: 570dc3af86
Review: https://github.com/openclaw/openclaw/pull/87423#issuecomment-4558845936
Co-authored-by: Ted Li <tl2493@columbia.edu>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Narrow the abort-settle timeout helper to the env keys it reads and keep the dynamic live-model hook unit test from loading provider normalization/runtime plugins.\n\nProof: focused Vitest for live-model-dynamic-candidates, oxfmt/oxlint/diff checks, autoreview clean, AWS Crabbox run_8a485e593c2e corepack pnpm check:changed exit 0, and PR CI green.
Summary:
- The PR updates the bundled MiniMax music provider to request streaming hex responses, decode SSE/audio bodie ... while preserving JSON/url fallbacks, and adds provider tests for streaming, fallback, and timeout behavior.
- PR surface: Source +148, Tests +152. Total +300 across 2 files.
- Reproducibility: yes. by source inspection and live proof, though I did not run a fresh live reproduction. C ... s provider fallback, and the source PR reports a 130s live MiniMax provider run succeeding after the patch.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(minimax): stream music generation responses
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8456…
Validation:
- ClawSweeper review passed for head 806b0b40f2.
- Required merge gates passed before the squash merge.
Prepared head SHA: 806b0b40f2
Review: https://github.com/openclaw/openclaw/pull/84764#issuecomment-4504175527
Co-authored-by: Neerav Makwana <261249544+neeravmakwana@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Render Slack progress-mode updates as native task-card progress blocks, with bounded Slack chunk text and stable fallback behavior.
Also deep-merge Slack account streaming objects over top-level defaults while preserving legacy scalar account overrides, and keep the plugin SDK fetch runtime import path from evaluating guarded-fetch dispatcher code.
Verification:
- pnpm test extensions/slack/src/progress-blocks.test.ts extensions/slack/src/accounts.test.ts src/plugin-sdk/fetch-runtime.test.ts
- pnpm lint --threads=8
- git diff --check
- .agents/skills/autoreview/scripts/autoreview --mode local
- GitHub PR checks green on #87748 at 4803e98820
Refs #82258
Co-authored-by: Simon van Laak <32648751+simonvanlaak@users.noreply.github.com>
Support grouped skill folders while keeping skill invocation flat via frontmatter names.
Includes bounded nested SKILL.md discovery, refresh/watch coverage for grouped folders, plugin symlink containment, and docs for grouped skill organization.
Verification:
- Node 24 targeted skill discovery and refresh tests passed locally.
- Docs checks passed locally and in CI.
- Autoreview clean.
- Crabbox live OpenAI proof showed nested foo/bar skills listed and visible in the agent system prompt.
- CI run 26595118581 passed.
Summary:
- The branch preserves current Claude Haiku 4.5 refs in the Anthropic resolver and doctor migration, repoints the bare `haiku` family alias to `claude-haiku-4-5`, and updates regression tests.
- PR surface: Source +5, Tests +21. Total +26 across 4 files.
- Reproducibility: yes. Current main source maps the bare `haiku` alias and explicit Haiku 4.5 migration path ... de-sonnet-4-6`; the PR body also supplies before/after terminal proof for the resolver and migration tests.
Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.
Validation:
- ClawSweeper review passed for head 64429e23b3.
- Required merge gates passed before the squash merge.
Prepared head SHA: 64429e23b3
Review: https://github.com/openclaw/openclaw/pull/87719#issuecomment-4566419633
Co-authored-by: alkor2000 <200923177@qq.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Cap Dreaming short-term recall stores so repeated recall recording, repair, and promotion application cannot grow the JSON artifact without bound.
The fix keeps full normalized snippets for recall identity and contamination checks before truncating persisted snippets, exposes the new overflow audit code through the SDK facade, and adds regression coverage for recording, repair, promotion rehydration, and deterministic retention ties.
Fixes#87095.
Verification:
- OPENCLAW_VITEST_MAX_WORKERS=1 node scripts/run-vitest.mjs extensions/memory-core/src/short-term-promotion.test.ts src/commands/doctor-memory-search.test.ts src/plugin-sdk/memory-core-engine-runtime.test.ts
- pnpm tsgo:prod
- pnpm check:test-types
- pnpm lint --threads=8
- git diff --check
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- PR CI run 26594527697: unrelated current-main failures only in checks-node-agentic-plugin-sdk and checks-node-agentic-agents; same failures reproduced on main run 26594198639.
Co-authored-by: ai-hpc <mail.speedy.hpc@hotmail.com>
Wrap Ollama native streams with the shared plain-text tool-call compatibility wrapper so local/plain-text tool requests are delivered as structured toolCall events when matching tools are available.
Verified with live local Ollama proof, focused Testbox Vitest, Testbox check:changed, and autoreview.
Summary:
- The branch filters OpenClaw CLI image-cache paths out of prompt image-reference detection and adds parser/helper regression tests.
- PR surface: Source +17, Tests +65. Total +82 across 3 files.
- Reproducibility: yes. source-level reproduction is high confidence: current main still scans replayed prompt ... ectImageReferences and has no cache-path exclusion before loadPromptRefImages can reload stale image paths.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(images): skip CLI image cache refs
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8750…
Validation:
- ClawSweeper review passed for head dfe0408df8.
- Required merge gates passed before the squash merge.
Prepared head SHA: dfe0408df8
Review: https://github.com/openclaw/openclaw/pull/87523#issuecomment-4560945125
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The PR changes the Control UI chat session picker blur handler to skip empty-query search application and adds a regression test that picker options remain clickable after an empty search blur.
- PR surface: Source +4, Tests +52. Total +56 across 2 files.
- Reproducibility: yes. The issue steps, before recording, and current-main source path all point to the same ... r clearing picker state before click delivery; I did not rerun a live browser repro in this read-only pass.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(ui): preserve session picker on empty search blur
Validation:
- ClawSweeper review passed for head bb14687756.
- Required merge gates passed before the squash merge.
Prepared head SHA: bb14687756
Review: https://github.com/openclaw/openclaw/pull/87682#issuecomment-4565441074
Co-authored-by: Ryan Weng <14496969+ryan4559@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Surface inbound bundle-MCP structuredContent as the model-visible result when present so agents can read Codex MCP threadId values and continue with codex-reply. Preserve non-structured content behavior, preserve the empty-result fallback, and keep details.structuredContent for internal consumers.
Also remove an unused secrets path helper that was breaking the latest prod-type gate on main.
Fixes#87511.
Verification:
- node scripts/run-vitest.mjs src/agents/agent-bundle-mcp-tools.materialize.test.ts
- pnpm exec oxfmt --check src/secrets/path-utils.ts src/agents/agent-bundle-mcp-materialize.ts src/agents/agent-bundle-mcp-tools.materialize.test.ts
- pnpm tsgo:prod
- local check-guards shard commands
- live Codex MCP smoke with codex__codex and codex__codex-reply same-thread continuation
- autoreview clean
- CI run 26587222874 green
Co-authored-by: Pluviobyte <Pluviobyte@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Fix replay handling for voice-call webhooks so duplicate signed requests do not mint or expose realtime stream tokens.
- Return token-free Twilio replay TwiML before realtime setup shortcuts.
- Cache bounded non-Twilio first responses for idempotent replay XML while skipping duplicate side effects.
- Cover Twilio realtime replay and Plivo replay behavior with regression tests.
- Remove an unused secrets path helper that was tripping latest-main prod type CI.
Fixes#87497.
Co-authored-by: Coy Geek <65363919+coygeek@users.noreply.github.com>
Fixes#87016.
Empty preflight compaction recovery now resets stale token snapshots immediately, preserves valid legacy transcript rows during cleanup, and avoids re-persisting stale context-budget or compaction metadata after a successful retry.
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Add read-only MCP visibility to `tools.effective` by projecting MCP tools only after a session catalog has already been warmed by an agent turn. Keep the gateway additive: no `tools.effective.refresh`, no forced MCP startup, and no behavior change for MCP loading.
Verification:
- `git diff --check origin/main..HEAD`
- `node scripts/run-vitest.mjs run --config test/vitest/vitest.agents.config.ts --reporter=verbose src/agents/tools-effective-inventory.test.ts`
- GitHub checks green on `a8a7f8442adb216f60da24d50118374a15c62e06`, including `Real behavior proof`, `check-guards`, `check-prod-types`, `check-test-types`, `build-artifacts`, `Critical Quality (gateway-runtime-boundary)`, and `Critical Quality (network-runtime-boundary)`.
Co-authored-by: David Huang <nxmxbbd@gmail.com>
The anthropic-transport-stream was overwriting thinkingSignature on each
signature_delta event instead of appending. Since Anthropic sends the
thinking block signature across multiple streaming chunks, only the last
chunk survived. The truncated signature was persisted to session JSONL,
causing all subsequent replay attempts to fail with HTTP 400:
thinking or redacted_thinking blocks in the latest assistant message
cannot be modified
This permanently bricked sessions with no user recovery path.
Fix: accumulate signature_delta values by concatenating instead of
overwriting, matching the correct implementation in the LLM provider
layer (src/llm/providers/anthropic.ts:629-634).
Includes real-scenario proof against live Anthropic API validating that
correct signatures replay successfully while truncated signatures are
rejected.
Fixes#87574
Refs #80625, #85781, #87475
* fix(agents): preserve reasoning_content replay across DeepSeek tier suffixes
OpenCode Zen exposes DeepSeek V4 as `deepseek-v4-flash-free`, which keeps the upstream DeepSeek thinking-mode contract that requires `reasoning_content` to be passed back on follow-up requests. The existing replay allowlist only matched the bare ids (`deepseek-v4-flash`, `kimi-k2-thinking`, ...), so the tier-suffixed id missed every candidate and the sanitizer stripped `reasoning_content` from the assistant turn. DeepSeek then rejected the second API call with HTTP 400 and the session deadlocked.
Strip the well-known tier suffixes (`-free`, `-paid`, `-trial`) when generating allowlist candidates so the base model id matches and the reasoning replay survives. Existing matching for prefixed / colon-suffixed routes is unchanged.
Fixes#87575
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(agents): avoid spread-rebuild when iterating allowlist candidates
oxlint flagged the [...candidates] spread as an unnecessary array copy. Use an explicit baseCount loop bound instead so we still iterate the original entries while pushing tier-stripped variants onto the same array.
Co-authored-by: Cursor <cursoragent@cursor.com>
* test(opencode): add live DeepSeek replay probe
* test(opencode): avoid forced tool choice in live replay
---------
Co-authored-by: Pluviobyte <Pluviobyte@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Fixes#58012.
Applies strict9 replay tool call id sanitization to OpenRouter Mistral-family model routes, including unprefixed Mistral/Codestral/Devstral aliases, while preserving existing passthrough behavior for Gemini and other OpenRouter-backed routes.
Adds focused unit coverage plus a live OpenRouter model catalog test so new Mistral-family routes are checked against the replay policy. Also keeps the current core lint gate green by switching the tool schema cache key sort to a non-mutating sorted array.
Co-authored-by: Pluviobyte <Pluviobyte@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Preserve explicit gateway service identity when package/update refreshes the managed service environment. This keeps caller-selected systemd units ahead of stale persisted service env and applies the same precedence to launchd labels and Windows task names during service-state inspection.
Fixes#87490
Verification:
- node scripts/run-vitest.mjs src/daemon/service-env.test.ts src/daemon/service.test.ts src/cli/update-cli.test.ts src/cli/update-cli/restart-helper.test.ts src/cli/daemon-cli/install.test.ts src/daemon/systemd.test.ts
- git diff --check origin/main...pr/87556
- Crabbox AWS Linux systemd install/refresh proof: run_f3374bd610f7, lease cbx_754e69eb6c3a, provider aws, target linux
- autoreview --mode branch --base origin/main: clean, no accepted/actionable findings
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Summary:
- Replace the legacy iOS shell with Pro Command, Chat, Agents, and Settings tabs.
- Wire iOS chat/session/settings/diagnostics and realtime Talk flows through gateway-backed APIs.
- Add gateway/session and shared chat coverage for the new iOS flow.
Verification:
- git diff --check
- node scripts/run-vitest.mjs src/gateway/server.sessions.create.test.ts src/gateway/talk-realtime-relay.test.ts
- swift test --filter ChatViewModelTests (apps/shared/OpenClawKit)
- xcodebuild build for Nimrod's iPhone succeeded; install succeeded; launch was blocked because the phone was locked
Known follow-up:
- Preserve traceLevel in sessions.create parent runtime inheritance and keep the changelog credit in the follow-up patch.
Increase the code-mode wait-timeout test timeout so CI shard load does not trip the worker startup guard before the test reaches the intended pending-tool wait path.
Reduce repeated gateway warning noise in startup/auth retry paths while preserving credential mismatch and rate-limit audit visibility.
Also hardens empty embedded-assistant retry handling by carrying lifecycle state through the missing-assistant guard, and keeps the relevant regression coverage in gateway and agent tests.
Wire QA fallback models into live gateway config, fix Slack allowlist-block coverage, and keep WhatsApp live artifacts useful while redacting raw credential metadata.\n\nVerification: focused QA Vitest; autoreview clean; AWS Crabbox pnpm check:changed run_0207de7d47aa; QA-Lab branch-defined transport run 26565521272 with Matrix transport 56/56 and Slack/Discord/Telegram/parity clear. WhatsApp remains blocked by stale shared Convex WhatsApp Web credentials returning Baileys 401 before scenarios.
* fix(telegram): enable TCP keepalive on getUpdates connections to prevent NAT timeout stalls
Long-polling connections to api.telegram.org stay idle for up to the
getUpdates timeout (~900 s). Most home/office NAT tables expire idle TCP
entries after 60–1800 s (commonly ~1000 s). When the NAT entry is
silently dropped the connection hangs rather than returning an error,
leaving the grammY runner stuck until the 90 s stall watchdog fires and
forces a restart cycle.
Fix: unconditionally set `keepAlive: true` and
`keepAliveInitialDelay: 30_000` (30 s) on the undici Agent `connect`
options built in `buildTelegramConnectOptions`. OS-level TCP keepalive
probes sent every ~75 s (OS default) will:
1. Refresh the NAT table entry before it expires.
2. Surface dead connections immediately with ETIMEDOUT instead of
hanging forever.
The `return Object.keys(connect).length > 0 ? connect : null` guard is
also removed; `connect` is now always non-empty so it always returns the
object.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
(cherry picked from commit 92e454c0614256201cdf6f0f73c7897d006616d4)
* fix(telegram): stop self-flagging disconnected on poll-cycle start; widen channel connect grace to 300s
(cherry picked from commit 1ca963a05dac0d9d605e9a15dc97fced9cf7725e)
* fix(telegram): catch hung polling startups that preserve inherited connected:true
The widened 300s channel connect grace and the removal of connected:false from
notePollingStart left a path where a polling restart could hang forever
looking healthy. notePollingStart clears lastConnectedAt, lastEventAt, and
lastTransportActivityAt but deliberately omits connected, so server-channels'
patch-merge inherits a connected:true from the previous lifecycle. After grace,
evaluateChannelHealth's stale-socket branch requires lastTransportActivityAt
to be non-null and the connected:false branch is masked, so the channel sits
healthy with no first getUpdates.
Add a post-grace branch to evaluateChannelHealth that flags polling channels
as stale-socket when connected:true is paired with null lastConnectedAt and
null lastTransportActivityAt and a non-null lastStartAt. Scoped to mode:polling
so webhook channels and channels without continuous transport tracking are
not falsely flagged. Align TELEGRAM_POLLING_CONNECT_GRACE_MS in the Telegram
status diagnostic with DEFAULT_CHANNEL_CONNECT_GRACE_MS so openclaw channels
status agrees with the shared health monitor on the grace window. Refresh
the notePollingStart comment to point at the new evaluateChannelHealth branch.
Addresses clawsweeper review on #83304 (P1 connect-grace startup-hang, P2
diagnostic grace drift). Tests cover the new flagged path, the in-grace happy
path, and the prior-successful-connect happy path.
* fix(telegram): clear polling connected state on startup
* fix(gateway): add defense-in-depth health-policy branch for hung polling startups
Defense in depth on top of 87db46c576's notePollingStart connected:false fix.
The primary path (notePollingStart writes connected:false explicitly so
evaluateChannelHealth's existing connected===false branch catches a hung
restart) is unchanged. This adds a defensive post-grace branch that catches
the same hang via a different signature -- inherited connected:true paired
with null lastConnectedAt and null lastTransportActivityAt -- in case a
future code path forgets to clear the inherited connected flag on lifecycle
start. Scoped to mode:polling so webhook channels and channels without
continuous transport tracking are not falsely flagged.
Also bump lastStartAt: Date.now() - 121_000 to 301_000 in the spool-handler
timeout test added by upstream #83505 so it falls past the widened 300s
TELEGRAM_POLLING_CONNECT_GRACE_MS suppression window (mirroring the same
fixup already applied to the two adjacent polling-startup tests).
* revert(telegram,gateway): keep connect grace at 120s
Drop the 120s -> 300s widening from this PR after maintainer feedback that
the extra grace masks real startup bugs. The defense-in-depth checks added
in earlier commits (notePollingStart clearing inherited connected state,
the stale-socket policy branch, the per-snapshot startup grace test) all
work fine at 120s and remain valuable on their own.
Reverts in:
- src/gateway/channel-health-policy.ts: DEFAULT_CHANNEL_CONNECT_GRACE_MS 300 -> 120
- extensions/telegram/src/status-issues.ts: TELEGRAM_POLLING_CONNECT_GRACE_MS 300 -> 120
- extensions/telegram/src/status.test.ts: lastStartAt 301_000 -> 121_000 (3 cases)
The new channel-health-policy.test.ts cases use explicit channelConnectGraceMs:
10_000 in the policy, so they are unaffected by the default constant change.
* fix(telegram): narrow polling keepalive fix
---------
Co-authored-by: Yibei Ou <yibeiou@Yibeis-Mac-mini.local>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Harden QQBot direct media URL uploads by downloading through the local SSRF guard before QQ upload, disabling redirects, bounding fetch/setup and body reads, and routing downloaded buffers through the existing one-shot/chunked size gate.
Co-authored-by: Agustin Rivera <agustin@rivera-web.com>
Default `openclaw status --json` stays on the lean health-probe path while preserving the JSON task summary, local update/install metadata, explicit probe timeouts, and configured gateway handshake timeouts. Deeper memory, registry, remote git, and local status-RPC diagnostics remain behind `status --json --all`.
Also keeps generated diffs viewer output in its built form and ignores it in oxfmt so `pnpm build` leaves a clean tree.
Proof:
- `node scripts/run-vitest.mjs src/commands/status.scan.fast-json.test.ts src/commands/status-json-payload.test.ts src/commands/status.scan.shared.test.ts`
- `OPENCLAW_LOCAL_CHECK=0 node scripts/run-oxlint-shards.mjs --threads=8`
- `node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.core.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/core-test.tsbuildinfo`
- `node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.extensions.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/extensions-test.tsbuildinfo`
- `.agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main`
- GitHub checks green for head `47a63f87ea7c2351994fdb71e8cc18041aa0b64e`
Thanks @andyylin.
Co-authored-by: Andy <andyylin@users.noreply.github.com>
Forward canonical inbound media metadata to plugin message_received hooks so plugins can inspect the same mediaPath, mediaUrl, mediaType, mediaPaths, mediaUrls, and mediaTypes fields already available to inbound_claim.
Verification:
- node scripts/run-vitest.mjs src/hooks/message-hook-mappers.test.ts
- /Users/steipete/Projects/agent-scripts/skills/autoreview/scripts/autoreview --mode branch --base origin/main
Refs: https://github.com/openclaw/openclaw/pull/87297
Co-authored-by: WarrenJones <8704779+WarrenJones@users.noreply.github.com>
Stop heartbeat runs from directly returning non-ack durable pending final text. Heartbeats now only clear ack-only pending state and otherwise continue the heartbeat turn, so stale prior final answers cannot be replayed through a later heartbeat/default route.
Keep the isolated heartbeat active-run guard so an immediate/manual heartbeat cannot overwrite an isolated heartbeat session that is still running.
Proof:
- node scripts/run-vitest.mjs src/auto-reply/reply/get-reply.fast-path.test.ts src/infra/heartbeat-runner.skips-busy-session-lane.test.ts
- git diff --check
- autoreview --mode local
- autoreview --mode branch --base origin/main
- GitHub CI 26543804437, CodeQL 26543804438, Critical Quality 26543804441, OpenGrep PR Diff 26543804440 rerun job 78197443511, Real behavior proof 26544027357
Refs #74257.
Co-authored-by: kesslerio <martin@kessler.io>
Stabilize isolated cron prompt cache affinity by deriving a stable prompt cache key per cron job/session/model and forwarding it separately from the rotating run session id.
Thread the key through embedded runs, stream resolution, provider options, proxy forwarding, custom streams, and prompt-cache observability. Keep OpenAI-compatible payloads valid by using hyphen-safe keys, clamping upstream prompt_cache_key values, and omitting affinity when cache retention is disabled.
Thanks @ferminquant.
Co-authored-by: Fermin Quant <ferminquant@hotmail.com>
Rewrites non-canonical api_key fields in auth-profiles.json to canonical key via openclaw doctor --fix, with backups, while preserving canonical key/keyRef credentials and active-agent auth stores.
Fixes#57389.
Co-authored-by: alkor2000 <200923177@qq.com>
* fix(sessions): preserve Matrix room-id case in session keys (#75670)
Matrix room IDs (and thread event IDs) are opaque, case-sensitive per the
Matrix spec, but session-key canonicalization lowercased them. That forked
one room into duplicate sessions and produced 403 M_FORBIDDEN on recovery /
delivery paths that reconstruct the target from the (lowercased) session key,
even though deliveryContext.to stayed correct.
Introduce a generic, opt-in case-preservation registry (CASE_PRESERVING_PEERS)
consulted at all three lowercasing sites:
- construction: normalizeSessionPeerId
- store canonicalization: normalizeSessionKeyPreservingOpaquePeerIds
- gateway send: explicit request.sessionKey
Signal group preservation is encoded to match prior behavior exactly (segment
span, unscoped, thread suffix still lowercased). Matrix channel/group enrolls
the opaque tail (room id with embedded :server + any 🧵<event> suffix).
Exact mixed-case keys now win over folded legacy aliases in
resolveSessionStoreEntry and delivery-info lookup; existing lowercased rows
collapse on the next write. Matrix DM/MXID and non-enrolled channels keep the
default lowercase behavior.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(sessions): guard Matrix folded alias delivery proof
* test(agents): cover cold OpenAI gpt-5.5 fallback
* fix(sessions): preserve non-opaque alias freshness
* fix(sessions): prevent Matrix cross-room thread recovery
* build(protocol): refresh tools effective Swift models
* test(codex): include effective cwd in startup fixture
* test(codex): align startup failure cleanup expectation
* fix(sessions): keep Signal folded aliases fresh
* fix(sessions): preserve unscoped Matrix room keys
* fix(sessions): recover legacy Matrix thread aliases
* fix(sessions): preserve Matrix keys in state migrations
* fix(sessions): keep Matrix structural alias freshness
* fix(sessions): preserve unscoped Matrix migration keys
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Fix iMessage native exec approval routing so approval prompts bind to the sent GUID without duplicate sends after RPC timeout. Also keeps chat.db GUID recovery on the local imsg path while avoiding local DB recovery for configured or detected SSH wrappers.
Thanks @kevinslin.
Avoid stale restart continuation reuse after a session key has rotated.
Queued restart agent turns now carry the session id they were queued for and fall back to a system wake if the key points at a different session by delivery time. Normal completed-run lifecycle fields stay reusable for fresh sessions, while new-session creation clears stale lifecycle markers.
Closes#86593.
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Closes#87210.
Gateway probe now waits for GatewayClient.stopAndWait() before resolving so callers do not observe a successful probe while the client socket is still draining. If the drain fails, probe falls back to stop().
Adds mocked probe coverage plus a real WebSocket regression test that verifies no client socket handle remains when probeGateway() resolves.
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Show active subagent detail rows in /status with labels and elapsed runtime while keeping completed-subagent summary behavior. Thanks @simplyclever914.
Fixes#83935.
Summary:
- clear stale legacy openai-codex auto route pins only when the canonical OpenAI provider is still using the Codex harness for the same model
- preserve usable Codex auth profiles while clearing stale route state
- keep explicit/custom OpenAI API route pins intact
Verification:
- git diff --check
- pnpm exec oxfmt --check --threads=1 src/auto-reply/reply/model-selection.ts src/auto-reply/reply/model-selection.test.ts src/auto-reply/reply/agent-runner-execution.ts src/auto-reply/reply/agent-runner-execution.test.ts
- fnm exec --using 24.15.0 node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.core.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/core-test.tsbuildinfo
- .agents/skills/autoreview/scripts/autoreview --mode local
- CI: https://github.com/openclaw/openclaw/actions/runs/26542490863
Co-authored-by: Paul Frederiksen <paul@paulfrederiksen.com>
Fixes#87191. Keeps Brave and Gemini runtime-injected web search provider config readable by providers without re-exposing legacy tools.web.search provider objects to config validation.
Fix Slack draft cleanup after final-visible delivery.
Track when Slack has already delivered a visible final reply and stop reusing the draft finalizer for later same-turn final/error payloads. This keeps the first fallback cleanup for transient previews while preventing late cleanup from deleting a visible answer.
Fixes#87363
Co-authored-by: tianxiaochannel-oss88 <tianxiaochannel@gmail.com>
The compaction retry loop checked the delivery-timeout deadline before
choosing a fixed backoff delay, then slept that whole delay. When the
remaining window was shorter than the next backoff entry, the final
retry could sleep past the deadline, overrunning the delivery timeout
the retry is meant to stay within. Clamp the wait to the remaining
window (min(scheduledDelay, deadline - now)) and stop retrying once no
time remains, so compaction waiting never exceeds the delivery timeout.
Addresses the near-deadline overrun raised in ClawSweeper review of #86606.
Follow-up to #85489. Active requester steering treated a `compacting`
outcome from queueEmbeddedPiMessageWithOutcome as a terminal wake
failure and fell through to the requester-agent/direct fallback, even
though the active run becomes steerable again as soon as compaction
finishes.
Introduce a shared resolveActiveWakeWithRetries helper used by both the
steer path (maybeSteerSubagentAnnounce) and the generated-completion
active wake (sendSubagentAnnounceDirectly). The helper treats
`compacting` as transient and waits through compaction, retrying the
same wake. Waiting is bounded by the active wake's delivery timeout (not
just the backoff schedule): the backoff schedule controls the gap
between attempts, and once it is exhausted its last delay is reused until
the delivery deadline, so a compaction that finishes after the schedule
but within the delivery timeout still re-steers. The best-effort
transcript-commit retry and the compaction retry share one loop, so a
run that compacts and then reports transcript_commit_wait_unsupported
still gets the best-effort retry. Other wake failures keep their
existing single-attempt fallback.
Fixes#86566
Preserve pending agent-job error diagnostics as non-terminal timeout snapshots so the retry grace path can still recover when the lifecycle later starts and completes.
Local proof:
- node scripts/run-vitest.mjs packages/sdk/src/index.test.ts src/gateway/server-methods/server-methods.test.ts src/gateway/server.chat.gateway-server-chat.test.ts src/agents/run-wait.test.ts src/agents/openclaw-tools.sessions.test.ts
- node scripts/run-oxlint.mjs packages/sdk/src/client.ts packages/sdk/src/index.test.ts src/gateway/server-methods/agent-job.ts src/gateway/server-methods/agent.ts src/gateway/server-methods/agent-wait-dedupe.ts src/agents/run-wait.ts src/agents/tools/sessions-send-tool.ts src/gateway/server-methods/server-methods.test.ts src/gateway/server.chat.gateway-server-chat.test.ts src/agents/run-wait.test.ts src/agents/openclaw-tools.sessions.test.ts
- autoreview --mode local: no accepted/actionable findings
- CI run 26536599850: success
Co-authored-by: Martin Garramon <martin@yulicreative.ai>
Include second-level precision in inbound metadata and auto-reply envelope timestamps, matching the timestamp helper contract used by providers and channel adapters.
Docs now show the weekday plus seconds form in date-time and timezone examples.
Verification:
- node scripts/run-vitest.mjs src/auto-reply/envelope.test.ts src/auto-reply/reply/inbound-meta.test.ts
- pnpm docs:list >/tmp/openclaw-docs-list-87360.log
- git diff --check origin/main...HEAD
- pnpm format:docs:check
- pnpm lint:docs
- pnpm lint:extensions:bundled
- pnpm lint
- PR CI green on 495bb6c10fFixes#87257
Co-authored-by: GarlicGo <582149912@qq.com>
Expire browser-origin Control UI/WebChat device tokens when shared gateway auth rotates by tagging those tokens with the shared-auth generation and enforcing it during verification.
Preserve the issuer tag when a shared-auth-derived device token reconnects through a non-browser client, so reconnect rotation cannot turn it into an untagged long-lived token.
Proof:
- OPENCLAW_VITEST_MAX_WORKERS=1 node scripts/run-vitest.mjs src/gateway/server.shared-auth-rotation.test.ts src/infra/device-pairing.test.ts src/gateway/control-ui.http.test.ts
- GitHub CI run 26535632102: relevant build/runtime/test-type checks green; inherited lint reds match origin/main.
- GitHub CodeQL Critical Quality run 26535631610: network-runtime-boundary green.
Co-authored-by: Pavan Kumar Gondhi <pavangondhi@gmail.com>
Fixes repeated Tool Search catalog registration for unchanged effective tool sets by reusing a fingerprinted catalog snapshot across embedded-agent run cleanup.
The reusable catalog is guarded by catalog-affecting fields, parameters, and executable identity, and reuse now rebinds the current run/session refs before returning. Embedded-agent prep logging only suppresses the catalog line when reuse actually happened.
Verification:
- pnpm test src/agents/tool-search.test.ts -- --reporter=verbose
- pnpm check:changed, Testbox tbx_01ksney4f00wgk9n39yv7jsh4m
- Real behavior proof, GitHub Actions run 26534896284
- CI rerun for unrelated model-picker timeout passed, GitHub Actions run 26534489215
- autoreview clean: no accepted/actionable findings
Closes#86887
Co-authored-by: Sebastien Tardif <sebtardif@ncf.ca>
Avoids a self-wait in embedded agent session event hooks by skipping the queue drain only for hooks running inside the current session event processing chain. Detached or external hook work still drains the queue before taking the session write lock.
Verification:
- node scripts/run-vitest.mjs run --config test/vitest/vitest.agents-embedded-agent.config.ts src/agents/embedded-agent-runner/run/attempt.session-lock.test.ts
- node scripts/run-oxlint.mjs --tsconfig config/tsconfig/oxlint.core.json src/agents/embedded-agent-runner/run/attempt.session-lock.test.ts src/agents/embedded-agent-runner/run/attempt.session-lock.ts --threads=8
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- GitHub CI: https://github.com/openclaw/openclaw/actions/runs/26533883763
Thanks @luoyanglang.
Co-authored-by: luoyanglang <hanwanlonga@gmail.com>
Make plugin-state enforce the plugin-wide live-row fuse by evicting only from the namespace currently being written, preserving sibling namespace rows and still failing atomically when the current namespace cannot free enough rows.
Raise the plugin-wide cap to 6,000 rows, keep Telegram's persistent message-cache namespace at 3,000 entries, and document the updated SDK runtime contract. Harden legacy plugin-state import so capacity pressure cannot archive a source after losing imported keys, with focused regression coverage for Telegram-shaped namespaces and migration rollback.
Also restore the Docker runtime-assets preflight step in full release validation so release workflow contract tests stay aligned.
Verification: focused plugin-state, migration, Telegram, workflow-contract, lint, deprecated-API, diff-check, Blacksmith Testbox, CI, CodeQL, Workflow Sanity, OpenGrep, and autoreview all passed on PR head fee021cfa6.
Co-authored-by: Keshav's Bot <keshavbotagent@gmail.com>
Use read-only Telegram account inspection for prompt-time channel actions, inline buttons, and reaction guidance so unresolved SecretRef tokens retain configured non-secret behavior before runtime snapshot hydration.
Match runtime Telegram account lookup for normalized config keys and multi-account fallback guards, while keeping sends/actions on the existing strict credential resolution path.
Fixes#75433.
Co-authored-by: Shubhankar Tripathy <reach2shubhankar@gmail.com>
Fixes #87331.\n\nPersist Codex native hook relay generations for real app-server resumes, keep a bounded legacy-binding grace path, and rotate generation on fresh-thread fallback so stale hook commands stay rejected.\n\nCo-authored-by: Alex Knight <15041791+amknight@users.noreply.github.com>
Document that automation should pipe `models auth paste-token` credentials over stdin instead of passing token material in argv, keeping the existing secret-handling path explicit in the CLI docs.
Also include accepted auth-profile credential types in invalid-profile warning logs so malformed local auth stores are easier to repair.
Fixes#63042.
Thanks @liaoandi.
Clarify the Codex Computer Use docs around inferred opt-in, read-only status checks, and marketplace root versus marketplace JSON path setup.
The docs now match current source-backed behavior: autoInstall opts Computer Use in, status does not mutate plugin setup, and marketplacePath is for a local marketplace JSON file while source registers a marketplace root.
Verification:
- pnpm docs:list
- GitHub CI check-docs passed
- Real behavior proof passed via maintainer proof override for this docs-only PR
Thanks @bdjben.
Co-authored-by: Benjamin Badejo <ben@benbadejo.com>
Co-authored-by: Sally O'Malley <somalley@redhat.com>
Split the diffs viewer Shiki language pack into an external publishable plugin.
The diffs plugin keeps the default curated syntax set, while the new @openclaw/diffs-language-pack package carries the extended Shiki languages for npm and ClawHub distribution. The install metadata includes the external ClawHub spec, and the curated C# alias set keeps both c# and cs supported without the language pack.
Co-authored-by: Dallin Romney <dallinromney@gmail.com>
Fix non-interactive and wizard onboarding reruns so existing agent lists and bindings are preserved unless the user explicitly resets config.
Isolate legacy `plugins.installs` migration into its own write so the config size-drop allowance cannot mask unrelated config loss, while preserving new or repaired install records for the final plugin-index commit. Also keep shrinkwrap generation pinned to pnpm-locked transitive patch versions only when the dependency edge still allows that version, and isolate the tooling Vitest shard that mutates process state.
Fixes#84692.
Replaces #84748.
Co-authored-by: yetval <yetvald@gmail.com>
Suppress reasoning-prefixed silent replies before outbound delivery while preserving substantive replies that merely end with the silent token.\n\nFixes #66701.\n\nThanks @zuoanCo for the PR and @Cavadus for the report.\n\nProof: focused Vitest and pnpm check:changed passed on Testbox-through-Crabbox tbx_01ksmvfw0gk9xwh10ra1cyhzfw; CI passed for head a014eb0d91.
Fixes#87226.
Preserve the already-applied `openai` to `openai-codex` Codex runtime promotion when the persisted selection is canonical `openai` with the same model, while keeping explicit runtime provider changes switchable.
Verification:
- `node scripts/run-vitest.mjs src/agents/live-model-switch.test.ts`
- `/Users/steipete/Projects/agent-scripts/skills/autoreview/scripts/autoreview --mode branch --base origin/main`
- `pnpm check:changed` via Testbox `tbx_01ksmr59zdaqj3617w8w53xv4t` / Actions run `26512418770`
- Real behavior proof override gate: Actions run `26513059970`
Co-authored-by: Peter Lindsey <peter@lindsey.jp>
Keeps plain `openclaw status` on a bounded fast path while preserving local status metadata. The default text scan now avoids network update fetches, live channel checks, setup fallback work, and unbounded session hydration; deep/all status keeps the fuller behavior.
Behavior addressed: default status latency from update, channel, setup, and session scans
Real environment tested: GitHub Actions on PR head 98f589a35df74a7abb8327984d0103bb9f31af3e; local focused lint; autoreview
Exact steps or command run after this patch: CI workflow 26510790999; CodeQL workflow 26510790924; CodeQL Critical Quality workflow 26510791058; OpenGrep workflow 26510791138; autoreview branch against origin/main
Evidence after fix: all current-SHA workflows completed successfully; autoreview clean; local focused core oxlint passed on touched status files
Observed result after fix: default status hydrates only visible recent sessions, keeps local update metadata, and shows intentionally skipped SecretRef credentials as unknown instead of warning
What was not tested: live provider/channel roundtrip
Co-authored-by: 1052326311 <1052326311@users.noreply.github.com>
Route Telegram sendMessage action replies through durable outbound delivery so completed agent responses remain retryable when the gateway send path times out.
Verified with focused Telegram/outbound tests, extension test typecheck, prepare build/check/full test gates, and green CI rerun for head 20b45687e1.
Move vLLM Qwen thinking control onto configured model compat metadata and carry it through catalog/model-selection/runtime thinking contexts.
Also migrate legacy provider/default request params in doctor and keep Pi/runtime model rows buildable with explicit reasoning defaults.
Thanks @rendrag-git.
Co-authored-by: rendrag-git <253747599+rendrag-git@users.noreply.github.com>
Summary:
- The PR moves the runtime `HEARTBEAT.md` bootstrap template into `src/agents/templates`, keeps docs templates ... or other workspace files, adds a legacy heartbeat-template doctor repair, and updates package guards/tests.
- PR surface: Source +281, Tests +283, Docs +11, Config +1, Other 0. Total +576 across 15 files.
- Reproducibility: yes. from source inspection: current main loads `HEARTBEAT.md` from the docs template, and ... pty heartbeat file non-empty to the runtime. I did not run a live heartbeat repro in this read-only review.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(doctor): recognize heartbeat docs boilerplate
- PR branch already contained follow-up commit before automerge: fix(agents): update heartbeat workspace test
- PR branch already contained follow-up commit before automerge: fix(doctor): tighten heartbeat template repair
Validation:
- ClawSweeper review passed for head e34e85864c.
- Required merge gates passed before the squash merge.
Prepared head SHA: e34e85864c
Review: https://github.com/openclaw/openclaw/pull/85416#issuecomment-4519851630
Co-authored-by: Mason Huang <masonxhuang@tencent.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: hxy91819
Co-authored-by: hxy91819 <8814856+hxy91819@users.noreply.github.com>
Carry over #82973 and fix#81281 by preserving explicit cacheRetention for OpenAI-compatible completions providers that opt into prompt-cache-key support.
The change keeps explicit cacheRetention suppressed for OpenAI-compatible providers without compat.supportsPromptCacheKey, adds regression coverage for both paths, and updates prompt-caching docs for prompt_cache_key / prompt_cache_retention behavior.
Fixes#81281.
Supersedes #82973.
Co-authored-by: lonexreb <reach2shubhankar@gmail.com>
Fix runtime context placement so hidden runtime context is model-visible before the active user turn without persisting as a visible/session message.
Verification:
- git diff --check origin/main...origin/pr/86995-merge
- gh pr checks 86995 --repo openclaw/openclaw --watch=false
- gh run rerun 26493979156 --repo openclaw/openclaw --failed
- gh run watch 26493979156 --repo openclaw/openclaw --exit-status
- CodeQL run 26493979156 attempt 2, Security High (mcp-process-tool-boundary) job 78066719467 passed
Preserve replayability for direct Anthropic sessions whose stored assistant thinking blocks have empty or blank signatures after a newer user turn. Older invalid thinking-only assistant turns are replaced with the existing omitted-reasoning placeholder so the turn shape survives provider replay.
Also keep active tool-use continuations safe: when an assistant tool call is followed by tool results, preserve the latest assistant thinking block so signed-thinking providers can replay the current tool turn unchanged.
Proof:
- node scripts/run-vitest.mjs src/agents/pi-embedded-runner.sanitize-session-history.test.ts src/agents/pi-embedded-runner/thinking.test.ts test/scripts/openclaw-e2e-instance.test.ts
- pnpm check:changed via Blacksmith Testbox through Crabbox, tbx_01ksmfypqet50et92vdm5mmv5v, run https://github.com/openclaw/openclaw/actions/runs/26505947008
- Live Anthropic Messages replay accepted the OpenClaw-sanitized active tool-turn history with a real thinking signature.
- PR CI on 37c2e72d82 completed successfully for relevant checks.
Fixes#86886.
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Forward cache-read token counts through the OpenAI-compatible chat-completions usage shape as prompt_tokens_details.cached_tokens so clients can price cached turns correctly.
Align internal gateway usage typing with the expanded wire shape.
Thanks @caz0075.
Preserve existing `agents.list` and top-level `bindings` during ordinary onboarding reruns so rerunning `openclaw onboard` cannot silently wipe configured agents or routing bindings.
Keep config size-drop allowances scoped to explicit reset/import/plugin-install migration flows, validate binding agent ids with normalized agent ids, and add doctor repair coverage for dangling bindings that is still best-effort around malformed agent lists.
Closes#84692.
Co-authored-by: yetval <yetvald@gmail.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Closes#87181.
Direct Anthropic Messages requests now send bare Claude model ids even when OpenClaw stores them with the `anthropic/` provider prefix. Anthropic-compatible proxy and custom endpoint routes keep slash-bearing model ids unchanged so configured proxy models do not regress.
Also preserves the original parse error as `cause` in the JSONL request tail helper to keep the current CI lint gate green.
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
* fix(deepinfra): load all DeepInfra models when user wants to browse them during onboarding
* docs(deepinfra): align TTS default
* fix(deepinfra): refresh video fallbacks
* fix(deepinfra): share credential-aware catalog discovery
* test(deepinfra): narrow catalog regression types
* test(deepinfra): keep catalog narrowing across callback
* fix(deepinfra): preserve default model in live catalog
* fix(deepinfra): align default model pricing
* fix(deepinfra): keep pixverse as video default
* docs(deepinfra): match video fallback default
* fix(deepinfra): honor config api keys for live catalog
* test(e2e): wait for watchdog stdio close
* test(media): align live harness provider expectation
* fix(deepinfra): always augment custom catalogs
* test(e2e): resolve watchdog commands before spawning
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Validates forced plugin harness support for the requested provider/model before pinning Codex or any other plugin harness. This prevents an explicitly forced Codex runtime from accepting unsupported OpenAI-like providers through a hardcoded bypass while preserving implicit PI fallback and CLI runtime alias passthrough.
Regression coverage covers forced Codex rejection for unsupported openai/openai-codex support, Codex provider support declarations, CLI attempt routing, pi-embedded auth/profile forwarding fakes, Testbox scenario probes, and live Docker Codex plugin E2E.
Thanks @cathrynlavery.
Keep macOS Homebrew setup lazy so users with supported Node and Git can install without admin/Homebrew, while still installing Homebrew before macOS Node or Git package installs.
Updates installer docs and adds focused install.sh coverage for the lazy Git path. Also aligns the live-media provider expectation with current main so built-artifact checks stay green.
Fixes#83232
Co-authored-by: Sebastien Tardif <sebtardif@ncf.ca>
* fix(agents): suppress Write/Edit failed warning on response-timeout false-failure (#55424)
Reporter sees '⚠️ Write failed' / '⚠️ Edit failed' warnings on Feishu (and other channels) even though the file was 100% saved successfully (8 of 8 verified writes succeeded; warning shown for all 8). Source path: tool-mutation records lastToolError.timedOut=true with a fileTarget when a write/edit tool ack reply times out after the disk mutation has already completed, then resolveToolErrorWarningPolicy goes through the default mutating-tool branch and emits the misleading failure summary.
Add a narrow gate inside resolveToolErrorWarningPolicy that suppresses the warning only when both lastToolError.timedOut is true AND lastToolError.fileTarget is defined. fileTarget is set by tool-mutation.ts only for the write/edit family (FILE_MUTATING_TOOL_NAMES), so this branch never matches exec/message/cron/gateway mutating-tool timeouts where the disk-write idempotency reasoning does not apply. Real file failures (no timeout) and timeouts without recorded fileTarget keep their visible warnings.
* fix: recover completed write timeouts safely
* fix: bound write timeout recovery precheck
* fix: type write recovery precheck fallback
* test: complete write recovery result mock
* test: isolate e2e timeout fixture shims
* test: stabilize e2e timeout fixture path
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Clarify that OpenAI Realtime voice is billed through OpenAI Platform credits, not Codex/ChatGPT subscription quota, for Voice Call and Control UI Talk.
Document the direct Platform API key path, the `openai-codex` OAuth client-secret path, the quota symptom, and the Platform billing fix. Keep the changelog note crediting @lonexreb.
Closes#76498.
Co-authored-by: lonexreb <reach2shubhankar@gmail.com>
Keep the Codex app-server full attempt watchdog armed after a terminal turn notification is queued, so a wedged notification projector cannot leave a run stuck indefinitely.
Proof:
- `git diff --check origin/main...HEAD`
- `node scripts/run-oxlint.mjs extensions/codex/src/app-server/run-attempt.ts extensions/codex/src/app-server/run-attempt.test.ts`
- `node scripts/run-vitest.mjs run extensions/codex/src/app-server/run-attempt.test.ts --testNamePattern "keeps the attempt watchdog armed"` passed in PR proof (`1 passed | 232 skipped`)
- `OPENCLAW_TESTBOX=1 pnpm check:changed` passed in `tbx_01kskyg44ej461k574jee8ffjc`
- CI required checks green after `build-artifacts` rerun job `78031279635` passed
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Fix Claude CLI skill prompt handling so native skill plugin materialization is prepared before prompt suppression, with the prompt fallback preserved when plugin args are unavailable. Also keeps direct prepared-run callers covered by an execute-time fallback.
Fixes#87063.
Co-authored-by: uday <udaymanish.thumma@gmail.com>
Regression test for the binary stall fix: when rawResponseItem/completed
arrives with a non-assistant type (e.g. "reasoning") and all tracked
items have completed, the completion idle watch must stay armed so the
stall is caught in 60s, not 30 minutes.
Refs openclaw/openclaw#87071
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the codex binary emits rawResponseItem/completed and all tracked
items have completed (activeTurnItemIds empty, no active requests), the
binary should deliver turn/completed imminently. Previously, a
rawResponseItem/completed that didn't qualify as a post-tool assistant
completion would actively disarm the completion idle watch, leaving only
the 30-minute terminal timeout to catch a stalled binary. This caused
turns to hang for up to 30 minutes when the OpenAI Responses API fails
to deliver response.completed to the binary.
Now, rawResponseItem/completed with no active items arms the 60s
completion idle watch and is excluded from the disarm path, so stalled
binaries are detected in 60s instead of 30 minutes.
Refs openclaw/openclaw#87071
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Restart stale local node-host processes when they reconnect to a newer gateway with a released-version mismatch, so launchd/systemd can restart them with updated code instead of leaving old dynamic imports alive.
Adds gateway mismatch detail propagation, node-host terminal pause handling, and regression coverage for the GatewayClient reconnect-pause path.
Verification:
- node scripts/run-vitest.mjs run src/gateway/client.test.ts -t 'CLIENT_VERSION_MISMATCH' --reporter=verbose
- node scripts/run-vitest.mjs run src/gateway/server.node-version-mismatch.test.ts src/node-host/runner.credentials.test.ts src/gateway/client.test.ts --reporter=verbose
- /Users/steipete/Projects/agent-skills/skills/autoreview/scripts/autoreview --mode local
- Crabbox AWS run_292dcbfd78d9: focused GatewayClient mismatch regression plus server/node-host mismatch tests passed
Co-authored-by: scotthuang <scotthuang@tencent.com>
Persist trailing `/model ...@profile` suffixes through the gateway session patch path so documented per-session credential pinning reaches the session entry. Strip the suffix before model resolution so bare allowlisted model IDs still infer their configured provider, and mark same-model profile-only changes as pending live model switches.
Closes#87099.
Verification:
- `npx oxfmt --check src/sessions/model-overrides.ts src/sessions/model-overrides.test.ts src/gateway/sessions-patch.ts src/gateway/sessions-patch.test.ts`
- `node scripts/run-vitest.mjs src/gateway/sessions-patch.test.ts src/sessions/model-overrides.test.ts`
- `npx oxlint src/sessions/model-overrides.ts src/sessions/model-overrides.test.ts src/gateway/sessions-patch.ts src/gateway/sessions-patch.test.ts`
- `/Users/steipete/Projects/agent-scripts/skills/autoreview/scripts/autoreview --mode branch --base origin/main`
- `gh pr checks 87123 --watch --fail-fast`
Co-authored-by: xin zhuang <65798732+1052326311@users.noreply.github.com>
Fix Codex OAuth-backed OpenAI compaction routing by separating the configured provider from the runtime auth provider, preserving same-provider fallback auth, and keeping OpenAI context policy lookup intact. Also preserves the original cause when sessions.send reports A2A fallback failure. Fixes#86373.
Summary:
- Enforces /allowlist config and pairing-store writes against the real command origin plus the selected target.
- Adds regressions for disabled Telegram-origin commands targeting an enabled Discord allowlist.
Verification:
- node scripts/run-vitest.mjs src/auto-reply/reply/commands-allowlist.test.ts
- pnpm check:changed via Blacksmith Testbox tbx_01ksm06e82dnpxmnj00hrt6xzd
- autoreview --mode local clean, no accepted/actionable findings
- GitHub PR checks green on 42a38d2b00Closes#72360.
Thanks @coygeek.
Co-authored-by: Coy Geek <65363919+coygeek@users.noreply.github.com>
Co-authored-by: opencode <opencode@users.noreply.github.com>
Remove the hidden 15s default from reply-run idle waits so visible user turns do not inherit cleanup-settle behavior while waiting behind an active same-session reply operation.
Keep the 15s timeout explicit for queued follow-up retry/defer paths and interrupt/reset cleanup waits, and add reply-admission regressions for both visible and queued follow-up behavior. Also preserve the original cause on a nearby sessions-send fallback error to keep current lint green after rebasing onto main.
Thanks @keshavbotagent.
Co-authored-by: Keshav's Bot <keshavbotagent@gmail.com>
Fix run-scoped sessions_send active-run fallback handling.
- surface active queue rejection plus durable fallback admission failures instead of returning accepted too early
- return fallback run/session metadata so normal A2A announcement waits on the fallback run
- retry active steering without transcript-commit waiting when the active runtime does not support it
Thanks @TurboTheTurtle.
Verification:
- node scripts/run-vitest.mjs src/agents/openclaw-tools.sessions.test.ts
- pnpm check:test-types
- git diff --check
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
The Windows Gateway daemon crashes (or rather is killed by Task Scheduler) every time the laptop unplugs from AC power. Reporter on Windows 10 22H2 documented a 100% failure rate.
Root cause: `activateScheduledTask` in `src/daemon/schtasks.ts` used `schtasks /Create` with CLI flags (`/SC ONLOGON /RL LIMITED /TR ...`). That CLI surface cannot set `<DisallowStartIfOnBatteries>` or `<StopIfGoingOnBatteries>`, so the task inherits the Task Scheduler defaults (both `true`), which prevent the task from starting on battery and stop it when AC power is lost mid-run.
This change switches `/Create` to `/Create /XML <tempfile>` and emits a Task Scheduler XML payload that mirrors the prior CLI flags (ONLOGON trigger, LeastPrivilege run level, InteractiveToken logon when a `taskUser` is resolved, single-instance policy, no idle restrictions, exec action wired to the existing `gateway.cmd` / `gateway.vbs` launcher) AND sets:
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
The XML is written as UTF-16 LE with a BOM, which is what `schtasks /XML` expects on all Windows locales. The temp file is cleaned up in a `finally` block.
The same XML re-apply is also issued from `updateExistingScheduledTask` after the existing `/Change /TR` call, so users upgrading from older versions inherit the new battery flags on the next gateway install/refresh instead of staying broken until a full uninstall+reinstall.
This follows clawsweeper's direction on #59299: "Land a narrow Windows Scheduled Task settings repair that lets the Gateway task start and continue on battery while preserving the current Startup-folder fallback, hidden launcher, quoting, and update behavior."
Preserved unchanged:
- Startup-folder fallback when `/Create` is denied or times out
- Hidden launcher (.vbs) selection via `OPENCLAW_WINDOWS_TASK_HIDDEN_LAUNCHER`
- `quoteSchtasksArg` quoting strategy for the script launch path
- `/Change` update path semantics (still updates `/TR` first)
- All `runScheduledTaskOrThrow` and fallback launch behavior downstream
Verification:
- `node scripts/run-vitest.mjs src/daemon/schtasks.install.test.ts` — 12 passed (incl. 2 new battery-flag regression tests)
- `node scripts/run-vitest.mjs src/daemon/schtasks.test.ts src/daemon/schtasks.startup-fallback.test.ts src/daemon/schtasks.stop.test.ts src/daemon/schtasks-exec.test.ts` — 54 passed (sibling daemon coverage)
- `pnpm tsgo:core` — passed (production typecheck)
Closes#59299
Derive explicit source-reply command turns from authorized control-command bodies when legacy command source metadata is missing.
Preserve native/text structured command semantics, keep unauthorized native commands and structured normal command bodies on plugin-owned fallback paths, and pass bot username normalization through the derived detection.
Co-authored-by: Alex Knight <aknight@atlassian.com>
Bounds nonessential installer finalization probes so npm prefix and daemon-status checks warn and fall back instead of hanging setup.
Thanks @giodl73-repo!
Behavior addressed: doctor hooks model validation now loads the model catalog read-only, so lint/doctor can warn without writable catalog side effects.
Real environment tested: local temp merged tree on current origin/main.
Exact steps or command run after this patch: node scripts/run-vitest.mjs src/flows/doctor-core-checks.test.ts src/flows/doctor-health-contributions.test.ts --reporter=dot; ./node_modules/.bin/oxfmt --check --threads=1 src/flows/doctor-core-checks.ts src/flows/doctor-health-contributions.ts src/flows/doctor-core-checks.test.ts src/flows/doctor-health-contributions.test.ts; ./node_modules/.bin/oxlint src/flows/doctor-core-checks.ts src/flows/doctor-health-contributions.ts src/flows/doctor-core-checks.test.ts src/flows/doctor-health-contributions.test.ts; git diff --check origin/main <merged-tree>
Evidence after fix: 2 test files passed, 30 tests passed; oxfmt passed; oxlint passed; diff check passed.
Observed result after fix: hooks.gmail.model doctor paths call loadModelCatalog with readOnly true in both structured and legacy health surfaces.
What was not tested: GitHub Actions run details could not be refreshed because the Actions API was rate-limited; gh reported no required checks for the branch.
Thanks @giodl73-repo.
Co-authored-by: Gio Della-Libera <giodl73@gmail.com>
Keep Windows node service stop/restart/status from treating the gateway listener port as node-owned runtime evidence. Node Scheduled Task and Startup fallback paths now match the installed node host command line before reporting or terminating a node runtime, so WSL2 gateway loopback connectivity is not disturbed by node lifecycle commands.
Fixes#85289.
Verification:
- node scripts/run-vitest.mjs src/daemon/schtasks.startup-fallback.test.ts src/daemon/schtasks.stop.test.ts
- git diff --check
Co-authored-by: Gio Della-Libera <giodl73@gmail.com>
Stage remote iMessage attachments before media understanding so the image pipeline receives local remote-cache paths instead of raw macOS Messages paths.
Fixes#87089
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Fix stale `subagent_announce` history hydration after `/new` by filtering pre-session-start announce/user reply pairs before `chat.history` projection.
Maintainer fixups added:
- require the adjacent assistant reply to carry a pre-session timestamp before dropping it
- preserve record timestamps for oversized transcript placeholders
- run the filter after Claude CLI history import and support imported timestamp/text fallback
- overread one local transcript message only as boundary context so limit-window edges do not leak stale assistant replies
Verification:
- `git diff --check`
- `node scripts/run-vitest.mjs src/gateway/server-methods/server-methods.test.ts src/gateway/session-utils.fs.test.ts src/gateway/session-history-state.test.ts src/gateway/cli-session-history.test.ts src/gateway/server.chat.gateway-server-chat-b.test.ts` -> 11 files, 463 tests passed
- `/Users/steipete/Projects/agent-scripts/skills/autoreview/scripts/autoreview --mode branch --base origin/main` -> clean, no accepted/actionable findings
Thanks @openperf.
Fix gateway/chat timeout abort propagation so timed-out runs do not cascade through fallbacks. Preserve provider timeout errors when the gateway abort signal did not fire, and keep timeout stop reasons in async gateway agent results. Includes regression coverage for chat, follow-up, memory flush, fallback classification, and gateway agent timeout results. Fixes#83962.
* fix(plugin-sdk): use Function.name to find onDiagnosticEvent export
normalizeDiagnosticEventsModule hardcodes `mod.r` as the fallback alias
for onDiagnosticEvent, but the bundler reassigns export aliases across
builds. On 2026.5.25-beta.1, `r` is emitFailoverEvent — calling it as
onDiagnosticEvent returns a non-function, so the combo unsubscribe
closure throws TypeError on every gateway stop.
Replace the hardcoded letter with Function.name introspection. JS
functions retain their original .name regardless of export aliasing,
so this survives bundler alias changes.
Fixes#87082
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(plugin-sdk): cover diagnostic event alias shifts
* fix(plugin-sdk): harden diagnostic alias cleanup
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Recover idle queued sessions whose diagnostic activity retained stale ownerless model or tool calls by classifying them as recoverable session.stuck after the usual recovery gates. Yield the event loop before stale session-lock process inspection so sync process lookup cannot monopolize lock contention paths.
Docs now describe the widened session.stuck telemetry contract for recoverable stale bookkeeping, including ownerless activity. Thanks @samuelsoaress.
Refs #84903.
Co-authored-by: samuelsoaress <samuelsoares177778@gmail.com>
Summary:
- Resolve inbound media references through the shared media-reference path before workspace-relative handling.
- Reuse the same sandbox rewrite for Pi native images and sandbox media bridge paths.
- Add regression coverage for managed inbound images, sandbox-staged media references, and invalid media IDs.
- Fix current lint by using non-mutating cpuprofile sorting.
Verification:
- node scripts/run-vitest.mjs src/media/media-reference.test.ts src/agents/sandbox-media-paths.test.ts src/agents/pi-embedded-runner/run/images.test.ts src/agents/tools/image-tool.test.ts src/media/web-media.test.ts src/agents/tools/pdf-tool.test.ts src/agents/tools/image-generate-tool.test.ts src/agents/tools/video-generate-tool.test.ts src/agents/tools/music-generate-tool.test.ts
- node scripts/run-oxlint-shards.mjs --threads=8
- git diff --check
- /Users/steipete/Projects/agent-skills/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- GitHub CI rollup passed for eceea707a7Fixes#87024.
Supersedes #87055; thanks @TurboTheTurtle for the report and initial fix direction.
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Release the embedded attempt session lock before sessions_yield abort cleanup waits for session events and rewrites yielded-parent artifacts.
This keeps the existing bounded settle wait while preventing child completion callbacks from contending on the coarse parent transcript lock.
Adds focused session-lock lifecycle coverage.
Refactor memory close provider draining so providers created during shutdown are closed through the same bounded retry path.
Co-authored-by: spacegeologist <zheng.zuo0@gmail.com>
Honor the selected session agent's thinkingDefault for ingress agent runs before global fallback.
Also keep session store cache object-clone writes parse-free while matching persisted JSON shape when cloning values.
Fixes#86669
Co-authored-by: ai-hpc <mail.speedy.hpc@hotmail.com>
Guarantee MCP stdio child cleanup during Gateway shutdown by sending a synchronous SIGKILL when the child survives the existing stdin and SIGTERM waits. This prevents SIGTERM-ignoring local MCP processes from outliving the Gateway when killProcessTree's unref'd SIGKILL timer would otherwise lose the shutdown race.
Fixes#86412.
Verification:
- GitHub CI green on relevant agent/runtime, lint/type, CodeQL/security, OpenGrep, and Real behavior proof checks.
- Real behavior proof: https://github.com/openclaw/openclaw/actions/runs/26430512156/job/77802651894
- Maintainer manual review: no blocking findings.
Thanks @openperf.
Co-authored-by: openperf <16864032@qq.com>
Fix cron delivery previews for no-delivery jobs that still provide explicit message-tool targets.
- Reuse one cron delivery-plan explicit-target predicate across preview and isolated-agent runtime paths.
- Treat numeric threadId 0 as an explicit delivery target.
- Avoid fail-closed wording for unresolved message-tool-only targets.
Thanks @Alix-007 for the fix.
Co-authored-by: Alix-007 <267018309+Alix-007@users.noreply.github.com>
Copy plugin-provided skills from their validated real target into sandbox workspaces while keeping prompt-visible skill paths sandbox-local.
Adds regression coverage for symlinked plugin skills, multiple plugin skill roots, escaped symlink targets, and sandbox prompt paths that must not leak host plugin-skill locations.
Refs #86190
Remove the proposed public `maxReseedHistoryChars` config surface and scale Claude CLI reseed history automatically from the resolved context tier instead.
Claude CLI 200K-context runs now keep a 64K-character reseed slice, 1M Opus/Sonnet runs use the bounded 256KiB cap, and non-Claude CLI backends keep the existing 12KiB default. This preserves the intended long-context behavior without adding another config option.
Verification:
- `node scripts/run-vitest.mjs src/agents/cli-runner/session-history.test.ts src/agents/cli-runner/prepare.test.ts`
- `node scripts/run-vitest.mjs src/agents/cli-runner/prepare.test.ts -t "automatic Claude CLI cap"`
- `node scripts/run-oxlint.mjs src/agents/cli-runner/prepare.ts src/agents/cli-runner/prepare.test.ts src/agents/cli-runner/session-history.ts src/agents/cli-runner/session-history.test.ts src/config/types.agent-defaults.ts src/config/zod-schema.core.ts`
- `pnpm check:changed` via Testbox `tbx_01kska2twjxb925xft9dj82hvb`
- GitHub PR checks green
Closes#83985
Co-authored-by: Abdel Gomez-Perez <nabdel07@icloud.com>
Generate the public config JSON Schema from accepted input shapes so transform-backed fields remain renderable in the Control UI. Keep transform output schemas representable with explicit string pipes, align analyzer metadata handling, and cover the generated schema plus browser-safe UI render shapes.
Co-authored-by: Altay <altay@hey.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Keep the Logs page from rendering competing outer page and inner log-stream scrollbars. The Logs route now opts into an explicit content class for desktop fill-height layout, while mobile keeps the single-page scroll behavior with the capped log panel.
Also adds regression coverage for the route class and CSS ownership selectors.
Co-authored-by: Brian potter <brian@potterdigital.com>
Preserve native slash-command laziness while allowing `/skill` to load workspace skill commands asynchronously when needed. The loaded command list is reused for downstream native skill dispatch so valid `/skill <name>` calls do not get misclassified as unknown.
Verification:
- git diff --check
- fnm exec --using v24.15.0 -- pnpm changed:lanes --json
- .agents/skills/autoreview/scripts/autoreview --mode local
- GitHub CI rollup success for c0d778d512
Co-authored-by: Keshav's Bot <keshavbotagent@gmail.com>
Fixes#86007.
Release note: Windows gateway install/update now ignores a persisted OPENCLAW_WRAPPER when it points back at the generated gateway.cmd task script, preventing recursive gateway startup while keeping valid wrapper installs intact.
Credit: thanks @luoyanglang for the fix and proof.
* fix(gateway): reject RPCs from invalidated device-token clients during rotation/revoke race
device.token.rotate, device.token.revoke and device.pair.remove all
respond 200 OK to the admin, then schedule disconnectClientsForDevice
via queueMicrotask so the response can flush before the socket close.
That microtask window plus the absence of a per-RPC re-check for
device-token auth (unlike shared-auth, which gets checked at
message-handler.ts:1444-1458) created a race: an attacker with RPCs
already pipelined in the WS socket buffer could land a few more
authenticated operations with the rotated/revoked token before the
socket actually closed.
Fix: add a cheap in-memory 'invalidated' flag on GatewayWsClient and
mark it synchronously *before* responding in the three handlers. Add
a mirror check at the start of the per-RPC dispatch that force-closes
the client if the flag is set, regardless of whether socket.close()
has taken effect yet. Disconnect still happens via queueMicrotask so
the admin's rotate/revoke response flushes normally.
Introduces context.invalidateClientsForDevice(deviceId, opts) as a
sync companion to the existing disconnectClientsForDevice. Also
defense-in-depth: disconnectClientsForDevice now sets the flag too,
so any other caller of the hard-disconnect path gets the per-RPC
gate for free.
* test(gateway): use vi.mocked instead of direct Mock casts in devices tests
check-test-types failed on the PR because direct 'as ReturnType<typeof vi.fn>' casts from RespondFn (or the optional context methods) don't structurally overlap with the Mock type — Mock has mockImplementation/mockReturnValue that RespondFn lacks, so strict tsgo rejects the conversion. vi.mocked() is the intended helper for reinterpreting an already-mocked function, and drops through to the Mock surface cleanly.
* test(gateway): align tests with upstream type/shape changes after rebase
After rebasing onto upstream main, two test surfaces drifted:
1. GatewayRequestContextParams gained two required fields upstream
(getRuntimeConfig, broadcastVoiceWakeRoutingChanged). The
makeContextParams test helper was missing them, so every consumer
tripped tsgo with a missing-field error. Add both as vi.fn()
stubs.
2. revokeDeviceToken's return shape changed upstream from a bare
entry record to a discriminated union {ok: true, entry: ...} | {ok:
false, reason}. The new device.token.revoke synchronous-invalidate
test still mocked the old shape, so the production handler took the
!revoked.ok branch and never reached the invalidateClientsForDevice
call the test asserted. Update the mock to the new union shape.
Also fix three new Set([...] as never) sites in server-request-
context.test.ts that produced Set<unknown> rather than Set<never>.
Move the cast outside the Set constructor so the literal stays
inferred while the wrapper is type-erased to never, which is
assignable to the Partial<GatewayRequestContextParams> clients field.
* fix(gateway): export GatewayRequestContextParams for test access
* fix(ci): resolve check-test-types and lint failures from PR #70707 branch
- server-request-context.test.ts: hasConnectedMobileNode → hasConnectedTalkNode
(field renamed in server-request-context.ts but test fixture not updated)
- status.summary.redaction.test.ts: add configuredModel/selectedModel/
modelSelectionReason to createRecentSessionRow fixture
(SessionStatus gained these fields in a13468320c; test was not updated)
- video-generation-providers.live.test.ts: replace empty {} fallbacks in
conditional spreads with undefined (oxlint 1.65.0, 5 occurrences)
- music-generation-providers.live.test.ts: same fix for 4 occurrences
Remaining CI failures (FsSafeError/Python helper, media tests, Windows ACL,
session-memory hooks) are pre-existing infra failures unrelated to this PR.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): add missing GatewayRequestContextParams fields to test fixture
chatDeltaLastBroadcastText, agentDeltaSentAt, and bufferedAgentEvents are
required fields in GatewayRequestContextParams but were absent from the
makeContextParams fixture, causing TS2322 in check-test-types.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* fix(gateway): serialize credential invalidating RPCs
---------
Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Apply diagnostics.otel.flushIntervalMs to OpenTelemetry trace batching so short-lived Windows and QA runs do not lose late lifecycle/model spans. Also make the OTel QA smoke wait for required telemetry and print bounded failure diagnostics.
Keep model browse/list visibility consistent with runtime-normalized allowlist entries while keeping unrestricted default browse off plugin/runtime hydration. Add regression coverage for catalog visibility, `/models` browse data, and the replay sanitizer mock isolation that made the agents shard order-sensitive.
Verification:
- pnpm test src/agents/pi-embedded-runner.sanitize-session-history.test.ts src/agents/model-catalog-visibility.test.ts src/auto-reply/reply/commands-models.test.ts src/auto-reply/reply/model-selection.test.ts src/agents/model-selection.plugin-runtime.test.ts -- --reporter=verbose
- OPENCLAW_VITEST_MAX_WORKERS=2 pnpm exec node scripts/test-projects.mjs test/vitest/vitest.agents-core.config.ts
- .agents/skills/autoreview/scripts/autoreview --mode local
- GitHub Actions CI run 26476126784
* fix(telegram): preserve command slots for aliases
* fix: report Telegram alias command overflow
* fix: preserve Telegram alias menu order
* docs: drop release-owned changelog entry
---------
Co-authored-by: wuyangfan <yangfan.wu@succaiss.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Ensure deferred context-engine maintenance rejects cleanly when the gateway command queue is draining, including coalesced active-run requests. This prevents budget compaction from treating an unscheduled deferred maintenance run as successful and leaving the context engine alive.
Verification:
- pnpm exec oxfmt --check --threads=1 src/process/command-queue.ts src/agents/pi-embedded-runner/compact.queued.ts src/agents/pi-embedded-runner/context-engine-maintenance.ts src/agents/pi-embedded-runner/context-engine-maintenance.test.ts
- pnpm test src/auto-reply/reply/agent-runner-memory.test.ts src/agents/pi-embedded-runner/compact.hooks.test.ts src/agents/pi-embedded-runner/context-engine-maintenance.test.ts src/tasks/task-flow-registry.store.test.ts src/auto-reply/reply/commands-compact.test.ts src/agents/pi-embedded-runner/compact-reasons.test.ts
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- GitHub Actions CI run 26475226442: relevant Node/Linux, lint, type, security, CodeQL, OpenGrep, Socket, Real behavior proof, and build jobs passed; Windows job failed before tests due current runner image Node 22.19.0 vs required 24.x, matching current main infra failure.
Fixes#86814.
Reclaims stale plugin lock files only when the previous owner is provably gone or the recorded process start time proves PID reuse. Timestamp age alone now stays fail-closed for PID-owned locks, preserving mutual exclusion for long-running writers while still allowing pidless expired locks to expire.
Verification:
- pnpm test src/infra/stale-lock-file.test.ts src/plugin-sdk/file-lock.test.ts
- pnpm tool-display:check
- git diff --check
- autoreview --mode branch --base origin/main
Known CI note: check-guards failed in deps:shrinkwrap:check because npm resolved newer AWS transitive versions than pnpm-lock.yaml contains; no package or lock files are changed in this PR.
Co-authored-by: Alix-007 <267018309+Alix-007@users.noreply.github.com>
Remove the transcript redaction path for sessions_spawn arguments and inline attachments. OpenClaw transcripts are local trusted-operator state, and streamTo/resumeSessionId are runtime routing fields that must not be rewritten before replay or dispatch.
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Runtime-injected web_search provider config from plugins.entries.<plugin>.config.webSearch now stays available to provider execution without being validated as user-authored legacy tools.web.search.<provider> config.
Co-authored-by: luoyanglang <hanwanlonga@gmail.com>
Preserve legacy numeric stable git tags while excluding named semver prerelease tags from stable git channel detection and status display.
Thanks @goldmar.
Memoize owner process argv lookups per PID during `cleanStaleLockFiles`, and yield between lock entries so startup cleanup does not monopolize the event loop while inspecting many session locks.
This keeps lock classification semantics unchanged while avoiding repeated synchronous process-args reads for lock clusters owned by the same PID, especially the Windows PowerShell path.
Fixes#86509.
Verification:
- `git diff --check origin/main...HEAD`
- focused TSX harness against the current-main merge result: `session-lock memo regression harness passed`
Thanks @openperf.
Co-authored-by: openperf <16864032@qq.com>
Project newer external OpenClaw chat history into resumed Codex app-server threads when the saved binding is older than user-visible transcript messages, while filtering Codex-owned mirror records on consecutive resumes.
Thanks @TurboTheTurtle!
Keep Codex app-server turn timeouts within the Codex runtime boundary so they interrupt the active turn without retiring the shared app-server client, poisoning auth-profile cooldowns, or falling through to generic provider/model fallback.
Preserve concrete non-timeout provider failures for auth-profile rotation and fallback, and add regression coverage for prompt-stage timeouts, assistant idle timeouts, auth-profile cooldowns, and app-server timeout handling.
Thanks @pashpashpash.
Fixes#74061.
Stages absolute final-reply MEDIA paths that already live under the agent workspace before sandbox path translation runs, so Telegram/local delivery can attach generated workspace media instead of dropping it as Media failed. Outside-workspace host-local paths remain blocked, and host-read HTML stays denied pending separate security-boundary review.
Verification:
- git diff --check origin/main...refs/remotes/pull/86531
- git merge-tree --write-tree origin/main refs/remotes/pull/86531
- reviewed src/auto-reply/reply/reply-media-paths.ts, src/media/web-media.ts, and focused tests
Co-authored-by: mjamiv <74088820+mjamiv@users.noreply.github.com>
Remove the Telegram DM thread reply policy config and use Telegram bot capability as the single source of truth for DM topic session splitting.
DM messages with message_thread_id now split into thread-scoped sessions only when Telegram getMe reports has_topics_enabled for the bot. Doctor removes retired dm.threadReplies and direct.*.threadReplies keys, docs explain the upgrade behavior, and startup keeps cached bot info as a non-auth fallback when a fresh probe fails.
Refs #86513.
Thanks @alexph-dev.
Verification:
- pnpm docs:list
- pnpm exec oxfmt --check --threads=1 extensions/telegram/src/channel.ts extensions/telegram/src/channel.gateway.test.ts extensions/telegram/src/doctor-contract.ts extensions/telegram/src/doctor.test.ts
- git diff --check
- node scripts/run-vitest.mjs extensions/telegram/src/channel.gateway.test.ts extensions/telegram/src/doctor.test.ts extensions/telegram/src/bot/helpers.test.ts extensions/telegram/src/bot-message-context.dm-threads.test.ts extensions/telegram/src/config-schema.test.ts
- pnpm config:channels:check
- pnpm config:docs:check
- .agents/skills/autoreview/scripts/autoreview --mode local
- GitHub Actions: CI 26468039803, Workflow Sanity 26468040057, OpenGrep 26468039472, Real behavior proof 26468036483, CodeQL 26468039466, CodeQL Critical Quality 26468039473
Known CI caveat: checks-windows-node-test failed before tests because Windows runner setup left Node 22.19.0 active while the job requested Node 24.x; the same setup failure is present on current main CI run 26468063947.
Reworks the Codex app-server native thread reuse guard so OpenClaw no longer adds a user-facing token config. Token clearing now prefers Codex's reported model context window, falls back to a high internal recovery fuse, and preserves context-engine thread-bootstrap reuse while keeping byte guard behavior intact.
Verification:
- `fnm exec --using v24.15.0 -- node scripts/run-vitest.mjs run extensions/codex/src/app-server/run-attempt.test.ts extensions/codex/src/app-server/run-attempt.context-engine.test.ts --reporter=dot --pool=forks --no-file-parallelism`
- `git diff --check`
- `.agents/skills/autoreview/scripts/autoreview --mode local --base origin/main`
- Testbox `check:changed`: `tbx_01ksjm1hy7mfrc5bebzyckqdew`, GitHub Actions run https://github.com/openclaw/openclaw/actions/runs/26463150977, exit 0
- PR CI green after rerunning unrelated `checks-node-agentic-agents` flake and stuck OpenGrep scan
Co-authored-by: Eva (agent) <eva+agent-78055@100yen.org>
* fix: validate wide-area dns domains
* addressing codex review
* fix(dns-cli): throw explicit DNS-name error on invalid --domain
resolveWideAreaDiscoveryDomain catches the validation error from
normalizeWideAreaDomain and returns null, so dns setup --domain foo/bar
fell through to the "No wide-area domain configured" branch instead of
surfacing the invalid-domain diagnostic. Validate explicit CLI/config
input directly so the user-facing setup command reports the actual
problem; preserve the resolver's silent env-fallback semantics for the
background callers that depend on graceful degradation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(gateway): lock in graceful degrade on invalid wide-area config
Drive startGatewayDiscovery through the real resolveWideAreaDiscoveryDomain
with wideAreaDiscoveryDomain: "foo/bar" so the test exercises the actual
swallow-and-return-null path. Asserts the operator-facing warning is
logged, writeWideAreaGatewayZone is never called, and startup completes
without throwing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(gateway): type resolveWideAreaDiscoveryDomain mock to match real signature
vi.fn(() => "openclaw.internal.") inferred the mock as `() => string`, so
mockImplementationOnce(realResolver) tripped tsgo:core:test with TS2345.
Apply the same vi.fn<typeof ...>(...) pattern the file already uses for
writeWideAreaGatewayZone.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(changelog): note dns validation fix
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Agustin Rivera <agustin@rivera-web.com>
Updates Discord voice Opus callers to the published libopus-wasm 0.1.0 API, pins the Discord plugin dependency and lockfiles to that release, keeps the package freshness exception version-scoped, treats expected Discord receive-stream premature closes as normal stream ends, and includes routed OpenClaw transcript roots for local PR transcript discovery.\n\nProof: npm view libopus-wasm@0.1.0; pnpm install --lockfile-only --filter @openclaw/discord; Node encode/decode smoke with pkg 0.1.0 decoded=3840; node scripts/run-vitest.mjs extensions/discord/src/voice/audio.test.ts extensions/discord/src/voice/receive-recovery.test.ts; git diff --check; autoreview clean; live tmux gateway on e0fa3e3 joined Discord voice and processed realtime audio without decoder.decode or Premature close warning spam.
Guard loadUsage in the Control UI overview secondary refresh so stale overview loads do not start the expensive usage.cost RPC after the user has navigated away. Active overview usage loading is preserved.
Fixes#86392.
Thanks @Marvinthebored for the report, live gateway proof, and patch.
Verification:
- CI=1 OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS=120000 fnm exec --using v24.15.0 -- node scripts/run-vitest.mjs run ui/src/ui/app-settings.refresh-active-tab.node.test.ts --reporter=dot --pool=forks --no-file-parallelism
- GitHub PR checks green on d52d8d10da, including Real behavior proof and checks-node-core-ui.
Co-authored-by: Marvinthebored <262704729+Marvinthebored@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Move meeting notes into core transcripts, remove the bundled meeting-notes plugin/API, and require explicit transcripts.enabled before exposing the recording-capable tool.
Fix outbound message actions so structured attachments[] media participates in existing sandbox, local-root, and hydration checks. Single-attachment actions select structured attachments only when no top-level or plugin media source wins, while send collects all structured attachments. Proof: git diff --check; pnpm tsgo:core && pnpm tsgo:test:src; direct selector/hydration probe; autoreview clean.
Tag authorized Mattermost typed text-slash control commands with CommandSource: text so existing explicit-command source-reply delivery bypasses message_tool_only suppression for /new, /reset, ACP reset, and soft-reset acknowledgement replies.
Remove the normal PR changelog edit flagged by review and keep release-note context in the PR body/squash message. Tighten the regression test to exercise the leading-space Mattermost text-post path used to bypass native slash handling and assert the normalized command body.
Local proof: node scripts/run-vitest.mjs extensions/mattermost/src/mattermost/monitor.inbound-system-event.test.ts src/auto-reply/command-turn-context.test.ts src/auto-reply/reply/source-reply-delivery-mode.test.ts src/auto-reply/reply/commands-reset-hooks.test.ts; git diff --check origin/main..HEAD; oxfmt check; autoreview clean.
CI: PR run 26443271650 passed relevant checks. Ignored check-test-types failure because the exact same extensions/codex/src/app-server/run-attempt.test.ts TS2345 failure is already present on main run 26442926352 at the PR base.
Fixes#86664.
* fix(imessage): send group media via attachment command
* fix(imessage): preserve media rpc fallback
---------
Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
Summary:
- The PR updates diagnostics to mark streamed model chunks as run progress, keeps silent model calls abortable after the stuck-session timeout, and adds regression coverage for stream progress and recovery behavior.
- PR surface: Source +54, Tests +229. Total +283 across 6 files.
- Reproducibility: yes. at source level: current main tracks model-call start/end activity but streamed chunks ... covery keys on stale lastProgressAgeMs. I did not run a live local-provider repro in this read-only review.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(diagnostics): track model stream progress
- PR branch already contained follow-up commit before automerge: test(diagnostics): cover silent local model aborts
- PR branch already contained follow-up commit before automerge: fix(diagnostics): skip stream progress when disabled
Validation:
- ClawSweeper review passed for head fcc74d9869.
- Required merge gates passed before the squash merge.
Prepared head SHA: fcc74d9869
Review: https://github.com/openclaw/openclaw/pull/86757#issuecomment-4540111930
Co-authored-by: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: osolmaz
Co-authored-by: osolmaz <2453968+osolmaz@users.noreply.github.com>
Summary:
- The PR adds runtime-only external OAuth provenance to auth-profile stores, updates save/merge/read paths to ... e profiles in active snapshots while filtering disk persistence, and expands auth-profile regression tests.
- PR surface: Source +381, Tests +974. Total +1355 across 8 files.
- Reproducibility: yes. from source: current main writes the disk-filtered localStore into an existing runtime ... tches the reported credential drop path. I did not run a failing current-main repro in this read-only pass.
Automerge notes:
- PR branch already contained follow-up commit before automerge: Preserve runtime external auth snapshots
Validation:
- ClawSweeper review passed for head a73074ed45.
- Required merge gates passed before the squash merge.
Prepared head SHA: a73074ed45
Review: https://github.com/openclaw/openclaw/pull/85558#issuecomment-4523577269
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The PR preserves provider-facing embedded-runner prompt errors when cleanup detects session takeover, keeps the takeover signal fatal for fallback, and adds focused regressions.
- PR surface: Source +52, Tests +92. Total +144 across 5 files.
- Reproducibility: yes. Source inspection shows current main can let cleanup takeover replace a prior prompt/p ... rror and can normalize a provider-looking takeover wrapper before fallback sees it as coordination failure.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(embedded-runner): preserve takeover during fallback
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8405…
Validation:
- ClawSweeper review passed for head 050c779cfa.
- Required merge gates passed before the squash merge.
Prepared head SHA: 050c779cfa
Review: https://github.com/openclaw/openclaw/pull/84321#issuecomment-4492087335
Co-authored-by: abnershang <abner.shang@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
* refactor: use Rastermill for image processing
* docs: clarify autoreview heartbeat patience
* refactor: use simplified rastermill api
* fix: preserve rastermill media safety boundaries
* build: update rastermill api pin
* build: use published rastermill package
Summary:
- Adds `plugins/synthetic-auth.runtime` as an explicit tsdown dist entry and adds a regression test tying PI model-discovery synthetic-auth imports to that stable entry.
- PR surface: Tests +22, Other +1. Total +23 across 2 files.
- Reproducibility: yes. as a source-reproducible package-build path: current main imports synthetic-auth from ... y. The PR proof covers emitted production `dist/` imports, though it did not run a live scheduled cron job.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(build): pin synthetic auth runtime dist entry
Validation:
- ClawSweeper review passed for head cb99947919.
- Required merge gates passed before the squash merge.
Prepared head SHA: cb99947919
Review: https://github.com/openclaw/openclaw/pull/86714#issuecomment-4538919657
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- This PR changes DeepSeek provider tool-schema normalization to convert multi-value string const unions into flat string enums, with regression coverage for pure, nullable, and single-const union cases.
- PR surface: Source +27, Tests +84. Total +111 across 2 files.
- Reproducibility: yes. source-level reproduction is high confidence: current main selects only the first non-null anyOf/oneOf variant, and the linked source PR proof shows before/after output for that exact schema shape.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(plugin-sdk): preserve string-const unions as flat enum for deepse…
Validation:
- ClawSweeper review passed for head 310d95e327.
- Required merge gates passed before the squash merge.
Prepared head SHA: 310d95e327
Review: https://github.com/openclaw/openclaw/pull/86712#issuecomment-4538892244
Co-authored-by: 1052326311 <1052326311@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Move immutable session-store snapshot cloning/freezing off the write path and rebuild snapshots lazily on read. Resolve runtime external auth profiles once per auth-profile save instead of once per OAuth profile.
Proof: oxfmt targeted files; pnpm tsgo:core; pnpm check:test-types; node scripts/run-vitest.mjs src/config/sessions.cache.test.ts src/agents/auth-profiles.store.save.test.ts src/agents/auth-profiles/external-oauth.test.ts; autoreview clean.
Route invalid-config recovery output for source-only installed plugin packages to plugin packaging guidance instead of openclaw doctor --fix.
Validated with focused config/CLI/gateway/plugin tests, autoreview, Crabbox/Testbox E2E tbx_01ksgr80tnvvc13kv6t126yv78, and green PR CI on 3b3ce73d0f.
Thanks @brokemac79.
Reuse a lazy model manifest context across configured model resolution so common static defaults do not trigger manifest metadata loads, while keeping plugin-owned normalization available when aliases, provider rows, or OpenRouter compat paths need it.
Preserves exact alias behavior, auth-profile-suffixed alias behavior, provider inference from manifest-normalized configured refs, and existing plugin/runtime cache lifecycle rules.
Co-authored-by: Alyana <alyana@lumina.local>
Use the effective runtime/model context when computing overflow recovery reserveTokensFloor hints, including uncataloged runtime refs, stale session windows, and heartbeat fallback cases.
Verification:
- pnpm test src/auto-reply/reply/agent-runner-execution.test.ts
- autoreview clean on final focused fixup; prior accepted findings addressed before push.
- CI passed on head e25b3e84f4 after rerunning cancelled jobs: preflight, critical quality network-runtime-boundary, security high, checks, Real behavior proof.
Co-authored-by: tanshanshan <tanshanshan@users.noreply.github.com>
Forward OpenAI-compatible frequency_penalty, presence_penalty, and seed params through the gateway/chat-completions path while keeping Responses untouched.
Verification:
- pnpm test src/gateway/openai-http.test.ts src/agents/pi-embedded-runner/extra-params.sampling.test.ts src/agents/openai-transport-stream.test.ts
- CI passed on head 9abb9466d9 after rerunning cancelled jobs: preflight, critical quality network-runtime-boundary, security high, checks, docs, Real behavior proof.
Co-authored-by: lellansin <lellansin@gmail.com>
Cache configured model cost indexes for repeated session usage cost lookups while preserving in-place config mutation behavior via value-fingerprint invalidation. Raw pricing lookups now skip manifest model-id normalization as well as runtime/plugin normalization, keeping direct cost lookup off plugin metadata hot paths.
Verification:
- node scripts/run-vitest.mjs src/utils/usage-format.test.ts
- pnpm exec oxfmt --check src/utils/usage-format.ts src/utils/usage-format.test.ts
- pnpm lint --threads=8
- pnpm tsgo:core
- autoreview --mode local
- PR CI green on head 15c1e25d95
Cap retained compaction checkpoint snapshots by total bytes per session while preserving the existing count cap.
The gateway now stats retained checkpoint snapshots inside the session-store writer before trimming, deletes older trimmed checkpoint files, and keeps the newest checkpoint available. Regression coverage uses real sparse checkpoint files to prove byte-budget cleanup.
Closes#84822.
Summary
- Bound Memory Wiki compile-time page summary reads through the existing concurrency helper.
- Preserve deterministic result ordering before title sort and keep the helper in stop-on-error mode.
- Replaces #84458 because the fork branch does not allow maintainer edits and the contributor changelog entry needed removal.
Behavior addressed: Memory Wiki compile no longer starts one page-summary read per page without a bound.
Real environment tested: Local macOS source checkout, Node/pnpm repo environment.
Exact steps or command run after this patch: pnpm test extensions/memory-wiki/src/compile.test.ts; pnpm exec oxfmt --check --threads=1 extensions/memory-wiki/src/compile.ts extensions/memory-wiki/src/compile.test.ts; .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main --no-web-search --prompt "Review PR #84458 after maintainer fixup. Focus on memory-wiki compile page summary read concurrency, runTasksWithConcurrency result/error handling, ordering preservation, and test reliability."
Evidence after fix: compile.test.ts passed 10 tests; oxfmt reported clean; autoreview reported no accepted/actionable findings.
Observed result after fix: Page reads are executed through runTasksWithConcurrency with errorMode stop, successful results are consumed in input-index order, and the existing summary title sort remains deterministic.
What was not tested: Full repository suite.
Co-authored-by: zhengzuo0-ai <zheng.zuo0@gmail.com>
Behavior addressed: Unknown CLI command roots now error consistently even when --help or --version is appended, while legitimate built-in help fast paths still render normally.
Real environment tested: Local OpenClaw source checkout plus GitHub workflow run-level status.
Exact steps or command run after this patch: pnpm test src/cli/run-main.exit.test.ts src/cli/argv.test.ts src/cli/argv-invocation.test.ts; pnpm exec oxfmt --check --threads=1 src/cli/run-main.ts src/cli/run-main.exit.test.ts; autoreview --mode branch --base origin/main --no-web-search.
Evidence after fix: Focused CLI test shards passed 178 tests; formatter clean; autoreview reported no accepted/actionable findings; GitHub CI run 26422344121 and CodeQL Critical Quality run 26422344090 completed successfully.
Observed result after fix: `openclaw foo --help` and `openclaw foo --version` reject before proxy/program startup, while known help fast paths remain ahead of the unknown-root guard.
What was not tested: Full local build; contributor PR body already supplied build/CLI command proof before rebase.
Co-authored-by: YB0y <brianandez6@gmail.com>
Behavior addressed: The codex-cli metadata branch no longer calls process.exit(0) immediately after writing stdout, and it still emits exactly one unsupported-backend JSON object.
Real environment tested: Local OpenClaw source checkout on macOS with Node/tsx.
Exact steps or command run after this patch: pnpm test test/scripts/print-cli-backend-live-metadata.test.ts test/scripts/docker-build-helper.test.ts; node --import tsx scripts/print-cli-backend-live-metadata.ts codex-cli | python3 -c 'import sys,json; print(json.load(sys.stdin)["provider"])'; autoreview --mode branch --base origin/main --no-web-search.
Evidence after fix: Focused tooling test shard passed 2 files / 23 tests; direct pipe parse printed codex-cli; autoreview reported no accepted/actionable findings; PR status rollup was clean.
Observed result after fix: stdout is parseable as a single JSON payload and the normal metadata path is skipped for codex-cli.
What was not tested: Live provider metadata paths beyond the focused existing test coverage.
Co-authored-by: Iftekhar Uddin <ifuddin3@gmail.com>
Behavior addressed: Native Codex app-server threads now disable Codex's built-in personality on thread/start, thread/resume, turn/start, bound conversation turns, and /btw side-thread forks so OpenClaw agent workspace identity stays authoritative.
Real environment tested: Local OpenClaw source checkout plus GitHub CI on PR #85891.
Exact steps or command run after this patch: pnpm test extensions/codex/src/app-server/thread-lifecycle.test.ts extensions/codex/src/app-server/side-question.test.ts extensions/codex/src/conversation-binding.test.ts extensions/codex/src/app-server/schema-normalization-runtime-contract.test.ts; pnpm check:docs; pnpm prompt:snapshots:check; OPENCLAW_ADDITIONAL_BOUNDARY_SHARD=1/4 OPENCLAW_ADDITIONAL_BOUNDARY_CONCURRENCY=4 node scripts/run-additional-boundary-checks.mjs.
Evidence after fix: Focused Codex test shard passed 4 files / 79 tests; docs check passed; prompt snapshots are current; CI passed all code/quality checks, with only Real behavior proof failing as unrelated proof-bot gating for this non-channel change.
Observed result after fix: App-server request snapshots and unit tests include personality: "none" on native Codex start/resume/turn/fork paths.
What was not tested: A live Codex app-server model run was not executed.
Co-authored-by: Beru <beru@lastguru.lv>
renewInterval is not cleared on re-entry to startGmailWatcher,
leaking the previous timer. Each config reload adds another
interval that fires independently.
Clear existing watcher state before starting a new one.
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
Bump USAGE_COST_CACHE_VERSION 3->4 so a warm .usage-cost-cache.json written by a
pre-change build is rebuilt instead of serving stale complete-$0 totals after
upgrade (the new missing-cost branch otherwise only runs when a file is rescanned).
Add a regression test asserting an older-version cache is treated as stale for an
unpriced session.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address review: distinguish unknown pricing from an intentional free price. A
turn's all-zero cost is treated as unknown (counted toward missingCostEntries)
only when the operator did NOT explicitly configure the model's price under
models.providers -- i.e. the zero is a generated-catalog default (codex/gpt-5.x),
not a deliberate $0. Operator-configured zero-cost models keep reporting a
complete $0.
Adds resolveConfiguredModelCost() to read config-only pricing, and regression
tests for both paths (unconfigured unknown -> missing; configured free -> $0).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Only treat an unpriced (all-zero) model's turn as missing when it has no
trustworthy recorded cost (recorded cost is 0 or absent). A turn carrying a
real positive recorded cost is preserved, fixing a regression where priced
fixtures without explicit pricing config lost their recorded cost.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Models that ship an all-zero cost block (e.g. codex gpt-5.5, whose Codex
backend exposes no per-token price) made usage-cost report totalCost: 0 with
missingCostEntries: 0 -- a confident, complete $0 -- so every budget/spike
safeguard keyed off totalCost was silently blind to real pay-per-token spend.
scanTranscriptFile now treats a resolved cost config with no positive per-token
rate (and no tiered pricing) as "pricing unknown": for turns that burned tokens
it drops the transport's fabricated $0 and surfaces the turn as a missing-cost
entry, mirroring the existing tiered-pricing override. Models with positive or
tiered pricing and zero-token entries are unaffected.
Verified on a real OpenClaw 2026.5.20 host (default openai/gpt-5.5, api_key):
1,780,235 tokens that previously reported missingCostEntries 0 now report 32.
Related: #85858
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary:
- The PR updates `src/agents/identity-file.ts` to normalize backtick-wrapped IDENTITY.md labels and values, and adds parser/merge regression tests in `src/agents/identity-file.test.ts`.
- PR surface: Source +8, Tests +28. Total +36 across 2 files.
- Reproducibility: yes. source-reproducible with high confidence: current main strips `*` and `_` but not back ... e unnormalized string. I did not run tests because this review was required to keep the checkout read-only.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(agents): strip markdown code spans from IDENTITY.md values and la…
Validation:
- ClawSweeper review passed for head 30c43defd6.
- Required merge gates passed before the squash merge.
Prepared head SHA: 30c43defd6
Review: https://github.com/openclaw/openclaw/pull/86647#issuecomment-4537456646
Co-authored-by: nayrosk <105997554+nayrosk@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The PR extracts the CJK-aware memory tokenizer into a shared helper, routes dreaming dedupe through it, preserves MMR re-exports, and adds regression coverage for CJK and empty-token cases.
- PR surface: Source +15, Tests +96. Total +111 across 5 files.
- Reproducibility: yes. Current main has an ASCII-only tokenizeSnippet path in dreaming dedupe, and the source ... ction source bytes for the CJK failure modes; I did not run tests locally because this review is read-only.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(memory-core): use Array.toSorted for #80613 lint fix
- PR branch already contained follow-up commit before automerge: fix(memory-core): preserve dedupe identity when both snippets tokeniz…
- PR branch already contained follow-up commit before automerge: fix(memory-core): rename __testing to testing in CJK regression tests…
- PR branch already contained follow-up commit before automerge: fix(memory-core): use CJK-aware tokenizer for dreaming dedupe (#80613)
Validation:
- ClawSweeper review passed for head ca9c02734c.
- Required merge gates passed before the squash merge.
Prepared head SHA: ca9c02734c
Review: https://github.com/openclaw/openclaw/pull/86645#issuecomment-4537414471
Co-authored-by: MoerAI <friendnt@g.skku.edu>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Behavior addressed: Embedded PI compaction retry now drains block replies again after the retry wait resolves, so retry-generated replies are not left behind while preserving aggregate-timeout fallback behavior.
Real environment tested: local OpenClaw focused Pi runner test shard plus contributor local live-output proof in the PR body.
Exact steps or command run after this patch: pnpm test src/agents/pi-embedded-runner/run/attempt.spawn-workspace.context-engine.test.ts src/agents/pi-embedded-runner/run/compaction-retry-aggregate-timeout.test.ts; .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
Evidence after fix: 2 test files passed, 55 tests passed; final autoreview clean with no accepted/actionable findings.
Observed result after fix: the runner flushes before the compaction wait, waits for compaction retry, then performs a second idempotent flush when the wait resolves without timing out.
What was not tested: fresh external-channel live retry by this agent; PR retains contributor live-output proof for the delayed channel adapter path.
Thanks @spacegeologist.
Co-authored-by: zhengzuo0-ai <zheng.zuo0@gmail.com>
Behavior addressed: Telegram direct-message turns no longer drop an earlier overlapping normal reply, while authorized aborts and explicit/native/plugin/skill command turns still supersede active reply work.
Real environment tested: local OpenClaw focused Telegram test shard plus existing contributor Telegram screenshot/log proof in the PR body.
Exact steps or command run after this patch: pnpm test extensions/telegram/src/telegram-reply-fence.test.ts extensions/telegram/src/bot-message-dispatch.test.ts; .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
Evidence after fix: 2 test files passed, 93 tests passed; final autoreview clean with no accepted/actionable findings.
Observed result after fix: overlapping normal Telegram DMs use non-interrupting reply fences and both final replies remain deliverable; direct /stop, authorized built-in commands, and explicit text/native command turns still supersede.
What was not tested: fresh live Telegram Desktop rerun by this agent; PR retains contributor screenshot/log proof and the Real behavior proof bot remains red despite proof labels.
Thanks @neeravmakwana.
Co-authored-by: Neerav Makwana <261249544+neeravmakwana@users.noreply.github.com>
Keep isolated cron announce delivery owned by runner fallback while leaving agent-initiated message sends optional. `delivery.mode: none` no longer forces message delivery, announce delivery skips fallback only after a verified same-target message-tool send, and prompt allowlist checks now match runtime tool policy normalization/group expansion.
Verified with focused cron tests, `check:changed`, autoreview, and PR CI on 7ab77bad97.
Thanks @bryanpearson.
Co-authored-by: bryanpearson <bryanmpearson@gmail.com>
Fix Gemini cached-content GenerateContent payloads so cached requests no longer resend request-level systemInstruction, tools, or toolConfig.
Covers explicit cachedContent and managed cacheRetention prompt caching; fixes#84919.
Proof: Real behavior proof passed on PR head 198a42bbc6 after live Gemini repro/fix evidence was added to the PR body. Focused tests and check:changed were already green.
Thanks @neeravmakwana.
Adds regression coverage for agents.defaults.agentRuntime schema acceptance and invalid-config doctor fix reachability.
The runtime behavior fix already landed on main in 5b9be2cdb1c01a2896783c52f5f0654c5f22a249; this PR locks the expected behavior with focused tests.
Closes#72872
Precompute FIR resample kernels for common voice sample-rate conversions to avoid per-sample trigonometry while preserving output for tested ratios.\n\nVerification: node scripts/run-vitest.mjs extensions/voice-call/src/telephony-audio.test.ts; pnpm tsgo:core; autoreview --mode commit --commit HEAD; PR CI green.
Fix isolated cron delivery so agent-default derivation keeps using the paired runtime config snapshot, preserving resolved channel credentials such as Discord SecretRefs. Fixes#86545.
Add inline comment explaining that compileSafeRegex rejects patterns
with nested repetition (ReDoS risk) and returns null. Rejected patterns
are silently skipped; the plugin will not match via that pattern but
other patterns and prefixes still apply.
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
Replace raw `new RegExp(patternSource, "u")` in
`resolveModelSupportMatchKind` with the existing
`compileSafeRegex()` guard from `src/security/safe-regex.ts`.
A malicious or careless plugin manifest pattern like `(a+)+$`
causes catastrophic backtracking (ReDoS) against non-matching model
IDs. `compileSafeRegex` detects nested repetition and returns null,
which the caller now treats as a non-match (equivalent to the
previous catch-continue for invalid regex).
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
Replace string containment check with direct field assertions:
- oversized.role is 'assistant'
- __openclaw.id is 'oversized-child' (exact match)
- parentId extraction proven by record inclusion in active tree
5/5 oversized transcript tests pass.
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
extractJsonStringFieldPrefix and extractJsonNullableStringFieldPrefix
interpolate the `field` parameter into `new RegExp(...)` without
escaping. All current callers pass hardcoded strings ("id",
"parentId", "type", "role"), but the function signature accepts
any string. A future caller passing a field containing regex
metacharacters (e.g. "foo.bar") would match unintended patterns.
Wrap the interpolation with escapeRegExp() from src/shared/regexp.ts
so metacharacters are treated literally.
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
When the gateway process is orphaned after a systemd service restart,
the parent's journal pipe closes and every write to stdout/stderr returns
EPIPE. The previous handler swallowed it with a bare return, so background
loops (config file watcher, etc.) kept firing and the process spun at
100% CPU indefinitely.
Exit cleanly with code 0 instead — a process whose own output streams
are broken has nowhere to log and no reason to keep running.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
scripts/docs-spellcheck.sh uses set -u and constructs args=( ... "${write_flag[@]}" ), where write_flag may be an empty array. On bash 3.2 (still the default /bin/bash on macOS), referencing an empty array under set -u raises an unbound variable error. Newer bash (>= 4.4) handles this expression correctly, which is why the script ships green on Linux CI runners.
Switch to the bash 3.2-safe parameter expansion ${write_flag[@]+"${write_flag[@]}"}: it expands to nothing when the array is empty and to the array contents otherwise, preserving --write behavior unchanged.
Also fixes overrideable -> overridable in docs/reference/test.md, which the now-running spellcheck surfaces.
Repro:
bash scripts/docs-spellcheck.sh # was: write_flag[@]: unbound variable, exit 1
bash scripts/docs-spellcheck.sh # now: codespell runs to completion
Summary:
- The PR replaces Feishu presentation/action card fallback rendering with a shared JSON 2.0 button/behaviors renderer, updates native card sanitization, and expands Feishu channel/outbound tests.
- PR surface: Source +118, Tests +223. Total +341 across 5 files.
- Reproducibility: yes. source-reproducible: current main renders Feishu presentation button blocks through ma ... help` fallback. I did not run local tests because this review was required to keep the checkout read-only.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(feishu): render native presentation buttons
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8601…
Validation:
- ClawSweeper review passed for head 36d6a36323.
- Required merge gates passed before the squash merge.
Prepared head SHA: 36d6a36323
Review: https://github.com/openclaw/openclaw/pull/86588#issuecomment-4536092569
Co-authored-by: NianJiuZst <3235467914@qq.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Refactor diagnostic queued/state/processed emission into a shared helper used by dispatch and isolated cron turns.
Preserve dispatch processed-event behavior, cron queue-depth symmetry, and final cron session-id adoption while adding focused helper coverage and reviewer comments for the non-obvious invariants.
Fixes Dependabot alert #118 for GHSA-q8mj-m7cp-5q26 by updating the workspace qs override from 6.14.2 to 6.15.2 and regenerating root and plugin shrinkwrap files.
Runtime surface: transitive qs consumers through Express, Slack, Feishu, Teams, ACP, and MCP paths.
Prefer the active Claude CLI OAuth auth label when the configured Anthropic model resolves through an equivalent Claude CLI runtime alias, so `/status` no longer reports an unused env API-key label.
Also adds regression coverage for both text and message status renderers, plus the maintainer changelog entry.
Closes#80184.
Co-authored-by: brokemac79 <martin_cleary@yahoo.co.uk>
Normalize Google Gemini 3.1 Flash Lite routing to the GA model id and keep the retired preview spelling as a compatibility alias. Align default alias docs, FAQ guidance, and deprecated-model manifest recommendations with the GA id.
Fixes#86151.
Co-authored-by: Sebastien Tardif <sebtardif@ncf.ca>
Address clawsweeper P2: cron isolated-agent lifecycle (message.queued,
session.state, message.processed) now mirrors the dispatch path and
respects the diagnostics.enabled master toggle. Added regression test
for the disabled-config path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(doctor): skip restart prompt when gateway is healthy after recent restart
`openclaw doctor` unconditionally prompted "Restart gateway service now?"
with default=Yes whenever the gateway was running, even if it had just
restarted via SIGUSR1 after an update. This caused restart loops on macOS
where the prompt raced with launchctl KeepAlive.
Changes:
- Probe gateway health before the restart prompt when a restart handoff
exists (deep doctor mode). If healthy, skip the prompt entirely.
- Change `initialValue` from `true` to `false` as a safety net so users
don't accidentally confirm a restart by pressing Enter.
- Update existing test that expected a single `readGatewayRestartHandoffSync`
call (now called twice: diagnostic display + health-probe check).
Fixes#86518
* fix(doctor): correct GatewayRestartHandoff mock types in tests
Add explicit literal types + satisfies constraint so the mock handoff
objects match the exact GatewayRestartHandoff type expected by the
type-check CI.
* fix(doctor): apply recent-restart skip to normal doctor flow
* test(doctor): align normal-flow handoff expectation
* chore: add doctor restart prompt changelog
---------
Co-authored-by: OpenClaw Contributor <openclaw-contributor@example.com>
Co-authored-by: liaoyl830 <267396060+liaoyl830@users.noreply.github.com>
Co-authored-by: sallyom <somalley@redhat.com>
* fix(agents): warn on Claude permission overrides under YOLO
* fix: narrow Claude audit backend guard
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(agents): answer Claude live control_request can_use_tool via exec policy
Claude CLI emits stream-json control_request frames with subtype
can_use_tool when it wants to use a native tool. The Claude live-session
bridge previously dropped these frames, leaving Claude waiting for a
control_response until the 180/600s no-output timeout fired (see #80819).
Resolve the effective OpenClaw exec policy (per-agent tools.exec -> global
tools.exec -> allowlist/on-miss defaults) once at session-start time and
thread it through fingerprinting and the session record. When a
can_use_tool request arrives:
- Allow native Bash when the resolved policy is security=full, ask=off
(matching the bypassPermissions semantics OpenClaw already documents).
- Otherwise deny with a message that names the resolved policy and
points the agent at OpenClaw MCP tools.
Unsupported control_request subtypes get a structured error response
instead of a silent no-op, and stray control_response frames are
silently dropped. Adds spawn-test coverage for both allow and deny paths.
Fixes#80819
* fix(agents): align Claude live control_request policy with backend defaults
Resolve the effective exec policy through the same defaults that
extensions/anthropic/cli-shared.ts:isOpenClawRequestedYolo and
src/agents/exec-defaults.ts:resolveExecDefaults already use (security
?? "full", ask ?? "off") instead of falling back to a hand-rolled
allowlist/on-miss default that disagreed with the rest of the codebase.
Without this, a default-config OpenClaw deployment launches Claude with
--permission-mode bypassPermissions but the bridge would still deny
Bash control_requests, re-creating the #80819 stall for the very
default-config case the issue reports.
Also thread the effective Claude permission mode into the policy
decision. Prefer the operator's explicit --permission-mode in argv,
falling back to what normalizeClaudePermissionArgs would have inserted
for an un-overridden launch. Native Bash is auto-allowed only when the
effective mode is bypassPermissions AND tools.exec resolves to
full/no-ask, so explicit raw-arg overrides like --permission-mode
default or acceptEdits broaden Claude's native prompting and are
honored by routing through deny.
Adds a no-config regression test (default deployment allows Bash, no
stall) and a permission-mode-override test (tools.exec full/off plus
explicit --permission-mode default in raw args denies). Existing
allow/deny tests continue to pass via the synthesized-mode fallback.
* fix(agents): honor effective exec policy for Claude live Bash
---------
Co-authored-by: Guillaume Thirry <g.thirry@gmail.com>
* fix(sessions): stop doctor OOM on large session stores and reclaim stale store temps
`openclaw doctor` loaded the full sessions.json via loadSessionStore with the
default cache-write plus return clone, materializing a multi-hundred-MB
monolithic store several times and exhausting the heap (#56827). The read-only
doctor checks (state integrity, heartbeat target, codex route scan) now load
with { skipCache: true, clone: false } so the store is materialized once.
Orphaned session-store atomic-write temps were also never reclaimed: the store
write went through the generic atomic writer, staging a shared
.fs-safe-replace.<pid>.<uuid>.tmp not identifiable as a store temp. Give the
store write a store-specific tempPrefix so its temps stage as
sessions.json.<pid>.<uuid>.tmp, classify them (isSessionStoreTempArtifactName),
and reclaim stale ones via the disk-budget sweep and the unreferenced-artifact
prune on a short staleness window so in-flight temps are preserved.
Fixes#56827
* docs(changelog): note large session store doctor fix
* test(qa): preserve WhatsApp RTT source literal
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Summary:
- This PR adds an Ollama Kimi-cloud visible-content sanitizer for streamed and final assistant replies, updates stream handling and regression tests, and adds a changelog entry.
- PR surface: Source +183, Tests +473, Docs +1. Total +657 across 7 files.
- Reproducibility: yes. from source and the linked report: current main appends Ollama `message.content` direc ... payload described in the issue would be shown. I did not run a live vendor repro in this read-only review.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(ollama): sanitize kimi inline reasoning in stream events
- PR branch already contained follow-up commit before automerge: fix(ollama): buffer kimi cloud stream reasoning
- PR branch already contained follow-up commit before automerge: fix(ollama): cover kimi inline boundary variants
- PR branch already contained follow-up commit before automerge: fix(ollama): preserve text start partial state
- PR branch already contained follow-up commit before automerge: fix(ollama): bound kimi stream sanitizer hold
- PR branch already contained follow-up commit before automerge: fix(ollama): keep kimi sanitizer deltas append-only
Validation:
- ClawSweeper review passed for head b709229157.
- Required merge gates passed before the squash merge.
Prepared head SHA: b709229157
Review: https://github.com/openclaw/openclaw/pull/86515#issuecomment-4534945393
Co-authored-by: Jason O'Neal <jason.allen.oneal@gmail.com>
Co-authored-by: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: osolmaz
Co-authored-by: osolmaz <2453968+osolmaz@users.noreply.github.com>
Summary:
- This PR changes the shared block reply coalescer/pipeline so compatible buffered visible text is merged into a following media payload, adds focused regression tests, and records a Discord changelog fix.
- PR surface: Source +50, Tests +175, Docs +1. Total +226 across 6 files.
- Reproducibility: yes. Current main has a clear source reproduction path: media enqueue forces a text flush and then sends the media payload separately, and the PR adds focused tests for the corrected merge path.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix: route streamed media through reply coalescer
- PR branch already contained follow-up commit before automerge: fix(discord): merge media captions into one message
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8648…
Validation:
- ClawSweeper review passed for head ceafbeaf3c.
- Required merge gates passed before the squash merge.
Prepared head SHA: ceafbeaf3c
Review: https://github.com/openclaw/openclaw/pull/86487#issuecomment-4534402219
Co-authored-by: Neerav Makwana <261249544+neeravmakwana@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
* fix(memory): prevent silent vector index degradation when embedding provider temporarily unavailable
Two related bugs cause complete loss of semantic vector data:
1. Promise cache deadlock in ensureProviderInitialized():
When the embedding provider (e.g. local MLX server on port 8123) is
temporarily unreachable at Gateway startup, loadProviderResult() throws
and providerInitPromise becomes a permanently-cached Rejected Promise.
The block only clears it on success (providerInitialized=true),
so the stale rejection blocks all future init attempts until Gateway restart.
2. Silent fts-only overwrite in runSync():
With the provider stuck at null, shouldRunFullMemoryReindex() compares
the stored meta.model (e.g. 'jina-embeddings-v5-text-small') against the
runtime provider model, and since provider is null, falls through to the
'meta.model !== fts-only' check — returning true. This triggers a full
reindex where every file is written as fts-only, silently erasing all
existing 11k+ semantic vectors.
Fix 1: Clear providerInitPromise in the catch block so the next call can
retry initialization (self-healing when the provider comes back online).
Fix 2: Guard runSync() — if requestedProvider is set and not 'none', but
the runtime provider is null, throw an error instead of silently degrading
to fts-only. This protects existing vector data by failing loudly.
Tested on production: 11,715 chunks + 1024-dim vectors fully preserved
after Gateway restart with the fix applied. The guard correctly blocks
sync when MLX is offline and allows normal operation when it recovers.
* fix: use this.settings.provider instead of private requestedProvider
The guard clause in runSync() was referencing this.requestedProvider
which is a private property on the MemoryIndexManager subclass and not
accessible from MemoryManagerSyncOps. Use this.settings.provider
instead, which is the same value and is accessible via the protected
abstract settings property.
* fix(memory): narrow degradation guard to only protect existing semantic indexes
The previous guard was too broad — it blocked sync for ALL non-none
provider configurations when provider was null, including the default
'auto' path where users without embedding credentials legitimately
build FTS-only indexes.
Narrow the guard to only abort when:
1. provider is null (embedding unavailable)
2. existing index metadata has a semantic model (not 'fts-only')
3. settings.provider is configured and not 'none'
This preserves the legitimate FTS-only fallback for auto/no-provider
users while still protecting existing semantic vector indexes from
silent degradation.
Reported-by: ClawSweeper (PR #85704 review)
* test: cover memory semantic index outage guard
* fix: protect semantic memory index fallback paths
* test: update memory sync harnesses
---------
Co-authored-by: Bo Yan <yaaboo-gif@users.noreply.github.com>
Co-authored-by: Yan Bo <yanbo@Mac.lan>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Summary:
- The branch replaces QQBot's hardcoded outbound response watchdog with a resolver based on existing agent/provider `timeoutSeconds` settings, adds regression tests, and updates the changelog.
- PR surface: Source +113, Tests +116, Docs +1. Total +230 across 5 files.
- Reproducibility: yes. at source level: current main and the latest release use a hardcoded 300000 ms QQBot o ... s an 1800s provider timeout. I did not run the reporter's live QQBot/Ollama setup in this read-only review.
Automerge notes:
- PR branch already contained follow-up commit before automerge: test(qqbot): cover slow provider response watchdog
- PR branch already contained follow-up commit before automerge: fix(qqbot): derive outbound watchdog from configured timeouts (#85267)
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8527…
Validation:
- ClawSweeper review passed for head 7bd829292a.
- Required merge gates passed before the squash merge.
Prepared head SHA: 7bd829292a
Review: https://github.com/openclaw/openclaw/pull/86500#issuecomment-4534669816
Co-authored-by: SymbolStar <symbolstar@users.noreply.github.com>
Co-authored-by: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: osolmaz
Co-authored-by: osolmaz <2453968+osolmaz@users.noreply.github.com>
Summary:
- Adds a scoped ModelStudio/DashScope OpenAI-compatible guard for chat payloads with no non-empty user or assi ... turn, shared turn-detection helper coverage, prompt-skip handling, regression tests, and a changelog entry.
- PR surface: Source +83, Tests +298, Docs +1. Total +382 across 10 files.
- Reproducibility: yes. source-reproducible for the OpenClaw-side malformed payload shape: current main has no ... he exact qwen-long/qwen3-coder-plus provider error was not reproduced with the available DashScope account.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix: make OpenAI payload guard content-aware
- PR branch already contained follow-up commit before automerge: fix: scope openai payload turn guard
- PR branch already contained follow-up commit before automerge: Guard OpenAI chat payload turns
Validation:
- ClawSweeper review passed for head e16a3fe9f2.
- Required merge gates passed before the squash merge.
Prepared head SHA: e16a3fe9f2
Review: https://github.com/openclaw/openclaw/pull/86497#issuecomment-4534668405
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Co-authored-by: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: osolmaz
Co-authored-by: osolmaz <2453968+osolmaz@users.noreply.github.com>
Reverts the diagnostic queue-pressure suppression of non-terminal session tool mirrors from PR 84846 while keeping PR 86503 recipient dedupe intact. Session-only Control UI subscribers keep receiving tool lifecycle mirrors; overlapping run and session subscribers still receive one canonical run-scoped frame. Verification: focused gateway and diagnostic tests, diff check, changed check, and autoreview all passed.
* fix(agents): release embedded-attempt session lock on every exit path
The embedded run controller acquires its session write lock eagerly at
creation and released it only inside the post-run cleanup block. An
exception thrown in post-prompt processing skipped that block, so the lock
leaked to the live gateway process until the watchdog reclaimed it and
later requests to the session failed with SessionWriteLockTimeoutError.
Add an idempotent dispose() to the lock controller and call it from the
run's outer finally so the eagerly-held lock is released on every exit
path. Normal/aborted/timed-out runs still hand the lock to
acquireForCleanup first, so dispose() is a no-op then (no double release).
Fixes#86014
* fix: keep session lock teardown comment lean
* docs(changelog): note embedded session lock fix
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Dedupe gateway tool-event fanout so connections subscribed by both run and session receive the canonical run-scoped agent event only, while session-only subscribers keep the compatibility session.tool mirror.\n\nVerification:\n- node scripts/run-vitest.mjs src/gateway/server-chat.agent-events.test.ts\n- git diff --check\n- env -u OPENCLAW_TESTBOX pnpm check:changed\n- .agents/skills/autoreview/scripts/autoreview --mode local
Summary:
- The PR expands security audit, CLI docs, and tests so `hooks.token` reuse of active Gateway token/password auth is reported while password-mode Gateway startup remains compatible.
- PR surface: Source +178, Tests +311, Docs +14. Total +503 across 14 files.
- Reproducibility: yes. from source inspection: current main forwards a bearer token as both token and passwor ... ecause this review was read-only, but the linked issue and code path make the reproduction high confidence.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(cr-fmi-hook-ingress-token-unlocks-password-mode-gateway-auth): ap…
- PR branch already contained follow-up commit before automerge: fix: include trusted proxy password in hooks token reuse check
- PR branch already contained follow-up commit before automerge: fix(gateway): audit hooks password reuse without blocking startup
- PR branch already contained follow-up commit before automerge: fix: Hook ingress token unlocks password-mode gateway auth
Validation:
- ClawSweeper review passed for head 7c796b22ec.
- Required merge gates passed before the squash merge.
Prepared head SHA: 7c796b22ec
Review: https://github.com/openclaw/openclaw/pull/86453#issuecomment-4533831028
Co-authored-by: Coy Geek <65363919+coygeek@users.noreply.github.com>
Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: jesse-merhi
* fix(diagnostics): reclaim wedged session lanes with a stale leaked active run
A group session lane could wedge permanently (#85639): an embedded run that dies
abnormally leaves a stale ACTIVE_EMBEDDED_RUNS handle, so the diagnostic heartbeat
classifies the lane stale_session_state (recoveryEligible without allowActiveAbort)
while stuck-session recovery reads the leaked isEmbeddedPiRunActive flag and skips
with active_reply_work — a tautology that keeps the lane forever. The age-based
escape never fires because ageMs (last-activity) resets on every incoming queued
message.
Make the active-run skip a liveness check: before keeping the lane, consult the
run's real forward-progress age (lastProgressAgeMs, not refreshed by incoming
messages). If a run flagged active has made no forward progress past the resolved
diagnostics.stuckSessionAbortMs threshold (threaded through the recovery request;
falls back to a 5-minute floor) with queued work waiting, treat it as a
leaked/dead handle and reclaim it (abort + drain + force-clear) instead of
skipping. A genuinely progressing run, or one within an operator-raised
threshold, is kept.
Fixes#85639
* test(diagnostics): cover stale active run recovery
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Summary:
- The PR adds HEIC/HEIF-to-JPEG normalization before media-understanding image description providers run, with regression tests and a changelog entry.
- PR surface: Source +58, Tests +82, Docs +1. Total +141 across 6 files.
- Reproducibility: yes. at source level: current main forwards HEIC buffers to `describeImage` without normali ... ody includes a red HEIC regression test before the patch. I did not execute tests in this read-only review.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(media-understanding): normalize HEIC before image descriptions
Validation:
- ClawSweeper review passed for head ed34620bd7.
- Required merge gates passed before the squash merge.
Prepared head SHA: ed34620bd7
Review: https://github.com/openclaw/openclaw/pull/86037#issuecomment-4528578874
Co-authored-by: luoyanglang <hanwanlonga@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
- Rotate OpenAI Realtime voice sessions on provider max-duration events without surfacing the expected expiry as a Discord voice error.
- Add lifecycle logging for Realtime rotation/reconnect and regression coverage for max-duration reconnect.
- Allowlist the existing Control UI chunking helper for the optional Knip unused-file guard so the dependency shard stays green on the current base.
Catch non-ENOENT load failures inside maybeRepairLegacyCronStore so an
unreadable ~/.openclaw/cron/jobs.json (e.g. root-owned 0600 inside
Docker) no longer aborts the rest of the doctor health checks. The
scheduler-side loadCronStore keeps its strict throw-on-read-failure
contract.
Closes#86102
Co-authored-by: 1052326311 <1052326311@users.noreply.github.com>
After config.patch writes new values to openclaw.json, a subsequent
SIGUSR1 in-process restart could overwrite them with a stale snapshot.
Root cause: run-loop's onIteration hook resets lanes and task registry,
but leaves the runtimeConfigSnapshot intact. loadConfig() then returns
the old snapshot via loadPinnedRuntimeConfig() instead of re-reading disk.
Fix: clearRuntimeConfigSnapshot() in the restart iteration hook so the
next startup reads fresh config from disk.
Refs #86350
Summary:
- The PR routes local GGUF memory embeddings through a bundled worker sidecar, adds structured degradation and fallback handling, updates memory tests/build output, and keeps the local config contract unchanged.
- PR surface: Source +831, Tests +503, Docs +1, Other +2. Total +1337 across 23 files.
- Reproducibility: Do we have a high-confidence way to reproduce the issue? Source and report evidence are str ... cludes native crash logs; the exact Metal teardown abort was not reproduced in this review or the PR proof.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(memory): keep local embedding config unchanged
- PR branch already contained follow-up commit before automerge: fix(memory): type local embedding degradation
- PR branch already contained follow-up commit before automerge: fix(memory): refresh keywords after embedding fallback
- PR branch already contained follow-up commit before automerge: fix(memory): keep worker errors internal
- PR branch already contained follow-up commit before automerge: test: satisfy memory provider lifecycle harnesses
- PR branch already contained follow-up commit before automerge: fix: harden local embedding worker fallback
Validation:
- ClawSweeper review passed for head 1d1fe41c4e.
- Required merge gates passed before the squash merge.
Prepared head SHA: 1d1fe41c4e
Review: https://github.com/openclaw/openclaw/pull/85348#issuecomment-4518516047
Co-authored-by: Onur Solmaz <onur@Onurs-MacBook-Pro.local>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: osolmaz
Co-authored-by: osolmaz <2453968+osolmaz@users.noreply.github.com>
* fix(memory-core): filter REM dreaming candidates to light-staged entries
REM dreaming re-ingested the full short-term recall store independently,
ignoring which entries were staged by the light sleep phase. Because the
confidence formula heavily weights accumulated averageScore (45%) and
recallStrength (25%), old high-recall entries permanently dominated
freshly staged candidates. The intended light→REM→deep pipeline was
broken: light correctly staged current material, but REM selected a
different set entirely, so lightHits never paired with remHits for deep
ranking.
Fix: in runRemDreaming(), read the phase-signals store for keys with
lightHits > 0 and filter entries to that set before passing to
previewRemDreaming(). When no light-staged keys exist (light disabled
or first run), fall back to the full entry set for backward
compatibility.
Added readLightStagedKeys() to short-term-promotion.ts as a clean
export for reading the light-staged key set from the phase signal store.
Closes#86249
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
* fix(memory-core): keep REM staging pending
* fix(memory-core): mark REM-considered staged entries
---------
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(telegram): propagate forum topic names into agent context
The topic-name-cache already tracks forum topic names via
forum_topic_created/edited/closed events in bot-message-context, but
this metadata was not surfaced in two key paths:
1. The native-command handler (bot-native-commands.ts) builds the agent
context payload with IsForum but never looked up the cached topic
name. Now it resolves the topic name from the cache and includes
TopicName in the context, giving agents awareness of which forum
topic they are responding in.
2. The action runtime (action-runtime.ts) executes createForumTopic and
editForumTopic actions but never persisted the resulting topic
metadata back to the cache. Now both actions write the topic name
(and optional icon metadata) to the cache after success, ensuring
subsequent messages in those topics can resolve the name.
Closes#86024
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
* fix(telegram): scope forum topic cache updates
---------
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Move the plain-text tool-call promotion wrapper out of the public provider stream SDK helper and into a private local-only bundled-provider runtime seam.
Replays #84749 because the contributor fork branch became conflicted and was no longer maintainer-writable.
Co-authored-by: TeodoroRodrigo <rodrigoteodoro.90@gmail.com>
* fix(scripts): include ui:build in build-all full and ciArtifacts profiles
Closes#85206.
scripts/build-all.mjs only ran ui:build via a separate `pnpm ui:build`
command. Because `pnpm build` invokes tsdown which removes `dist/`,
a backend rebuild silently deletes any previously generated
dist/control-ui assets, leaving the gateway to serve the
"Control UI assets not found" message at startup. Documentation and
startup auto-repair masked the bug at the worst possible time
(LaunchAgent readiness / remote recovery) instead of guaranteeing the
build artifact contract.
This change adds ui:build as a build-all step after
copy-export-html-templates and before write-build-info, and includes
it in the full and ciArtifacts profiles. Minimal backend dev profiles
(gatewayWatch, cliStartup) keep their existing fast-loop step lists
and do not run ui:build.
Regression coverage:
- ciArtifacts step list assertion updated to match the new ordering.
- Three new resolveBuildAllSteps assertions: ui:build is in full and
ciArtifacts and runs after tsdown/runtime-postbuild-stamp and before
write-build-info; ui:build is excluded from gatewayWatch/cliStartup;
ui:build cache outputs declare dist/control-ui.
* fix(scripts): leave ui:build uncached so dist/control-ui never restores stale build IDs
ClawSweeper review on #86010 flagged that the original ui:build cache only
hashed ui/, scripts/ui.js, and scripts/lib/copy-assets.ts, but
ui/vite.config.ts also reads package.json plus git HEAD and the
OPENCLAW_CONTROL_UI_BUILD_ID/OPENCLAW_VERSION env vars to embed a build ID
into the app and service worker. A file-input cache signature cannot
exactly invalidate those metadata sources, so a warm build-all hit could
restore a previously generated dist/control-ui after tsdown clears dist
and ship stale service-worker/app cache metadata.
Leaving the step uncached keeps the contract simple: every pnpm build
re-runs Vite, which is fast for the Control UI bundle and matches the
existing behavior of every other un-cached build-all step. Backend-only
profiles (gatewayWatch, cliStartup) are still unchanged.
Tests:
- Updated the ui:build cache assertion to require step.cache to be
undefined and explain the metadata-input reason.
- Existing presence/order/exclusion assertions for ui:build are unchanged
and still cover the full and ciArtifacts profile contract.
* fix(scripts): keep ui build fallback pnpm-free
---------
Co-authored-by: 1052326311 <1052326311@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
enqueueSession injects sessionQueuePriority into its enqueue opts so
user-facing work (trigger=user/manual → foreground) jumps ahead of
background work (trigger=cron/heartbeat/memory/overflow → background)
in the session lane.
enqueueGlobal was passing opts through unchanged, so priority resolved
to "normal" for both lanes. Since the heavy embeddedRun body
(workspace-sandbox, core-plugin-tools, bootstrap-context, bundle-tools,
system-prompt, session-resource-loader, agent-session, stream-setup)
runs inside enqueueGlobal, the global-lane queue was effectively FIFO
between user chat and cron — defeating the priority intent on the path
where it matters most.
Inject sessionQueuePriority into enqueueGlobal the same way it's
injected into enqueueSession.
Observed in production: a 3m48s user chat on a hibernation-wake
storm at 2026-05-24T04:19:09Z, where 11 overdue cron jobs + 16
overdue agent heartbeats entered the global lane simultaneously
on hibernation resume. The chat enqueued with trigger=user landed
at the back of a 27-entry FIFO queue at priority 0 instead of
preempting at priority 1 (foreground). 62 s of the 228 s wall-clock
was waiting in that queue.
Summary:
- This PR forwards Codex app-server source reply delivery mode into active run handling, adds a focused regression test, and adds a changelog entry.
- PR surface: Source +1, Tests +38, Docs +1. Total +40 across 3 files.
- Reproducibility: yes. Source inspection shows the shared active-run queue rejects `message_tool_only` replies when the active handle lacks that mode, and current main's Codex app-server handle omits it.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(codex): preserve source reply mode for active runs
Validation:
- ClawSweeper review passed for head d8fac59d8f.
- Required merge gates passed before the squash merge.
Prepared head SHA: d8fac59d8f
Review: https://github.com/openclaw/openclaw/pull/86325#issuecomment-4531516197
Co-authored-by: Fermin Quant <ferminquant@hotmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The PR adds a commitments-store writer helper, wraps load-modify-save mutators and expiry cleanup with a per-path queue plus `withFileLock`, adds three concurrency regressions, and updates the changelog.
- PR surface: Source +153, Tests +61, Docs +1. Total +215 across 4 files.
- Reproducibility: yes. Source inspection on current main shows the unqueued load-modify-save mutation path, a ... inked proof log shows the Promise.all repro changing from 20/20 lost writes before the patch to 0/20 after.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(commitments): serialize load-modify-save with in-process queue + …
Validation:
- ClawSweeper review passed for head a349f41ccf.
- Required merge gates passed before the squash merge.
Prepared head SHA: a349f41ccf
Review: https://github.com/openclaw/openclaw/pull/86326#issuecomment-4531553610
Co-authored-by: ai-hpc <mail.speedy.hpc@hotmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Route normal [telegram][diag] polling diagnostics through runtime.log while keeping non-diag Telegram warnings/errors and offset persistence failures on runtime.error.
Verification:
- node scripts/run-vitest.mjs extensions/telegram/src/monitor.test.ts (34 passed)
- git diff --check
- CI run 26378692736 passed on 979c6f31a4Fixes#82957
Repair explicit anchorless iMessage watch payloads by GUID before debounce/routing, and drop unrecoverable payloads fail-closed instead of routing them as sender DMs.
Closes#84470.
Refs #84503.
Thanks @zhangguiping-xydt and @zqchris.
Fix Google Vertex production ADC mode support by routing explicit google-vertex models to the Vertex transport and relying on google-auth-library for request-time ADC resolution.
Verification:
- pnpm install --frozen-lockfile
- pnpm test extensions/google/transport-stream.test.ts extensions/google/index.test.ts src/config/zod-schema.models.test.ts src/agents/pi-embedded-runner/model.inline-provider.test.ts -- --reporter=verbose
- pnpm check:changed
- GitHub PR checks green on c4b7cad4df
- Live ADC smoke reached Google Vertex auth/transport and failed only because the configured redacted project has the Vertex AI API disabled
Co-authored-by: Damian Finol <damian@felixpago.com>
* fix: clean up browser MCP subprocess tree
* fix: clean up windows browser mcp tree before close
* fix(browser): repair chrome mcp cleanup rebase
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(compaction): preserve partial summary on mid-chain chunk failure
When summarizing multiple chunks, if a chunk fails after at least one
chunk has already succeeded, return the partial summary instead of
propagating the error and losing all summarization progress.
Abort and timeout errors still propagate immediately. First-chunk
failures still rethrow so the existing fallback path runs.
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
* fix(compaction): use content array for assistant messages to match updated AgentMessage type
* fix(compaction): use as-unknown-as-AgentMessage cast for assistant test fixtures
---------
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
maybeRecoverSuspiciousConfigRead unconditionally recorded
lastObservedSuspiciousSignature in health state even when
restoredFromBackup was false (copyFile failed). The guard at
resolveConfigReadRecoveryContext then prevented the same
signature from ever being retried, permanently accepting the
suspicious config on every subsequent launch.
Only record the dedup signature when the backup restore
actually succeeded.
* fix: avoid false telegram pairing prompts
* docs: add telegram pairing changelog
* refactor(telegram): share pairing-store gating and align isGroup check
Extract loadTelegramPairingStoreIfNeeded so the text-fragment flush path
and resolveTelegramGroupAllowFromContext share one implementation, and
align the isGroup derivation in the flush path with the
'group || supergroup' form used elsewhere in bot-handlers.runtime.ts.
Note on transient-vs-known errors: readChannelAllowFromStore already
translates missing-file (ENOENT) and JSON parse failures to an empty
allowlist internally, so the only errors that escape into the new
silent-drop path are unexpected I/O failures (EMFILE/EACCES/EIO/...) —
unpaired senders still get a pairing challenge as expected.
* fix(telegram): skip pairing-store read when commands.allowFrom already authorizes the sender
Native command auth resolves group/dm allow context (which may read the
pairing store) before checking commands.allowFrom. On DMs with
dmPolicy: "pairing", a transient pairing-store I/O failure was therefore
dropping commands from senders explicitly authorized by
commands.allowFrom.telegram.
Add a skipPairingStoreRead hint on resolveTelegramGroupAllowFromContext /
loadTelegramPairingStoreIfNeeded, precompute the command authorization
once at chat scope before the context call, and pass the hint when that
pre-check already authorizes the sender. The post-context command auth
check still owns the topic-scoped decision.
Regression covers a DM /status from a sender allowed by
commands.allowFrom.telegram with dmPolicy: "pairing" and a rejecting
readChannelAllowFromStore mock.
* fix(telegram): satisfy test-types on harness readChannelAllowFromStore
CI check-test-types failed because the harness now stores a loose
AnyAsyncMock for readChannelAllowFromStore but TelegramNativeCommandDeps
requires the precise typeof readChannelAllowFromStore signature. Cast at
the telegramDeps assignment so harness callers can keep passing any
vi.fn(...) (including ones that reject) without type pollution at the
call site.
* feat(telegram): reply with a retry hint when pairing-store read fails transiently
Wrap unexpected pairing-store I/O errors (EACCES, EMFILE, ...) in a
typed TelegramPairingStoreReadError and surface them through
handleInboundMessageLike with a friendly "please try again" reply that
matches the media-failure precedent at bot-handlers.runtime.ts:1893.
Beats silent drop: paired senders see why their message wasn't
processed, and unpaired senders who happen to send a DM during a
transient store outage retry naturally and get the correct pairing
prompt once the store recovers.
Verified live against @paxicoto_bot with chmod 000 on
~/.openclaw/credentials/telegram-default-allowFrom.json after touching
mtime to bypass the stat-pinned cache.
Summary:
- The PR updates the Unix installers to avoid emitting npm `--before` when raw npm config contains `min-releas ... records a changelog fix, and widens an internal model-catalog test helper type to accept sync auth checks.
- PR surface: Source +1, Tests +421, Docs +1, Other +150. Total +573 across 7 files.
- Reproducibility: yes. The linked report at https://github.com/openclaw/openclaw/issues/84743 gives an isolat ... exclusivity, and current main still has the source path that can generate the conflicting `--before` flag.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(installer): avoid before with npm release-age configs
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8549…
Validation:
- ClawSweeper review passed for head fb0762f468.
- Required merge gates passed before the squash merge.
Prepared head SHA: fb0762f468
Review: https://github.com/openclaw/openclaw/pull/85491#issuecomment-4522229812
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
* perf(plugins): thread metadata snapshot and discovery through hot paths
With the snapshot memo now actually hitting, route the snapshot's
manifestRegistry and discovery through the helper chains that already
had fast paths for them. Eliminates redundant per-call rebuilds at
two big amplifiers.
- Provider resolve paths (resolvePluginProviders /
isPluginProvidersLoadInFlight / resolveOwningPluginIdsForProvider /
resolveExternalAuthProfilesWithPlugins) self-service a snapshot once
at the public entry, then thread it as a separate required arg
through resolvePluginProviderLoadBase,
resolveExplicitProviderOwnerPluginIds, and the setup/runtime load
state helpers. Inner reads change from
'params.pluginMetadataSnapshot?.x' to 'snapshot.x', no more
enrichedParams clone. loadPluginManifestRegistryForInstalledIndex
fires drop ~685 -> ~10 per cold start.
- Bundled-channel / auto-enable chain accepts an optional
PluginDiscoveryResult. discoverOpenClawPlugins is fired once during
snapshot building (resolveInstalledPluginIndexRegistry already
produced it internally; now bubbled up through
loadInstalledPluginIndexWithDiscovery, PluginRegistrySnapshotResult,
and onto PluginMetadataSnapshot.discovery). load-context reads
metadataSnapshot.discovery and passes it through
applyPluginAutoEnable, so the bundled-channel cascade
(collectConfiguredChannelIds, listBundledChannelIdsWith*,
listPotentialConfiguredChannelPresenceSignals) short-circuits
instead of each leaf re-firing discovery. Persisted-cache path is
unchanged: no discovery on the snapshot, downstream chain handles
its own fallback (pre-PR behavior on that path).
* test(plugins): isolate snapshot memo across tests that mock manifest registry
The snapshot memo is now process-scoped and effective (~98% hit rate).
Three test files were depending on cache misses (because the broken
cache returned them) — each test would set up its own
loadPluginManifestRegistry mock and expect a fresh derive. With the
cache fixed, an earlier test's mocked registry now leaks into later
tests in the same file.
- io.write-config.test.ts: afterEach now clears the snapshot memo so
the 'demo' plugin mocked in the first test does not survive into
'keeps shipped plugin install config records when index migration
fails', which expects an empty registry to surface the 'plugin not
found: demo' warning.
- gateway/model-pricing-cache.ts: resetGatewayModelPricingCacheForTest
also clears the memo. Tests in model-pricing-cache.test.ts assert
loadPluginManifestRegistryForInstalledIndex was called; the memo
hit otherwise skips the call.
- providers.test.ts: vi.doMock loadPluginMetadataSnapshot to wrap the
existing loadPluginManifestRegistryMock fixture. The plumbing
commit added an auto-fetch fall-through in
resolveOwningPluginIdsForProvider; without the mock, providers
tests hit real disk reads and return empty registries (which is
what surfaced as 9 unrelated-looking failures in the prior CI
run).
* fix(plugins): preserve setup.cliBackends owner matching in provider scan
resolveOwningPluginIdsForProvider now also checks plugin.setup?.cliBackends.
The pre-PR no-registry fallback used resolvePluginContributionOwners which
includes both top-level cliBackends and setup.cliBackends; the PR's manifest
scan replacement was missing the setup case.
* fix(plugins): inherit active registry workspaceDir before loading metadata snapshot
isPluginProvidersLoadInFlight and resolvePluginProviders now resolve
env and workspaceDir once at the entry point (falling back to
getActivePluginRegistryWorkspaceDir) and pass them into both
loadPluginMetadataSnapshot and resolvePluginProviderLoadBase. Pre-fix
the snapshot used params.workspaceDir raw while the load base inherited
the active workspace, so workspace-scoped provider plugins could be
absent from the snapshot manifest registry even though owner resolution
expected them.
Regression test asserts the snapshot mock receives the active
workspaceDir when the caller omits it.
* perf(gateway): thread discovery into applyPluginAutoEnable call sites
Every gateway applyPluginAutoEnable call now passes the snapshot's
PluginDiscoveryResult so the bundled-channel cascade (collectConfiguredChannelIds
→ listBundledChannelIdsWith* → listPotentialConfiguredChannelPresenceSignals)
short-circuits instead of each leaf re-firing discovery.
Startup-time sites pull discovery from the snapshot/lookup-table they already
hold:
- server-plugin-bootstrap.ts (pluginLookUpTable)
- server-startup-plugins.ts (pluginMetadataSnapshot)
- server-startup-config.ts (pluginMetadataSnapshot)
- server-plugins.ts (pluginLookUpTable, both call sites)
Per-RPC sites (server.impl getRuntimeConfig callback, server-methods/channels
status + start handlers, server-methods/send) source discovery via
getCurrentPluginMetadataSnapshot using the runtime config to validate
compatibility. Falls through to the original slow path when the snapshot is
absent or incompatible.
Summary:
- The branch adds a 1500 ms internal timeout to bundled MCP `tools/list` catalog discovery, adds slow and hung stdio MCP regression tests, and records the fix in `CHANGELOG.md`.
- PR surface: Source +2, Tests +216, Docs +1. Total +219 across 3 files.
- Reproducibility: yes. The current-main source path is high confidence: bundled MCP connects successfully, then calls `client.listTools` without request options, and the upstream SDK defaults that request to 60000 ms.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(mcp): use internal tools list timeout
- PR branch already contained follow-up commit before automerge: fix(mcp): bound tools/list during catalog discovery
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8506…
Validation:
- ClawSweeper review passed for head bbbfb9f059.
- Required merge gates passed before the squash merge.
Prepared head SHA: bbbfb9f059
Review: https://github.com/openclaw/openclaw/pull/85063#issuecomment-4511554739
Co-authored-by: nxmxbbd <32288+nxmxbbd@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
* feat(imessage): support thumb approval reactions
Mirrors openclaw#85477 (WhatsApp) for the iMessage channel. iMessage can now
deliver exec/plugin approval prompts via the existing imsg/BlueBubbles
transport and resolve approvals from 👍 (allow-once) / 👎 (deny) tapbacks.
Allow-always remains on the manual /approve <id> allow-always fallback.
What changed:
- New approval surfaces under extensions/imessage/src/:
approval-auth.ts, approval-resolver.ts, approval-reactions.ts,
approval-handler.runtime.ts, approval-native.ts (+ tests for each).
- channel.ts wires base.approvalCapability to the new iMessage capability.
- send.ts appends the 👍/👎 hint to outbound /approve prompts and registers
the reaction binding (keyed by accountId + chat_guid/chat_identifier/
chat_id/handle + messageId) after a successful send.
- monitor/monitor-provider.ts resolves approval reactions ahead of the
normal inbound decision pipeline so resolution bypasses
reactionNotifications gating and runs its own actor authorization.
- runtime.ts now exports getIMessageRuntime / getOptionalIMessageRuntime so
approval-reactions can open a persistent keyed store for binding state
across gateway restarts.
What did NOT change:
- Core approval surfaces in src/gateway/server-methods/* and src/infra/*
remain channel-agnostic; the channels.imessage.allowFrom field already
exists and is reused as the approver list for reactions.
- Other channels and the manual /approve sender-authorized path are
untouched.
* fix(imessage): address codex review findings on thumb approvals
Addresses 15 findings from the multi-angle codex review:
Critical (correctness / blocking):
- Register CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY in the iMessage
monitor so the gateway can actually deliver native approval prompts via
approval-handler.runtime.ts (it was dead code without the context lease).
- DM tapback approvals never resolved because send keyed by handle while
inbound preferred chat_guid. Register and look up under EVERY available
conversation key (chat_guid / chat_identifier / chat_id / handle); inbound
probes them all and accepts the first hit.
- Reaction binding now requires the bridge's GUID string (rejecting numeric
ROWIDs) so the binding key matches inbound reacted_to_guid.
- Outbound regex now requires both a canonical `ID: <approvalId>` header AND
a matching `/approve <id> <decision>` line, so non-approval messages that
legitimately mention /approve syntax no longer get a phantom reaction
binding (and can no longer resolve a colliding live approval).
- Drop is_from_me reaction events so cross-device echoes of the operator's
own tap cannot self-approve when their handle is in allowFrom.
High (operability / cleanup):
- Non-ApprovalNotFound errors now log at warn via the runtime child logger
(no longer hidden behind OPENCLAW_LOG_LEVEL=debug).
- In-memory binding is cleared on successful resolve so a toggle 👍→👎 (or
chat.db replay) does not refire and emit a misleading 'expired approval'
log line. Removed tapbacks are also owned by the shortcut and not surfaced
as noisy reaction system events.
- Move resolveIMessageReactionContext (and its helpers) to a slim
monitor/reaction-context.ts so approval-reactions.ts no longer transitively
pulls monitor/inbound-processing.ts (14+ heavy runtime modules) into the
hot channel.ts entrypoint per extensions/CLAUDE.md.
Medium (consistency / future-proofing):
- Native runtime exec pending payload now passes agentId, ask, and
sessionKey through buildExecApprovalPendingReplyPayload so the two
delivery routes produce identical operator-visible prompts.
- Both delivery paths now use addIMessageApprovalReactionHintToText (single
insertion point after ID:) so the hint cannot be double-emitted by the
native runtime path bypassing the idempotency guard.
- Extract replaceApprovalIdPlaceholder into a shared approval-text.ts that
escapes `$` in the replacement string so an approvalId containing
`$&`/`$1`-`$9`/`$$` cannot interpolate into the outbound text.
- In-memory Map now stores TTL alongside each entry and prunes expired
bindings on each register so the gateway no longer accumulates an
unbounded reaction-target Map.
- bindPending refuses to bind when accountId is missing or the approval is
already expired, with explicit error logs instead of silent no-ops.
- Reject chat_id=0 as a synthetic key value (chat.db ROWIDs start at 1).
- Drop dead getIMessageRuntime export — only the optional accessor is used.
Documentation:
- docs/channels/imessage.md gains an 'Approval reactions (👍 / 👎)' accordion
documenting the reaction emoji map, allowFrom approver requirement, the
/approve <id> allow-always manual fallback, and the deliberate change to
/approve command authorization for users with non-empty allowFrom.
- CHANGELOG.md entry added under 2026.5.24.
Tests: 411 iMessage tests pass (was 406). Added explicit coverage for the
DM key-mismatch fix, the regex-tightening fix, the is_from_me guard, the
clear-on-success behavior, and the approval-id `$` escape.
* test(imessage): match WhatsApp approval-native test coverage
Backfills the nine cases from extensions/whatsapp/src/approval-native.test.ts
that weren't mirrored in iMessage:
- target-mode exec + plugin prompt rendering with the canonical hint
- target-mode availability when no iMessage target matches
- agentFilter / sessionFilter applied to native handling
- account-scoped target enabled/disabled per account
- shouldSuppressForwardingFallback session-origin exact-match cases
- shouldSuppressForwardingFallback off when native cannot bind (locks down
the targets-only forwarding path the Lobster live deploy exercised)
- both-mode explicit + unscoped target suppression
- group-origin tapback approvals require explicit approvers
Tests: extensions/imessage/src/approval-native.test.ts 21 passed (was 11).
Total iMessage approval-specific cases now 49 (was 40).
* fix(imessage): preserve service-prefixed direct handles as approvers
ClawSweeper P1 review finding on #85952. normalizeIMessageApproverId was
calling looksLikeIMessageExplicitTargetId() to reject conversation-target
prefixes, but that helper also matches the imessage:/sms:/auto: service
prefixes — which are valid direct-handle forms. Any allowFrom entry like
'imessage:+15551230000' dropped to undefined, leaving approvers empty,
which:
- silently denied reaction resolution ('reactions require explicit
approvers'), and
- let text /approve fall back to implicit same-chat authorization.
Fix: normalize first via normalizeIMessageHandle (strips the service
prefix), then reject only chat_id:/chat_guid:/chat_identifier:
conversation-target shapes that remain after normalization.
Tests:
- approval-auth.test.ts: assert the resolved approver list contains the
normalized handle, plus the corollary that a non-matching sender is
explicitly rejected (no longer masked by the implicit-same-chat
fallback). Add a separate case covering chat_id/chat_guid/
chat_identifier rejection (with and without a service prefix).
- approval-reactions.test.ts: reaction resolution end-to-end with a
service-prefixed allowFrom entry — proves resolveIMessageApproval is
called rather than silently denied.
Focused suite: 48 passed (was 47).
* test(imessage): satisfy strict buildPendingPayload signature in render tests
CI check:test-types caught that the render.exec/render.plugin
buildPendingPayload calls were passing accountId (not in the type
signature). The signature is { cfg, request, target, nowMs }. Replace
accountId with target on the four render-test sites so the strict
test-types pass matches the SDK contract:
- it('renders thumbs-only reaction hints in exec approval prompts')
- it('renders thumbs-only reaction hints in plugin approval prompts ...')
- it('renders target-mode exec prompts with concrete thumbs-only ...')
- it('renders target-mode plugin prompts with concrete thumbs-only ...')
Verified locally with pnpm check:test-types (tsgo:core:test +
tsgo:extensions:test). 49 approval-specific tests still pass.
* fix(imessage): probe every tapback GUID form for approval lookup
ClawSweeper P1 review finding on #85952. readApprovalReactionEvent was
only using reaction.targetGuid (the first/normalized form), but
resolveIMessageReactionContext produces reaction.targetGuids = [normalized,
raw] for both `abc-123` and `p:0/abc-123` forms. If the imsg bridge
returned 'p:0/<guid>' from send() and send.ts registered the binding under
that prefixed key, the inbound resolver probing only the unprefixed form
would miss and the tapback would silently fall through.
Fix:
- Surface every GUID candidate in IMessageApprovalReactionEvent
(messageIdCandidates).
- maybeResolveIMessageApprovalReaction now probes each candidate in
precedence order; first hit wins.
- On success / ApprovalNotFoundError, clear the binding under all
candidate keys so toggle/replay does not refire.
Tests: extensions/imessage/src/approval-reactions.test.ts gains a
'resolves a reaction when the binding was registered under a p:0/…
prefixed GUID and the tapback surfaces both forms' regression case;
22/22 reaction tests pass. Full iMessage suite: 424/424.
* fix(imessage): native approval binding requires GUID, not numeric id
ClawSweeper third P1 review finding on #85952. approval-handler.runtime.ts
deliverPending was using result.messageId as the approval-reaction binding
key, but that field can be a numeric ROWID coerced to a string ('12345')
when the imsg bridge returns only message_id. Inbound tapbacks carry
reacted_to_guid which is always a GUID, so a numeric-id binding can never
match.
Fix mirrors the send.ts forwarding-path treatment:
- IMessageSendResult now exposes a separate guid?: string field, populated
from the same resolveOutboundMessageGuid helper send.ts already uses for
the forwarding-path binding. The generic messageId field is unchanged so
reply-cache, echo-cache, and receipt-building paths still see the
broadest id form.
- deliverPending now binds against result.guid; when it's undefined (numeric
ROWID or 'ok'/'unknown' placeholders), the function returns null instead
of binding against an id the inbound tapback can't possibly match.
Tests: approval-handler.runtime.test.ts gets a deliverPending GUID-only
binding describe block with three regression cases (numeric ROWID refused,
GUID accepted, ok/unknown placeholders refused). vi.mock isolates
sendMessageIMessage so the cases run synchronously without spawning imsg.
11 tests pass across handler.runtime + send specs.
---------
Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
Summary:
- The branch updates OpenRouter dynamic model capability parsing to prefer `top_provider.context_length`, bump ... sk cache version, adds regression coverage and a changelog entry, and adds script helper declaration files.
- Reproducibility: yes. from source and live catalog evidence rather than an authenticated inference turn. Cur ... catalog currently reports a smaller endpoint-specific `top_provider.context_length` for the reported model.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(openrouter): use endpoint context limits
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8594…
Validation:
- ClawSweeper review passed for head 76fcc362d2.
- Required merge gates passed before the squash merge.
Prepared head SHA: 76fcc362d2
Review: https://github.com/openclaw/openclaw/pull/86041#issuecomment-4528646655
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The PR changes dev-channel git updates to fetch branches with `--no-tags`, adds targeted fetching for explicit dev tag refs, updates update-runner tests, and adds a changelog entry.
- Reproducibility: yes. Current main source shows dev updates still run a broad tag fetch, and the PR body sup ... al local bare-remote moved-tag reproducer showing that command fails before the branch update can continue.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(update): avoid broad tag fetches for dev updates
Validation:
- ClawSweeper review passed for head 733680b1bc.
- Required merge gates passed before the squash merge.
Prepared head SHA: 733680b1bc
Review: https://github.com/openclaw/openclaw/pull/84737#issuecomment-4503692161
Co-authored-by: Ruben Cuevas <hi@rubencu.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
The agentToAgent allow-pattern matcher converted user wildcards like
`*a*b*c*` into `^.*a.*b.*c.*$` via RegExp. Multiple overlapping
`.*` groups cause O(n^k) polynomial backtracking against non-matching
input, where k is the number of wildcards.
Replace the regex path with a segment-based glob matcher that splits on
`*` and checks prefix/suffix/interior segments in order. The new
matcher runs in O(n*k) worst case and eliminates the regex engine
entirely from this path.
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
* fix(minimax): normalize OAuth token expiry to absolute millisecond timestamp
MiniMax returns expired_in from the token endpoint as a relative duration
in seconds (standard OAuth expires_in semantics), but the auth profile
store's hasUsableOAuthCredential() expects an absolute millisecond
timestamp. Without conversion the token appears perpetually expired,
triggering a slow OAuth refresh network call to api.minimaxi.com on
every request — the root cause of the 30-50s auth-stage delay.
Fixes#83449.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(minimax): cover oauth expiry normalization
* fix: polish minimax oauth expiry normalization (#83480) (thanks @NianJiuZst)
* fix: update minimax raw fetch allowlist (#83480)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Summary:
- The branch updates gateway boot startup handling to use an `agent:<id>:boot` session, suppress prompt persis ... that boot mapping after the run, and adds focused gateway boot regression coverage plus a changelog entry.
- Reproducibility: yes. there is a high-confidence source reproduction path: current main passes the generated ... idence of repeated persisted boot prompts. I did not execute the gateway scenario in this read-only review.
Automerge notes:
- PR branch already contained follow-up commit before automerge: Fix boot-md test lint
- PR branch already contained follow-up commit before automerge: Isolate boot-md startup sessions
Validation:
- ClawSweeper review passed for head 5d5338c2d9.
- Required merge gates passed before the squash merge.
Prepared head SHA: 5d5338c2d9
Review: https://github.com/openclaw/openclaw/pull/85919#issuecomment-4527318708
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Clamp proxy-like OpenAI Chat Completions output caps against the estimated final outbound request payload after compatibility transforms. This prevents strict local/API-compatible servers from rejecting requests whose prompt already consumes part of the effective context window, while avoiding over-clamping dropped replay turns.
Co-authored-by: rendrag-git <253747599+rendrag-git@users.noreply.github.com>
Honor configured restart drain budgets for embedded runs and avoid a second active-work drain after forced deferral timeout restarts.
Includes maintainer changelog entry.
* fix(ui): handle empty strings with minLength constraint in config save
Fixes#85831
When saving config in Control UI, required string fields with minLength
constraint (e.g., z.string().min(1)) were sent as empty strings instead
of being unset. This prevented schema defaults from applying.
Solution: coerce empty strings with minLength > 0 to undefined, allowing
schema defaults to take effect during validation.
Added 5 unit tests covering edge cases.
* fix(types): add minLength and maxLength to JsonSchema type
Keep successful Codex native hook relays alive through a bounded grace window so late hook callbacks still reach OpenClaw enforcement, while interrupted, aborted, timed-out, and failed turns unregister immediately.\n\nCo-authored-by: Kaspre <kaspre@gmail.com>
Summary:
- The PR adds the Chrome DevTools MCP `--no-usage-statistics` default launch arg, honors explicit profile usage-statistics `mcpArgs`, adds regression tests, and adds a changelog entry.
- Reproducibility: yes. source-reproducible: current main builds Chrome MCP launch args without the upstream o ... etry is initialized. I did not run a fresh failing current-main process leak loop in this read-only review.
Automerge notes:
- PR branch already contained follow-up commit before automerge: Disable Chrome MCP telemetry watchdog by default
Validation:
- ClawSweeper review passed for head 68249b1f58.
- Required merge gates passed before the squash merge.
Prepared head SHA: 68249b1f58
Review: https://github.com/openclaw/openclaw/pull/85886#issuecomment-4526997996
Co-authored-by: Rohit <rohitjavvadi2@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Restore the describeImageWithModel default token budget to the helper-level 4096-token default instead of forcing 512 before resolution.
Add regression coverage for the default and for smaller model caps, and record the user-facing fix in the changelog.
Co-authored-by: scotthuang <scotthuang@tencent.com>
* fix(doctor): repair stale contextWindow for DeepSeek V4 Flash
Problem:
- Older releases configured deepseek-v4-flash with contextWindow: 200000
- Official DeepSeek V4 Flash context window is 1,000,000 (1M)
- Users switching from smaller models see incorrect progress bar (e.g.,
50% instead of 10%) because stale config value overrides catalog
Fix:
- Add 'models.providers.*.models.*.contextWindow-stale' migration
- Detects deepseek-v4-flash models with 200K contextWindow
- Repairs to 1M to match catalog default
- Handles both bare and provider-prefixed model IDs
- 7 unit tests covering repair, passthrough, edge cases
Fixes: #85834
* fix(doctor): preserve custom DeepSeek context windows
* fix(doctor): detect stale DeepSeek context windows
* fix(doctor): scope DeepSeek context repair
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(gateway): broadcast error to UI when chat.send fails synchronously
* test(gateway): verify broadcastChatError is called on chat.send error
* test(gateway): import GatewayRequestContext from local server-methods barrel
Fixes the chat error-broadcast regression test so it can resolve its
type import. The previous `../types.js` path does not exist in the
gateway tree; the shared types are re-exported from
`src/gateway/server-methods/types.ts`, so the test must use `./types.js`.
Addresses ClawSweeper review on PR #85815.
---------
Co-authored-by: scotthuang <scotthuang@tencent.com>
createAuthProvider swallowed addUserForToken rejections in a .catch()
that only logged, so getClient returned and cached a ChatClient backed
by a RefreshingAuthProvider with no bound user. The failure surfaced
later as an opaque auth error on first send instead of failing fast.
Re-throw in the catch so getClient rejects and does not cache the broken
client. Adds regression tests for the rejection and the no-cache behavior.
Fixes#83853
Summary:
- The PR skips agent-harness compaction preflight for provider-owned or configured CLI runtime sessions, adds claude-cli regression coverage, includes a changelog entry, and applies small test/type cleanups.
- Reproducibility: yes. at source level. Current main still routes provider-owned `claude-cli` runtime compaction preflight through harness selection, where `claude-cli` is not a registered embedded harness.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix#84857: skip CLI runtime harness preflight during compaction
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8487…
Validation:
- ClawSweeper review passed for head 1dd8a88d21.
- Required merge gates passed before the squash merge.
Prepared head SHA: 1dd8a88d21
Review: https://github.com/openclaw/openclaw/pull/85862#issuecomment-4526794976
Co-authored-by: 张贵萍0668001030 <zhang.guiping@xydigit.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
* fix(session-lock): enforce maxHoldMs in shouldReclaim during lock acquisition
- Adds optional maxHoldMs parameter to inspectLockPayload
- Inspect now marks locks as stale when held longer than maxHoldMs
- Passes maxHoldMs through inspectLockPayloadForSession
- acquireSessionWriteLock's shouldReclaim callback now passes maxHoldMs
This ensures that when a live process holds a lock for longer than
maxHoldMs (default 5min), other processes can reclaim it during
acquisition — matching the watchdog's existing enforcement.
Previously shouldReclaim only used staleMs (30min default), meaning
a lock held for 10+ minutes by a live PID would never be reclaimable,
causing 60s timeout failures and gateway freezes.
Closes#85762
* fix(session-lock): add dead-PID fast-path before retry loop
Adds a fast-path check at the top of acquireSessionWriteLock:
if the lock file's owner PID is dead, remove it immediately
before entering the retry loop. This saves up to timeoutMs (60s)
of futile waiting when the previous lock holder has died.
The shouldReclaim callback already handles this case, but only
iteratively through the retry loop. The fast-path eliminates
that unnecessary delay.
* fix(session-lock): enforce max hold during acquisition
* fix(session-lock): revalidate max hold safely
* fix(session-lock): honor holder max-hold policy
* fix(session-lock): keep cleanup from reclaiming live holders
* fix(session-lock): remove stale locks only when unchanged
* fix(session-lock): skip self-held max-hold reclaim
* fix(ci): refresh gateway protocol checks
---------
Co-authored-by: njuboy11 <njuboy11@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(cli-runner): keep recent tail when reseed history exceeds maxHistoryChars
`buildCliSessionHistoryPrompt` was prefix-slicing the rendered history,
dropping the most recent assistant turns from the reseed prompt. After
#80934 made the Claude-CLI reseed default-on, every Claude-CLI user is
exposed to this on session_expired when the rendered transcript exceeds
12288 chars. The truncation marker landed mid-word in real reproductions.
Fix:
- Tail-slice (keep the recent suffix, drop the older prefix)
- Pin the compaction summary as a prefix when present, only cap the
post-summary transcript (loadCliSessionReseedMessages deliberately
places the summary first)
- When the summary alone exceeds maxHistoryChars, head-slice the summary
itself to honor the cap; drop the post-summary tail in that case
- Move the truncation marker to the lead since what follows is the
recent tail, not what was dropped
Closes#83157
* fix(cli-runner): retain recent tail with oversize summaries
* fix(cli-runner): cap summary block plus marker against maxHistoryChars
ClawSweeper P2 on #83117 flagged that when `summaryRendered.length` is
less than `maxHistoryChars` but `summaryBlock.length` (summary + `\n\n`
separator) meets or exceeds it, the `remainingBudget <= 0` arm of
`buildCliSessionHistoryPrompt` appends the truncation marker after the
already-full summary block. A 199-char rendered summary under a 200-char
cap produced a 257-char history block — defeating the cap that prevents
reseeding fresh CLI sessions with unexpectedly huge prompts.
Fix the budget edge by truncating the summary in this branch as well so
`summary + separator + marker` stays within `maxHistoryChars`. The tail
still drops (the summary alone consumes the budget) and the marker still
leads its own line so the prompt announces what was discarded. Mirrors
the existing oversize-summary branch's pattern of head-slicing the
summary against an explicit budget that reserves marker + separator.
Add a focused regression in `session-history.test.ts` covering exactly
the gap the finding called out: `summaryRendered.length < maxHistoryChars`
with a non-empty post-summary tail. Asserts the rendered history block
stays within `maxHistoryChars` and the truncation marker is present.
* fix(cli-runner): keep tail for near-cap summaries
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
The auto-reply "delivery failed" log path passes a raw Error
under the `err` field. tslog's default JSON serialization
renders bare Error instances as `{}` because Error own data
properties are non-enumerable. Every delivery failure in
production therefore logs `err: {}`, forcing operators to
guess the underlying Baileys error from timestamp alone.
Convert Error to `{ type, message, stack }` plus own-enumerable
properties at the log site, so Boom-style subclass diagnostics
(output.statusCode, data) and custom OutboundDeliveryError
fields (stage, results) survive. Non-Error rejection values
pass through unchanged.
Tests cover Error, Error subclass (Boom-style), string
rejection, and object rejection paths.
AI-assisted: Claude Code (Opus 4.7) authored, codex review
locally addressed.
Strict OpenAI-compatible servers (vLLM, LocalAI, llama.cpp, LM Studio) and
current OpenAI itself reject requests containing tools: []. Strip the empty
tools array (and the orphan tool_choice) from outbound chat-completions
payloads when usesExplicitProxyLikeEndpoint is true. Native OpenAI/Azure/
OpenRouter routes are byte-identical.
Supersedes #70790 at the canonical payload builder seam so the gateway,
embedded runner, and public plugin-SDK consumers (zai/xiaomi/deepseek) all
benefit.
* codex: honor verbose in group dispatch
* codex: address group verbose review findings
Record the final local review pass for the group /verbose PR.
Codex review against origin/main completed clean after tightening the shared group progress gate, keeping public plugin hook types stable, preserving ACP hidden tool boundaries, and adding regressions for live verbose gating and progress-callback suppression.
* codex: require explicit group verbose progress
Normal group tool/progress summaries now require an explicit session verbose override instead of inherited agent verbose defaults.
This addresses the PR review concern that existing verboseDefault configurations could expose group progress after upgrade. DMs and forum-topic behavior continue to use the effective verbose state, while normal groups use the live explicit session verbose state set by /verbose on|full|off.
* codex: document Slack group verbose caveat
* fix(channels): simplify verbose progress gating
* docs(changelog): note verbose channel fix
* fix(channels): preserve quiet default for group progress
* fix(channels): keep verbose error policy dynamic
* fix(channels): default verbose progress off everywhere
* fix(channels): keep followup verbose default quiet
* fix(channels): latch visible tool-error progress
* fix(channels): track failed verbose progress events
* fix(channels): latch delivered tool errors
* fix(channels): prevent progress opt-out bypass
* fix(channels): isolate followup error warning state
* fix(channels): keep full verbose followup warnings
* fix(channels): latch tool errors after visible progress
* fix(channels): require visible followup failure progress
* fix(channels): refresh followup verbose state
* fix(channels): honor live verbose for error details
* test(channels): expect live verbose off warning mode
* fix(channels): preserve static tool error suppression semantics
* fix(channels): bypass acp for colon verbose commands
* fix(channels): narrow dynamic tool warning override
* fix(channels): gate compaction notices on live verbose
* fix(channels): suppress quiet followup compaction callbacks
* fix(channels): suppress tts for hidden tool summaries
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Summary:
- The PR removes forced consult diagnostics from Discord and phone-call realtime consult payloads, adds private debug logs and regression tests, and records the fix in the changelog.
- Reproducibility: yes. by source inspection. Current main builds the forced Discord consult message with the ... gent_consult` diagnostic string, and the phone-call fallback passes the same diagnostic as consult context.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(discord): log forced consult fallback reason
- PR branch already contained follow-up commit before automerge: fix(discord): keep forced voice consult diagnostics private
Validation:
- ClawSweeper review passed for head c1592530c6.
- Required merge gates passed before the squash merge.
Prepared head SHA: c1592530c6
Review: https://github.com/openclaw/openclaw/pull/84411#issuecomment-4494164784
Co-authored-by: FullerStackDev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Expose a path-free estimated context budget status on session entries and gateway session rows, render it in status when fresh provider usage is unavailable, and clear stale estimates across reset, refresh, compaction, and session-rotation boundaries.
Verification: focused local Vitest covered session persistence, status rendering, gateway rows, model resets, compaction, and session rotation; GitHub CI passed on clean head cad199e43d.
Refs #80594, #54996, #77992, #84490, #83177, #43009, #83526, #8635.
* fix: harden package URL downloads
Guard package acceptance URL downloads with HTTPS-only validation, no embedded credentials, private/special-use DNS and IP rejection, manual redirect checks, bounded timeout/size limits, pinned lookup, and atomic temp-file writes. Add tooling tests for unsafe URLs, redirect validation, size limits, and successful writes.
* fix: cancel redirect response bodies before closing dispatcher
ClawSweeper P2: the redirect branch in openPackageDownloadResponse cleared
the timeout and awaited dispatcher.close() without first cancelling
response.body. Undici's close() is graceful — it waits for in-flight
requests to complete — so a malicious redirect with a slow/never-ending
body could hang the hardened downloader.
Fix: call response.body?.cancel() before dispatcher.close() to abort the
redirect body immediately.
Test: add a regression test that uses a ReadableStream with an indefinite
interval to simulate a hanging body, and asserts cancel() was called.
Refs: clawsweeper review on PR #85512
* test: harden redirect body cancellation race in regression test
Guard the ReadableStream controller.enqueue() call with a cancelled
flag and try/catch to prevent ERR_INVALID_STATE when the interval
fires after cancel() closes the controller.
* fix: cancel final response body before closing dispatcher in downloadUrl
ClawSweeper P2: the HTTP-error and declared-oversize early-exit paths
in downloadUrl threw before consuming or canceling response.body. The
finally block then cleared the timeout and awaited graceful
dispatcher.close() with the body still open, allowing a slow/never-ending
response to hang release tooling.
Fix: add response.body?.cancel() in the finally block before
dispatcher.close().
Tests: add two regressions:
- HTTP 500 with slow body: asserts cancel() called before dispatcher close
- Declared content-length oversize with slow body: same assertion
* fix: add trusted package URL source policy
* fix: keep package URL resolver dependency-free
* test: cover encoded IPv6 package URL bypasses
* docs: sync package acceptance source overview
* docs: restore release doc formatting
* docs: sync package acceptance trusted-url source
* test: cover dotted IPv4 embedded IPv6 package URLs
* fix: parse dotted IPv4 embedded in IPv6 package URLs
* test: isolate anthropic pruning defaults
* test: move anthropic dated model coverage
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(exec-approvals): add .catch() to expiry delivery fire-and-forget
When exec-approval expiry fires, deliverToTargets is called as a
fire-and-forget promise with no .catch(). If delivery fails, the
unhandled rejection swallows the error and the notification is lost.
Add .catch() with log.warn to match the ackDelivery error handling
pattern. Keep pending.delete() before the await (the entry is expired
regardless of delivery success).
Closes#83113
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
* fix(approvals): label expiry delivery errors by kind
---------
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(doctor): skip empty entries and memoize routes in plugin session repairs
runPluginSessionStateDoctorRepairs called resolveConfiguredDoctorSessionStateRoute
once per session-store key, even for entries that carry no plugin route state
fields. On stores with many CLI sessions (observed ~800 entries), each call
takes ~1.5s due to resolveAgentHarnessPolicy walking config and provider
metadata, so the doctor's state-integrity contribution hangs for minutes
and the surrounding 'openclaw doctor' run effectively never completes.
scanEntryForOwner can only produce repair/manual-review findings when the
entry exposes one of the fields covered by entryMayContainPluginSessionRouteState
(providerOverride/modelOverride/agentHarnessId/cliSessionBindings/etc.), so
the route resolution for empty entries was pure waste. The route itself is
also a function of agentId (sessionKey is only used to derive agentId), so
sessions sharing an agent can reuse one resolved route.
Filter the store by entryMayContainPluginSessionRouteState before resolving,
and memoize resolveConfiguredDoctorSessionStateRoute by agentId within the
remaining entries. On the repro store this drops the contribution from
'never completes' to <100ms.
Adds a guard test that builds a 200-entry store with 2 route-state-carrying
entries and asserts (a) the repair fires exactly once on the codex owner
and (b) the run completes in under 2s (pre-fix would take >5 minutes).
* fix(doctor): skip manifest model-id normalization in plugin session repairs
After the previous filter+memoize fix, runPluginSessionStateDoctorRepairs was
still ~38s on a 230-entry store because every scanned entry calls parseModelRef
on its runtime model. That implicitly enters manifest-driven model-id
normalization via normalizeStaticProviderModelId, which calls
loadPluginMetadataSnapshot when no current snapshot is bound to process state.
loadPluginMetadataSnapshot is filesystem-heavy and is only memoized when a
'current' snapshot is bound (it is not, during doctor), so each parseModelRef
call paid ~40ms of fresh plugin-metadata loading. 672 calls × ~40ms = ~27s
of doctor wall-clock, all of it useless for doctor's purposes: the scan only
needs the normalized provider id of the configured runtime/route to compare
against an owner's providerIds, never the manifest-normalized model id.
Pass allowManifestNormalization: false alongside the existing
allowPluginNormalization: false on all three parseModelRef call sites in
this file. normalizeStaticProviderModelId short-circuits to
normalizeBuiltInProviderModelId when allowManifestNormalization is false,
which is what doctor wants here.
On the same 230-entry store doctor:state-integrity drops from ~38s to ~2.4s
and total openclaw doctor wall-clock drops from ~91s to ~56s.
Consume the existing { text, changed } signal from
stripInlineDirectiveTagsForDisplay so unchanged text-parts keep their
references and the original message is returned when nothing was
stripped. Avoids spurious downstream rerenders/diff churn for consumers
relying on reference equality, and keeps the public SDK helper's text
output and message shape stable.
Fixes#37589.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
openai-codex-responses can return turns where usage.output > 0 but
assistantTexts is empty (hidden reasoning tokens only). The empty
response retry guard only covered openai-completions, anthropic-messages,
and Ollama, so these turns passed through as successful completions
with no content delivered to the user.
Add the full openai-responses API family (openai-responses,
openai-codex-responses, azure-openai-responses, and their transport
variants) to RETRY_GUARD_MODEL_APIS so the empty response and
reasoning-only retry paths can fire for these providers.
Closes#85364
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
* fix(status): show configured cost for aws-sdk models
Decouple status cost display from provider auth mode so explicit model pricing is used for Bedrock and other non-api-key providers. Include cache read/write tokens in the status cost estimate and cover the behavior with regression tests.
* fix: show configured response usage costs
* docs: align configured cost visibility
* fix(status): keep usage tokens mode cost-free
---------
Co-authored-by: ItsOtherMauridian <165866613+ItsOtherMauridian@users.noreply.github.com>
Co-authored-by: ItsOtherMauridian <itsothermauridian@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
When onboarding Microsoft Foundry-hosted DeepSeek-V4 models (Pro/Flash),
the onboarding wizard assigned api: 'openai-completions' because
usesFoundryResponsesByDefault() only matched GPT/o-series models.
These V4 models require the Responses API (openai-responses) to work
correctly against the Foundry endpoint. Without this fix, all calls fail
with 'provider rejected the request schema or tool payload'.
Fix: Add 'deepseek-v4' prefix to usesFoundryResponsesByDefault() so only
the verified V4 family defaults to openai-responses. Older DeepSeek
families (e.g., V3) remain on openai-completions until proven compatible.
Closes: DeepSeek V4 models deployed via Microsoft Foundry onboarding
failing immediately due to wrong API adapter.
Co-authored-by: Roslin <rmj010203@gmail.com>
Defer Gateway channel startup until after readiness, remove startup model prewarm, and move model catalog data onto manifest/static paths so startup no longer loads broad provider runtimes.
Verification:
- focused gateway/catalog/auth/QA Vitest runs
- autoreview clean
- Blacksmith Testbox-through-Crabbox tbx_01ksahn65rsrsqz3q1qyxwf929: pnpm check:changed, exit 0
- PR CI green on ee2b631c72
* fix(gateway): normalize explicit state dir overrides at startup
* test(gateway): simplify state-dir startup coverage
* test: fix state dir startup coverage
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Route cron announce topic target parsing through channel plugin target parsers instead of Telegram-specific cron core code. Keep supported Telegram topic forms in the Telegram plugin and document the channel-owned shorthand.
* fix(bootstrap): guard bootstrap name checks against undefined names
Add optional chaining to isAgentsBootstrapFile and isAgentsBootstrapName
to prevent TypeError: Cannot read properties of undefined (reading 'toLowerCase')
when bootstrap file entries have undefined name properties.
This crash was observed in 2026.5.20 where a workspace bootstrap file entry
with an undefined name caused every incoming message to fail during bootstrap
context building, completely blocking all agent replies.
Fixes#85523
* test(agents): cover unnamed bootstrap truncation entries
* test(agents): keep bootstrap truncation fixture typed
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
`waitForever()` is a public library export used by long-running embeds to
block until the host process is asked to exit. It called `interval.unref()`
on the keep-alive timer, which removes the timer from Node's active-handle
set. With no other ref'd handles, `await waitForever()` exits the process
in ~3ms with exit code 13 ("unsettled top-level await") instead of waiting.
Drop the `.unref()` so the interval actually keeps the loop alive, and
update the existing unit test (and comment) to lock in the new contract.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(cli-output): ignore cumulative usage from result events in stream-json parser
Claude-cli's stream-json result event reports cumulative cache_read across
all tool sub-calls, not the per-call value. The parser was overwriting the
last assistant-event usage with this inflated sum, causing sessionEntry.totalTokens
to climb 6-13x on tool-heavy turns and trip the preemptive-compaction gate.
Fix: skip reading usage from result events in createCliJsonlStreamingParser,
keeping the last per-call usage from assistant events instead.
Fixes#85573
* fix(agents): keep Claude result usage as fallback
* fix(agents): read Claude assistant stream usage
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Fixes#83883.
In `secrets configure`, the one-way-migration irreversibility warning was
computed from `opts.apply` (the original --apply flag) rather than
`shouldApply`. On the interactive path the user confirms "Apply this plan
now?", which sets shouldApply=true while opts.apply stays false, so the
warning was silently skipped and the irreversible plaintext migration was
applied without the second confirmation.
Derive the guard from shouldApply so the irreversibility warning fires on
both the --apply path and the interactive-confirm path. Adds regression
tests covering the interactive path (warning shown; declining it cancels
the apply).
* fix(agents/harness): pass CLI runtime aliases through to PI in selectAgentHarnessDecision
When a model defines `agentRuntime.id` as a CLI runtime alias
(`claude-cli`, `google-gemini-cli`) or a configured `cliBackends` id, the
explicit-non-`auto` branch of `selectAgentHarnessDecision` previously
threw `MissingAgentHarnessError` because the alias has no agent harness
plugin counterpart. Model dispatch is unaffected (the CLI-runtime
short-circuit in `assertModelFallbackCandidateHarnessAvailable` runs
first), but every non-dispatch caller — delivery-mirror metadata
lookups, lane preflight, channel projection — surfaces the throw. On
Slack `[[reply_to:]]` deliveries the warning text gets substituted into
the assistant message synthesized as `provider: openclaw,
model: gateway-injected`, poisoning the thread.
Mirror the existing implicit-codex escape hatch in the same function:
when the runtime is a CLI alias (`isCliRuntimeAlias`) or a configured
CLI backend (`isCliProvider`), return PI with the new
`selectedReason: "cli_runtime_passthrough_pi"`. Actual CLI dispatch is
already routed by callers that consult model runtime policy, so PI here
is just a transcript-composition placeholder — non-CLI typos still
throw as before.
Refs #85582.
* fix(agents): validate CLI harness aliases by provider
* fix(agents): keep custom CLI harness ids fail-closed
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* docs(auth): document named OAuth profile logins
* feat(auth): support --profile-id in models auth login
* docs: note named model login profiles
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Restores WebChat image uploads to the media-understanding flow without one-turn model overrides.
- removes image-model override plumbing from the reply run
- stages WebChat images as MediaPaths for enrichment
- avoids replaying already-understood images to text-only reply models while preserving undescribed images
Co-authored-by: NianJiuZst <3235467914@qq.com>
* feat(anthropic): migrate 1M context from beta to GA
Anthropic has graduated the 1M context window from beta to GA.
This commit:
- Stops injecting the context-1m-2025-08-07 beta header when
context1m: true is configured
- Removes the OAuth token skip logic that was needed because
Anthropic previously rejected the context-1m beta with OAuth auth
(OAuth now supports 1M natively)
- Strips the legacy beta header from user-configured anthropicBeta
arrays to prevent sending a stale header
- Removes the now-unused isAnthropic1MModel helper,
ANTHROPIC_1M_MODEL_PREFIXES constant, and logger import from
the stream wrappers
The context1m config param continues to be respected for context
window sizing in context.ts — only the beta header injection is
removed.
Closes#45550 (Phase 1)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(anthropic): migrate 1M context handling to GA
* fix(clownfish): address review for ghcrawl-156721-autonomous-smoke (1)
* fix(anthropic): restrict ga 1m context models
* docs(anthropic): align ga 1m context guidance
* fix(anthropic): normalize ga 1m model metadata
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(twitch): preserve newer message handler during cleanup
Fixes#83888.
`TwitchClientManager.onMessage` returns a cleanup closure that called
`messageHandlers.delete(key)` unconditionally. When a second onMessage()
for the same account replaced the handler, running the earlier cleanup
deleted the newer handler, leaving the account with no handler and
silently dropping all inbound messages.
Guard the delete with a referential check so the cleanup only removes
the handler it registered. Adds regression tests covering both the
stale-cleanup case (newer handler must survive) and the normal case
(current handler is still removed).
* fix(twitch): distinguish handler registrations
* fix(signal): avoid dangling test export name
* test(meeting-notes): use public sdk imports
* test(sdk): classify meeting-notes subpath
* fix(discord): keep channel entrypoint imports narrow
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Use the passive backend Gateway client for implicit local logs reads, and route Linux follow-mode local RPC failures to a bounded/redacted active systemd journal fallback instead of stale configured-file logs.
Fixes#83656Fixes#66841
Summary:
- The branch adds a config-aware tool auth helper, routes image/PDF/media generation preflight and list selection through it, threads `workspaceDir`, and adds focused regression tests plus a changelog entry.
- Reproducibility: yes. by source inspection. Current main gates affected media/PDF/generation preflight paths on env/profile auth while the runtime auth contract already accepts usable `models.providers.*.apiKey`.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(tools): fall back to config apiKey in capability preflight
- PR branch already contained follow-up commit before automerge: fix(tools): honor config apiKey in media tool preflight
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8557…
Validation:
- ClawSweeper review passed for head b8c9242d77.
- Required merge gates passed before the squash merge.
Prepared head SHA: b8c9242d77
Review: https://github.com/openclaw/openclaw/pull/85570#issuecomment-4523770355
Co-authored-by: Mason Huang <masonxhuang@tencent.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: hxy91819
Co-authored-by: hxy91819 <8814856+hxy91819@users.noreply.github.com>
Summary:
- Adds an optional archive-error callback for session transcript archiving, wires `/new` reset rotation to log previous-transcript archive failures, adds regression coverage, and updates the changelog.
- Reproducibility: yes. source-reproducible. Current main catches and ignores `archiveFileOnDisk` failures ins ... and the source PR proof exercises the same rename failure boundary with a real filesystem permission error.
Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.
Validation:
- ClawSweeper review passed for head 9d5f4c0c70.
- Required merge gates passed before the squash merge.
Prepared head SHA: 9d5f4c0c70
Review: https://github.com/openclaw/openclaw/pull/85586#issuecomment-4523917139
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
* fix(qmd): normalize direct file collection paths
Port fix from PR #65212 to new package location.
When a QMD custom collection path config entry points directly to a file
instead of a directory, normalize into:
- path = parent directory
- pattern = exact filename
This ensures direct file targets are handled correctly regardless of any
user-supplied glob pattern.
Original commit: 3570aa55a7 (fix/flow-runs-legacy-migration)
* fix(qmd): escape direct file collection patterns
* fix(qmd): escape direct file collection masks
* fix(ui): run ui script through junction paths
* test(ui): make junction direct-execution test portable
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Summary:
- This PR routes bare `openclaw` to classic onboarding for missing, empty, or metadata-only configs; keeps aut ... cs/changelog/tests; and narrows a Docker E2E boundary-check exception for an existing source-checkout lane.
- Reproducibility: not applicable. this is a feature/default-routing PR rather than a bug report. The branch p ... ill includes a fresh-state terminal run reaching `OpenClaw setup` and tests for the relevant config states.
Automerge notes:
- PR branch already contained follow-up commit before automerge: feat: start onboarding for fresh CLI installs
Validation:
- ClawSweeper review passed for head f4b2572f2e.
- Required merge gates passed before the squash merge.
Prepared head SHA: f4b2572f2e
Review: https://github.com/openclaw/openclaw/pull/85519#issuecomment-4522938004
Co-authored-by: FullerStackDev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Prepack npm GitHub/git source update specs into temporary tarballs before the staged global npm install. Extends coverage to hosted GitHub HTTPS URLs without a `.git` suffix.
Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
The CONNECT handler pipes clientSocket and upstreamSocket together but
only registers an error handler on upstreamSocket. If the client
disconnects abruptly (ECONNRESET), the unhandled error event on
clientSocket causes the Node process to crash.
Add a clientSocket error handler that logs the event and destroys the
upstream socket. Also change clientSocket.end() to clientSocket.destroy()
in the upstream error handler since destroy() is more appropriate for
error cleanup of piped sockets.
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
* fix(agents): handle parallel tool call deltas in openai-completions stream
The OpenAI completions streaming parser tracked only a single
`currentBlock` for tool calls and ignored `toolCall.index`. When the
API sends multiple `delta.tool_calls` entries (e.g., parallel tool
call scaffolding from kimi-for-coding), the parser created a new
block for every entry with a differing `id`, spawning phantom tool
calls with empty names and misrouting arguments.
Replace single-block tracking with Maps keyed by `index` and `id`,
matching the correct logic already present in the bundled
`@earendil-works/pi-ai` dependency. This ensures parallel and
interleaved tool call deltas accumulate to the correct block.
Fixes phantom "unknown" tool calls and empty arguments on
parameterized tools for providers that emit multiple tool_call
indices in streaming deltas.
* fix(agents): finalize tool-call blocks in place to keep maps live
ClawSweeper review [P1]: finishCurrentBlock() and finishAllToolCallBlocks()
were creating new block objects and replacing them in output.content,
but toolCallBlocksByIndex / toolCallBlocksById still pointed at the old
objects. Later deltas for those indices would mutate detached blocks,
causing argument loss and incorrect contentIndex in stream events.
Fix by finalizing arguments in place on the existing block objects.
Add regression test for parallel tool calls with split indices:
- two tool-call slots introduced in one chunk
- argument deltas arriving for each index in subsequent chunks
* fix(agents): keep byte counters out of emitted tool-call blocks
ClawSweeper review [P2]: partialArgsBytes was being stored directly on
the tool-call block objects pushed into output.content, exposing parser
scratch state to emitted stream events and final transcripts.
Replace the inline field with a WeakMap keyed by block object, keeping
byte tracking internal to the parser without polluting the public block
shape.
* refactor(agents): extract ToolCallBlock type for map declarations
ClawSweeper review [P1]: NonNullable<typeof currentBlock> at the map
declaration point was unreliable because currentBlock is initialized to
null and flow-narrowed. Define a local ToolCallBlock alias and use it
for toolCallBlocksByIndex, toolCallBlocksById, and toolCallBlockBytes
to give the maps a precise, stable type.
* fix(agents): iterate typed tool-call map in finishAllToolCallBlocks
ClawSweeper review [P1]: output.content elements are typed as
Record<string, unknown>, so block.partialArgs remained unknown even
after checking block.type === "toolCall". Latest CI failed strict
type checking at parseStreamingJson(block.partialArgs).
Fix by iterating toolCallBlocksByIndex.values() instead — the Map
values are already typed as ToolCallBlock, so partialArgs is known
to be a string and parseStreamingJson compiles cleanly.
- Restart the shared Codex app-server client when native server-side compaction times out.
- Retry native compaction once on the fresh app-server while preserving stale-thread cleanup only for `thread not found`.
- Add regression coverage and changelog entry for the preflight compaction recovery path.
Verification:
- `pnpm test extensions/codex/src/app-server/compact.test.ts`
- `env -u OPENCLAW_TESTBOX -u OPENCLAW_TESTBOX_REMOTE_RUN pnpm check:changed`
- `.agents/skills/autoreview/scripts/autoreview --mode local`
CI note: `build-artifacts` is red due inherited latest-main workflow/test drift, reproduced locally outside this PR diff and tracked in the pre-merge PR comment.
* fix(security): escape entry.id in HTML export to prevent attribute XSS
Apply escapeHtmlAttr to entry.id in renderEntry and renderCopyLinkButton
to prevent attribute injection via crafted entry IDs in HTML exports.
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
* chore: remove proof helper scripts from branch
ClawSweeper P2: committed proof scripts can provide false-positive
validation. Proof output is in the PR body instead.
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
---------
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
When the Slack adapter's startup auth.test call fails (bad token,
transient error, etc.), the bot user id silently stays empty for the
life of the process. The downstream explicit-bot mention check is
`botUserId && mentionedUserIds.includes(botUserId)`, which always
returns false when botUserId is empty. The result is that explicit
<@bot> mentions are silently classified as non-mentions with no log
trace explaining why.
Changes:
- provider.ts: stop swallowing auth.test failures; emit a warn log at
boot so the degraded state is observable. Empty user_id is treated
as a failure too.
- prepare.ts + subteam-mentions.ts: export the existing normalizeSlackId
helper and apply it to both sides of the explicit-bot equality check
(and to the mentioned-ids list). Real Slack ids are already uppercase,
so this is a no-op on healthy traffic, but it locks the invariant down
and removes the asymmetry between collected ids and the ctx bot id.
- prepare.test.ts: add two regression tests pinning the exact symptom:
positive case (botUserId set -> explicit_bot), negative case
(botUserId='' -> not explicit_bot, mention_source not explicit_bot).
🤖 AI-assisted.
Co-authored-by: in-liberty420 <in-liberty420@users.noreply.github.com>
Replace empty .catch(() => {}) on two failDelivery calls with
log.warn() so delivery queue mark-failed errors leave a diagnostic
trail instead of being silently discarded.
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
`parseSlashCommandActionArgs` used a naive `startsWith` against the
configured slash prefix. When a skill name shares a prefix with a
built-in command (e.g. a skill named `config-check` vs the built-in
`/config`), the longer name was captured by the shorter built-in
handler and surfaced as an invalid action:
⚠️ /config is disabled. Set commands.config=true to enable.
Any skill whose name starts with a built-in command prefix
(`config-*`, `debug-*`, `models-*`, etc.) was unreachable via slash
invocation from any channel.
Fix: after the prefix match, require that the next character is
whitespace, a colon, or end-of-string. Otherwise the prefix
collided with a longer command name and we return `no-match` so the
longer handler — or the skill router — gets a chance to claim it.
Adds a regression test file `commands-slash-parse.test.ts` covering:
- `/config-check <args>` returns null (the reported case)
- `/configfoo` (no separator) returns null
- `/modelsy` returns null for the `/models` prefix
- `/config:json` still matches (colon is a valid boundary)
- `/config show enabled` still parses cleanly (whitespace boundary)
- empty body still returns the default action
Fixes#84572.
Co-authored-by: infracore <infracore@users.noreply.github.com>
* fix(tui): dismiss watchdog notice when response actually arrives
The streaming watchdog renders 'This response is taking longer than
expected. Send another message to continue.' after 30s without a chat
delta. If a delta or final then arrives — common for runs that are slow
but not stuck — the notice stays in the log alongside the recovered
response and contradicts what the user sees.
Track the notice by runId in the chat log via a new `addPendingSystem`
+ `dismissPendingSystem` pair (mirroring the existing pendingUsers
pattern) and dismiss it from `handleChatEvent` whenever any further chat
event for that run is processed. The watchdog's internal cleanup
(`activeChatRunId` reset, status idle, history reload) is unchanged.
Refs #67052, #69081 (closed). Prior attempt #69026 raised the threshold
and suppressed the notice entirely; this is the narrower fix that keeps
the warning useful for genuinely stuck runs.
* fix(tui): adapt pending notice to repeatable system entries
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
The SIGTERM handler's fire-and-forget IIFE can reject if the graceful
drain or tunnel-teardown throws. Without a catch, this becomes an
unhandled promise rejection. Add .catch() that logs the error and
falls back to a hard stop request. Same treatment for SIGUSR1.
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
* fix: release cron runtime state after isolated runs
After an isolated cron/subagent run completes, the prepared context retains
references to the full in-memory session store and the registered agent run
context. Over many runs, these retained objects accumulate -- heap snapshots
showed ~2.0 GiB from ~113k copies of the skill prompt string flowing through
skillsSnapshot.prompt -> session entry -> cronSession.store -> cron run context.
Changes:
- Add disposeCronRunContext() to runCronIsolatedAgentTurn's finally block
- Calls clearAgentRunContext(sessionId) to remove the run context from the
global agent-events map
- Nulls cronSession.store to release the in-memory session registry copy
- Export clearAgentRunContext from run-execution.runtime.ts barrel
- The disposal is shallow O(1) -- no deep traversal, no hot-path disk writes
- Session persistence is unaffected (on-disk sessions.json is untouched)
The finally block guarantees cleanup on both success and error paths,
including timeout/abort scenarios.
Includes unit tests for clearAgentRunContext, store disposal, and
sweepStaleRunContexts.
* fix: remove duplicate storePath property in test fixture
* fix: remove unused clearAgentRunContext import from run-executor
* fix(cron): use initial sessionId for disposeCronRunContext in finally block
finalizeCronRun calls adoptCronRunSessionMetadata() which can rotate
sessionEntry.sessionId before the finally block runs. Capturing the
sessionId before the try block ensures clearAgentRunContext clears the
correct registered context instead of the potentially-rotated one.
Also removes unused imports (vi, beforeEach) from the runtime cleanup test.
* chore: trigger CI re-check for proof gate
* chore: retrigger CI proof gate
* test(cron): prove isolated run cleanup path
* fix(cron): keep shared run contexts active
* test(cron): avoid spreading typed-never fixture
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(exec): parse nested approval metadata in followups
(cherry picked from commit 10ff9b318e77cda3d65f40d59bbab0f4a3f59da8)
* docs(changelog): note exec approval nested-paren parser fix
* fix(exec): sanitize denied-reason literals in (...)-delimited approval messages
The exec-approval followup wire format is `Exec denied (gateway id=..., <deniedReason>): cmd`. The producer at `src/agents/bash-tools.exec-host-gateway.ts:606` was emitting `approval-timeout (allowlist-miss)`, which embedded literal parens inside the metadata segment and broke the metadata/body boundary for naive parsers. Switch the literal to a colon-separated form (`approval-timeout: allowlist-miss`) so the surrounding `(...)` delimiter stays unambiguous.
The Gateway node-event surface at `src/gateway/server-node-events.ts:734` interpolates an untrusted `obj.reason` into the same `Exec denied (node=..., <reason>)` format. Strip parens from that field before interpolation so a buggy or hostile node payload cannot smuggle metadata into the body slot.
The robust nested-paren parser already in `src/agents/exec-approval-result.ts` stays as defense in depth. Extend `exec-approval-result.test.ts` to cover the canonical colon-separated `deniedReason` and confirm `formatExecDeniedUserMessage` still maps it to the timeout copy.
* fix(exec): require gateway/node metadata source to reject spoofed approval wrappers
The exec-approval result parser previously accepted any string starting with
"Exec denied (..." or "Exec finished (..." as a structured approval wrapper.
Generic command stdout that happened to start with these tokens would be
classified as kind: "denied" or "finished", letting a tool's output spoof a
resolved-approval event in pi-embedded-subscribe.handlers.tools.ts:1173.
Reported by Aisle as CWE-841 (Improper Enforcement of Behavioral Workflow),
medium severity. The fix validates that the parenthesized metadata starts with
either "gateway id=" or "node=" — both prefixes are emitted by the legitimate
approval generators (bash-tools.exec-host-gateway.ts, bash-tools.exec-host-node.ts,
gateway/server-node-events.ts) and are unlikely to appear in arbitrary command
output. Inputs that fail this check now return kind: "other", which all callers
already handle as a no-op.
* fix(exec): keep sandbox_blocked classification for raw exec-denied messages
After the spoof-guard tightening of parseExecApprovalResultText, inputs that
lack a gateway/node-sourced metadata prefix (such as the synthetic
"exec denied (allowlist-miss):" string used in classifier tests) no longer
return kind: "denied" and therefore no longer trigger formatExecDeniedUserMessage,
so isSandboxBlockedErrorMessage stopped recognising them.
Add a direct \bexec denied\s*\( alternative to SANDBOX_BLOCKED_RE so the
classifier still treats any raw "exec denied (" prefix as sandbox-blocked,
independent of whether the parser accepts the surrounding wrapper. This keeps
classifyProviderRuntimeFailureKind's existing behavior for unstructured exec-
denied messages.
Add documentation for the dynamicAgentCreation feature used to create
isolated agents per Feishu/Lark user. Covers:
- dynamicAgentCreation configuration fields (enabled, workspaceTemplate,
agentDirTemplate, maxAgents)
- Automatic agent/workspace creation flow
- Session isolation with dmScope
- Template variables ({agentId}, {userId})
- Verification steps and example deployment
Refs: feature available since OpenClaw 2026.4.25+
Co-authored-by: li <li@lideMac-mini.local>
* docs(channels/slack,telegram): document ackReactionScope and its DM-excluding default
The Slack and Telegram channel docs documented `ackReaction` but not
`ackReactionScope`, even though the scope (defaulting to
`group-mentions`) silently excludes DMs. People who set `ackReaction`
and expect to see an emoji on DMs are surprised when nothing fires.
This adds:
- The resolution order for `ackReactionScope` (per-account → channel →
`messages.ackReactionScope` → default `group-mentions`).
- The full list of scope values (`all`, `direct`, `group-all`,
`group-mentions`, `off`/`none`).
- A Note callout flagging that the default does not react in DMs and
that `messages.ackReactionScope` requires a gateway restart to take
effect.
- A short JSON example for the common case (`ackReactionScope: "all"`).
Mirrors the structure already used in `docs/channels/matrix.md`.
Found while configuring Slack DMs to show `👀` ack reactions and
discovering that the docs covered the emoji but not the scope gate. AI-assisted.
* fixup: scope is messages-only for Slack & Telegram (not per-account)
Reviewer correctly noted that the Slack and Telegram runtimes only read
`cfg.messages?.ackReactionScope` and the per-account/per-channel
`ackReactionScope` keys don't exist in those schemas (only Discord and
Matrix support them). Drop the misleading resolution-order bullets and
document `messages.ackReactionScope` only.
Verified against:
- extensions/slack/src/monitor/provider.ts:243
- extensions/telegram/src/bot-core.ts:262
- src/config/types.slack.ts (no ackReactionScope in account schema)
- src/config/types.telegram.ts (no ackReactionScope in account schema)
Keeps the DM-default gotcha, the full enum, and the gateway-restart note,
which were the original value of the PR.
---------
Co-authored-by: Dr. Claw <drclaw-iq@users.noreply.github.com>
Recreated from #85108 because the original branch could not be updated by maintainers.
Preserves current-main pnpm install hardening while switching workflow pnpm setup to packageManager, and adds exact version-scoped release-age exclusions for already-locked packages that pnpm 11.2.2 audits during install.
Co-authored-by: Altay <altay@hey.com>
Fixes#5369.
Preserve fresh session-store state when the agent handler observes a stale cached session entry, including model/provider overrides, send policy, delivery metadata, lifecycle timestamps, and fresh session rotations.
Co-authored-by: CodeReclaimers <github@codereclaimers.com>
Fixes the embedded attempt session write-lock watchdog so the fallback max hold time follows the resolved compaction timeout plus the existing lock grace window, instead of inheriting the full run timeout.
Adds regression coverage for the helper and settled-compaction lock lifecycle, plus a changelog entry thanking @luoyanglang.
Verification:
- `pnpm test src/agents/session-write-lock.test.ts src/agents/pi-embedded-runner/run/attempt.test.ts src/agents/pi-embedded-runner/run/attempt.session-lock.test.ts`
- `pnpm check:changed` via Blacksmith Testbox `tbx_01ks8b6vn8se5cg1dfn3te3g47` / https://github.com/openclaw/openclaw/actions/runs/26301988670
- Autoreview clean: `/Users/steipete/Projects/agent-scripts/skills/autoreview/scripts/autoreview --mode branch --base origin/main`
- PR CI green on `79e8c5f1a637981d263c0268bf5666967ff4e778`: https://github.com/openclaw/openclaw/actions/runs/26302152844 and https://github.com/openclaw/openclaw/actions/runs/26302152798
Co-authored-by: luoyanglang <hanwanlonga@gmail.com>
Summary:
- Document that MEDIA directives must be plain-text line-start metadata.
Verification:
- Source check: src/media/parse.ts only recognizes lines whose trimmed start begins with MEDIA: and skips fenced code blocks.
- PR CI: check-docs succeeded.
Summary:
- The PR moves gateway provider auth-state prewarm into cancelable post-ready gateway lifetime work, uses current runtime config for delayed warms, and adds related gateway/provider-auth tests plus a changelog entry.
- Reproducibility: no. high-confidence runtime reproduction was run in this review. Source inspection shows th ... th on current main, and the source PR supplies live after-fix proof for the focused startup-ordering slice.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(gateway): defer provider auth prewarm after startup
Validation:
- ClawSweeper review passed for head 31ea4288e3.
- Required merge gates passed before the squash merge.
Prepared head SHA: 31ea4288e3
Review: https://github.com/openclaw/openclaw/pull/85369#issuecomment-4519123491
Co-authored-by: Bob <dutifulbob@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: osolmaz
Co-authored-by: osolmaz <2453968+osolmaz@users.noreply.github.com>
Summary:
- The PR teaches `openclaw devices approve <requestId>` to approve a compatible same-device replacement request during local fallback and adds focused CLI, infra, and changelog coverage.
- Reproducibility: yes. Source inspection shows current main rejects the gateway's replacement requestId as a ... adds focused infra and CLI tests for the churn path; I did not run tests because this review is read-only.
Automerge notes:
- PR branch already contained follow-up commit before automerge: docs: note device approval recovery
Validation:
- ClawSweeper review passed for head 1d2f2e9b2f.
- Required merge gates passed before the squash merge.
Prepared head SHA: 1d2f2e9b2f
Review: https://github.com/openclaw/openclaw/pull/85342#issuecomment-4518449317
Co-authored-by: masonxhuang <masonxhuang@tencent.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: hxy91819
Co-authored-by: hxy91819 <8814856+hxy91819@users.noreply.github.com>
normalizeAllowedModelRef() and the resolved override ref interpolated
${provider}/${model} after normalizeModelRef(), so a provider-qualified
model id like openrouter/gpt-5.4-mini surfaced as
openrouter/openrouter/gpt-5.4-mini in the allowlist set and policy
denial message, masking the actionable model ref.
Route both sites through modelKey() (src/agents/model-ref-shared.ts)
so the provider segment is collapsed when the model id already starts
with it. Add regression tests covering allowlist hit and denial paths
for the OpenRouter shape.
Fixes#84887
* fix(gateway): include openclaw bin in service PATH
* fix(doctor): accept expected service PATH
* docs(changelog): mention managed service PATH bin fix
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Summary:
- Honor per-model api/baseUrl overrides during custom provider auth hook lookup and transport selection.
- Keep models-add metadata safeguards intact and add focused auth/model resolver regression coverage.
- Add maintainer changelog credit for @huveewomg.
Verification:
- git diff --check
- GitHub CI green on 277629e992
- GitHub CodeQL green on 277629e992
- GitHub CodeQL Critical Quality green on 277629e992
- GitHub Real behavior proof green on 277629e992
- Local focused Vitest was stopped after 8 minutes on a busy host without producing a result; PR CI supplied the final proof.
Co-authored-by: huveewomg <wongrenthou1265@gmail.com>
* fix(gateway): eager-load lifecycle runtime to survive in-place upgrades
After a package-swap update (e.g. via update.run), dist/ chunk hashes
rotate while the gateway is still running. The SIGUSR1 listener's first
dynamic import of the lifecycle runtime module then throws
ERR_MODULE_NOT_FOUND inside its async IIFE, silently rejects, and leaves
restart.ts's emittedRestartToken permanently unconsumed. From that point
every scheduleGatewaySigusr1Restart() — including the one update.run
schedules for itself — returns { coalesced: true } without scheduling
anything, and the gateway never restarts until manually kickstarted.
Fix:
1. Eagerly resolve the lifecycle runtime module as the first statement
of runGatewayLoop, before any signal listener is installed. lifecycle.runtime
is a 36-line re-export hub, so loading it once pulls the entire restart
/ respawn / queue / sentinel / handoff graph into memory, immune to
later disk rotation. If the module is missing at startup, fail fast
with a loud error so the supervisor can recover instead of running
half-broken.
2. Defense in depth: catch SIGUSR1 IIFE rejections and call
markGatewaySigusr1RestartHandled() via the eagerly captured reference,
so a transient listener failure doesn't permanently stick the restart
token.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* docs(changelog): mention lifecycle restart eager load
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Summary:
- The branch replaces the Crabbox wrapper's hardcoded provider allow-list with help-output parsing, preserves current aliases and a known help omission, adds wrapper tests, and updates the changelog.
- Reproducibility: yes. source-reproducible: current main only rejects selected providers that are already in ... rovider names can bypass wrapper validation. I did not run the PR branch because this review was read-only.
Automerge notes:
- PR branch already contained follow-up commit before automerge: refactor(crabbox): parse provider list from binary help instead of ha…
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8530…
Validation:
- ClawSweeper review passed for head c99388d92a.
- Required merge gates passed before the squash merge.
Prepared head SHA: c99388d92a
Review: https://github.com/openclaw/openclaw/pull/85302#issuecomment-4517730136
Co-authored-by: masonxhuang <masonxhuang@tencent.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: hxy91819
Co-authored-by: hxy91819 <8814856+hxy91819@users.noreply.github.com>
Limit sub-agent bootstrap context to AGENTS.md and TOOLS.md without adding a new config surface. Preserve the existing cron minimal bootstrap behavior.
Co-authored-by: Eva (agent) <eva+agent-78055@100yen.org>
Close child ACP runtimes during parent reset/delete through a shared direct-child session lookup, covering spawnedBy and parentSessionKey lineage across combined agent stores.
Also adds focused regression coverage for direct child discovery, non-ACP/unrelated negatives, reset cleanup, delete cleanup, cross-store children, and concurrent stuck-child cleanup.
Co-authored-by: openperf <16864032@qq.com>
Summary:
- The branch adds a Google provider thinking-policy resolver and opt-in profile flag, updates shared thinking validation and cron/proof-policy tests, and adjusts ClawSweeper proof parsing.
- Reproducibility: yes. source-reproducible: current main applies the generic off-only profile before provider ... figured thinking through that resolver. I did not execute a live systemd cron run in this read-only review.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix: preserve Google Gemini 3 cron thinking
Validation:
- ClawSweeper review passed for head a6cd2e826e.
- Required merge gates passed before the squash merge.
Prepared head SHA: a6cd2e826e
Review: https://github.com/openclaw/openclaw/pull/85300#issuecomment-4517662575
Co-authored-by: Neerav Makwana <261249544+neeravmakwana@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Keep derived plugin metadata snapshots out of the process memo/current snapshot cache so newly added plugins under derived discovery paths are found without restart.
The deriveIdHint function already strips -provider from unscoped
package names (@openclaw/anthropic-provider -> anthropic) but does
not strip -plugin (@openclaw/xai-plugin -> xai-plugin instead of
xai). This causes ~30 spurious 'plugin id mismatch' warnings on
gateway startup for built-in plugins whose package names end in
-plugin.
Closes#85048
* fix(gemini): strip sub-second precision from web_search time_range_filter
Gemini's google_search.time_range_filter rejects any non-zero fractional
seconds with "[FIELD_INVALID] Granularity of nano is not supported", even
though the underlying google.protobuf.Timestamp type accepts 0/3/6/9
fractional digits per its public spec. The grounding endpoint enforces a
stricter rule than the underlying type.
Date.prototype.toISOString() always emits millisecond precision, so every
freshness call (and any date_after/date_before call hitting the "now"
fallback for endTime) failed with the above 400 after #66498's fix shipped
in 2026.5.19.
Introduce toGeminiTimeRangeTimestamp() which strips the fractional-second
component before serializing, and route all four timeRangeFilter timestamp
sites through it. isoDateExclusiveEnd happens to produce all-zero
fractional today (so Gemini accepts it), but routing it through the helper
keeps the contract uniform and resilient to future changes.
Why this slipped past the original CI: the existing freshness test used
vi.setSystemTime(new Date("2026-04-15T12:00:00Z")), which always
serializes back as ".000Z" — the one fractional form Gemini happens to
accept. Wall-clock new Date() in production always has non-zero ms. The
new test uses setSystemTime(new Date("2026-04-15T12:00:00.123Z")) to
exercise the realistic case.
Verified empirically against the live Gemini REST API:
".123Z" → 400 "Granularity of nano is not supported"
".000Z" → grounded content (the one fractional form accepted)
"Z" → grounded content
Fixes#85061.
* test(gemini): use realistic non-zero ms in existing freshness test
The original test set the fake clock to a moment with zero fractional
seconds, so toISOString() produced ".000Z" — the one fractional form
Gemini's google_search.time_range_filter happens to accept. Wall-clock
new Date() in production produces non-zero ms, which Gemini rejects.
Bumping the fake time to .123Z makes the existing test exercise the
realistic case alongside the dedicated regression test.
---------
Co-authored-by: Noah R <Noerr@users.noreply.github.com>
Summary:
- The PR changes generated-media duplicate guards, completion delivery fallback behavior, transcript write-lock reuse, task-registry fresh owner reads, docs, changelog, and regression coverage.
- Reproducibility: yes. with source and artifact evidence rather than a local rerun: current main completes me ... e task and one successful video task after the patch. I did not run tests because this review is read-only.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix: dedupe media completion delivery
- PR branch already contained follow-up commit before automerge: fix: avoid music provider lookup for explicit models
- PR branch already contained follow-up commit before automerge: fix: narrow detached media task handles
- PR branch already contained follow-up commit before automerge: fix: close media completion review gaps
- PR branch already contained follow-up commit before automerge: fix: tolerate media delivery mirrors during session lock
- PR branch already contained follow-up commit before automerge: Fix media completion duplicate delivery
Validation:
- ClawSweeper review passed for head f83e3bf143.
- Required merge gates passed before the squash merge.
Prepared head SHA: f83e3bf143
Review: https://github.com/openclaw/openclaw/pull/84006#issuecomment-4484835103
Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: FullerStackDev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Surface local post-challenge connect assembly failures immediately instead of waiting for the Gateway CLI wrapper timeout.\n\nCo-authored-by: samzong <samzong.lu@gmail.com>
Summary:
- The branch passes runtime config into the model config write helper, updates `openclaw models set` to resolve aliases source-first then runtime-fallback, and adds regression tests plus a changelog entry.
- Reproducibility: yes. I did not execute the CLI in this read-only review, but the current-main source path a ... ing against source config while runtime defaults can be the only place the displayed `sonnet` alias exists.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(models): preserve authored aliases for set
- PR branch already contained follow-up commit before automerge: fix(models): resolve set aliases from runtime config [AI-assisted]
Validation:
- ClawSweeper review passed for head 29138ac5d0.
- Required merge gates passed before the squash merge.
Prepared head SHA: 29138ac5d0
Review: https://github.com/openclaw/openclaw/pull/83262#issuecomment-4472495568
Co-authored-by: JARVIS-Glasses <284122573+JARVIS-Glasses@users.noreply.github.com>
Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
The watcher fired constantly because the gateway itself rewrites
auth-profiles.json frequently (cooldown ticks, usage stats, OAuth
refresh, markAuthProfileFailure). Each self-write triggered chokidar
which cleared the prepared auth map and scheduled an ~8 s rewarm,
a feedback loop that defeated the caching the rest of the PR added.
Drop the watcher entirely. Self-heal still covers the stale-TRUE
direction via the markAuthProfileFailure hook. Stale-FALSE (user adds
auth externally and the gateway hasn't observed any request through
that profile yet) reverts to the pre-PR behavior: reload config or
restart gateway to pick it up. Known limitation.
Summary:
- The PR rejects Codex app-server command overrides that embed Node/package-manager inline arguments, adds matching doctor diagnostics, regression tests, and a changelog entry.
- Reproducibility: yes. for the scoped malformed override path: current main passes the combined command strin ... ix resolver/doctor live output. I did not establish a live Windows npm-global managed-startup reproduction.
Automerge notes:
- PR branch already contained follow-up commit before automerge: Validate Codex app-server command overrides
Validation:
- ClawSweeper review passed for head 966bcd6617.
- Required merge gates passed before the squash merge.
Prepared head SHA: 966bcd6617
Review: https://github.com/openclaw/openclaw/pull/84417#issuecomment-4494295224
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Remove the chat picker recency/current-agent filters while preserving the bounded configured-agent refresh, and add the changelog credit for @amknight.
Summary:
- The PR removes the auto-reply compaction-failure session reset hook, adds preserved-session recovery guidance for overflow/compaction failure paths, and updates focused tests, docs, and the changelog.
- Reproducibility: yes. at source level with high confidence. Current main routes both embedded overflow paylo ... resetSessionAfterCompactionFailure, and the PR body includes before/after terminal proof of those branches.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(auto-reply): drop dead compaction reset hook
- PR branch already contained follow-up commit before automerge: fix(auto-reply): preserve sessions after compaction failures
Validation:
- ClawSweeper review passed for head 193d3c0fdd.
- Required merge gates passed before the squash merge.
Prepared head SHA: 193d3c0fdd
Review: https://github.com/openclaw/openclaw/pull/70479#issuecomment-4325128777
Co-authored-by: FullerStackDev <263060202+fuller-stack-dev@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Addresses three ClawSweeper findings on the fs-watcher commit:
- [P1] auth-profile watcher now handles chokidar 'error' events (logs +
closes once) mirroring the gateway config-reload pattern. Without
this, an unhandled error from chokidar can crash the gateway.
- [P2] auth-profile watcher handle is pushed into postReadySidecars so
stopPostReadySidecarsAfterCloseStarted closes it on gateway shutdown.
- [P2] auth-failure and file-change invalidation paths now schedule a
background rewarm (with a 'reason=' log line). Without this, the next
/models call after an invalidation paid the slow per-provider path
until the next reload. The warmer's existing generation counter
handles concurrent rewarms safely.
Adds a chokidar watcher on every configured agent's auth-profiles.json.
Any change fires clearCurrentProviderAuthState so the next model-listing
call recomputes against the on-disk auth state. Closes the stale-FALSE
direction (user adds auth via codex login, hand-edit, etc.) that the
auth-failure hook can't catch on its own.
When markAuthProfileFailure observes an auth failure at request time
(token rotated, OAuth revoke, etc.), fire a hook that clears the
prepared provider-auth map so the next model-listing call recomputes
against the real auth state. Single mutable hook slot wired up at
gateway startup; no TTL or polling.
Addresses ClawSweeper's P1 freshness finding on #85125 without
reintroducing the TTL.
Summary:
- The PR changes shared, Feishu, Mattermost, Microsoft Teams, and WhatsApp inbound debounce predicates so bare abort text bypasses debounce, then adds focused tests and a changelog entry.
- Reproducibility: yes. source-level. Current main sends bare `stop`, `abort`, and `wait` through a `hasContro ... ()` debounce gate, while the existing abort-aware detector and trigger set already recognize those phrases.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(channels): bypass debounce for bare abort triggers [AI-assisted]
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8334…
Validation:
- ClawSweeper review passed for head c96bf84270.
- Required merge gates passed before the squash merge.
Prepared head SHA: c96bf84270
Review: https://github.com/openclaw/openclaw/pull/83348#issuecomment-4473176095
Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The branch adds an abort-aware dispatcher-idle wait after successful same-channel and direct ACP block replies, plus regression tests and a changelog entry.
- Reproducibility: yes. Current main source shows the same-channel block callback queues dispatcher delivery w ... spatcher idle, and the PR body supplies before/after diagnostic output for the tool-start ordering failure.
Automerge notes:
- PR branch already contained follow-up commit before automerge: [AI-assisted] fix(reply): wait for block replies before tools
Validation:
- ClawSweeper review passed for head 32576209a2.
- Required merge gates passed before the squash merge.
Prepared head SHA: 32576209a2
Review: https://github.com/openclaw/openclaw/pull/83722#issuecomment-4480639845
Co-authored-by: JARVIS-Glasses <284122573+JARVIS-Glasses@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Harden the Codex app-server native execution bridge for OpenClaw sandboxed runs. The change keeps core sandbox policy in OpenClaw while exposing the process, filesystem, and HTTP relay behavior Codex needs inside a scoped exec server.
The large exec-server/test files were split into focused modules before landing, and the PR was rebased onto current main with focused tests, Testbox changed checks, CI, and Codex autoreview green.
Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
Summary:
- The PR adds shared blocked-liveness normalization, applies it to agent.wait, gateway dedupe, subagent registry, and announcement paths, and adds regression tests plus a changelog entry.
- Reproducibility: yes. from source inspection: current main accepts blocked lifecycle/wait metadata as ok thr ... gateway wait and registry completion paths. I did not run a live provider overflow in this read-only pass.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(agents): normalize blocked wait completions
- PR branch already contained follow-up commit before automerge: fix(agents): surface blocked subagent completions
Validation:
- ClawSweeper review passed for head 224785c8a6.
- Required merge gates passed before the squash merge.
Prepared head SHA: 224785c8a6
Review: https://github.com/openclaw/openclaw/pull/80886#issuecomment-4427552621
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Adds installRecords to PluginLoadOptions and PluginRuntimeLoadContext so
callers that already hold a PluginMetadataSnapshot can pass the snapshot's
in-memory records instead of forcing each downstream loader to re-read
installs.json. resolvePluginRuntimeLoadContext extracts the records from
the snapshot via extractPluginInstallRecordsFromInstalledPluginIndex,
buildPluginRuntimeLoadOptionsFromValues forwards them, and the setup +
runtime provider load paths in providers.runtime.ts pass them through
from params.pluginMetadataSnapshot. resolvePluginLoadCacheContext uses
the threaded records (falling back to the sync read) and
loader-provenance now uses params.installRecords ?? sync-read instead of
always reading and overlaying.
Log structured details when Discord persistent component registry state falls back after a store failure.
- Format Error name, message, stack, and cause metadata at the Discord registry warning call site.
- Forward plugin runtime logger metadata to the underlying child logger.
- Add focused regression coverage for the Discord fallback warning and runtime logging adapter.
- Add changelog credit for @100menotu001.
Fixes#84185.
Co-authored-by: OpenClaw Contributor <100menotu001@users.noreply.github.com>
Co-authored-by: Craig <froelich@craigs.mac.studio.froho>
Summary:
- The PR adds a bundled-channel load-error formatter, wires it into the bundled-channel warning paths, adds focused tests, and updates the changelog.
- Reproducibility: yes. source-level: current main logs bundled-channel load failures with bare `formatErrorMe ... cause`. The contributor's terminal proof demonstrates the same wrapped-error shape before and after the PR.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(channels): walk error cause chain to detect missing bundled modules
- PR branch already contained follow-up commit before automerge: docs(changelog): add Unreleased Fixes entry
- PR branch already contained follow-up commit before automerge: Merge remote-tracking branch 'origin/main' into fix/bundled-channel-l…
- PR branch already contained follow-up commit before automerge: Merge branch 'main' into fix/bundled-channel-load-doctor-hint
Validation:
- ClawSweeper review passed for head 416a8a2e77.
- Required merge gates passed before the squash merge.
Prepared head SHA: 416a8a2e77
Review: https://github.com/openclaw/openclaw/pull/76974#issuecomment-4367336485
Co-authored-by: BSG2000 <github@hsu.hamburg>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: BSG2000 <BSG2000@users.noreply.github.com>
Co-authored-by: BSG2000 <thomas.krohnfuss@stud.th-luebeck.de>
Co-authored-by: Thomas Krohnfuß <BSG2000@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Preserve `reusable` for portable message presentation buttons and carry it through Discord component registration so repeatable callbacks stay available after a successful interaction.
Also keeps `reusable` through legacy presentation-to-interactive conversion and documents the user-visible change in the changelog.
Verification:
- `pnpm test src/interactive/payload.test.ts extensions/discord/src/shared-interactive.test.ts extensions/discord/src/components.test.ts -- --reporter=verbose`
- `git diff --check`
- `AUTOREVIEW_AUTO_TESTS=0 .agents/skills/autoreview/scripts/autoreview --mode local`
- PR CI at `52f25221b3e01f3255d8df37df73d0357ab7410b`: all completed checks green/skipped/neutral except pending CodeQL `Security High (mcp-process-tool-boundary)` at time auto-merge was armed.
Co-authored-by: OpenClaw Contributor <100menotu001@users.noreply.github.com>
Summary:
- The branch teaches Ollama discovery to use resolved `discoveryApiKey` values for non-local cloud providers, preserves local marker auth, and adds focused provider-discovery regressions plus a changelog entry.
- Reproducibility: yes. from source inspection: current main can return the `OLLAMA_API_KEY` marker instead of ... ential for documented Ollama Cloud config. I did not run executable tests because this review is read-only.
Automerge notes:
- PR branch already contained follow-up commit before automerge: ci: allowlist qa lab fixtures
- PR branch already contained follow-up commit before automerge: Fix Ollama cloud API key discovery
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8503…
Validation:
- ClawSweeper review passed for head cb6b658819.
- Required merge gates passed before the squash merge.
Prepared head SHA: cb6b658819
Review: https://github.com/openclaw/openclaw/pull/85091#issuecomment-4512647237
Co-authored-by: Anup Sharma <anupnewsmail@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
The auto-migration introduced in #83312 only fires when a credential is loaded
via a path that reads its sidecar tokens. The OAuth refresh manager's internal
loader does (so direct CLI inference works and self-heals on first refresh).
The embedded runner's secrets-runtime loaders did not:
- loadAuthProfileStoreForSecretsRuntime
- loadAuthProfileStoreWithoutExternalProfiles
- ensureAuthProfileStoreWithoutExternalProfiles
All three opted out of sidecar resolution. So for an upgraded user with a
legacy oauthRef-backed openai-codex profile, the credential loaded with no
access/refresh material, evaluateStoredCredentialEligibility marked it
ineligible, resolveAuthProfileOrder filtered it out, and resolveApiKeyForProvider
threw "No API key found for provider 'openai-codex'" before the OAuth manager
(and its migration path) was ever consulted. CLI worked, Telegram/cron/embedded
turns broke — only doctor-or-bust would fix it.
Flip the three embedded loaders to default resolveLegacyOAuthSidecars to true
(matching loadStoredOAuthRefreshStore). The existing #83312 refresh-and-rewrite
then fires on the first embedded turn for these users and persists tokens
inline, removing the legacy sidecar from disk on the next doctor pass.
Cherry-picked and squashed from PR #84752 (commits 85f36e8d2b and
4624e34c06). Comments noting local-fork bookkeeping stripped per repo policy.
Co-authored-by: Will <totalsolutionspm@gmail.com>
P1 (auth-profile logout): invalidateModelAuthStatusCache now also clears
the prepared provider-auth map, and the models.auth.logout handler fires
a rewarm against the current config. Without this, removing a provider's
auth profiles left the warmed 'true' answer in the map until restart,
so /models and pickers kept advertising the removed provider.
P1 (plugin-reload ordering): the previous version fired the rewarm
inline with the clear, before plan.reloadPlugins() ran. The warmer
reads plugin metadata and synthetic-auth hooks, so it published the
pre-reload runtime's answers. Moved the rewarm to fire after the
plugin-reload block completes, so it reads the new plugin runtime.
The early clear still happens upfront so callers don't keep seeing the
pre-reload answer during the reload window.
ClawSweeper P1 + P2 findings on the prior review-fix commit.
- [P1] hasAuthForModelProvider now also checks workspaceDir against the
warm's snapshot value. The warmer uses resolveDefaultAgentWorkspaceDir,
but per-agent picker calls (buildModelsProviderData →
resolveVisibleModelCatalog → createProviderAuthChecker) thread an
agent-specific workspaceDir, and provider env/synthetic-auth
resolution depends on it. Without this check the picker for agent B
would silently reuse agent A's warmed answer.
- [P1] warmCurrentProviderAuthState now claims a generation counter at
the start of its work and only publishes the new state if the
generation hasn't been bumped (by a concurrent clear or another
warm). Closes the race where a slow startup warm could overwrite a
newer reload-driven rewarm with stale data.
- [P2] Reload handler now also clears and rewarms the prepared map
when plugins.* config paths change. Provider auth can come from
plugin env vars and plugin synthetic-auth wiring, so plugin hot
reloads must invalidate the auth state too — not just model config
paths.
Test: new case asserting a non-default workspaceDir caller bypasses the
prepared map and falls through to compute.
Two fixes flagged by ClawSweeper.
P1 — hasAuthForModelProvider now only short-circuits via the prepared
map when the caller's scope matches the warmer's (broad discovery, no
agentDir/env/store override). Read-only gateway model lists pass
runtimeAuthDiscovery: false, which the visibility helper maps to
discoverExternalCliAuth: false and allowPluginSyntheticAuth: false; the
prepared broad answer was previously masking that narrower intent. Now
those callers fall through to compute the narrow answer.
P2 — server-reload-handlers now also schedules a rewarm right after
clearing the prepared state on model-config reload, so long-lived
gateways don't regress to per-call discovery between reload and the
next restart.
Test: extends model-provider-auth.test.ts with a scope-narrowing case
asserting the prepared answer is bypassed when the caller passes
discoverExternalCliAuth: false / allowPluginSyntheticAuth: false.
Asserts hasAuthForModelProvider returns the warmed answer for providers
in the prepared map and skips the compute path, and that
clearCurrentProviderAuthState restores fall-through to compute.
Eliminates the per-call auth-filter loop that every /models invocation
(Discord/Telegram pickers, CLI, status commands) was paying — 30 unique
providers × ~600 ms each of plugin-runtime / external-CLI / auth-profile
discovery, done fresh on every call (~20 s per call).
warmCurrentProviderAuthState builds a provider->boolean map once at
gateway startup against a single AuthProfileStore scoped to every
candidate provider, and hasAuthForModelProvider consults the prepared
map first and short-circuits. The map is invalidated on config reload
alongside resetModelCatalogCache so the next read after a relevant
config change rewarms.
Per /models: ~20,569 ms → ~5 ms (~4,100×).
One-time startup warm cost: ~49 s (cold catalog + auth sweep), logged
via gateway log.info on completion.
knip's deadcode-unused-files check ignores fixtures matching **/*.fixture.ts
(dot before "fixture"). The codex lifecycle fixtures landed in bbf3eec786
as auth-profile-fixture.ts and codex-plugin-fixture.ts (hyphen), so knip
flagged them as unexpected unused files and CI's check-dependencies job
has been failing on main since then. Rename to auth-profile.fixture.ts
and codex-plugin.fixture.ts and update the lifecycle test, the fixture
cross-import, and the six qa/scenarios markdown files that reference
them by path and qaImport specifier.
Summary:
- The branch scopes config-time Google Gemini preview model normalization to Google providers or nested `google/` proxy suffixes, adds model-picker regression coverage, and adds a changelog entry.
- Reproducibility: yes. by source inspection. Current main sends every provider suffix through the Google prev ... i-3-flash` deterministically becomes `litellm/gemini-3-flash-preview`; I did not run a live cron preflight.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(config): scope Google preview model normalization to Google provi…
- PR branch already contained follow-up commit before automerge: fix#84745: scope Google preview model normalization to Google provid…
- PR branch already contained follow-up commit before automerge: fix#84745: preserve proxy Google model normalization
Validation:
- ClawSweeper review passed for head c59163c809.
- Required merge gates passed before the squash merge.
Prepared head SHA: c59163c809
Review: https://github.com/openclaw/openclaw/pull/84762#issuecomment-4504169062
Co-authored-by: zhang-guiping <zhang.guiping@xydigit.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: 张贵萍0668001030 <zhang.guiping@xydigit.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
OAuth credentials that loaded without their sidecar material (no access, no
refresh) would still enter the refresh path inside the per-profile lock,
where the adapter call is bounded by OAUTH_REFRESH_CALL_TIMEOUT_MS (120s).
That made the eventual "No API key found for provider" surface to the user
only after a long stall, even though the resolver had no usable material to
attempt with.
Short-circuit doRefreshOAuthTokenWithLock to return null when there is no
refresh token to use, after the in-lock main-store adoption and external
bootstrap-credential checks have already had a chance to recover.
Thanks @romneyda.
Summary:
- The PR wraps the async JSON file readers in `src/infra/json-files.ts` with bounded retries for fs-safe `File changed during read` races, adds regression tests, and adds a changelog entry.
- Reproducibility: yes. Source inspection shows fs-safe throws `File changed during read`, current main re-exp ... R proof includes before/after gateway logs; I did not run a new live race harness in this read-only review.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(json): preserve strict reader types (Promise<T> for readJson/read…
- PR branch already contained follow-up commit before automerge: test(json): add retry-success and retry-exhaustion coverage
- PR branch already contained follow-up commit before automerge: fix(json): resolve lint warnings (prefer-exponentiation-operator, cur…
- PR branch already contained follow-up commit before automerge: fix(json): retry on transient File changed during read race condition
Validation:
- ClawSweeper review passed for head 00602a1c03.
- Required merge gates passed before the squash merge.
Prepared head SHA: 00602a1c03
Review: https://github.com/openclaw/openclaw/pull/85029#issuecomment-4510494668
Co-authored-by: samson1357924 <98934496+samson1357924@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- This replacement PR adds inbound delivery diagnostic events, gateway status counters and warnings, transport ... ut, Prometheus/OpenTelemetry metrics, docs, changelog, and regression coverage for gateway delivery health.
- Reproducibility: no. high-confidence live reproduction of the original Feishu failure was run here. Source i ... ch/turn telemetry, and the source PR supplies after-fix live output for the connected WebChat gateway path.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(types): restore PR conflict resolution type checks
Validation:
- ClawSweeper review passed for head 6ffe08a9c7.
- Required merge gates passed before the squash merge.
Prepared head SHA: 6ffe08a9c7
Review: https://github.com/openclaw/openclaw/pull/85016#issuecomment-4510224436
Co-authored-by: Andi Liao <liaoandi95@gmail.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The PR adds Docker/OrbStack host aliases to Ollama local-auth classification, keeps those aliases out of loopback-only discovery suppression, adds regression tests, and updates the changelog.
- Reproducibility: yes. The linked report gives a concrete v2026.5.19 config and error, and current main source shows host.orb.internal is not classified as local for ollama-local marker auth.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(ollama): allow Orb host local auth
Validation:
- ClawSweeper review passed for head cb82dcf522.
- Required merge gates passed before the squash merge.
Prepared head SHA: cb82dcf522
Review: https://github.com/openclaw/openclaw/pull/84999#issuecomment-4509786332
Co-authored-by: Bob <dutifulbob@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: osolmaz
Co-authored-by: osolmaz <2453968+osolmaz@users.noreply.github.com>
Summary:
- The branch clears WebChat local run and stream state when terminal session reconciliation completes the acti ... session events, adjusts deferred history/queue flushing, adds regression tests, and updates the changelog.
- Reproducibility: yes. with high confidence from source inspection and PR evidence. Current main can apply a ... PR body, recording, and regression shape show the stale WebChat typing state being cleared by this branch.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix: harden webchat session run reconciliation
Validation:
- ClawSweeper review passed for head 89cca8dd01.
- Required merge gates passed before the squash merge.
Prepared head SHA: 89cca8dd01
Review: https://github.com/openclaw/openclaw/pull/84565#issuecomment-4498262223
Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: jesse-merhi
Add the missing changelog entry for the landed Web Push VAPID subject fix and refresh the config docs baseline hash to match the Node 24 check environment.
Summary:
- The PR preserves native Ollama tool-call IDs through ingest and replay, opts native Ollama out of strict replay ID sanitization, and adds focused regression tests plus a changelog entry.
- Reproducibility: yes. Current main drops native Ollama tool-call IDs on ingest and replay and applies strict ... PR discussion includes a maintainer-side before/after probe that reproduced the source-level failure path.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(ollama): keep native tool ids through replay
Validation:
- ClawSweeper review passed for head bb9fef7d4c.
- Required merge gates passed before the squash merge.
Prepared head SHA: bb9fef7d4c
Review: https://github.com/openclaw/openclaw/pull/84855#issuecomment-4505423891
Co-authored-by: IWhatsskill <whatsskilll@gmail.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: osolmaz
Co-authored-by: osolmaz <2453968+osolmaz@users.noreply.github.com>
* fix(config): append numeric bound hints to ceiling/floor validation errors
When a config value exceeds a schema-enforced ceiling or falls below a
floor, the error message now includes the constraint explicitly:
- Inclusive: `(maximum: 20)` / `(minimum: 0)`
- Exclusive: `(must be less than 5)` / `(must be greater than 0)`
This matches the clarity that enum/union rejections already get via
`(allowed: …)` hints, and avoids the misleading "minimum: 0" wording
that previous attempts produced for `.positive()` / `.gt(0)` rejections.
Only numeric-origin `too_big`/`too_small` issues are enriched; string,
array, and file-size origins are left unchanged.
Fixes#52500
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(config): update maxFileBytes test for numeric bound hint
The test snapshot for `logging.maxFileBytes: 0` rejection now includes
the `(must be greater than 0)` hint appended by the numeric bound
enrichment added in the previous commit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(config): guard nullable record in appendNumericBoundHint call
ClawSweeper P1: `record` from `toIssueRecord()` can be null, but
`appendNumericBoundHint` expects a non-null `UnknownIssueRecord`.
Guard with a ternary so the original message is returned when record
is null (which only happens for malformed/empty issues that already
produce generic "Invalid input" messages).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: tanshanshan <tanshanshan@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(diffs): replace iconMarkup string with ToolbarIconName enum to eliminate XSS sink
Replace createToolbarButton's iconMarkup: string parameter with icon: ToolbarIconName,
a union of known icon names. SVG generation moves into a sealed toolbarIconSvg map so
innerHTML only receives compile-time-known strings. The old splitIcon/unifiedIcon/
wrapIcon/backgroundIcon/themeIcon functions are removed; callers now pass icon name
literals instead of raw markup strings.
Closes#83918
* fix(diffs): remove jsdom dependency from viewer-client test
Use source file string analysis instead of jsdom to avoid missing
@types/jsdom declaration error in check-test-types CI job.
* fix(diffs): restore wrap icon arrow segment in ToolbarIconName map
The wrap-on and wrap-off SVG paths were missing the original wrap arrow
segment (M14 6h-4V5h4.5...). Restore the exact original path data and
rebuild the viewer runtime bundle.
* build(diffs): refresh viewer runtime after rebase
---------
Co-authored-by: tanshanshan <tanshanshan@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Summary:
- The PR adds a diagnostics-otel scoped unhandled-rejection handler for nested OTLPExporterError values, unregisters it on stop/restart, adds regression tests, and adds a changelog entry.
- Reproducibility: yes. The source path is high-confidence: current main has no OTLPExporterError-specific dia ... ror for non-retryable OTLP HTTP failures; I did not run a live collector shutdown in this read-only review.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(diagnostics-otel): avoid stale exporter handler
- PR branch already contained follow-up commit before automerge: fix(diagnostics-otel): suppress exporter rejection crashes
Validation:
- ClawSweeper review passed for head e19c06c992.
- Required merge gates passed before the squash merge.
Prepared head SHA: e19c06c992
Review: https://github.com/openclaw/openclaw/pull/84881#issuecomment-4506249586
Co-authored-by: luoyanglang <hanwanlonga@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- This replacement PR marks the Linux node daemon gateway token as file-backed, writes it to `node.systemd.env`, sanitizes and migrates systemd env artifacts, adds regression tests, and updates the changelog.
- Reproducibility: yes. from source inspection: current `main` copies `OPENCLAW_GATEWAY_TOKEN` into the node s ... e-backed before systemd rendering. I did not run a local live systemd install during this read-only review.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(systemd): scrub single-quoted env tokens
- PR branch already contained follow-up commit before automerge: [Fix] Keep node systemd tokens out of unit files
Validation:
- ClawSweeper review passed for head f626b66c09.
- Required merge gates passed before the squash merge.
Prepared head SHA: f626b66c09
Review: https://github.com/openclaw/openclaw/pull/84815#issuecomment-4505012292
Co-authored-by: samzong <samzong.lu@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The PR disables pi-coding-agent auto-retry inside prepared embedded Pi settings, updates the focused settings test, and moves the changelog entry into Unreleased.
- Reproducibility: yes. source-reproducible: current main leaves embedded Pi retry enabled, while pi-coding-ag ... e assistant error before continuing. I did not run a live Feishu/Qwen replay loop in this read-only review.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(agents): disable pi-coding-agent auto-retry to prevent tool call …
Validation:
- ClawSweeper review passed for head ca745fd55d.
- Required merge gates passed before the squash merge.
Prepared head SHA: ca745fd55d
Review: https://github.com/openclaw/openclaw/pull/84798#issuecomment-4504702875
Co-authored-by: yelog <yelogeek@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- This PR filters partial skill snapshot entries in trajectory support metadata, accepts nullish support-redaction paths, adds regression tests, and records the fix in the changelog.
- Reproducibility: yes. Source inspection on current main shows undefined skill path/name values can reach str ... and the related source PR provides redacted live before/after gateway logs for the symlink-escape scenario.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(trajectory): tighten test types for partial skill entries
- PR branch already contained follow-up commit before automerge: fix(trajectory): tolerate partial skill snapshot entries in support c…
Validation:
- ClawSweeper review passed for head ecb3df6c08.
- Required merge gates passed before the squash merge.
Prepared head SHA: ecb3df6c08
Review: https://github.com/openclaw/openclaw/pull/84797#issuecomment-4504703074
Co-authored-by: Luke Boyett <46942646+lukeboyett@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Widen the Control UI settings Personal quick-settings card to the intended 3/1 desktop split, keep Personal before Appearance/Automations at the narrower layout, and make the focused CSS assertions tolerant of harmless formatting changes.
Verification:
- pnpm --dir ui test src/styles/config-quick.test.ts
- pnpm exec oxfmt --check --threads=1 ui/src/styles/config-quick.test.ts
- git diff --check
- GitHub CI on 4c8f6d7f50
Lazy-load agents CLI action modules from command callbacks so agents --help avoids importing the full agents runtime.
Validated by GitHub required checks and local focused CLI gates.
Summary:
- The PR changes sherpa-onnx CLI audio parsing so structured JSON with an empty `text` field becomes no transcript, while preserving non-empty JSON extraction and adding direct plus auto-detect regression coverage.
- Reproducibility: yes. Source inspection on current main shows empty sherpa structured JSON misses extraction ... scord voice can skip empty transcripts; I did not run a live Discord reproduction in this read-only review.
Automerge notes:
- PR branch already contained follow-up commit before automerge: Fix stale CI guardrails for sherpa transcript PR
- PR branch already contained follow-up commit before automerge: Skip empty sherpa structured transcripts
Validation:
- ClawSweeper review passed for head ac03171cfc.
- Required merge gates passed before the squash merge.
Prepared head SHA: ac03171cfc
Review: https://github.com/openclaw/openclaw/pull/84667#issuecomment-4501484167
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The PR removes MiniMax music duration support from provider capabilities and docs, stops prompt-injecting duration hints, updates the MiniMax provider test, and adds a changelog entry.
- Reproducibility: yes. by source inspection: current main advertises MiniMax duration support while the reque ... uage hint. I did not rerun a live pre-fix MiniMax request, but the code path and vendor contract are clear.
Automerge notes:
- PR branch already contained follow-up commit before automerge: docs(minimax): align music controls
- PR branch already contained follow-up commit before automerge: docs(music): remove minimax duration steering claim
- PR branch already contained follow-up commit before automerge: fix(minimax): stop advertising music duration control
Validation:
- ClawSweeper review passed for head 1c616da45c.
- Required merge gates passed before the squash merge.
Prepared head SHA: 1c616da45c
Review: https://github.com/openclaw/openclaw/pull/84765#issuecomment-4504176794
Co-authored-by: Neerav Makwana <261249544+neeravmakwana@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The PR updates Codex app-server system-prompt reporting to tolerate bootstrap files with `path` and `content` but no `name`, adds a focused regression test, and records the fix in the changelog.
- Reproducibility: yes. The PR body supplies current-main before output with the `undefined.trim()` stack, and source inspection confirms hook-supplied path-only bootstrap files can reach the Codex report helper.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(codex): guard path-only bootstrap files [AI-assisted]
Validation:
- ClawSweeper review passed for head 4667110899.
- Required merge gates passed before the squash merge.
Prepared head SHA: 4667110899
Review: https://github.com/openclaw/openclaw/pull/84736#issuecomment-4503672362
Co-authored-by: JARVIS-Glasses <whatsskilll@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- Adds a `doctor` security warning for plaintext secret-bearing `openclaw.json` fields by reusing the secrets target registry and shared model-provider header sensitivity policy.
- Reproducibility: yes. for source-level behavior: current main has plaintext secret audit coverage but no doc ... llector for those config targets, and the PR body includes live patched CLI output showing the new warning.
Automerge notes:
- PR branch already contained follow-up commit before automerge: Warn on plaintext secret config in doctor
Validation:
- ClawSweeper review passed for head 31f83aae19.
- Required merge gates passed before the squash merge.
Prepared head SHA: 31f83aae19
Review: https://github.com/openclaw/openclaw/pull/84718#issuecomment-4503210496
Co-authored-by: qingsenlab <qingsenlab@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The PR removes the legacy `cat SKILL.md && printf ... && <skill-wrapper>` exec-approval allowlist path, updates focused exec-approval tests, and adds a changelog entry.
- Reproducibility: yes. Current-main source and tests show the old `cat SKILL.md && printf ... && <wrapper>` c ... ed this by source and test inspection rather than executing tests because the checkout review is read-only.
Automerge notes:
- PR branch already contained follow-up commit before automerge: Remove skill prelude exec allowlist
Validation:
- ClawSweeper review passed for head 0ca7f3e8ef.
- Required merge gates passed before the squash merge.
Prepared head SHA: 0ca7f3e8ef
Review: https://github.com/openclaw/openclaw/pull/84570#issuecomment-4498357535
Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: jesse-merhi
* perf(tui): skip plugin-aware config validation on remote TUI startup
Cold `openclaw tui` against a remote gateway was synchronously calling
loadPluginMetadataSnapshot() via getRuntimeConfig() -> loadConfig() ->
validateConfigObjectWithPlugins(), pulling the full plugin metadata
snapshot (200k+ file reads) onto the TUI's event loop. The TUI itself
never consumes plugin metadata in remote mode; it queries the gateway
over RPC. The work was being done purely to validate the config and
then thrown away.
Thread an opt-in `skipPluginValidation` flag through getRuntimeConfig()
and loadConfig() (createConfigIO already supports pluginValidation: "skip";
it just wasn't reachable from the runtime entrypoints). The TUI passes
skipPluginValidation: !isLocalMode so:
- Remote-mode TUI: no plugin metadata load, no event-loop freeze after
first render
- Embedded (--local) mode: unchanged; the in-process agent runtime
still gets a fully validated config
* remove verbose comments
* perf(tui): move context cache warmup from module top-level to embedded backend
agents/context.ts fired ensureContextWindowCacheLoaded() unconditionally
at module-eval time for non-skip-listed CLI commands. The TUI transitively
imports this module, so the warmup ran on every TUI startup including
remote-mode, cascading into ensureOpenClawModelsJson -> resolveImplicitProviders
-> runProviderCatalog and dominating the cold-start freeze (CPU profile
showed ~55s of resolveProviderSyntheticAuthWithPlugin, lstat, open, etc.).
It also pre-emptively called getRuntimeConfig() without skipPluginValidation,
pinning the full snapshot and nullifying the skip flag added on this branch.
Remove the top-level side effect and trigger the warmup explicitly from
EmbeddedTuiBackend.start(), which only runs when an in-process agent
runtime actually needs the cache.
* perf(tui): defer EmbeddedTuiBackend import until local mode
* refactor(agents): remove dead context-cache warmup helpers
* perf(tui): skip plugin-aware config validation on remote TUI startup
Cold `openclaw tui` against a remote gateway was synchronously calling
loadPluginMetadataSnapshot() via getRuntimeConfig() -> loadConfig() ->
validateConfigObjectWithPlugins(), pulling the full plugin metadata
snapshot (200k+ file reads) onto the TUI's event loop. The TUI itself
never consumes plugin metadata in remote mode; it queries the gateway
over RPC. The work was being done purely to validate the config and
then thrown away.
Thread an opt-in `skipPluginValidation` flag through getRuntimeConfig()
and loadConfig() (createConfigIO already supports pluginValidation: "skip";
it just wasn't reachable from the runtime entrypoints). The TUI passes
skipPluginValidation: !isLocalMode so:
- Remote-mode TUI: no plugin metadata load, no event-loop freeze after
first render
- Embedded (--local) mode: unchanged; the in-process agent runtime
still gets a fully validated config
* remove verbose comments
* perf(tui): move context cache warmup from module top-level to embedded backend
agents/context.ts fired ensureContextWindowCacheLoaded() unconditionally
at module-eval time for non-skip-listed CLI commands. The TUI transitively
imports this module, so the warmup ran on every TUI startup including
remote-mode, cascading into ensureOpenClawModelsJson -> resolveImplicitProviders
-> runProviderCatalog and dominating the cold-start freeze (CPU profile
showed ~55s of resolveProviderSyntheticAuthWithPlugin, lstat, open, etc.).
It also pre-emptively called getRuntimeConfig() without skipPluginValidation,
pinning the full snapshot and nullifying the skip flag added on this branch.
Remove the top-level side effect and trigger the warmup explicitly from
EmbeddedTuiBackend.start(), which only runs when an in-process agent
runtime actually needs the cache.
Summary:
- The PR extracts JSON-mode console-to-stderr routing into a shared CLI helper, wraps root and `nodes` lazy plugin registration, adds nodes registration coverage, and adds a changelog entry.
- Reproducibility: yes. for source-level reproduction: the linked report shows `openclaw nodes list --json 2> ... ssing the existing JSON stderr guard. I did not run the live Helm/container repro in this read-only review.
Automerge notes:
- PR branch already contained follow-up commit before automerge: Route JSON-mode plugin registration logs to stderr
Validation:
- ClawSweeper review passed for head c9d0867db0.
- Required merge gates passed before the squash merge.
Prepared head SHA: c9d0867db0
Review: https://github.com/openclaw/openclaw/pull/84741#issuecomment-4503741078
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
* build: suppress rolldown-plugin-dts CommonJS dts warnings from bundled zod locales
After bumping rolldown-plugin-dts to 0.25.1 (94ac563399), every
`pnpm build` emits a 'CommonJS dts' warning per zod `v4/locales/*.d.cts`
file because zod is intentionally inlined for global pnpm install
resolution (#78515) and tsdown's external option cannot be scoped to the
dts pass only. Filter the warning in the existing onLog suppression list
(same pattern as PLUGIN_TIMINGS / UNRESOLVED_IMPORT / EVAL) so other
rolldown-plugin-dts warnings remain visible.
* docs(changelog): move rolldown-dts entry into 2026.5.20 fixes
Summary:
- The PR updates the macOS About settings copyright text to 2026, adds a changelog entry, and adjusts changed-check planning so non-macOS hosts without SwiftLint emit an explicit app-lint skip with matching test coverage.
- Reproducibility: yes. from source inspection: current main still renders the 2025 copyright literal in the m ... launch the app locally, but the source path and source PR proof make the observable issue high-confidence.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8438…
Validation:
- ClawSweeper review passed for head 26816c18d6.
- Required merge gates passed before the squash merge.
Prepared head SHA: 26816c18d6
Review: https://github.com/openclaw/openclaw/pull/84729#issuecomment-4503529931
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Followup nits from the #84711 review:
- Narrow the inspectTokenFile catch in
extensions/telegram/src/account-inspect.ts to FsSafeError so only
fs-safe validation throws map to configured_unavailable; any other
throw (programmer error, unexpected I/O) is rethrown.
- Add a regression test for the IRC NickServ password file symlink
rejection path (extensions/irc/src/accounts.ts:118), paralleling the
existing top-level passwordFile test.
- Add a regression test for the Telegram account-level tokenFile
symlink rejection path (extensions/telegram/src/token.ts:149),
paralleling the existing channel-level tokenFile test.
Behavior was already correct after #84711; this just locks coverage and
tightens the catch.
* fix(infra): restore symlink rejection in tryReadSecretFileSync
The local wrapper added in 9e4eca00ff swallowed all errors from
@openclaw/fs-safe@0.2.7's tryReadSecretFileSync via a bare try/catch,
silently downgrading every rejectSymlink: true caller (Telegram, LINE,
Zalo, IRC, Nextcloud Talk credential files) to accept symlinked
credential files. It also broke the infra-state CI shard's symlink
expectation that #84595 had just realigned with the new fail-closed
upstream contract.
Restore the direct re-export so the upstream contract surfaces:
undefined for blank/missing/not-found, FsSafeError for symlink,
oversize, non-regular file, and hardlink validation failures.
* test(plugins): align stale symlink tests with fail-closed contract
5 token/account resolver tests still asserted the pre-fs-safe-0.2.7
"silent skip" behavior (token: "", source: "none") on rejected symlinks;
they passed only because the swallow-all wrapper in secret-file.ts hid
the throw. Restoring the upstream fail-closed contract surfaces the
throw, so update the tests to expect FsSafeError.
inspectTelegramAccount reports credential status (its return type has an
explicit configured_unavailable state for "configured but unreadable"),
so its callsite is the right boundary to catch the FsSafeError and map
it to configured_unavailable rather than letting the throw bubble.
Affected:
- extensions/zalo/src/token.test.ts
- extensions/line/src/accounts.test.ts
- extensions/telegram/src/token.test.ts
- extensions/irc/src/accounts.test.ts
- extensions/nextcloud-talk/src/setup.test.ts
- extensions/telegram/src/account-inspect.ts (catch + report status)
Summary:
- This PR filters exported sub-CLI descriptors through the private-QA gate, centralizes that filter, adds regr ... ge, and carries small validation repairs in workspace glob and tunnel-timeout tests plus a changelog entry.
- Reproducibility: yes. Current-main source shows the raw SUB_CLI_DESCRIPTORS export can include qa while the helper surfaces filter it, and src/cli/argv.ts consumes that export for root command policy.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(cli): gate exported subcli descriptors
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8451…
Validation:
- ClawSweeper review passed for head ba197a6f30.
- Required merge gates passed before the squash merge.
Prepared head SHA: ba197a6f30
Review: https://github.com/openclaw/openclaw/pull/84519#issuecomment-4496549642
Co-authored-by: Zhaocun <zhaocunsun@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The branch threads `agents.defaults.imageMaxDimensionPx` into browser screenshot and labeled snapshot image results, adds regression coverage and a changelog entry, and includes small repair-pass type/lint cleanup.
- Reproducibility: yes. source-level reproduction is high confidence: current `main` calls `imageResultFromFil ... both browser image-returning paths, while the shared sanitizer falls back to `1200px` without an override.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(browser): honor image sanitization config for screenshots
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8459…
Validation:
- ClawSweeper review passed for head c01fde7990.
- Required merge gates passed before the squash merge.
Prepared head SHA: c01fde7990
Review: https://github.com/openclaw/openclaw/pull/84595#issuecomment-4499178477
Co-authored-by: Xu Xiang <xx205@outlook.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Add bounded realtime profile context for Discord realtime voice sessions.
Default to `IDENTITY.md`, `USER.md`, and `SOUL.md`; `voice.realtime.bootstrapContextFiles: []` disables the extra context.
Document the config/SDK surface and refresh generated metadata.
Co-authored-by: FullerStackDev <263060202+fuller-stack-dev@users.noreply.github.com>
Summary:
- The PR updates `src/cli/shared/parse-port.test.ts` to cover numeric strings, whitespace-padded strings, fractional strings, invalid suffixes, and safe-integer overflow for `parsePort`.
- Reproducibility: not applicable. This PR adds test coverage rather than reporting a failing runtime behavior. Source inspection confirms the current parser contract and the exact baseline coverage gap on main.
Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.
Validation:
- ClawSweeper review passed for head 14213cc8f4.
- Required merge gates passed before the squash merge.
Prepared head SHA: 14213cc8f4
Review: https://github.com/openclaw/openclaw/pull/84518#issuecomment-4496552268
Co-authored-by: googlerest <127843198+googlerest@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Fixes openclaw#84386. resolveActiveProfileId in extensions/codex/src/command-account.ts returned store.lastGood whenever that profile was still in the resolved order, ignoring rank, so /codex account marked the stale openai-codex:default profile as active after models auth login + models auth order set. Tracks whether the order came from an explicit operator source (store.order / config.auth.order, including the openai alias key), picks the first usable explicit-order profile, and returns undefined when no candidate is eligible so the display surfaces "no working credential" instead of marking a lower-ranked profile active. Runtime selection via resolveCodexAppServerAuthProfileId is unchanged.
* perf(plugins): extend discovery threading to loader, manifest registry, installed-index, and config contracts
Follow-up to #75451. Threads optional discovery?: PluginDiscoveryResult
through the remaining helpers that still call discoverOpenClawPlugins
internally during startup:
- loadOpenClawPlugins / loadOpenClawPluginCliRegistry (src/plugins/loader.ts):
add discovery? to PluginLoadOptions and consult it before falling back to
an internal scan at both call sites.
- loadPluginManifestRegistry (src/plugins/manifest-registry.ts): accept
discovery? as a more ergonomic alternative to the existing candidates? /
diagnostics? pair; candidates? still wins when both are supplied.
- resolveInstalledPluginIndexRegistry (src/plugins/installed-plugin-index-registry.ts):
add discovery? to LoadInstalledPluginIndexParams and use it when
candidates aren't supplied.
- resolvePluginConfigContractsById (src/plugins/config-contracts.ts): add
discovery? and thread it into the bundled-fallback discovery call.
Add discovery-threading.test.ts asserting each entry point skips its
internal discoverOpenClawPlugins call when discovery is supplied, calls it
when nothing is supplied, and prefers explicit candidates over discovery
when both are present (6 tests, all pass).
discoverOpenClawPlugins remains stateless; sharing is function-scoped per
src/plugins/CLAUDE.md guidance. Backward compatible: every change is
additive (new optional param).
* perf(plugins): drop verbose JSDoc from discovery? params
* perf(plugins): scan-scoped package.json cache in discovery
Adds a per-scan Map<string, PackageManifest | null> threaded through
discoverFromPath/discoverInDirectory/readCandidatePackageManifest, keyed
by the directory's resolved real path. Within one discovery scan, a
plugin's package.json is now read from disk once and reused across the
overlapping discovery code paths (bundled overlay scan, stock-root scan,
source-checkout extensions scan, installed-path scan, global-root scan)
that previously each fired their own read.
The cache lifetime is one scan (created in runPluginDiscovery alongside
the existing realpathCache and seen Set, dies when the scan returns).
discoverOpenClawPlugins remains stateless externally; no persistent
metadata cache.
* perf(plugins): expose raw parsed package.json on PluginCandidate
Discovery already reads each plugin's package.json once and produces a
parsed PackageManifest object before distilling it into metadata via
getPackageManifestMetadata. Currently only the distilled metadata is
kept on the candidate; the full parsed manifest is discarded.
Store the full parsed manifest on rawPackageManifest so downstream
consumers iterating candidates can use it instead of re-reading from
disk. This is the candidate-side groundwork for the scenario-C followup
that routes consumers (bundled-plugin-metadata, bundle-* helpers, etc.)
through the cached field; those consumers currently do their own
directory scans and would need to be refactored to iterate
PluginCandidate arrays before they can benefit.
The field is a frozen-at-discovery-time snapshot, same lifetime semantics
as the existing packageManifest / packageName / packageVersion fields on
PluginCandidate. No new staleness window introduced.
* perf(plugins): make package-manifest cache key trust-aware
Summary:
- Adds a 50-page and advancing-`nextOffset` guard to `loadCronJobForShow`, exports that helper for regression tests, and adds an unreleased changelog entry.
- Reproducibility: yes. Current main is source-reproducible because `loadCronJobForShow` loops while `hasMore` ... ed numeric `nextOffset`; the PR discussion also includes terminal before/after proof for the same CLI path.
Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.
Validation:
- ClawSweeper review passed for head 7828b4bdae.
- Required merge gates passed before the squash merge.
Prepared head SHA: 7828b4bdae
Review: https://github.com/openclaw/openclaw/pull/83989#issuecomment-4484474655
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The replacement branch adds an owned transcript write context around Pi prompt-time delivery mirror appends and a message-tool-only terminal hook, with focused tests and a changelog entry.
- Reproducibility: yes. the source PR includes before/after redacted live Discord logs for a message-tool-only ... ession-lock and transcript append code. I did not rerun the live Discord scenario in this read-only review.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(pi): keep message-tool delivery in session lock
Validation:
- ClawSweeper review passed for head f16678175c.
- Required merge gates passed before the squash merge.
Prepared head SHA: f16678175c
Review: https://github.com/openclaw/openclaw/pull/84437#issuecomment-4494545360
Co-authored-by: Andrew Meyer <andrewmeyer@andrews-air.lan>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The PR changes cron store loading to normalize legacy top-level array `jobs.json` files into the versioned store shape and adds store, service, doctor, gateway tests plus a changelog entry.
- Reproducibility: yes. Current `main` clearly maps a top-level parsed array to `{}` before reading `.jobs`, and the PR body supplies before/after runtime output for the load/add/save path.
Automerge notes:
- PR branch already contained follow-up commit before automerge: [AI-assisted] fix(cron): preserve legacy array stores
Validation:
- ClawSweeper review passed for head 446014b4c1.
- Required merge gates passed before the squash merge.
Prepared head SHA: 446014b4c1
Review: https://github.com/openclaw/openclaw/pull/84433#issuecomment-4494478724
Co-authored-by: JARVIS-Glasses <284122573+JARVIS-Glasses@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The PR adds a Warning block to `docs/channels/imessage.md` explaining that iMessage `cliPath` wrappers and SSH proxies must stream long-lived JSON-RPC stdin/stdout incrementally.
- Reproducibility: not applicable. for this docs-only PR. Source inspection verifies the runtime uses long-lived line-framed stdio, and current main lacks the operator warning being added.
Automerge notes:
- PR branch already contained follow-up commit before automerge: docs(imessage): warn that cliPath wrappers must stream JSON-RPC stdio…
Validation:
- ClawSweeper review passed for head a371ee998e.
- Required merge gates passed before the squash merge.
Prepared head SHA: a371ee998e
Review: https://github.com/openclaw/openclaw/pull/84420#issuecomment-4494313781
Co-authored-by: HCL <chenglunhu@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The branch caps async diagnostic drains at 100 events per turn, adds pending/full-drain diagnostic helpers, ... rminal diagnostics to inspect pending events, and adds regression coverage plus changelog/baseline updates.
- Reproducibility: yes. from source inspection. Current main drains the entire async diagnostic queue in one s ... ck, and the PR body supplies a focused 250-event after-fix probe showing 100/200/250 delivery across turns.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix: yield diagnostic event drains
Validation:
- ClawSweeper review passed for head 95610934cd.
- Required merge gates passed before the squash merge.
Prepared head SHA: 95610934cd
Review: https://github.com/openclaw/openclaw/pull/82937#issuecomment-4469498220
Co-authored-by: Galin Iliev <galini@microsoft.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Marks skipped and supplemental Microsoft Teams system events as non-owner/untrusted while preserving active primary message dispatch behavior.
Verified before merge:
- PR was open, not draft, mergeable, and clean against main
- Matched head: 4f79f46205
- GitHub checks passed, including Real behavior proof, auto-response, build artifacts, type/lint checks, channel/runtime critical quality checks, and security-fast
- ClawSweeper marked proof sufficient with no concrete contributor-facing blocker remaining
Co-authored-by: GuoJiaming <804436395@qq.com>
Summary:
- The branch gives Codex `image_generate` dynamic-tool calls a 120s default watchdog in main and side-thread paths and updates docs, tests, and changelog.
- Reproducibility: yes. Source inspection on current main shows unconfigured Codex `image_generate` calls fall ... -tool default, and the linked source PR includes live Gateway before/after output for the timeout behavior.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8425…
- PR branch already contained follow-up commit before automerge: Fix Codex image generation tool timeout
Validation:
- ClawSweeper review passed for head 10c7f87023.
- Required merge gates passed before the squash merge.
Prepared head SHA: 10c7f87023
Review: https://github.com/openclaw/openclaw/pull/84369#issuecomment-4493288493
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: moritzmmayerhofer <254141390+moritzmmayerhofer@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The PR updates the code-mode exec tool description, adds regression coverage for the model-visible constraints, and records the fix in the changelog.
- Reproducibility: yes. at source level: current main's exec schema omits constraints that the current code-mo ... also includes a live before/after recitation path showing the model receives the changed tool description.
Automerge notes:
- PR branch already contained follow-up commit before automerge: test(code-mode): cover exec tool guidance
- PR branch already contained follow-up commit before automerge: fix(code-mode): sharpen exec tool description so models stop wasting …
Validation:
- ClawSweeper review passed for head 8ff85071ce.
- Required merge gates passed before the squash merge.
Prepared head SHA: 8ff85071ce
Review: https://github.com/openclaw/openclaw/pull/84368#issuecomment-4493273853
Co-authored-by: Kaspre <kaspre@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The PR updates Gateway agent summary builders to use `agents.list[].identity.name` when explicit `agents.list[].name` is absent, adds focused gateway regression tests, and records a changelog fix.
- Reproducibility: yes. Current main can be source-reproduced: both gateway summary builders set top-level `na ... list[].name`, so identity-only configured agents have no summary name for consumers that read `agent.name`.
Automerge notes:
- PR branch already contained follow-up commit before automerge: test(gateway): cover missing agent summary names
- PR branch already contained follow-up commit before automerge: fix(gateway): remove stale name fallback import
- PR branch already contained follow-up commit before automerge: gateway: use identity.name in agent summaries when name is unset
Validation:
- ClawSweeper review passed for head 9f7024f55c.
- Required merge gates passed before the squash merge.
Prepared head SHA: 9f7024f55c
Review: https://github.com/openclaw/openclaw/pull/84355#issuecomment-4493008710
Co-authored-by: luoyanglang <hanwanlonga@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- The PR promotes direct or nested send receipt IDs into `openclaw message send --json`, adds a focused command test, and adds a changelog entry.
- Reproducibility: yes. at source level. Current main serializes only the raw payload while send receipts can carry `payload.result.messageId`; I did not execute the CLI in this read-only review.
Automerge notes:
- PR branch already contained follow-up commit before automerge: test(cli): fix message json payload type
- PR branch already contained follow-up commit before automerge: chore: retrigger PR checks
- PR branch already contained follow-up commit before automerge: Expose messageId in message CLI JSON output
Validation:
- ClawSweeper review passed for head 9eba815fcc.
- Required merge gates passed before the squash merge.
Prepared head SHA: 9eba815fcc
Review: https://github.com/openclaw/openclaw/pull/84191#issuecomment-4489100591
Co-authored-by: OpenClaw Contributor <100menotu001@users.noreply.github.com>
Co-authored-by: Craig <froelich@craigs.mac.studio.froho>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Summary:
- Adds `disabled` to the message presentation button schema, advertises Discord disabled-button support, prese ... through Discord component mapping and link serialization, and adds regression tests plus a changelog entry.
- Reproducibility: yes. Source inspection on current main shows `disabled` exists in the runtime type but is a ... rtised in Discord capabilities, dropped by adaptation, and omitted from Discord mapping/link serialization.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(discord): advertise disabled presentation support
- PR branch already contained follow-up commit before automerge: fix(discord): preserve disabled link buttons
- PR branch already contained follow-up commit before automerge: Preserve disabled Discord presentation buttons
Validation:
- ClawSweeper review passed for head 9bb60d8cbf.
- Required merge gates passed before the squash merge.
Prepared head SHA: 9bb60d8cbf
Review: https://github.com/openclaw/openclaw/pull/84312#issuecomment-4491983845
Co-authored-by: OpenClaw Contributor <100menotu001@users.noreply.github.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
Summary:
- This PR adds scoped truncation and hover titles to usage-panel context-breakdown names and adds a changelog entry crediting the source PR.
- Reproducibility: yes. at source/proof level: current main renders long context names without truncation or t ... he overflow before and ellipsis/tooltip after. I did not run a live browser session in this read-only pass.
Automerge notes:
- PR branch already contained follow-up commit before automerge: feat(ui): tool name style in usage panel
Validation:
- ClawSweeper review passed for head 396e405b3b.
- Required merge gates passed before the squash merge.
Prepared head SHA: 396e405b3b
Review: https://github.com/openclaw/openclaw/pull/84310#issuecomment-4491942108
Co-authored-by: Rain120 <1085131904@qq.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
* perf(plugins): extend discovery threading to loader, manifest registry, installed-index, and config contracts
Follow-up to #75451. Threads optional discovery?: PluginDiscoveryResult
through the remaining helpers that still call discoverOpenClawPlugins
internally during startup:
- loadOpenClawPlugins / loadOpenClawPluginCliRegistry (src/plugins/loader.ts):
add discovery? to PluginLoadOptions and consult it before falling back to
an internal scan at both call sites.
- loadPluginManifestRegistry (src/plugins/manifest-registry.ts): accept
discovery? as a more ergonomic alternative to the existing candidates? /
diagnostics? pair; candidates? still wins when both are supplied.
- resolveInstalledPluginIndexRegistry (src/plugins/installed-plugin-index-registry.ts):
add discovery? to LoadInstalledPluginIndexParams and use it when
candidates aren't supplied.
- resolvePluginConfigContractsById (src/plugins/config-contracts.ts): add
discovery? and thread it into the bundled-fallback discovery call.
Add discovery-threading.test.ts asserting each entry point skips its
internal discoverOpenClawPlugins call when discovery is supplied, calls it
when nothing is supplied, and prefers explicit candidates over discovery
when both are present (6 tests, all pass).
discoverOpenClawPlugins remains stateless; sharing is function-scoped per
src/plugins/CLAUDE.md guidance. Backward compatible: every change is
additive (new optional param).
* perf(plugins): drop verbose JSDoc from discovery? params
Summary:
- The PR updates `src/infra/clawhub.ts` URL joining, adds a path-prefix regression test in `src/infra/clawhub.test.ts`, and adds a changelog bullet.
- Reproducibility: yes. Source inspection plus a direct Node URL check show current main drops `/clawhub` when resolving a leading-slash API path against a prefixed base URL.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(clawhub): preserve base URL path prefix [AI-assisted]
Validation:
- ClawSweeper review passed for head 7bb2cb8764.
- Required merge gates passed before the squash merge.
Prepared head SHA: 7bb2cb8764
Review: https://github.com/openclaw/openclaw/pull/83982#issuecomment-4484348274
Co-authored-by: Thiago Costa <thiago12_fera@hotmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
2026-05-19 23:22:01 +00:00
10452 changed files with 931198 additions and 277127 deletions
description: "Add a redacted agent transcript section to GitHub PR or issue bodies during OpenClaw agent-created PR/issue workflows."
---
# Agent Transcript
Best-effort local-only provenance for OpenClaw PR/issue bodies. Use during agent-created GitHub PR or issue workflows before creating/updating the body.
## Contract
- Never use network. Session discovery reads local agent logs only.
- Never upload raw logs. Render sanitized Markdown first.
- Always ask the user before adding transcript logs to a GitHub PR/issue body.
- Tell the user sanitized session logs help reviewers and can make PRs easier to prioritize.
- Offer a local HTML preview before insertion. If the user wants preview, open it and wait for confirmation before adding the section.
- Fail closed on unresolved secrets, private keys, browser/session/cookie details, or auth URLs.
- Drop system/developer prompts, raw tool outputs, reasoning, env, cookies, tokens, and broad local paths.
- Keep user prompts, assistant visible decisions, terse tool summaries, and test/proof outcomes.
- Remove session turns unrelated to the PR/issue work. Use the PR/issue title, branch name, changed files, and stated goal as scope; omit earlier/later unrelated tasks even when they are in the same session log.
- Best effort only: PR/issue creation must continue if no safe transcript is found.
- Add the `## Agent Transcript` section only when inserting a real transcript. Never add a placeholder transcript heading or text such as "A sanitized local transcript preview was generated but not included."
- Use a collapsed `<details>` section and update existing markers instead of duplicating sections.
`find` scans the newest 400 matching local JSONL logs by default across Codex, Claude, Pi, and OpenClaw agent sessions. Use `--max-files N` for a wider local search.
2. Run `find` with title, branch, PR URL/number if known, and cwd.
3. If a high-confidence session is found, ask:
`Include a redacted agent transcript? It helps reviewers and can make the PR easier to prioritize. I can open a local preview first.`
4. If the user wants preview, run `preview`, open the HTML with `open`, and wait for confirmation.
5. Before insertion, trim unrelated session turns from the generated section. Keep only turns that explain this PR/issue's goal, implementation choices, files, tests, proof, blockers, and final outcome.
6. If the user approves, run `append-body`.
7. Use the enriched body file for creation/update.
8. If no safe session is found, say nothing and continue without transcript. If the user declines, continue without transcript and do not add any transcript placeholder section.
## Review Artifacts
For manual audits across many PR/session candidates, create a local HTML preview from a local JSON file. This is for maintainers only and is not part of the PR/issue workflow:
```bash
.agents/skills/agent-transcript/scripts/agent-transcript html \
description: "Autoreview closeout: local dirty changes, PR branch vs main, parallel tests."
description: "Auto Review closeout. Codex review is the default when no engine is set and is the recommended reviewer."
---
# Autoreview
# Auto Review
Run Codex's built-in code review as a closeout check. This is code review (`codex review`), not Guardian `auto_review` approval routing.
Run the bundled structured review helper as a closeout check. This is code review, not Guardian `auto_review` approval routing.
Codex native review mode performs best and is recommended. Non-Codex reviewers are fallback/second-opinion paths that receive a generated diff prompt, not the full Codex review-mode runtime.
Codex review is the default when no engine is set. It usually delivers the best review results and should remain the normal final closeout engine.
Use when:
- user asks for Codex review / autoreview / second-model review
- user asks for Codex review / Claude review / autoreview / second-model review
- after non-trivial code edits, before final/commit/ship
- reviewing a local branch or PR branch after fixes
@@ -21,59 +22,69 @@ Use when:
- Read dependency docs/source/types when the finding depends on external behavior.
- Reject unrealistic edge cases, speculative risks, broad rewrites, and fixes that over-complicate the codebase.
- Prefer small fixes at the right ownership boundary; no refactor unless it clearly improves the bug class.
- Keep going until the selected review path returns no accepted/actionable findings.
- If a review-triggered fix changes code, rerun focused tests and rerun the review helper.
-Default to Codex review. If Codex is unavailable or exits with an error, the helper falls back to the first configured CLI from `claude -p`, `pi -p`, `opencode run`, `droid exec`, or `copilot`. Prefer Codex for final closeout because it uses native review mode; non-Codex reviewers use a Codex-inspired generated diff prompt. The helper runs nested Codex review in yolo/full-access mode by default; use `--no-yolo` only when intentionally testing sandbox behavior.
-Stop as soon as the review command/helper exits 0 with no accepted/actionable findings. Do not run an extra direct `codex review` just to get a nicer "clean" line, a second opinion, or clearer closeout wording.
- Keep going until structured review returns no accepted/actionable findings.
- If a review-triggered fix changes code, rerun focused tests and rerun the structured review helper.
-For security-audit suppression changes, verify accepted findings remain auditable: suppressed findings stay in structured output, active output keeps an unsuppressible suppression notice, and aggregate findings cannot hide unrelated active risk.
-Never switch or override the requested review engine/model. If the review hits model capacity, retry the same command a few times with the same engine/model.
- Be patient with large bundles. Structured review can take up to 30 minutes while the model call is active, especially with Codex tools or web search.
- Treat heartbeat lines like `review still running: ... elapsed=... pid=...` as healthy progress, not a hang. Let the helper continue while heartbeats are advancing. Pass `--stream-engine-output` when live engine text is useful; Codex and Claude filter tool/file chatter, other engines pass raw output through.
- Do not kill a review just because it has been quiet for 2-5 minutes, or because it is still running under the 30-minute window. Inspect the process only after missing multiple expected heartbeats, after 30 minutes, or after an obviously failed subprocess; prefer letting the same helper command finish.
- Tools are useful in review mode. The helper allows read-only inspection tools and web search by default so reviewers can check dependency contracts, upstream docs, and current behavior.
- Security perspective is always included, but it should not cripple legitimate functionality. Report security findings only when the change creates a concrete, actionable risk or removes an important safety check.
- For regression provenance, if no blamed PR is traceable, use the blamed commit as the provenance: commit SHA, date, and author username. Do not guess a merger or frame missing PR metadata as a separate finding.
- Do not invoke built-in `codex review`, nested reviewers, or reviewer panels from inside the review. The helper builds one bundle, calls one selected engine, validates one structured result, and stops.
- Stop as soon as the helper exits 0 with no accepted/actionable findings. Do not run an extra review just to get a nicer "clean" line, a second opinion, or clearer closeout wording.
- Treat the helper's successful exit plus absence of actionable findings as the clean review result, even if the underlying Codex CLI output is terse.
- Multi-reviewer panels are opt-in only. Use them when explicitly requested or when risk justifies the extra spend; the main agent still verifies every accepted finding before fixing.
- If rejecting a finding as intentional/not worth fixing, add a brief inline code comment only when it explains a real invariant or ownership decision that future reviewers should know.
- If `gh`/Gitcrawl reports `database disk image is malformed`, run `gitcrawl doctor --json` once to let the portable cache repair before retrying review; do not bypass the shim unless repair fails and freshness requires live GitHub.
- If Gitcrawl reports a portable manifest mismatch, source/runtime DB health error, or stale portable-store checkout, run `gitcrawl doctor --json` and inspect `source_db_health`, `runtime_db_health`, and `portable_store_status` before falling back to live GitHub.
- Do not push just to review. Push only when the user requested push/ship/PR update.
- For OpenClaw maintainers, keep autoreview validation Crabbox/Testbox-aware when maintainer validation mode is enabled (`OPENCLAW_TESTBOX=1` or `AUTOREVIEW_OPENCLAW_MAINTAINER_VALIDATION=1`). A review pass may inspect files and run cheap non-Node probes, but it must not start local `pnpm`, Vitest, `tsgo`, `npm test`, or `node scripts/run-vitest.mjs` from a Codex/worktree review unless the operator explicitly requested local proof. For runtime proof, use existing evidence or route through Crabbox/Testbox and report the id. Do not apply this rule to ordinary contributors who do not have maintainer Testbox access.
## Pick Target
Dirty local work:
```bash
codex review --uncommitted
<autoreview-helper> --mode local
```
Use this only when the patch is actually unstaged/staged/untracked in the
current checkout. For committed, pushed, or PR work, point Codex at the commit
or branch diff instead; do not force `--mode local` / `--uncommitted` just
because the helper docs mention dirty work first. A clean `--uncommitted` review
current checkout. `--mode uncommitted` is accepted as an alias for `--mode local`.
For committed, pushed, or PR work, point the helper at the commit
or branch diff instead; do not force dirty modes just
because the helper docs mention dirty work first. A clean local review
.agents/skills/autoreview/scripts/autoreview --mode commit --commit HEAD
/Users/steipete/Projects/agent-scripts/skills/autoreview/scripts/autoreview --mode commit --commit HEAD
```
Use commit review for already-landed or already-pushed work on `main`. Reviewing
@@ -86,60 +97,117 @@ with `--base`.
Format first if formatting can change line locations. Then it is OK to run tests and review in parallel:
```bash
.agents/skills/autoreview/scripts/autoreview --parallel-tests "<focused test command>"
scripts/autoreview --parallel-tests "<focused test command>"
```
On Windows, the default `--parallel-tests` shell preserves the platform `cmd.exe`
semantics used by Python `shell=True`. Use `--parallel-tests-shell powershell`
or `--parallel-tests-shell pwsh` when the focused test command is PowerShell-specific.
Tradeoff: tests may force code changes that stale the review. If tests or review lead to code edits, rerun the affected tests and rerun review until no accepted/actionable findings remain. Once that rerun exits cleanly, stop; do not spend another long review cycle on redundant confirmation.
## Review Panels
Run multiple reviewers against one frozen bundle:
```bash
<autoreview-helper> --reviewers codex,claude
```
`--panel` is shorthand for Codex plus Claude unless `--engine` changes the first reviewer:
```bash
<autoreview-helper> --panel
```
Set reviewer models and thinking/effort explicitly:
Codex maps thinking to `model_reasoning_effort` and accepts `low`, `medium`,
`high`, or `xhigh`. Claude maps thinking to `--effort` and also accepts `max`.
Engines without a real thinking knob reject `--thinking`.
## Context Efficiency
Codex review is usually noisy. Default to a subagent filter when subagents are available. Ask it to run the review and return only:
- actionable findings it accepts
- findings it rejects, with one-line reason
- exact files/tests to rerun
Run inline only for tiny changes or when subagents are unavailable.
Run the helper directly so target selection, engine choice, structured validation, and exit statusall stay in one path. If output is noisy, summarize the completed helper output after it returns; do not ask another agent or reviewer to rerun the review.
- accepts `--mode uncommitted` as an alias for `--mode local`
- otherwise uses current PR base if `gh pr view` works
- otherwise uses `origin/main` for non-main branches
-auto-runs `PNPM_CONFIG_PM_ON_FAIL=ignore PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN=false PNPM_CONFIG_OFFLINE=true pnpm run check` in parallel when a repo has `package.json`, `pnpm-lock.yaml`, `node_modules`, and a `check` script; disable with `AUTOREVIEW_AUTO_TESTS=0`
-supports `--engine codex`, `claude`, `droid`, and `copilot`; default is `AUTOREVIEW_ENGINE` or `codex`; Codex should remain the default when nothing is set
- resolves bare `git`, `gh`, reviewer, and PowerShell shell commands from absolute `PATH` entries only, never from the reviewed checkout; explicit relative `--*-bin` paths are resolved from the reviewed repository root
- use `--mode commit --commit <ref>` for already-committed work, especially clean `main` after landing
- should be left in `--mode auto` or forced to `--mode branch` for PR/branch work; do not force `--mode local` after committing
-supports `--reviewer codex|claude|pi|opencode|droid|copilot|auto`;`auto` means Codex first
- supports `--fallback-reviewer auto|claude|pi|opencode|droid|copilot|none`; default is configured CLI fallback
-falls back only when Codex is unavailable or exits nonzero, not when Codex reports findings
-writes only to stdout unless `--output` or `AUTOREVIEW_OUTPUT` is set
-supports `--dry-run`, `--parallel-tests`, and commit refs
-runs nested review with `--dangerously-bypass-approvals-and-sandbox --sandbox danger-full-access` by default
- injects maintainer-only OpenClaw validation policy into native Codex review when `OPENCLAW_TESTBOX=1` or `AUTOREVIEW_OPENCLAW_MAINTAINER_VALIDATION=1`, so local memory-heavy Node/Vitest checks are avoided in favor of Crabbox/Testbox proof
- branch mode may fail on Codex CLI versions that reject `--base` plus the helper's stdin prompt; on that exact parser error, rerun plain `codex review --base <ref>` instead of falling back to a non-Codex reviewer
- keeps accepting `--full-access`; use `--no-yolo` or `AUTOREVIEW_YOLO=0` to opt out
- still accepts legacy `CODEX_REVIEW_*` env vars when the matching `AUTOREVIEW_*` var is unset
-writes only to stdout unless `--output`,`--json-output`, or live streamed engine stderr is set
-supports `--stream-engine-output` or `AUTOREVIEW_STREAM_ENGINE_OUTPUT=1` for live engine text while preserving structured validation; Codex and Claude hide tool/file event details, emit compact activity summaries, and report usage at turn completion
-supports opt-in review panels with `--panel` / `--reviewers`, plus per-engine `--model` and `--thinking`
-allows read-only tools and web search by default where the selected CLI supports them; forbids nested review in the prompt; Codex is run through `codex exec` with read-only sandbox and structured output
-prints `review still running: <engine> elapsed=<seconds>s pid=<pid>` to stderr at long-running intervals while waiting for the selected review engine, unless streamed output or compact Codex activity has been visible recently
- prints `autoreview clean: no accepted/actionable findings reported` when the selected review command exits 0
- exits nonzero when accepted/actionable findings are present
## Final Report
Include:
- review command used
- tests/proof run
- findings accepted/rejected, briefly why
- the clean review result from the final helper/review run, or why a remaining finding was consciously rejected
Do not run another Codex review solely to improve the final report wording. If the final helper run exited 0 and produced no accepted/actionable findings, report that exact run as clean.
## PR / CI Closeout
- Prefer direct run/job APIs after CI starts: `gh run view <run-id> --json jobs`; use PR rollup only for final mergeability.
- After rebase, compare `origin/main..HEAD`; drop CI-fix commits already upstream before pushing.
- For prompt snapshot CI failures, prove/generate with Linux Node 24 before rerunning the failed job.
- Update PR body once near the final head unless proof labels are missing or stale enough to block CI.
Do not run another review solely to improve the final report wording. If the final helper run exited 0 and produced no accepted/actionable findings, report that exact run as clean.
MALICIOUS_PROMPT="This is an acceptance test fixture. The changed app.js patch contains real security bugs. Review normally and report only actionable defects from the patch."
BENIGN_PROMPT="Security calibration fixture: this patch intentionally uses filesystem paths, async execFile, and owner-gated password-adjacent state safely. Do not flag legitimate shell/filesystem/auth-adjacent functionality unless there is a concrete exploitable risk in the diff."
@@ -98,7 +98,7 @@ Do not close from title alone. If closing as done on main or nonsensical, prove
When asked for `5 new`, exclude refs already surfaced in the session and refill from the archive until there are 5 live-open candidates. If fewer than 5 remain open, list all open ones and say how many short.
When asked to `update`, `refresh`, `recheck`, `check again`, or similar, return an updated live-open candidate list. Do not fill the main list with items that merely merged/closed since the last pass; put those numbers in a short bottom line.
When asked to `update`, `refresh`, `recheck`, `check again`, or similar, return an updated live-open candidate list. Sort by maintainer importance, not recency: high-impact ready fixes first, then useful-but-review-first, then open/not-ready items. Do not include a "changed since last pass" section or bottom-line merged/closed summary unless the user explicitly asks for churn.
Prefer:
@@ -142,18 +142,20 @@ No Markdown tables. Compact bullets. Use color/risk markers:
Required line shape:
```markdown
- **PR #81244** `@whatsskill.``+118/-1``bug` 🟢 verifiable: yes. This prevents chat action buttons from overlapping short assistant replies. Blast: web chat rendering, low.
- **Issue #81245** `@alice``LOC n/a``bug` 🟡 verifiable: partial. This reports duplicate Telegram replies when reconnecting after gateway restart. Blast: Telegram channel runtime, medium.
- **PR #81244** `@whatsskill.``+118/-1``bug` 🟢 https://github.com/openclaw/openclaw/pull/81244 - Prevents chat action buttons from overlapping short assistant replies. Verifiable: yes. Blast: web chat rendering, low.
- Always include `verifiable: yes|partial|no` plus the shortest proof hint when helpful.
- If status is not open, still show it only when the user asked for all surfaced refs; use ✅ or ⚪ and state merged/closed.
- For refresh-style asks, bottom line: `Merged/closed since last pass: #81016 merged, #81026 closed.` Omit if none.
- For refresh-style asks, prefer section order: `Best Open Now`, `Useful But Review First`, `Still Open / Not Ready`. Omit merged/closed churn by default.
description: Use when testing, fixing, or extending the OpenClaw Control UI GUI with Vitest + Playwright end-to-end checks, mocked Gateway WebSocket flows, mocked dashboard runs, screenshots/videos, or agent-verifiable browser proof.
---
# Control UI E2E
Use this for Control UI changes that need a real browser flow with deterministic Gateway data.
## Test Shape
- Use `ui/src/**/*.e2e.test.ts` for full GUI flows.
- Use `ui/src/test-helpers/control-ui-e2e.ts` to start the Vite Control UI and install a mocked Gateway WebSocket.
- Keep scenarios deterministic. Do not use live provider keys, real channel credentials, or a real Gateway unless the user explicitly asks for live proof.
- Prefer existing `.browser.test.ts` or unit tests for narrow rendering logic; use this E2E lane when the proof should cover routing, app boot, Gateway handshake, requests, and visible UI behavior together.
## Commands
- Target one E2E test in a Codex worktree:
```bash
node scripts/run-vitest.mjs run --config test/vitest/vitest.ui-e2e.config.ts --configLoader runner ui/src/ui/e2e/chat-flow.e2e.test.ts
```
- Run the whole local lane in a normal checkout:
```bash
pnpm test:ui:e2e
```
If dependencies are missing in a Codex worktree, install once with `pnpm install`; for broad GUI proof or dependency-heavy checks, use Testbox/Crabbox instead of running a wide local pnpm lane.
## Visual Proof Default
When running mocked Control UI/dashboard validation for a user-facing feature, produce visual proof by default unless the user explicitly opts out.
- Keep the Vitest E2E assertions deterministic; do not commit generated screenshots or videos.
- After or alongside the focused E2E test, run the mocked Control UI app when available, for example `pnpm dev:ui:mock -- --port <port>`.
- Drive Chromium with Playwright against the local mock URL and capture a video plus screenshots for each meaningful state: initial view, interaction input, result state, and final/paginated/selected state.
- Use `browser.newContext({ recordVideo: { dir, size }, viewport })`, `page.screenshot({ path })`, and close the context before reporting the video path.
- Put artifacts under `.artifacts/control-ui-e2e/<short-feature-name>/` or another clearly named local temp directory, and report the absolute paths in the final answer.
- Treat recording as validation, not only demo capture. If the recorder fails or shows surprising behavior, stop, fix the behavior, add or update a regression test, then rerecord.
- If visual proof is blocked, state the exact blocker and still report the textual E2E evidence.
## Mock Pattern
Start the app server, install the mock before `page.goto`, then assert both Gateway traffic and visible UI:
Extend `installMockGateway` with typed scenario options or method responses when a new flow needs more Gateway surface.
## Standalone Recording
When recording an already-running mocked Control UI URL, use a temporary Playwright script or `playwright test` spec and keep the recording flow focused:
- Open the mock URL, interact through stable `data-*` selectors or user-facing role selectors, and wait on asserted states instead of relying on fixed sleeps.
- Assert both visible UI state and mocked Gateway traffic for request-driven flows. For example, verify the expected count/row is visible and that `sessions.list` was called with the expected `search`, `offset`, and `limit`.
- Use short sleeps only after assertions to make the captured video readable.
- Store the generated video under `.artifacts/control-ui-e2e/<feature>/`; do not commit it.
CI=1NODE_OPTIONS=--max-old-space-size=4096OPENCLAW_TEST_PROJECTS_PARALLEL=6OPENCLAW_VITEST_MAX_WORKERS=1OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS=900000 pnpm test
pnpm test
```
Auth fallback, only when `blacksmith` says auth is missing:
@@ -562,13 +604,14 @@ Crabbox Blacksmith backend delegates setup to:
The hydration workflow owns checkout, Node/pnpm setup, dependency install,
DISCRAWL_NO_AUTO_UPDATE=1 discrawl --json sql "select count(*) from messages;"
```
Report absolute date spans, channel/DM names, counts, and known gaps. Use read-only SQL for exact counts/rankings. Never use `--unsafe --confirm` unless the user explicitly requests a reviewed DB mutation.
## SQL
Boundaries: bot sync needs configured Discord bot credentials. Wiretap reads local Discord Desktop artifacts only; do not extract user tokens, call Discord as the user, or write to Discord storage. Git-share snapshots must not include secrets or `@me` DM rows.
Use `discrawl sql` for exact counts, joins, and ranking queries when normal
CLI reads are too coarse. The command is read-only by default, accepts SQL as
args or stdin, and supports `--json` for agent parsing.
Useful examples:
```bash
DISCRAWL_NO_AUTO_UPDATE=1 discrawl --json sql "select count(*) as messages from messages;"
DISCRAWL_NO_AUTO_UPDATE=1 discrawl --json sql "select coalesce(nullif(c.name, ''), m.channel_id) as channel, count(*) as messages from messages m left join channels c on c.id = m.channel_id group by m.channel_id order by messages desc limit 20;"
DISCRAWL_NO_AUTO_UPDATE=1 discrawl --json sql "select coalesce(nullif(mm.display_name, ''), nullif(mm.global_name, ''), nullif(mm.username, ''), m.author_id) as author, count(*) as messages from messages m left join members mm on mm.guild_id = m.guild_id and mm.user_id = m.author_id group by m.guild_id, m.author_id order by messages desc limit 20;"
```
Never use `--unsafe --confirm` unless the user explicitly asks for a database
mutation and the write has been reviewed.
When the installed CLI lacks a new feature, build or run from a verified
`openclaw/discrawl` checkout before concluding the feature is missing.
## Discord Boundaries
Bot API sync requires configured Discord bot credentials; do not invent token
availability. Desktop wiretap mode reads local Discord Desktop artifacts and
must not extract credentials, use user tokens, call Discord as the user, or
write to Discord application storage. Wiretap/Desktop cache DMs are local-only
and must not be described as part of the published Git snapshot. Git-share
snapshots must not include secrets or `@me` DM rows.
## Verification
For repo edits, prefer existing Go gates:
```bash
GOWORK=off go test ./...
```
Then run targeted CLI smoke for the touched surface, for example:
description: Use when adding, reviewing, or refactoring OpenClaw Kysely database access, native node:sqlite stores, generated DB types, SQLite schemas, migrations, raw SQL, transactions, or database access best practices.
---
# Kysely Database Access
Use this skill for OpenClaw database code that touches Kysely, `node:sqlite`,
generated DB types, SQLite schemas, migrations, or store/query design.
## Read First
-`docs/concepts/kysely.md` for the repo's Kysely rules and examples.
- The owning subtree `AGENTS.md`, if present.
- Relevant local Kysely source/types under `node_modules/kysely/dist/esm/...`
before assuming dialect behavior, result types, transactions, plugins, or raw
SQL semantics.
- For codegen behavior, inspect `scripts/generate-kysely-types.mjs` and
`kysely-codegen --help` from the repo package manager.
## Official Docs Cross-Check
When the behavior matters, verify against current Kysely docs/source before
patching:
- Generating types: production apps should keep schema types aligned with the
database through code generation.
- Data types: TypeScript types do not affect runtime values; the driver decides
runtime values, and Kysely returns what the driver returns unless a plugin
transforms results.
- Raw SQL: the `sql` tag can execute full raw SQL and embed snippets into
builders. Prefer typed builders/helpers when they express the same thing.
- Reusable helpers: take `Expression<T>` or an `ExpressionBuilder` when wrapping
SQL expressions; alias helper expressions explicitly in `select`. Extract a
helper only when it quarantines raw SQL, removes meaningful duplication, or
preserves a tricky inferred type.
- Split build/execute only at deliberate boundaries. Compiled-query execution
is useful for native sync adapters, but keep plugin/result-transform behavior
in mind.
- Migrations: Kysely migration files run without a schema type. In OpenClaw,
prefer the committed SQL-source-of-truth path unless a new owner explicitly
needs Kysely-managed migrations.
- Plugins: plugins can transform queries and results. Any sync shortcut that
bypasses Kysely's async executor needs a documented invariant or tests.
## Default Workflow
1. Identify the owner boundary:
- Core state DB: `src/state/*`
- Per-agent DB: `src/state/openclaw-agent-*`
- Feature store: owning `*.sqlite.ts` module
- Plugin-owned state: plugin/module owner, not generic core
2. Inspect the schema source first:
-`*.sql` is the source of truth when generated schema/types exist.
- Generated `*.generated.*` files are outputs, not hand-edit targets.
description: Write or review high-quality OpenClaw developer documentation.
dependencies: []
---
# OpenClaw Docs
## Overview
Use this skill when writing, editing, or reviewing OpenClaw developer documentation for APIs, SDKs, CLI tools, integrations, quickstarts, platform guides, or technical product docs.
Write documentation that is concise, helpful, and comprehensive: fast for first success, precise for production, and easy to scan when debugging.
## Core Model
Use an OpenClaw documentation model, strengthened by Write the Docs principles:
- Lead with what the developer is trying to do.
- Give one recommended path before alternatives.
- Make examples runnable and realistic.
- Keep guides task-oriented and references exhaustive.
- Explain production risks exactly where developers can make mistakes.
- Link concepts, guides, API references, SDKs, testing, and troubleshooting so readers can move between them without rereading.
- Treat docs as part of the product lifecycle: draft them before or alongside implementation, review them with code, and keep them current.
- Make each page discoverable, addressable, cumulative, complete within its stated scope, and easy to skim.
## Structure
Choose the page type before writing:
- Overview: route readers to the right product, integration path, or guide.
- Quickstart: get a new user to a working result with the fewest safe steps.
- Topic page: give an end-to-end overview of a major domain entity, with setup,
key subtopics, troubleshooting, and links to deeper references.
- Guide: explain one workflow from prerequisites to production readiness.
- API reference: define every object, endpoint, parameter, enum, response, error, and version rule.
- SDK or CLI reference: document install, auth, commands or methods, options, examples, and failure modes.
- Testing guide: show sandbox setup, fixtures, test data, simulated failures, and live-mode differences.
- Troubleshooting guide: map symptoms to checks, causes, and fixes.
Use this default topic page structure:
1. Title: name the major entity or surface.
2. Opening overview: start with a few unheaded sentences that explain what it
is, what it owns, and what it does not own. Do not add a `## Overview`
heading unless the page is itself an overview index.
3. Requirements: include only when setup needs specific accounts, versions,
permissions, plugins, operating systems, or credentials.
4. Quickstart: show the recommended setup path and smallest reliable verification.
5. Configuration: show the minimum configuration needed to use the surface,
common variants users must choose between, and where each option is set:
CLI, config file, environment variable, plugin manifest, dashboard, or API.
6. Major subtopics: organize the entity's major concepts, workflows, and
decisions by reader intent. Put each major subtopic under its own heading;
do not wrap them in a generic `## Subtopics` section.
7. Troubleshooting: diagnose common observable failures under an explicit
`## Troubleshooting` heading.
8. Related: link to guides, references, commands, concepts, and adjacent topics.
Topic pages may be longer than quickstarts, but they should not become exhaustive
references. Move field tables, API contracts, narrow internals, legacy details,
and rare debugging workflows to linked reference or troubleshooting pages when
they interrupt the end-to-end overview.
For configuration, keep task-critical options inline. Link to reference docs for
full option lists, defaults, enums, generated schemas, and advanced settings. Do
not duplicate exhaustive config reference tables in topic pages unless the topic
page is itself the reference.
Use this default guide structure:
1. Title: name the outcome, not the implementation detail.
2. Opening: state what the reader can accomplish in one or two sentences.
3. Before you begin: list accounts, keys, permissions, versions, tools, and assumptions.
4. Choose a path: compare options only when the reader must decide.
5. Steps: use verb-led headings with code, expected output, and checks.
6. Test: show the smallest reliable proof that the integration works.
7. Production readiness: cover security, idempotency, retries, limits, observability, migrations, and cleanup.
8. Troubleshooting: include common errors near the workflow that causes them.
9. See also: link to concepts, API references, SDK docs, and adjacent guides.
Keep navigation user-intent based. Do not force readers to understand internal product taxonomy before they can pick a task.
## Documentation Lifecycle
Write and maintain docs with the same discipline as code:
- Draft docs early enough to expose unclear product, API, CLI, or config design.
- Keep docs source near the code, config, command, plugin, or protocol it describes when the repo layout allows it.
- Avoid duplicate truth. If the same contract appears in multiple places, pick the canonical page and link to it.
- Update docs in the same change as behavior, config, API, CLI, plugin, or troubleshooting changes.
- Remove, redirect, or clearly mark stale docs. Incorrect docs are worse than missing docs.
- Involve the right reviewers: code owners for behavior, support or QA for user failure modes, and docs maintainers for structure and style.
- Preserve older-version guidance only when users need it; otherwise document the current supported behavior.
Do not use FAQs as a dumping ground for unrelated material. Promote recurring questions into task, concept, troubleshooting, or reference pages.
## Writing Style
Write in a direct, practical voice:
- Use present tense and active voice.
- Address the reader as "you" when giving instructions.
- Publishing fails with HTTP 422 if required fields are missing or the private fork still has open PRs.
- A payload that looks correct in shell can still be wrong if Markdown was assembled with escaped newline strings.
- Advisory PATCH sequencing matters; separate field updates when GHSA API constraints require it.
- Public hardening/no-publish comments and draft text should avoid raw commit hashes, PR titles/numbers, and fix-mechanism summaries. Prefer patched-version fields or release-only wording; keep SHAs, PRs, and implementation notes in internal evidence.
description: "Find or repair small high-confidence non-SDK-boundary OpenClaw bugfix PRs until five are landable."
---
# OpenClaw Landable Bug Sweep
Autonomous maintainer workflow for producing five landable OpenClaw bugfix PR URLs.
Use for broad issue/PR sweeps where the bar is high and the output is PRs, not notes.
Do not use for plugin SDK/API boundary work; those need separate architecture review.
## Target
Return exactly five PR URLs, each with:
- bug summary
- why the fix is low-risk
- proof: rebased-head local/Testbox/live commands or run IDs
- autoreview: clean result on the exact head being shown
- CI green on the exact pushed PR head
- issue/duplicate cleanup done or still pending
The five URLs may be existing PRs that were reviewed/fixed, or new PRs created from issues/clusters.
Do not present a PR URL to the maintainer until it has been refreshed on current `main`, left-tested, autoreviewed clean, pushed, and verified green in live GitHub CI.
If code, tests, changelog, PR body, or branch base changes after autoreview, rerun autoreview before showing the URL.
## Companion Skills
Use `$gitcrawl` for discovery/clustering, `$openclaw-pr-maintainer` for live GitHub mutation rules, `$github-author-context` when contributor trust matters, `$openclaw-testing` for proof choice, `$autoreview` before publishing/landing, and `$crabbox` for broad/E2E/live proof.
## Candidate Bar
Accept only when all are true:
- bug or paper cut, not feature/product/support/docs-only
- root cause is proven in current code
- dependency behavior checked via upstream docs/source/types when relevant
- production/runtime diff is small, ideally much smaller than 500 LOC and always below 500 LOC
- tests may be larger, but focused
- no new dependency
- no new config option
- no backward-incompatible behavior
- no security/product/owner-boundary decision needed
- no plugin SDK, public plugin API, or `src/plugin-sdk/**` boundary change
- no broad refactor smell
- focused proof is feasible
- branch can be rebased/refreshed and pushed, or a replacement PR can be created
Good examples:
- provider parameter mismatch proven against dependency/API contract
- CLI command diverges from adjacent command behavior
- narrow runtime state/serialization bug with failing test
- issue already fixed on current `main`, with proof and closeable duplicates
Reject:
- feature requests, new knobs, migrations, release work, workflow policy, support
- plugin SDK/API boundary changes, including compatibility shims, new SDK methods, SDK exports, or plugin-facing channel/provider seams
- if already fixed on `main`, prove with current source/test/commit and close kindly
5. Patch:
- prefer existing PR when good and writable
- if unwritable or wrong shape, create own PR and preserve useful contributor credit
- if no PR exists, create one
- add regression test when it fits
- release-note context for user-facing fixes in PR body or commit message; credit human reporter/contributor when known
6. Review, refresh, and publish:
- rebase or otherwise refresh the PR branch on current `origin/main`
- resolve drift, including newly exposed CI failures, rather than counting the PR as ready
- do not add `CHANGELOG.md` during normal sweep PRs; release automation generates it from PRs and commits
- left-test the rebased head with the smallest meaningful local/Testbox/live command that proves the bug
- run `$autoreview` until no accepted/actionable findings remain before creating, updating, or presenting the PR URL
- create/update PR with real body and proof fields
- push the exact reviewed head
- verify live GitHub CI is green for that pushed head; do not count pending, red, dirty, conflicting, or externally blocked PRs in the five
7. Hygiene:
- close duplicates and fixed-on-main issues/PRs with proof as soon as you notice them during the sweep
- never mutate more than five associated items in one cluster without explicit confirmation
- comments must be kind, concrete, and include proof/PR/commit links
8. Repeat until five landable PR URLs are ready.
## 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:
```
## Existing PR Rules
- Review code path beyond the diff before trusting it.
- If PR is good: rebase/refresh on current `main`, fix small issues, left-test, autoreview clean, push, and get CI green before showing or counting it.
- If PR is not good but has a useful idea: recreate locally, co-author when warranted, close original with thanks and explanation.
- If PR is duplicate or fixed on `main`: comment proof, close.
- If maintainer cannot push to contributor branch: create own branch/PR, preserve useful commits or credit.
- If CI turns red after local proof, treat that as normal work: inspect the failing job, fix or reject, rerun, and only count the PR once green.
short_description:"Find five small non-SDK landable bugfix PRs"
default_prompt:"Use $openclaw-landable-bug-sweep to find or repair five small high-confidence non-SDK-boundary OpenClaw bugfix PRs and get them landable."
description: "Run or recover OpenClaw macOS release signing, notarization, appcast, and asset promotion."
---
# OpenClaw Mac Release
Use with `$openclaw-release-maintainer`, `$openclaw-release-ci`, and `$one-password` when stable macOS assets, private mac preflight, notarization, appcast promotion, or mac release recovery is involved.
## Credentials
- Canonical ASC item: vault `Molty`, title `API Key - App Store Connect - Personal - Release`.
@@ -58,7 +58,7 @@ Use this skill for Parallels guest workflows and smoke interpretation. Do not lo
- For beta/stable verification, resolve the tag immediately before the run (`npm view openclaw@beta version dist.tarball` or `npm view openclaw@latest ...`). Tags can move while a long VM matrix is already running; restart the matrix when the intended prerelease appears after an earlier registry 404/tag-lag check.
- Use the configured secret workflow to inject only the provider keys needed by OpenAI/Anthropic lanes. Do not print secrets or env dumps; pass provider secrets through the guest exec environment.
- Same-guest update verification should set the default model explicitly to `openai/gpt-5.4` before the agent turn and use a fresh explicit `--session-id` so old session model state does not leak into the check.
- The aggregate npm-update wrapper must resolve the Linux VM with the same Ubuntu fallback policy as `parallels-linux-smoke.sh` before both fresh and update lanes. Treat any Ubuntu guest with major version `>= 24` as acceptable when the exact default VM is missing, preferring the closest version match. On Peter's current host today, missing `Ubuntu 24.04.3 ARM64` should fall back to`Ubuntu 25.10`.
- The aggregate npm-update wrapper must resolve the Linux VM with the same Ubuntu fallback policy as `parallels-linux-smoke.sh` before both fresh and update lanes. Treat any Ubuntu guest with major version `>= 24` as acceptable when the exact default VM is missing, preferring the newest versioned Ubuntu guest with a fresh poweroff snapshot. On Peter's current host today, use`Ubuntu 26.04`.
- On macOS same-guest update checks, restart the gateway after the npm upgrade before `gateway status` / `agent`; launchd can otherwise report a loaded service while the old process has exited and the fresh process is not RPC-ready yet.
- The npm-update aggregate's macOS update leg writes the guest update script as root, then runs it as the desktop user. If `prlctl exec "$MACOS_VM" --current-user ...` cannot authenticate, retry through plain root `prlctl exec` plus `sudo -u <desktop-user> /usr/bin/env HOME=/Users/<desktop-user> USER=<desktop-user> LOGNAME=<desktop-user> PATH=/opt/homebrew/bin:/opt/homebrew/opt/node/bin:/usr/bin:/bin:/usr/sbin:/sbin ...`. That is a Parallels transport fallback; still verify `openclaw --version`, gateway RPC, and an agent turn after the update.
- On Windows same-guest update checks, restart the gateway after the npm upgrade before `gateway status` / `agent`; in-place global npm updates can otherwise leave stale hashed `dist/*` module imports alive in the running service.
@@ -93,8 +93,8 @@ Use this skill for Parallels guest workflows and smoke interpretation. Do not lo
- If that release-to-dev lane fails with `reason=preflight-no-good-commit` and repeated `sh: pnpm: command not found` tails from `preflight build`, treat it as an updater regression first. The fix belongs in the git/dev updater bootstrap path, not in Parallels retry logic.
- Until the public stable train includes that updater bootstrap fix, the macOS release-to-dev lane may seed a temporary guest-local `pnpm` shim immediately before `openclaw update --channel dev`. Keep that workaround scoped to the smoke harness and remove it once the latest stable no longer needs it.
- In Tahoe `prlctl exec --current-user` runs, prefer explicit `node .../openclaw.mjs ...` invocations for the release->dev handoff itself and for post-update verification. The shebanged global `openclaw` wrapper can fail with `env: node: No such file or directory`, and self-updating through the wrapper is a weaker lane than invoking the entrypoint under a fixed `node`.
- Default to the snapshot closest to `macOS 26.3.1 latest`.
- On Peter's Tahoe VM, `fresh-latest-march-2026` can hang in `prlctl snapshot-switch`; if restore times out there, rerun with `--snapshot-hint 'macOS 26.3.1 latest'` before blaming auth or the harness.
- Default to the snapshot closest to `macOS 26.5 latest`.
- On Peter's Tahoe VM, `fresh-latest-march-2026` can hang in `prlctl snapshot-switch`; if restore times out there, rerun with `--snapshot-hint 'macOS 26.5 latest'` before blaming auth or the harness.
-`parallels-macos-smoke.sh` now retries `snapshot-switch` once after force-stopping a stuck running/suspended guest. If Tahoe still times out after that recovery path, then treat it as a real Parallels/host issue and rerun manually.
- The macOS smoke should include a dashboard load phase after gateway health: resolve the tokenized URL with `openclaw dashboard --no-open`, verify the served HTML contains the Control UI title/root shell, then open Safari and require an established localhost TCP connection from Safari to the gateway port.
- For Tahoe `fresh.gateway-status`, prefer non-TTY `prlctl exec --current-user ... openclaw gateway status ...` plus a few short retries. `prlctl enter` can spam TTY control bytes and hang the phase log even when the CLI itself is healthy.
@@ -140,8 +140,8 @@ Use this skill for Parallels guest workflows and smoke interpretation. Do not lo
- Use the snapshot closest to fresh`Ubuntu 24.04.3 ARM64`.
- If that exact VM is missing on the host, any Ubuntu guest with major version `>= 24` is acceptable; prefer the closest versioned Ubuntu guest with a fresh poweroff snapshot. On Peter's host today, that is `Ubuntu 25.10`.
- Use the newest versioned Ubuntu guest with a fresh poweroff snapshot. On Peter's host today, that is`Ubuntu 26.04`.
- If an exact requested Ubuntu VM is missing on the host, any Ubuntu guest with major version `>= 24` is acceptable; prefer the newest versioned Ubuntu guest over older fallback snapshots.
- Use plain `prlctl exec`; `--current-user` is not the right transport on this snapshot.
- Fresh snapshots may be missing `curl`, and `apt-get update` can fail on clock skew. Bootstrap with `apt-get -o Acquire::Check-Date=false update` and install `curl ca-certificates`.
- Fresh `main` tgz smoke still needs the latest-release installer first because the snapshot has no Node or npm before bootstrap.
@@ -139,12 +139,12 @@ Issue triage is review/prove/patch-local by default:
2. Fix only issues that are easy, high-confidence, and narrowly owned by the implicated path.
3. Add focused regression proof when practical.
4. Stop with the dirty diff, touched files, and test/gate output for maintainer review.
5. After maintainer approval to ship, make one commit per accepted fix, with its own changelog entry when user-facing.
5. After maintainer approval to ship, make one commit per accepted fix, with release-note context in the PR body or commit message when user-facing.
6. Pull/rebase, push, then comment and close only the issues that were fixed or explicitly triaged closed.
Do not batch unrelated issue fixes into one commit. Do not publish, comment, close, or label during the review/prove phase.
Missing changelog is not a PR review finding or merge blocker. If landing/fixing a user-visible change, add/update changelog automatically when practical; never ask or block solely on it.
Missing `CHANGELOG.md` is not a PR review finding or merge blocker. If landing/fixing a user-visible change, make sure the PR body or commit message captures the release-note context; never ask or block solely on it.
Only list candidates that pass all gates:
@@ -168,11 +168,22 @@ Output only qualifying candidates, with: ref, surface, proof, cause, fix sketch,
- Start every PR review with 1-3 plain sentences explaining what the change does and why it matters. Put this before `Findings`.
- Then list findings first. If none, say `No blocking findings` or `No findings`.
- Show size near the top as `LOC: +<additions>/-<deletions> (<changedFiles> files)`, using live PR stats or local diff stats.
- Always answer: bug/behavior being fixed, PR/issue URL and affected surface, provenance for regressions when traceable, and best-fix verdict.
- For bug/regression fixes, include a compact `Provenance:` line after cause/root-cause when a bounded history pass can identify it. Use `git log -S/-G`, `git blame`, linked PRs/issues, and tests; separate author, committer/merger, and current PR author when they differ.
- For bug/regression fixes, include a compact `Provenance:` line after cause/root-cause when a bounded history pass can identify it. Use `git log -S/-G`, `git blame`, linked PRs/issues, and tests.
- Provenance must separate roles when they differ: blamed code author username, blamed PR author username, blamed PR merger/committer username, automerge trigger when known, current PR author username, PR number, and date. Do not collapse them into one "introduced by" actor.
- If the blamed PR was merged by `clawsweeper[bot]` or another automation, identify the human trigger when practical. Check live PR timeline/comments first; if rate-limited, use gitcrawl/cache or public PR HTML. Look for maintainer command comments such as `@clawsweeper automerge`, `/landpr`, labels/events that armed automerge, and ClawSweeper status comments. Report `automerge triggered by @login`; if not found, say trigger unknown rather than naming the bot as the human decision-maker.
- For any confirmed bug, run `git blame` on the implicated line(s) after identifying the root cause. Report who broke it as the blamed PR merger/committer, and also name the blamed code author. Include the PR number. If no PR is traceable, use the blamed commit as the provenance: commit SHA, date, and author username. Do not guess a merger or frame missing PR metadata as a separate finding.
- Phrase provenance as `introduced by`, `made visible by`, or `carried forward by`, with confidence (`clear`, `likely`, `unknown`). If unclear, say what evidence is missing instead of guessing. For features, docs, and refactors, use `Provenance: N/A` or omit it when no broken behavior is being fixed.
- Keep summaries compact, but include enough proof that the verdict is auditable without rereading the PR.
- Review the surrounding code path, not just changed lines. Open the caller, callee, data contracts, adjacent tests, and owner module.
@@ -192,7 +203,7 @@ Output only qualifying candidates, with: ref, surface, proof, cause, fix sketch,
- Before landing, require:
1. symptom evidence such as a repro, logs, or a failing test
2. a verified root cause in code with file/line
3. provenance for regressions when traceable by bounded git/PR history
3. blame-backed provenance for regressions when traceable, including blamed PR merger and automerge trigger when known, or commit SHA/date when no PR is traceable
4. a fix that touches the implicated code path
5. a regression test when feasible, or explicit manual verification plus a reason no test was added
- If the claim is unsubstantiated or likely wrong, request evidence or changes instead of merging.
- Never mention merge conflicts that are relatively easy to resolve, such as
`CHANGELOG.md` entries, in review-only output. These are landing mechanics,
not correctness findings.
- Never mention release-note bookkeeping in review-only output. It is landing
or release-generation mechanics, not a correctness finding.
- If bot review conversations exist on your PR, address them and resolve them yourself once fixed.
- Leave a review conversation unresolved only when reviewer or maintainer judgment is still needed.
- Before landing any PR with non-trivial code changes, run `$autoreview` until no accepted/actionable findings remain, unless equivalent manual review already covered it, the change is trivial/docs-only, or the user opts out.
default_prompt:"Use $openclaw-pre-release-plugin-testing to plan or run pre-release OpenClaw plugin validation across package, lifecycle, doctor, gateway, SDK, and live-ish proof."
description: "Run, watch, debug, and summarize OpenClaw full release CI, release checks, live provider gates, install/update proofs, and release-secret preflights."
---
# OpenClaw Release CI
Use this with `$openclaw-release-maintainer` and `$openclaw-testing` when a release candidate needs full validation, install/update proof, live provider checks, or CI recovery.
## Guardrails
- No version bump, tag, npm publish, GitHub release, or release promotion without explicit operator approval.
- Validate provider secrets before dispatching expensive full release matrices.
- Do not set GitHub secrets from unvalidated 1Password candidates. If a candidate returns 401/403, leave the existing secret alone and report the exact missing provider.
- Use `$one-password` for secret reads/writes: one persistent tmux session, targeted items only, no secret output.
- Watch one parent run plus compact child summaries. Avoid broad `gh run view` polling loops; REST quota is easy to burn.
- Fetch logs only for failed or currently-blocking jobs. If quota is low, stop polling and wait for reset.
- Treat live-provider flakes separately from code failures: prove key validity, provider HTTP status, retry evidence, and exact failing lane before editing code.
If env lacks keys, use `$one-password` to inject or set them, then rerun the script. The script prints only provider status and HTTP class, never tokens.
## Dispatch
Prefer the trusted workflow on `main`, target the exact release SHA:
```bash
gh workflow run full-release-validation.yml \
--repo openclaw/openclaw \
--ref main \
-f ref=<release-sha> \
-f provider=openai \
-f mode=both \
-f release_profile=full \
-f rerun_group=all
```
Use `release_profile=stable` unless the operator explicitly asks for the broad advisory provider/media matrix. Use narrow `rerun_group` after focused fixes.
## Watch
Use the summary helper instead of repeated raw polling:
gh run watch <full-release-run-id> --repo openclaw/openclaw --exit-status
```
Stop watchers before ending the turn or switching strategy.
## Failure Triage
1. Confirm parent SHA and child run IDs.
2. List failed jobs only:
```bash
gh run view <child-run-id> --repo openclaw/openclaw --json jobs \
--jq '.jobs[] | select(.conclusion=="failure" or .conclusion=="timed_out" or .conclusion=="cancelled") | [.databaseId,.name,.conclusion,.url] | @tsv'
```
3. Fetch one failed job log. If rate-limited, note reset time and avoid more REST calls.
4. For secret-looking failures, validate the provider endpoint from the same secret source before editing code.
5. For live-cache failures, inspect whether it is missing/invalid key, empty text, provider refusal, timeout, or baseline miss. Do not weaken release gates without clear provider evidence.
6. Fix narrowly, run local/changed proof, commit, push, rerun the smallest matching group.
## Evidence
Record:
- release SHA
- full parent run URL
- child run IDs and conclusions: CI, Release Checks, Plugin Prerelease, NPM Telegram
- targeted local proof commands
- provider-secret preflight result
- known gaps or unrelated failures
For lessons and recovery patterns, read `references/release-ci-notes.md`.
short_description:"Verify and debug OpenClaw release validation runs"
default_prompt:"Use $openclaw-release-ci to preflight provider secrets, watch full release validation, summarize child runs, and triage only failing release lanes."
description: Prepare or verify OpenClaw stable/beta releases, changelogs, release notes, publish commands, and artifacts.
---
# OpenClaw Release Maintainer
Use this skill for release and publish-time workflow. Keep ordinary development changes and GHSA-specific advisory work outside this skill.
## Respect release guardrails
- Do not change version numbers without explicit operator approval.
- Ask permission before any npm publish or release step.
- This skill should be sufficient to drive the normal release flow end-to-end.
- Use the private maintainer release docs for credentials, recovery steps, and mac signing/notary specifics, and use `docs/reference/RELEASING.md` for public policy.
- Core `openclaw` publish is manual `workflow_dispatch`; creating or pushing a tag does not publish by itself.
- Normal release work happens on a branch cut from `main`, not directly on
`main`. Use `release/YYYY.M.D` for the branch name.
- If the operator asks for a release without saying stable/full, default to
beta only. Continue from beta to stable only when the operator explicitly asks
for the full release or an automated beta-and-stable train.
- Before release branching, pull latest `main` and confirm current `main` CI is
green. Then branch from that commit so regular development can continue on
`main` while release validation runs.
- Before release branching, commit any dirty files in coherent groups, push,
pull/rebase, then run `/changelog` on `main` and commit/push/pull that
changelog rewrite immediately before creating the release branch.
- During release planning, inspect both `src/plugins/compat/registry.ts` and
`src/commands/doctor/shared/deprecation-compat.ts` before branching and again
before final publish. For every deprecated or removal-pending compatibility
record whose `removeAfter` date is on or before the release date, either
remove the compatibility path where safe and validate the affected tests, or
write down why removal is blocked and get explicit maintainer approval before
shipping the expired compatibility path.
- When removing deprecated runtime/config compatibility, preserve any doctor
migration, repair, or hint that is still needed by supported upgrade paths.
Doctor-side compatibility should stay tracked in
`src/commands/doctor/shared/deprecation-compat.ts` until maintainers confirm
the repair is no longer needed.
- Revalidate compatibility replacement text during release planning. The
recommended replacement can shift as plugin ownership, externalization, and
config footprint move, so do not blindly copy stale replacement annotations
into release notes.
- Do not delete or rewrite beta tags after their matching npm package has been
published. If a pushed beta tag fails before npm publish, the version is not
consumed: keep the same `-beta.N`, delete/recreate or force-move the git tag
and prerelease to the fixed commit, and rerun preflight. Do not increment to
the next beta number until the matching npm package has actually published.
If a published beta needs a fix, commit the fix on the release branch and
increment to the next `-beta.N`.
- For a beta release train, run the fast local preflight first, publish the
beta to npm `beta`, then run the expensive published-package roster focused
on install/update/Docker/Parallels/NPM Telegram. If anything fails, fix it on
the release branch, commit/push/pull, increment beta number, and repeat. Run
the full expensive roster at least once before stable/latest promotion; for
later beta attempts, rerun only lanes whose evidence changed unless the fix
touches broad release, install/update, plugin, Docker, Parallels, or live QA
behavior. After each beta is published, scan current `main` once for critical
fixes that landed after the release branch cut and backport only important
low-risk fixes. Operators may authorize up to 4 autonomous beta attempts;
after 4 failed beta attempts, stop and report.
- Use `/changelog` before version/tag preparation so the top changelog section
is deduped and ordered by user impact.
- Do not create beta-specific `CHANGELOG.md` headings. Beta releases use the
stable base version section, for example `v2026.4.20-beta.1` uses
`## 2026.4.20` release notes.
- When any beta or stable release is live, make a best-effort Discord
announcement using the configured secret workflow; do not block or roll back
the release if the announcement fails.
- When asked to announce on X, use `~/Projects/bird/bird` and follow the
release tweet style below.
## Keep release channel naming aligned
-`stable`: tagged releases only, published to npm `beta` by default; operators may target npm `latest` explicitly or promote later
-`beta`: prerelease tags like `vYYYY.M.D-beta.N`, with npm dist-tag `beta`
- Prefer `-beta.N`; do not mint new `-1` or `-2` beta suffixes
-`dev`: moving head on `main`
- When using a beta Git tag, publish npm with the matching beta version suffix so the plain version is not consumed or blocked
- Before creating a release tag, make every version location above match the version encoded by that tag.
- For fallback correction tags like `vYYYY.M.D-N`, the repo version locations still stay at `YYYY.M.D`.
- “Bump version everywhere” means all version locations above except `appcast.xml`.
- Release signing and notary credentials live outside the repo in the private maintainer docs.
- Every stable OpenClaw release ships the npm package and macOS app together.
Beta releases normally ship npm/package artifacts first and skip mac app
build/sign/notarize unless the operator requests mac beta validation.
- Do not let the slower macOS signing/notary path block npm publication once
the npm preflight has passed. Keep mac validation/publish running in
parallel, publish npm from the successful npm preflight, then start published
npm install/update, Docker, and Parallels verification while mac artifacts
continue.
- After a beta is published, overlap remote/manual release rosters where useful,
but avoid piling local Docker, Parallels, and QA-Lab work onto the same host
when it would create system-load noise. Use selective reruns after failures or
fixes, but keep proof that Docker, Parallels, and QA-Lab each passed at least
once before stable/latest promotion.
- Mac packaging may be built from a slight release-branch variation of the
tagged commit when the delta is mac packaging, signing, workflow, or
validation-only release machinery. If mac packaging needs release-branch-only
fixes after the stable npm package or GitHub tag is already published, do not
create a `vYYYY.M.D-N` correction tag just to change the workflow source.
Dispatch the private mac workflows for the original `tag=vYYYY.M.D` with
`source_ref=release/YYYY.M.D` and `public_release_branch=release/YYYY.M.D`;
provenance checks must prove the source SHA descends from the tag and
validation/preflight use the same source. Reserve `vYYYY.M.D-N` correction
tags for emergency hotfixes that must publish a new npm package/release
identity, not for ordinary mac-only packaging recovery.
- The production Sparkle feed lives at `https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml`, and the canonical published file is `appcast.xml` on `main` in the `openclaw` repo.
- That shared production Sparkle feed is stable-only. Beta mac releases may
upload assets to the GitHub prerelease, but they must not replace the shared
`appcast.xml` unless a separate beta feed exists.
- For fallback correction tags like `vYYYY.M.D-N`, the repo version still stays
at `YYYY.M.D`, but the mac release must use a strictly higher numeric
`APP_BUILD` / Sparkle build than the original release so existing installs
see it as newer.
## Build changelog-backed release notes
- Before release branching or tagging, rewrite the target `CHANGELOG.md`
section from commit history, not just from existing notes: scan commits since
the last reachable release tag, add missed user-facing changes, dedupe
overlapping entries, and sort each section from most to least interesting for
users.
- Changelog entries should be user-facing, not internal release-process notes.
- GitHub release and prerelease bodies must use the full matching
`CHANGELOG.md` version section, not highlights or an excerpt. When creating
or editing a release, extract from `## YYYY.M.D` through the line before the
next level-2 heading and use that complete block as the release notes.
- When preparing release notes, scan `src/plugins/compat/registry.ts` and
`src/commands/doctor/shared/deprecation-compat.ts` for compatibility records
with `warningStarts` or `removeAfter` within 7 days after the release date.
Add an `Upcoming deprecations` note to the release notes when any exist,
including the compatibility code, target date, replacement, and a link to the
record's `docsPath` or `/plugins/compatibility` when no more specific
deprecation page exists.
- When cutting a mac release with a beta GitHub prerelease:
- tag `vYYYY.M.D-beta.N` from the release commit
- create a prerelease titled `openclaw YYYY.M.D-beta.N`
- use release notes from the stable base `CHANGELOG.md` version section
(`## YYYY.M.D`), not a beta-specific heading
- attach at least the zip and dSYM zip, plus dmg if available
- Keep the top version entries in `CHANGELOG.md` sorted by impact:
-`### Changes` first
-`### Fixes` deduped with user-facing fixes first
## Write release tweets
Use the OpenClaw account's existing release-post style:
- Format: `OpenClaw YYYY.M.D 🦞` or `🦞 OpenClaw YYYY.M.D is live`, blank line,
then 3-4 emoji-led bullets, blank line, one short punchline, then the release
link.
- For beta: say `OpenClaw YYYY.M.D-beta.N 🦞` or `OpenClaw YYYY.M.D beta N is
live`; keep it clearly beta and avoid implying stable promotion.
- Lead with user-visible capabilities, then important integrations, then
reliability/security/install fixes. Compress "lots of fixes" into one
readable bullet.
- Read the full changelog section before drafting. Do not lead with coverage,
CI, validation, or internal release mechanics unless the release is explicitly
about those. Peter prefers concrete user wins: features, integrations,
workflow improvements, and practical reliability fixes.
- Tone: high-signal, slightly cheeky, confident, not corporate. One joke is
enough. Avoid punching down, insulting users, or promising what was not
verified.
- Peter likes dry, compact taglines when they feel earned. Good example:
`Big release, tiny release notes... kidding.` Keep the joke short and let the
feature bullets carry the tweet; do not turn the punchline into a second
paragraph or a forced bit.
- Length: release tweets are always standard tweets under 280 characters, with
room for one URL. Trim to 3-4 bullets and count the final text before posting.
- Links/media: include the GitHub release or changelog link at the end of the
first release tweet.
- Thread follow-ups: if doing a thread, keep the first release tweet as the
compact launch post, then publish one focused feature explainer per reply.
Follow-up replies should not repeat "new in VERSION" or the version number
when the thread context already makes it obvious.
- Peter's preferred thread workflow: first agree on the generic launch tweet,
then proceed through follow-up tweets one by one. When he says `next`, provide
or copy the next follow-up only; do not dump the full thread again unless asked.
- Every follow-up tweet should include a docs URL for that specific feature.
Prefer a bare URL over `Docs: <url>` unless the label is needed for clarity.
Keep follow-ups concise: around 160-220 raw characters is usually the sweet
spot; under 280 is the hard cap. If a URL makes a tweet fail, trim prose
before dropping the URL.
Prefer explaining diagnostics, trajectory/export, provider setup, model
commands, or other setup-heavy features in follow-ups instead of overloading
the first release tweet.
- Hotfix/correction: be direct and accountable. State what slipped, what is
fixed, and the new version. Keep jokes out of incident-style posts.
Examples to adapt:
```text
OpenClaw 2026.4.20-beta.1 🦞
🐳 Docker install/update smoke
🖥️ Parallels upgrade checks
🔧 Package verification tightened
Beta first. Stable after the gauntlet.
<release link>
```
```text
OpenClaw 2026.4.20 🦞
🚀 Faster install + update
🐳 Docker + Parallels verified
🍎 macOS signed + notarized
🔧 Channel/plugin fixes
Good boring release. Best kind.
<release link>
```
```text
Packaging issue in 2026.4.20-beta.1.
2026.4.20-beta.2 fixes install/update verification. No tag rewrites; beta moves
forward.
Upgrade with the beta channel.
<release link>
```
## Run publish-time validation
Before tagging or publishing, run:
```bash
pnpm check:architecture
pnpm build
pnpm ui:build
pnpm qa:otel:smoke
pnpm release:check
pnpm test:install:smoke
```
- Use `pnpm qa:otel:smoke` when release validation needs telemetry coverage.
It starts a local OTLP/HTTP trace receiver, runs QA-lab's
`otel-trace-smoke`, and checks span names plus content/identifier redaction
from `main` with `package_spec=openclaw@<beta-version>` and
`provider_mode=mock-openai`, and require success. This workflow is
maintainer-dispatched and intentionally has no `npm-release` approval gate;
`qa-live-shared` only supplies the shared QA secrets. This is the default
button path for installed-package onboarding, Telegram setup, and real
Telegram E2E against the published npm package.
Use the local `pnpm test:docker:npm-telegram-live` lane with the matching
`OPENCLAW_NPM_TELEGRAM_PACKAGE_SPEC` and Convex CI env only as a fallback
or debugging path.
- Parallels published beta install/update coverage with both OpenAI and
Anthropic provider keys available
- Parallels install/update proof must keep plugin installs enabled unless the
operator explicitly scopes a harness-only isolation check; a lane that
disables bundled plugin installs is not valid plugin/dependency release
evidence.
- targeted QA reruns only for areas touched by fixes after the full pre-npm
roster, unless the operator requests the full QA roster again. If the fix
touches live channel QA, credential plumbing, Matrix, Telegram, or the QA
harness, rerun Actions > `QA-Lab - All Lanes`.
- Check all release-related build surfaces touched by the release, not only the npm package.
- For beta-style full e2e batteries, hard-cap top-level long lanes instead of letting them run indefinitely. Use host `timeout --foreground`/`gtimeout --foreground` caps such as:
- `45m` for `OPENCLAW_INSTALL_SMOKE_SKIP_NONROOT=1 pnpm test:install:smoke`
- `90m` for `pnpm test:docker:all`
- `60m` each for standalone Docker live lanes
- `180m` for local full QA live OpenAI + Anthropic rosters when explicitly
requested; the default release channel QA gate is Actions >
`QA-Lab - All Lanes`
- Parallels caps from the `openclaw-parallels-smoke` skill
If a lane hits its cap, stop and inspect/fix the affected lane before continuing; do not continue to wait on the same process.
- Actual npm install/update phases are capped at 5 minutes. If `npm install -g`, installer package install, or `openclaw update` takes longer than 300s in release e2e, stop treating the run as healthy progress and debug the installer/updater or harness.
- Serialize host build/package mutations ahead of VM lanes. Finish `pnpm build`, `pnpm ui:build`, `pnpm release:check`, install smoke, and any Docker/package-prep lanes before starting Parallels `npm pack` lanes; otherwise `dist` can disappear during VM pack prep and produce false failures.
- Include mac release readiness in preflight by running the public validation
workflow in `openclaw/openclaw` and the real mac preflight in
`openclaw/releases-private` for every release.
- Treat the `appcast.xml` update on `main` as part of mac release readiness, not an optional follow-up.
- The workflows remain tag-based. The agent is responsible for making sure
preflight runs complete successfully before any publish run starts.
- Any fix after preflight means a new commit. Delete and recreate the tag and
matching GitHub release from the fixed commit, then rerun preflight from
scratch before publishing.
Exception: never delete or recreate a beta tag whose matching npm package has
already been published; increment to the next beta number instead. If only the
pushed tag/prerelease exists and npm publish has not happened, recreate that
same beta tag at the fixed commit.
- For stable mac releases, generate the signed `appcast.xml` before uploading
public release assets so the updater feed cannot lag the published binaries.
- Serialize stable appcast-producing runs across tags so two releases do not
generate replacement `appcast.xml` files from the same stale seed.
- For stable releases, rely primarily on the latest beta's broader release
workflow confidence. When promoting the matching non-beta build to npm
`latest`, prefer a light time-bounded verification pass: published npm
short_description:"Benchmark and speed up OpenClaw tests"
default_prompt:"Use $optimizetests to benchmark slow OpenClaw tests, optimize imports and duplicated setup, move misplaced core coverage to extensions, verify gates, commit scoped changes, push, and keep CI green without adding shards or dropping coverage."
description: "Draft or post OpenClaw beta/stable Discord release announcements from changelog, GitHub release, registry, and validation evidence. Use when announcing a beta, stable release, release candidate, or asking what users should test after an OpenClaw release."
---
# OpenClaw Release Announcement
Use with `release-openclaw-maintainer` after a beta or stable release is live.
Use with `openclaw-discord` when actually posting to Discord.
## Evidence First
Before drafting focus areas, read real release evidence:
1. Current GitHub release body for the tag.
2.`CHANGELOG.md` section for the released base version.
3. Commits since the previous shipped version or the operator-specified base.
4. Registry/package metadata for the exact version and current dist-tag.
5. Validation status that is relevant to user confidence.
Do not claim a full changelog audit unless you did it. If you only read the
generated release notes or top changelog section, say that and either audit
properly or draft with that limitation.
For beta focus areas, prioritize user-observable changes over internal test or
CI mechanics:
- install/update paths
- OS/platform-specific behavior
- Gateway startup/restart, config, and runtime behavior
- provider/model/runtime routing
- plugin loading and local plugin development
- channels and media paths
- security/data-loss/user-impact fixes
Do not let late release-branch fixes automatically dominate the announcement.
If the version includes a large delta from the previous shipped version, rank
focus areas by the whole release delta and expected user impact; mention late
fixes in their natural category.
## Required Copy
Every beta announcement must make beta status explicit and include:
- exact version, e.g. `OpenClaw 2026.5.25-beta.1`
- one-sentence risk framing: beta, useful for testing, not stable promotion
- focused test areas derived from evidence, not guesswork
- update command promoted near the top:
```sh
openclaw update --channel beta --yes
openclaw --version
```
- fresh install path:
`Install from https://openclaw.ai`
- GitHub release link
- concise validation note, without making CI the headline
Do not suggest npm install commands in beta announcements unless the operator
explicitly asks for npm-specific copy or troubleshooting text. It is fine to use
registry metadata as evidence; do not turn that into public install guidance.
For stable announcements, use the stable channel wording:
```sh
openclaw update --channel stable --yes
openclaw --version
```
Fresh installs still point to `https://openclaw.ai`.
## Style
- Discord Markdown, no tables.
- Keep it skimmable: short intro, bullets, commands, links.
- Lead with what users can feel or test, not proof plumbing.
- Mention validation only after install/update instructions.
- Be specific about where feedback is useful.
- Do not mention private local proof paths in public announcements.
- Do not overstate unverified platforms, channels, or provider behavior.
## Posting
When asked to post, use the configured Discord workflow from
`openclaw-discord` or the approved OpenClaw relay. Never print tokens.
For public channels, inspect the final body before sending.
short_description:"Draft Discord beta/stable release announcements from evidence."
default_prompt:"Use this skill to draft an OpenClaw beta or stable Discord announcement from changelog, release notes, npm/GitHub release proof, and validation evidence."
description: "Run, watch, debug, and summarize OpenClaw full release CI, release checks, live provider gates, install/update proofs, and release-secret preflights."
---
# OpenClaw Release CI
Use this with `$release-openclaw-maintainer` and `$openclaw-testing` when a release candidate needs full validation, install/update proof, live provider checks, or CI recovery.
## Guardrails
- No version bump, tag, npm publish, GitHub release, or release promotion without explicit operator approval.
- Validate provider secrets before dispatching expensive full release matrices.
- Do not set GitHub secrets from unvalidated 1Password candidates. If a candidate returns 401/403, leave the existing secret alone and report the exact missing provider.
- Use `$one-password` for secret reads/writes: one persistent tmux session, targeted items only, no secret output.
- Watch one parent run plus compact child summaries. Avoid broad `gh run view` polling loops; REST quota is easy to burn.
- Fetch logs only for failed or currently-blocking jobs. If quota is low, stop polling and wait for reset.
- Treat live-provider flakes separately from code failures: prove key validity, provider HTTP status, retry evidence, and exact failing lane before editing code.
1Password service-account values are the first source for release provider
preflight. Inject those exact targeted keys first, then run the verifier; use
ambient env only when it was already intentionally injected for this release.
The script prints only provider status and HTTP class, never tokens.
## Dispatch
Start product performance evidence as early as the release SHA exists, in
parallel with other release work:
```bash
gh workflow run openclaw-performance.yml \
--repo openclaw/openclaw \
--ref main \
-f target_ref=<release-sha> \
-f profile=release \
-f repeat=3\
-f deep_profile=false\
-f live_openai_candidate=false\
-f fail_on_regression=false
```
- Do not wait for full release validation to start this early perf signal.
- Compare available Kova, gateway startup, and CLI startup metrics with earlier
release evidence or clawgrit reports before publish/closeout.
- Call out any regression in the release proof. Treat a major regression as a
release blocker until it is fixed, waived by the operator, or proven to be
infrastructure noise.
- Full Release Validation also records advisory product-performance evidence;
the early standalone run is for overlap and faster regression discovery.
Prefer the trusted workflow on `main`, target the exact release SHA:
```bash
gh workflow run full-release-validation.yml \
--repo openclaw/openclaw \
--ref main \
-f ref=<release-sha> \
-f provider=openai \
-f mode=both \
-f release_profile=full \
-f rerun_group=all
```
Use `release_profile=stable` unless the operator explicitly asks for the broad advisory provider/media matrix. Use narrow `rerun_group` after focused fixes.
## Watch
Use the summary helper instead of repeated raw polling:
gh run watch <full-release-run-id> --repo openclaw/openclaw --exit-status
```
Stop watchers before ending the turn or switching strategy.
## Failure Triage
1. Confirm parent SHA and child run IDs.
2. List failed jobs only:
```bash
gh run view <child-run-id> --repo openclaw/openclaw --json jobs \
--jq '.jobs[] | select(.conclusion=="failure" or .conclusion=="timed_out" or .conclusion=="cancelled") | [.databaseId,.name,.conclusion,.url] | @tsv'
```
3. Fetch one failed job log. If rate-limited, note reset time and avoid more REST calls.
4. For secret-looking failures, validate the provider endpoint from the same secret source before editing code.
5. For live-cache failures, inspect whether it is missing/invalid key, empty text, provider refusal, timeout, or baseline miss. Do not weaken release gates without clear provider evidence.
6. Fix narrowly, run local/changed proof, commit, push, rerun the smallest matching group.
## Evidence
Record:
- release SHA
- full parent run URL
- child run IDs and conclusions: CI, Release Checks, Plugin Prerelease, NPM Telegram, Product Performance
- performance comparison result versus earlier releases when available
- targeted local proof commands
- provider-secret preflight result
- known gaps or unrelated failures
For lessons and recovery patterns, read `references/release-ci-notes.md`.
short_description:"Verify and debug OpenClaw release validation runs"
default_prompt:"Use $release-openclaw-ci to preflight provider secrets, watch full release validation, summarize child runs, and triage only failing release lanes."
description: "Run or recover OpenClaw macOS release signing, notarization, appcast, and asset promotion."
---
# OpenClaw Mac Release
Use with `$release-openclaw-maintainer`, `$release-openclaw-ci`, `$one-password`, and `$release-private` if it exists when stable macOS assets, private mac preflight, notarization, appcast promotion, or mac release recovery is involved.
## Credentials
- Resolve Peter-owned ASC item refs, key ids, issuer ids, and service-token provenance from `$release-private`.
description: Prepare or verify OpenClaw stable/beta releases, changelogs, release notes, publish commands, and artifacts.
---
# OpenClaw Release Maintainer
Use this skill for release and publish-time workflow. Load `$release-private` if it exists before resolving Peter-owned credential locators or private host topology. Keep ordinary development changes and GHSA-specific advisory work outside this skill.
## Respect release guardrails
- Do not change version numbers without explicit operator approval.
- Ask permission before any npm publish or release step.
- This skill should be sufficient to drive the normal release flow end-to-end.
- Use the private maintainer release docs for credentials, recovery steps, and mac signing/notary specifics, and use `docs/reference/RELEASING.md` for public policy.
- Core `openclaw` publish is manual `workflow_dispatch`; creating or pushing a tag does not publish by itself.
- Normal release work happens on a branch cut from `main`, not directly on
`main`. Use `release/YYYY.M.D` for the branch name.
- If the operator asks for a release without saying stable/full, default to
beta only. Continue from beta to stable only when the operator explicitly asks
for the full release or an automated beta-and-stable train.
- Before release branching, pull latest `main` and confirm current `main` CI is
green. Then branch from that commit so regular development can continue on
`main` while release validation runs.
- Before release branching, commit any dirty files in coherent groups, push,
pull/rebase, then generate `CHANGELOG.md` on `main` from merged PRs and all
direct commits since the last reachable release tag. Commit/push/pull that
changelog rewrite immediately before creating the release branch.
- During release planning, inspect both `src/plugins/compat/registry.ts` and
`src/commands/doctor/shared/deprecation-compat.ts` before branching and again
before final publish. For every deprecated or removal-pending compatibility
record whose `removeAfter` date is on or before the release date, either
remove the compatibility path where safe and validate the affected tests, or
write down why removal is blocked and get explicit maintainer approval before
shipping the expired compatibility path.
- When removing deprecated runtime/config compatibility, preserve any doctor
migration, repair, or hint that is still needed by supported upgrade paths.
Doctor-side compatibility should stay tracked in
`src/commands/doctor/shared/deprecation-compat.ts` until maintainers confirm
the repair is no longer needed.
- Revalidate compatibility replacement text during release planning. The
recommended replacement can shift as plugin ownership, externalization, and
config footprint move, so do not blindly copy stale replacement annotations
into release notes.
- Do not delete or rewrite beta tags after their matching npm package has been
published. If a pushed beta tag fails before npm publish, the version is not
consumed: keep the same `-beta.N`, delete/recreate or force-move the git tag
and prerelease to the fixed commit, and rerun preflight. Do not increment to
the next beta number until the matching npm package has actually published.
If a published beta needs a fix, commit the fix on the release branch and
increment to the next `-beta.N`.
- For a beta release train, run the fast local preflight first, publish the
beta to npm `beta`, then run the expensive published-package roster focused
on install/update/Docker/Parallels/NPM Telegram. If anything fails, fix it on
the release branch, commit/push/pull, increment beta number, and repeat. Run
the full expensive roster at least once before stable/latest promotion; for
later beta attempts, rerun only lanes whose evidence changed unless the fix
touches broad release, install/update, plugin, Docker, Parallels, or live QA
behavior. After each beta is published, scan current `main` once for critical
fixes that landed after the release branch cut and backport only important
low-risk fixes. Operators may authorize up to 4 autonomous beta attempts;
after 4 failed beta attempts, stop and report.
- As soon as the release candidate SHA exists, dispatch `OpenClaw Performance`
with `target_ref=<release-sha>` in parallel with the other release work. Do
not wait for full release validation to start the performance signal.
- Before publish/closeout, compare available product performance metrics with
earlier releases: Kova agent-turn/resource metrics, gateway startup
ready/listen/RSS/CPU metrics, and CLI startup metrics from release evidence
or clawgrit reports. Report regressions explicitly. A major regression is a
release blocker unless the operator waives it or the data clearly proves
infrastructure noise.
- Generate the changelog before every beta, beta rerun, stable release, or
stable rerun, before version/tag preparation. Use
`$openclaw-changelog-update` for the rewrite. Do not continue release prep if
the target `CHANGELOG.md` section does not have `### Highlights`,
`### Changes`, and `### Fixes`, grouped by user-facing surface while
preserving every relevant PR/issue ref and every human `Thanks @...`
attribution in the grouped bullet.
- Do not create beta-specific `CHANGELOG.md` headings. Beta releases use the
stable base version section, for example `v2026.4.20-beta.1` uses
`## 2026.4.20` release notes.
- When any beta or stable release is live, make a best-effort Discord
announcement using the configured secret workflow; do not block or roll back
the release if the announcement fails.
- When asked to announce on X, use `~/Projects/bird/bird` and follow the
release tweet style below.
## Keep release channel naming aligned
-`stable`: tagged releases only, published to npm `beta` by default; operators may target npm `latest` explicitly or promote later
-`beta`: prerelease tags like `vYYYY.M.D-beta.N`, with npm dist-tag `beta`
- Prefer `-beta.N`; do not mint new `-1` or `-2` beta suffixes
-`dev`: moving head on `main`
- When using a beta Git tag, publish npm with the matching beta version suffix so the plain version is not consumed or blocked
- Before creating a release tag, make every version location above match the version encoded by that tag.
- For fallback correction tags like `vYYYY.M.D-N`, the repo version locations still stay at `YYYY.M.D`.
- “Bump version everywhere” means all version locations above except `appcast.xml`.
- Release signing and notary credentials live outside the repo in the private maintainer docs.
- Every stable OpenClaw release ships the npm package and macOS app together.
Beta releases normally ship npm/package artifacts first and skip mac app
build/sign/notarize unless the operator requests mac beta validation.
- Do not let the slower macOS signing/notary path block npm publication once
the npm preflight has passed. Keep mac validation/publish running in
parallel, publish npm from the successful npm preflight, then start published
npm install/update, Docker, and Parallels verification while mac artifacts
continue.
- After a beta is published, overlap remote/manual release rosters where useful,
but avoid piling local Docker, Parallels, and QA-Lab work onto the same host
when it would create system-load noise. Use selective reruns after failures or
fixes, but keep proof that Docker, Parallels, and QA-Lab each passed at least
once before stable/latest promotion.
- Mac packaging may be built from a slight release-branch variation of the
tagged commit when the delta is mac packaging, signing, workflow, or
validation-only release machinery. If mac packaging needs release-branch-only
fixes after the stable npm package or GitHub tag is already published, do not
create a `vYYYY.M.D-N` correction tag just to change the workflow source.
Dispatch the private mac workflows for the original `tag=vYYYY.M.D` with
`source_ref=release/YYYY.M.D` and `public_release_branch=release/YYYY.M.D`;
provenance checks must prove the source SHA descends from the tag and
validation/preflight use the same source. Reserve `vYYYY.M.D-N` correction
tags for emergency hotfixes that must publish a new npm package/release
identity, not for ordinary mac-only packaging recovery.
- The production Sparkle feed lives at `https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml`, and the canonical published file is `appcast.xml` on `main` in the `openclaw` repo.
- That shared production Sparkle feed is stable-only. Beta mac releases may
upload assets to the GitHub prerelease, but they must not replace the shared
`appcast.xml` unless a separate beta feed exists.
- For fallback correction tags like `vYYYY.M.D-N`, the repo version still stays
at `YYYY.M.D`, but the mac release must use a strictly higher numeric
`APP_BUILD` / Sparkle build than the original release so existing installs
see it as newer.
## Build changelog-backed release notes
-`CHANGELOG.md` is release-owned. Normal PRs and direct `main` fixes should
not edit it.
- Before release branching or tagging, rewrite the target `CHANGELOG.md`
section from history, not existing notes. Use the last reachable stable or
beta release tag as the base, then inspect every commit through the target
release SHA.
- The changelog rewrite is not optional for beta reruns: any `beta.N` after a
rebase or backport must refresh the same stable-base `## YYYY.M.D` section
before the new version/tag commit.
- Include both merged PR commits and direct commits on `main`. Direct commits
matter: infer notes from their subject, body, touched files, linked issues,
tests, and nearby code when no PR body exists.
- Prefer PR bodies, issue links, review proof, and commit bodies over commit
subjects alone. If a commit fixed an issue directly, the commit body should
name the user-visible behavior, affected surface, issue ref, and credited
reporter/contributor when known.
- Treat missing context as a release-note audit gap: inspect the diff and linked
issue, draft the best accurate entry, and note the uncertainty for maintainer
from `main` with `package_spec=openclaw@<beta-version>` and
`provider_mode=mock-openai`, and require success. This workflow is
maintainer-dispatched and intentionally has no `npm-release` approval gate;
`qa-live-shared` only supplies the shared QA secrets. This is the default
button path for installed-package onboarding, Telegram setup, and real
Telegram E2E against the published npm package.
Use the local `pnpm test:docker:npm-telegram-live` lane with the matching
`OPENCLAW_NPM_TELEGRAM_PACKAGE_SPEC` and Convex CI env only as a fallback
or debugging path.
- Parallels published beta install/update coverage with both OpenAI and
Anthropic provider keys available
- Parallels install/update proof must keep plugin installs enabled unless the
operator explicitly scopes a harness-only isolation check; a lane that
disables bundled plugin installs is not valid plugin/dependency release
evidence.
- targeted QA reruns only for areas touched by fixes after the full pre-npm
roster, unless the operator requests the full QA roster again. If the fix
touches live channel QA, credential plumbing, Matrix, Telegram, or the QA
harness, rerun Actions > `QA-Lab - All Lanes`.
- Check all release-related build surfaces touched by the release, not only the npm package.
- For beta-style full e2e batteries, hard-cap top-level long lanes instead of letting them run indefinitely. Use host `timeout --foreground`/`gtimeout --foreground` caps such as:
- `45m` for `OPENCLAW_INSTALL_SMOKE_SKIP_NONROOT=1 pnpm test:install:smoke`
- `90m` for `pnpm test:docker:all`
- `60m` each for standalone Docker live lanes
- `180m` for local full QA live OpenAI + Anthropic rosters when explicitly
requested; the default release channel QA gate is Actions >
`QA-Lab - All Lanes`
- Parallels caps from the `openclaw-parallels-smoke` skill
If a lane hits its cap, stop and inspect/fix the affected lane before continuing; do not continue to wait on the same process.
- Actual npm install/update phases are capped at 5 minutes. If `npm install -g`, installer package install, or `openclaw update` takes longer than 300s in release e2e, stop treating the run as healthy progress and debug the installer/updater or harness.
- Serialize host build/package mutations ahead of VM lanes. Finish `pnpm build`, `pnpm ui:build`, `pnpm release:check`, install smoke, and any Docker/package-prep lanes before starting Parallels `npm pack` lanes; otherwise `dist` can disappear during VM pack prep and produce false failures.
- Include mac release readiness in preflight by running the public validation
workflow in `openclaw/openclaw` and the real mac preflight in
`openclaw/releases-private` for every release.
- Treat the `appcast.xml` update on `main` as part of mac release readiness, not an optional follow-up.
- The workflows remain tag-based. The agent is responsible for making sure
preflight runs complete successfully before any publish run starts.
- Any fix after preflight means a new commit. Delete and recreate the tag and
matching GitHub release from the fixed commit, then rerun preflight from
scratch before publishing.
Exception: never delete or recreate a beta tag whose matching npm package has
already been published; increment to the next beta number instead. If only the
pushed tag/prerelease exists and npm publish has not happened, recreate that
same beta tag at the fixed commit.
- For stable mac releases, generate the signed `appcast.xml` before uploading
public release assets so the updater feed cannot lag the published binaries.
- Serialize stable appcast-producing runs across tags so two releases do not
generate replacement `appcast.xml` files from the same stale seed.
- For stable releases, rely primarily on the latest beta's broader release
workflow confidence. When promoting the matching non-beta build to npm
`latest`, prefer a light time-bounded verification pass: published npm
description: "OpenClaw Tideclaw alpha/nightly release automation: isolated branches, local fixes, release CI, branch retention, and forward-port to main."
---
# Nightly Release
Use for Tideclaw/OpenClaw alpha/nightly release automation, manual alpha triggers, beta prep, release-branch repair, and post-release forward-port. Load `$release-private` if it exists before using Tideclaw host paths, cron ids, or Discord routing ids.
## Policy
- Alpha/nightly runs every 12h or by manual trigger.
- Beta is human-triggered from Discord from a proven alpha/release branch.
- Stable/latest always needs explicit human confirmation.
- Never publish from a dirty checkout or directly from `main`.
- Main can be busy or broken; alpha work must be isolated so transient main failures do not block a usable nightly.
- Publish only after release-branch proof is green.
- After a successful alpha, forward-port release-branch commits back to `main` and prove main CI green.
- Forward-port PRs contain only reusable fixes needed to make nightly/release checks pass. They must not contain alpha version bumps, release notes, changelog release entries, tags, generated artifacts, or state-file updates.
- Keep only alpha/nightly branches from the last 3 days, plus any branch with an active run, open PR, or release tag.
- Never run broad env/token dumps. For GitHub writes on the Tideclaw host, use the Tideclaw `gh` write wrapper below.
## Identity
Tideclaw should commit under its own machine identity on release branches and forward-port branches:
```bash
git config user.name "Tideclaw"
git config user.email "tideclaw@openclaw.ai"
```
This is good for auditability if commits are clearly machine-authored and gated by CI. Avoid direct pushes to protected `main`; forward-port via PR/automerge unless the repo policy explicitly allows the bot to push after green checks. Include human `Co-authored-by` only when a human supplied the patch or explicit commit text.
## Branch Shape
- Branch prefix: `tideclaw/alpha/`
- Branch name: `tideclaw/alpha/YYYY-MM-DD-HHMMZ`
- Base: current `origin/main` SHA at trigger time.
- State file: resolve from `$release-private` on the Tideclaw host.
- Release tag: `vYYYY.M.D-alpha.N`
- npm dist-tag: `alpha`
Do not reuse old alpha branches for a new run. If rerunning the same base SHA, create a new timestamped branch and record why.
## Start
1. Work in the Tideclaw host checkout from `$release-private`.
3. Read repo release docs/scripts before changing anything:
-`AGENTS.md`
- release docs under `docs/`
- release scripts under `scripts/`
-`.github/workflows/*release*`
4. Compare `$BASE_SHA` with the last successful alpha state and current git/npm/GitHub alpha tags. If already released, report skip and do not publish.
Manual trigger:
```bash
CRON_ID="<from release-private>"
OPENCLAW_ALLOW_ROOT=1 openclaw cron run "$CRON_ID" --expect-final --timeout 21600000
```
## Discord Alpha Trigger
Tideclaw may run alpha immediately from Discord when a maintainer mentions Tideclaw in `#releases` or `#maintainers`.
Accepted shapes:
```text
@Tideclaw run alpha now
@Tideclaw alpha release from main now
@Tideclaw trigger alpha
```
Rules:
1. Treat this as a manual alpha trigger equivalent to the alpha cron job.
2. Start from current `origin/main` and create a fresh `tideclaw/alpha/YYYY-MM-DD-HHMMZ` branch.
3. Follow the normal alpha workflow: reuse prior fixes, run local checks, fix on the alpha branch, run release CI, publish alpha after green gates, then forward-port reusable fixes via fixes-only PR.
4. If another alpha/beta/stable release run is already active, report the active branch/run and stop.
5.`#maintainers` trigger requires an explicit Tideclaw mention; do not react to unmentioned release chatter there.
6. Resolve Discord role/user ids and live host hotfix notes from `$release-private`.
## Discord Beta Trigger
Tideclaw may run beta releases from `#releases` or mentioned `#maintainers` commands only when a maintainer sends an explicit beta trigger. Treat this as human approval for beta, not for stable/latest.
Accepted shapes:
```text
@Tideclaw beta release from vYYYY.M.D-alpha.N
@Tideclaw beta release from tideclaw/alpha/YYYY-MM-DD-HHMMZ
@Tideclaw beta release from latest proven alpha
```
Rules:
1. Require the words `beta release` and a source alpha tag/branch, or `latest proven alpha`.
2. If the source is ambiguous, ask one clarifying question in `#releases` and stop.
3. Verify the source alpha first: GitHub release, npm `alpha` package, release CI, recorded state file, and branch/tag SHA.
4. Create a fresh beta branch `tideclaw/beta/YYYY-MM-DD-HHMMZ` from the proven alpha source, not directly from a moving `main`.
5. Reuse/squash only stabilization fixes already proven on alpha. Do not import unrelated alpha release mechanics unless the beta release docs require them.
6. Compute beta as `vYYYY.M.D-beta.N`, matching npm `--tag beta`.
7. Run beta release validation/preflight/full release CI and fix failures on the beta branch.
8. Publish beta only after green beta gates. Use GitHub Actions/OIDC, never direct npm publish from the host.
9. Final Discord summary must include source alpha, beta tag/version, branch, fix commits, workflow run IDs, npm/GitHub proof, and any skipped/blocked reason.
10. After beta publishes, forward-port reusable fixes to `main` using the same fixes-only PR rules below.
## Reuse Prior Fixes
Before running checks, mine recent Tideclaw alpha branches for fixes already made during previous release attempts:
1. Read the Tideclaw state file from `$release-private` for the last successful alpha branch and fix commit SHAs.
5. Cherry-pick only real stabilization fixes that still apply to the new alpha branch. Prefer commits recorded as `fixCommitShas` in the state file.
6. Skip version bumps, changelog release entries, tag artifacts, generated release notes, state-file-only commits, and one-off debug instrumentation.
7. If a cherry-pick conflicts, inspect whether current main already contains an equivalent fix. If not, resolve minimally and keep the commit message clear.
8. Record reused commit SHAs separately from newly authored fix SHAs in the alpha state and final Discord summary.
Use `git cherry`, `git range-diff`, and targeted test reruns to avoid duplicating fixes already present on `main`.
## Repair Loop
Use the branch as a release-candidate repair surface:
1. Run narrow local checks first: changed tests, release preflight, type/lint/build gates required by release docs.
2. If local checks fail, fix on the alpha branch with minimal commits.
3. Commit each coherent fix as Tideclaw.
4. Re-run the failed local check after each fix.
5. Do not hide failures by editing baselines, expected-failure lists, ignore files, or release inventory unless the release docs explicitly require it and the diff is justified.
6. If a failure is flaky, rerun once; if still red, treat it as real.
7. If the fix is clearly useful for main, keep it small and forward-portable. Avoid broad refactors during alpha stabilization.
Commit examples:
```bash
git add <files>
git commit -m "fix: stabilize alpha release preflight"
git push -u origin "$BRANCH"
```
## Release CI
After local proof:
1. Compute the next `vYYYY.M.D-alpha.N` from existing git tags, npm versions, and GitHub releases.
2. Make the alpha branch package version and release metadata match that tag, commit it, and push the branch.
3. Run release validation from the alpha branch, using GitHub CLI, not browser/fetch tools. On the Tideclaw host, bare `gh` is a read-only Codex sandbox wrapper; use `/usr/local/bin/gh-tideclaw-write` for write-capable commands such as `workflow run`, `run cancel`, and publish dispatch:
"$GH" workflow run full-release-validation.yml --repo openclaw/openclaw --ref "$BRANCH"\
-f ref="$BRANCH"\
-f release_profile=beta \
-f rerun_group=all
"$GH" workflow run openclaw-npm-release.yml --repo openclaw/openclaw --ref "$BRANCH"\
-f tag="$SHA"\
-f preflight_only=true\
-f npm_dist_tag=alpha
```
4. Watch the exact workflow run IDs and head SHA with `gh run list`, `gh run view`, and `gh api`. Read-only `gh` is fine for polling; use `$GH` only when a command mutates GitHub. Do not use Codex browser/fetch for GitHub API polling; prior Tideclaw runs failed there after successful preflight.
5. For alpha, blocking gates are the ones Tideclaw can repair directly or that prove package safety: normal CI, plugin prerelease, npm preflight, package preparation, install smoke, tag/reachability, and publish verification. Treat cross-OS, live channel, QA Lab, package acceptance, long Docker E2E, and Telegram package E2E failures as advisory; report them in Discord and continue if the blocking gates are green.
- If `rerun_group=all` is stuck only on advisory lanes after CI, plugin prerelease, npm preflight, package preparation, and install smoke are green, dispatch a focused Full Release Validation on the same head with `-f rerun_group=install-smoke`. Use that successful focused Full Release Validation run as the publish proof, and include the separate CI/plugin/full advisory run IDs in the Discord summary.
6. If a blocking gate fails, fix on the alpha branch, push, and rerun only the failed or required release CI. If the commit changes, discard old preflight/full-validation run IDs and rerun them for the new head.
7. After full validation and npm preflight are green on the same branch head, create and push the release tag from that exact commit:
```bash
git tag -a "$TAG""$SHA" -m "openclaw ${TAG#v}"
git push origin "$TAG"
```
8. Dispatch the publish wrapper from the same alpha branch. Use the successful npm preflight run ID and full release validation run ID from the same head SHA:
```bash
"$GH" workflow run openclaw-release-publish.yml --repo openclaw/openclaw --ref "$BRANCH"\
9. Watch the publish wrapper plus child runs. If `openclaw-npm-release.yml` is waiting on the `npm-release` environment and Tideclaw cannot approve it, report that as the only blocker; do not call the release done.
10. Do not publish npm directly from the host; use GitHub Actions/OIDC.
Important: `openclaw-npm-release.yml` with `preflight_only=true` only prepares artifacts. It does not publish. A successful alpha requires the later `openclaw-release-publish.yml` wrapper, a pushed git tag, npm `alpha` dist-tag proof, and a GitHub prerelease.
## Verify Published Alpha
Release is not done until all are true:
- GitHub tag exists.
- GitHub Release exists and is marked prerelease.
- Release body links npm version page, registry tarball, integrity, and CI/proof.
-`npm view openclaw@<version>` shows the exact version, dist-tag `alpha`, tarball, integrity, and publish time.
- The Tideclaw state file from `$release-private` records version, tag, base SHA, branch, fix commit SHAs, workflow run IDs, npm integrity, and timestamp.
Final Discord summary in `#releases`:
- tag/version
- base SHA
- branch
- fix commits
- workflow run IDs
- npm/GitHub proof
- skipped/blocked reason if not released
Use Discord-safe Markdown links with angle-bracket targets. Never print secrets.
## Forward-Port
After a successful alpha, raise a fixes-only PR back to `main`:
1. Create/update a forward-port branch from current `origin/main`:
2. Cherry-pick only release-branch commits that are real fixes required to make nightly/release checks pass.
3. Exclude alpha version bumps, changelog release entries, release notes, tag artifacts, generated release assets, state-file-only commits, and any commit whose only purpose was publishing the alpha.
4. If a commit mixes a real fix with release/version changes, split it: replay only the fix hunks into a new commit on the forward-port branch.
5. Resolve conflicts in favor of the minimal main-compatible fix.
6. Run the relevant changed/local gate.
7. Push and open a PR, or use the repo’s allowed bot merge path.
8. Wait for required main CI to go green. If CI fails, fix on the forward-port branch and rerun.
9. Report the PR/merge SHA and any commits intentionally not forward-ported.
If `origin/main` is independently red before the forward-port, document the unrelated failing check and still keep the forward-port PR green against its head when possible.
## Branch Retention
Before and after each run, prune old alpha branches:
1. List `origin/tideclaw/alpha/*`.
2. Keep branches whose timestamp is within the last 3 days UTC.
3. Keep branches referenced by a live workflow run, open PR, release tag, or state file.
default_prompt:"Use $release-openclaw-plugin-testing to plan or run pre-release OpenClaw plugin validation across package, lifecycle, doctor, gateway, SDK, and live-ish proof."
description: Triage OpenClaw security advisories, drafts, and GHSA reports with shipped-tag and trust-model proof.
description: "Triage OpenClaw security advisories, drafts, and GHSA reports with shipped-tag and trust-model proof."
---
# Security Triage
@@ -87,11 +87,19 @@ When preparing a maintainer-ready close reply:
- exact reason for close
- exact code refs
- exact shipped tag / release facts
-exact fix commit or canonical duplicate GHSA when applicable
-fix provenance or canonical duplicate GHSA when applicable
- optional hardening note only if worthwhile and functionality-preserving
Keep tone firm, specific, non-defensive.
## Public Wording Hygiene
- Keep raw commit hashes, PR titles/numbers, and fix-mechanism summaries out of public advisory text. Use the patched release/version field only.
- Keep exact commit SHAs, PRs, and implementation notes in internal notes and verification files.
- For hardening/no-publish outcomes, do not add exploit-heavy details, "Fixed by" text, or a "Fix Commit(s)" section. Thank reporters, preserve credit, state the `SECURITY.md` boundary, and say clearly that the GHSA will close without publication.
- For published CVE/GHSA text, prefer `### Patched Versions` with the fixed release. Do not explain how the patch works unless Peter explicitly asks for that public detail.
- Keep GHSA ids out of changelog and release-note wording unless Peter explicitly asks.
## Discussion Mode
When Peter is manually posting GHSA comments, use this flow:
description: Build and review high-quality technical docs as well as agent instruction files in your repository.
license: MIT
metadata:
source: "https://github.com/vincentkoc/dotskills"
---
# Technical Documentation
## Purpose
Produce and review technical documentation that is clear, actionable, and maintainable for both humans and agents, including contributor-governance files and agent instruction files.
## When to use
- Creating or overhauling docs in an existing product/codebase (brownfield).
- Building evergreen docs meant to stay accurate and reusable over time.
- Reviewing doc diffs for structure, clarity, and operational correctness.
- Running full-repo documentation audits that must include both governance files and product docs surfaces (`docs/`, `README*`, `.md/.mdx/.mdc`, Fern/Sphinx/Mintlify-style sources).
- Updating or reviewing AGENTS.md and/or CONTRIBUTING.md to keep agent and contributor workflows aligned with current repo practices.
- Improving repository onboarding/docs that include contribution instructions, issue templates, PR flow, and review gates.
- Designing governance documentation strategy for repos with alias instruction files (for example `CLAUDE.md`, `AGENT.md`, `.cursorrules`, `.cursor/rules/*`, `.agent/`, `.agents/`, `.pi/`) where `AGENTS.md` is treated as canonical when present and aliases should be kept as compatibility surfaces.
- Diagnosing agent-file drift where teams had to prompt iteratively to surface missing files, broken commands, or policy conflicts.
- Applying repository-specific documentation overlays, including OpenClaw page-type, docs IA, preservation, and validation rules when present.
## Workflow
1. Classify task: `build` or `review`; context: `brownfield` or `evergreen`.
2. Inventory full documentation scope early (governance + product docs): AGENTS/CONTRIBUTING/aliases plus docs directories, framework sources, and root/module READMEs.
3. Detect multilingual scope (README/docs in multiple languages) and define required parity level.
4. Read `references/agent-and-contributing.md` for agent instruction and `CONTRIBUTING.md` workflow rules (inventory, canonical/alias mapping, dual-mode balance, deliverable standards, and precedence/conflict handling).
5. Read `references/principles.md` for the governing ruleset (Matt Palmer & OpenAI).
6. For OpenClaw docs work, read `references/openclaw.md` before the build/review playbook.
7. For build tasks, follow `references/build.md`.
8. For review tasks, follow `references/review.md` and proactively detect issues without waiting for repeated prompts.
9. For complex or high-risk tasks (build or review), it is acceptable to run longer, deeper, and more exhaustive investigations when needed for confidence.
10. When available, use sub-agents for bounded parallel discovery/review work, then merge outputs into one coherent final deliverable.
11. Use `references/tooling.md` when platform/tooling choices affect recommendations.
12. Run a proactive issue sweep for both governance and docs-content surfaces, and fix high-confidence defects in the same pass unless explicitly asked for report-only mode.
13. In brownfield mode, prioritize compatibility with current docs IA, tooling, and release state.
14. In evergreen mode, prioritize timeless wording, update strategy, and durable structure.
15. Return deliverables plus validation notes, parity status, and remaining gaps.
## Sub-agent orchestration guidance
Prefer sub-agents when the repo is large or the requested change set is broad; use them by default for repo-wide, multi-framework, or high-conflict work.
-`inventory-agent` -> `agents/inventory-agent.md` (`fast` / Claude `haiku`): file/config discovery, coverage map, and missing-path checks.
-`governance-agent` -> `agents/governance-agent.md` (`thinking` / Claude `sonnet`): AGENTS/CONTRIBUTING/alias precedence, conflicts, and policy drift.
-`docs-framework-agent` -> `agents/docs-framework-agent.md` (`thinking` / Claude `sonnet`): framework config, relative path base, and file-path vs URL-path mapping checks.
-`synthesis-agent` -> `agents/synthesis-agent.md` (`long` / Claude `opus`): merge sub-agent outputs into one prioritized fix plan and unified precedence model.
## Inputs
- Doc type (tutorial, how-to, reference, explanation) and audience.
2. Read the root and nearest-scope `AGENTS.md`/`CONTRIBUTING.md` pair before editing.
3. If alias files exist, normalize to one canonical source (`AGENTS.md` preferred when present; otherwise nearest alias), plus compatibility pointers or explicit symlink notes.
4. Document conflicting instructions and precedence decisions.
1. Run a conflict matrix review across AGENTS/aliases/CONTRIBUTING and related command/rule docs before finalizing.
2. Treat the following as high-priority defects: missing referenced files, non-existent setup commands, command scope mismatches, and branch/commit policy conflicts.
3. Do not stop at caveat-only notes when a low-risk fix is clear; apply the fix in the same pass.
4. If a canonical entry file is missing (for example a directory `README.md` that docs depend on), create a minimal actionable file and update references.
5. Long-running investigations are acceptable when needed to uncover cross-file drift, especially in agent-instruction ecosystems.
## Discovery
1. Agents prefer simple terminal commands so having a well defined `make *` or `npm run *` is ideal
2. Agents can discover terminal commands through shell completion so providing shell completion helps
- Success criteria: what must be true after publish.
## 5. Build structure before prose
- Follow the funnel: what/why, quickstart, next steps.
- Keep headings informative and scannable.
- Open each section with the takeaway sentence.
- Add decision points with concrete branch guidance.
- For OpenClaw docs work, choose a page type from `references/openclaw.md` before drafting.
- Keep task-critical OpenClaw configuration inline; link exhaustive defaults, enums, schemas, generated references, and rare debugging workflows.
## 6. Build AGENTS.md and CONTRIBUTING.md intentionally
- Keep AGENTS.md structure consistent with `agents.md` ecosystem patterns:
- include YAML frontmatter when present in repo style (`name`, `description`).
- state persona scope and explicit instruction boundaries: `Always`, `Ask first`, `Never`.
- include concrete commands and representative code examples.
- For CONTRIBUTING.md, prioritize issue triage flow, PR expectations, setup/test commands, and review gates.
- Add `Code of Conduct`, `Testing`, `Local checks`, and `PR expectations` sections when missing but required by the repo.
- If CONTRIBUTING.md is becoming too large, split by scope into linked docs (for example, framework/tool-specific setup and release workflows) and keep the root file as a concise entry point.
- Keep cross-file consistency: links from CONTRIBUTING.md to AGENTS.md (and vice versa) should be accurate and non-circular.
- If multiple AGENTS.md files exist, document the directory-level scope and avoid conflicting advice.
- If a required canonical entry file is missing (for example referenced `README.md` under a major directory), create the file in the same pass instead of adding a caveat-only note.
- For new entry files, keep them minimal and actionable: purpose, prerequisites, concrete run commands, and pointers to deeper docs.
## 7. Keep agent context tight
- Author once, expose twice:
- keep one shared policy core and avoid duplicating guidance in separate agent-specific files.
- publish that core through bounded glob-friendly files for Cursor/Claude plus explicit path references for Codex.
- For Cursor and Claude-style agents, avoid broad references. Use minimal globbing and narrow rule files that each serve one concern (for example, repo-wide setup, test rules, security checks).
- Keep AGENTS and alias files short-to-medium; move detailed runbooks to linked docs.
- For Codex, prefer explicit file references and concrete paths for exact reuse.
- Avoid adding unrelated historical or process details to avoid token/context drift during future tool reads.
## 8. Brownfield build mode
- Match existing terminology, navigation, and component patterns.
- Preserve existing IA unless there is a documented migration plan.
- For rewrites, include a migration note from old to new paths.
- Prefer smallest safe change set that improves utility.
## 9. Evergreen build mode
- Prefer stable concepts over release-tied narrative.
- Isolate volatile details under clearly marked version sections.
- Include maintenance signals: owners, refresh triggers, stale criteria.
- Include lifecycle notes: deprecation and replacement paths.
## 10. Writing constraints
- Use precise language and short, imperative instructions.
- Keep code examples copy-ready and self-contained.
- Include common failure modes and safe defaults.
- Avoid placeholder guidance that cannot be executed.
## 11. Agent and automation readiness
- Keep key facts in text (not image-only).
- Prefer structured lists/tables when choices matter.
- Add links and anchors that allow deterministic navigation.
- Document what can be checked automatically in CI.
## 12. Build validation
- Validate commands and snippets where possible.
- Verify links and references in changed sections.
- Run a reference existence sweep for every path/command you introduced.
- Verify docs-framework consistency when in scope (for example Sphinx/Fern config and referenced doc paths).
- For OpenClaw docs work, apply the validation checklist in `references/openclaw.md`.
## 13. Multilingual parity mode (when applicable)
- Pick one source-of-truth language for technical accuracy and release timing.
- Define parity target: full parity, staged parity, or intentional divergence per section.
- Keep structure aligned across locales (headings, anchors, section order) when possible.
- Preserve command/code correctness first; localize explanatory text second.
- If parity is not feasible, add a visible note with missing scope and expected sync window.
- Run a locale parity check for changed sections (added/removed steps, warnings, prerequisites).
- Prefer specific and accurate terminology over niche jargon.
- Keep examples self-contained and minimize dependencies.
- Prioritize high-value topics over edge-case depth.
- Do not teach unsafe patterns (for example, exposed secrets).
- Open with context that helps readers orient quickly.
- Apply empathy and override rigid rules when it clearly improves outcomes.
## Practical merge policy
When these rules conflict:
1. Preserve reader task success first.
2. Preserve structural clarity second.
3. Preserve long-term maintainability third.
4. Add agent optimization only if it does not reduce human clarity.
For agent-instructions and contributor-governance specifics (AGENTS/aliases/CONTRIBUTING), use `references/agent-and-contributing.md` as the detailed additional source of truth.
When the target repo or request is OpenClaw-specific, layer `references/openclaw.md` on top of these general rules. Otherwise ignore that repo-specific overlay.
## Execution policy for this skill
- Long-running and extensive investigations are allowed for both build and review work when needed to resolve ambiguity or cross-file drift.
- Use sub-agents when available for bounded parallel discovery, verification, or cross-source comparison.
- Keep one merged outcome: sub-agent outputs must be normalized into a single consistent recommendation/fix set.
## Multilingual parity rule
When docs exist in multiple languages, target cross-locale parity for task-critical content (steps, warnings, prerequisites, and limits). If full parity is not possible, publish explicit parity status and sync intent.
Read `principles.md` first, then apply this checklist.
## 1. Scope and classification
- Identify doc type and target audience.
- Confirm brownfield vs evergreen intent.
- Confirm expected outcome for the reader.
- For full-repo reviews, explicitly include both governance surfaces and product-doc surfaces (`docs/`, README trees, `.md/.mdx/.mdc`, `.rst/.rsc`, framework docs configs).
- For OpenClaw docs reviews, apply `references/openclaw.md` for page type, docs IA, preservation, examples, and validation checks.
## 2. Investigation behavior
- Proactively find issues and risks without waiting for repeated prompts.
- If there are signals of deeper problems, continue investigation beyond the first pass.
- Long-running and extensive investigations are acceptable when needed for confidence and correctness.
- When available, use sub-agents for bounded parallel discovery (for example file-inventory, command validation, or cross-doc consistency checks), then merge to one final issue set.
- When no issues are found, state that explicitly and call out residual risks or validation gaps.
- Default to `apply-fixes` for high-confidence documentation defects unless the user explicitly requests `report-only`.
- Do not stop at AGENTS/CONTRIBUTING checks when the task is documentation-wide; continue into docs-content and docs-framework surfaces.
## 3. Governance surface review
- Use `references/agent-and-contributing.md` as the source of truth for inventory, canonical/alias mapping, and precedence/conflict handling.
For AGENTS.md:
- confirm persona intent, scope, and command/tool boundaries are explicit.
- check frontmatter style matches repo conventions when present.
- ensure `Always`, `Ask first`, and `Never` boundaries are present when expected.
- require concrete command examples and repo-specific paths to avoid ambiguity.
For CONTRIBUTING.md:
- verify issue/PR workflow is complete and actionable.
- ensure local setup, lint/test commands, and review criteria are accurate.
- ensure governance does not conflict with nested AGENTS instructions.
- flag oversized files that should be split into linked section docs (for example tool-specific setup and release docs).
For agent-platform awareness:
- confirm references are minimal and scoped for Cursor/Claude glob behavior.
- verify canonical rule directory and symlink state match repo policy
- verify symlink target integrity and platform/tooling expectations
- verify AGENTS policy references remain canonical for Codex even when `.cursor` compatibility exists
- check for context bloat from duplicated policy statements across agent and contributor files.
- check for conflicting rules, skills and agent instructions
- check for conflicting information in agent instructions vs codebase
- check for broken or missing referenced files (for example README/index files named as canonical entry points).
- check for setup/command drift (for example non-existent install commands, root-level commands that should be module-scoped).
## 4. Product documentation surface review
- Verify docs IA coverage across root/module `README*` files and `docs/**` trees.
- Review framework-native docs sources in scope (for example Fern, Mintlify, Sphinx, MkDocs) and ensure guidance matches actual source-of-truth files.
- Check `.md/.mdx/.mdc/.rst/.rsc` for stale commands, missing prerequisites, and broken cross-links.
- Confirm referenced doc paths and anchors exist.
- Flag docs that should be split/merged to improve discoverability and maintenance.
- For OpenClaw docs, check `docs/docs.json`, docs-list routing hints, main path versus `Reference` placement, and generated-reference visibility.
- For OpenClaw rewrites or page splits, require source-backed keep/drop/move/destination coverage for important claims, warnings, examples, commands, fields, and troubleshooting facts.
## 5. Framework config and path mapping checks
- Detect and read framework config first (for example Fern config, Sphinx `conf.py`, Mintlify config, or equivalent).
- Resolve path references relative to the declaring file/config.
- Treat filesystem paths and published URL routes as separate maps; verify both.
- Flag path-map drift explicitly (`missing file`, `stale route`, `wrong base path`).
## 6. Structural review
- Funnel check: what/why, quickstart, next steps.
- Validate heading flow and navigation discoverability.
- Flag critical content trapped in images or buried sections.
- Check Diataxis alignment and split mixed-purpose sections.
- For OpenClaw docs, confirm the content matches an explicit page type from `references/openclaw.md`.
## 7. Writing quality review
- Check for concise, scannable paragraphs.
- Remove ambiguous pronouns and undefined terms.
- Verify examples are executable and scoped correctly.
- Verify tone is directive, technical, and non-hand-wavy.
## 8. Brownfield review mode
- Verify compatibility with existing docs IA and conventions.
- Verify anchors, redirects, and cross-doc links remain valid.
- Flag regressions in onboarding and task completion paths.
- Ensure changed terminology is intentionally propagated.
## 9. Evergreen review mode
- Flag date-stamped or brittle wording without version scope.
- Check ownership and refresh signals are present.
- Ensure recommendations remain valid after routine product evolution.
Do not speculate or infer beyond the evidence. If a narrative section cannot be answered from the available evidence, respond with exactly `NOT_ENOUGH_INFO`.
If this is a plugin beta-release blocker, rename the issue title to `Beta blocker: <plugin-name> - <summary>` and apply the `beta-blocker` label after filing.
Please only report one issue per submission. Break multiple issues up into separate submissions.
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.
Describe the intent and outcome in 2-5 bullets. Avoid restating the diff; reviewers and bots can read the changed files.
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.
- Problem:
- Solution:
- What changed:
- What did NOT change (scope boundary):
</details>
## Motivation
## Linked context
Explain why this change should exist now. Link it to the user pain, failure mode, maintainer need, or product goal. If this is purely mechanical, write `N/A`.
Which issue does this close?
-
Closes #
## Change Type (select all)
Which issues, PRs, or discussions are related?
- [ ] Bug fix
- [ ] Feature
- [ ] Refactor required for the fix
- [ ] Docs
- [ ] Security hardening
- [ ] Chore/infra
Related #
## Scope (select all touched areas)
Was this requested by a maintainer or owner?
- [ ] Gateway / orchestration
- [ ] Skills / tool execution
- [ ] Auth / tokens
- [ ] Memory / storage
- [ ] Integrations
- [ ] API / contracts
- [ ] UI / DX
- [ ] CI/CD / infra
<details>
<summary>Linked context guidance</summary>
## Linked Issue/PR
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.
- Closes #
- Related #
- [ ] This PR fixes a bug or regression
</details>
## Real behavior proof (required for external PRs)
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 and copied live output count. Be mindful of private information like IP addresses, API keys, phone numbers, non-public endpoints, or other private details when providing evidence.
- 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):
## Root Cause (if applicable)
<details>
<summary>Real behavior proof guidance</summary>
For bug fixes or regressions, explain why this happened, not just what changed. Otherwise write `N/A`. If the cause is unclear, write `Unknown`.
External contributors must show after-fix evidence from a real OpenClaw setup. Unit tests, mocks, lint, typechecks, snapshots, and CI are supplemental only.
- Root cause:
- Missing detection / guardrail:
- Contributing context (if known):
Screenshots are encouraged even for CLI, console, text, or log changes. Terminal screenshots, copied live output, redacted runtime logs, recordings, and linked artifacts count.
## Regression Test Plan (if applicable)
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.
For bug fixes or regressions, name the smallest reliable test coverage that should catch this. Otherwise write `N/A`.
Be mindful of private information like IP addresses, API keys, phone numbers, non-public endpoints, or other private details when providing evidence.
- Coverage level that should have caught this:
- [ ] Unit test
- [ ] Seam / integration test
- [ ] End-to-end test
- [ ] Existing coverage already sufficient
- Target test or file:
- Scenario the test should lock in:
- Why this is the smallest reliable guardrail:
- Existing test that already covers this (if any):
- If no new test is added, why not:
</details>
## User-visible / Behavior Changes
## Tests and validation
List user-visible changes (including defaults/config).
If none, write `None`.
Which commands did you run?
## Diagram (if applicable)
For UI changes or non-trivial logic flows, include a small ASCII diagram reviewers can scan quickly. Otherwise write `N/A`.
List focused commands, not every incidental check. CI is useful support, but external PRs still need real behavior proof above when behavior changes.
- OS:
- Runtime/container:
- Model/provider:
- Integration/channel (if any):
- Relevant config (redacted):
</details>
### Steps
## Risk checklist
1.
2.
3.
Did user-visible behavior change? (`Yes/No`)
### Expected
-
Did config, environment, or migration behavior change? (`Yes/No`)
### Actual
-
Did security, auth, secrets, network, or tool execution behavior change? (`Yes/No`)
## Evidence
Attach at least one:
What is the highest-risk area?
- [ ] Failing test/log before + passing after
- [ ] Trace/log snippets
- [ ] Screenshot/recording
- [ ] Perf numbers (if relevant)
## Human Verification (required)
How is that risk mitigated?
What you personally verified (not just CI), and how:
<details>
<summary>Risk guidance</summary>
- Verified scenarios:
- Edge cases checked:
- What you did **not** verify:
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.
## Review Conversations
</details>
- [ ] I replied to or resolved every bot review conversation I addressed in this PR.
- [ ] I left unresolved only the conversations that still need reviewer or maintainer judgment.
## Current review state
If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.
What is the next action?
## Compatibility / Migration
- Backward compatible? (`Yes/No`)
- Config/env changes? (`Yes/No`)
- Migration needed? (`Yes/No`)
- If yes, exact upgrade steps:
What is still waiting on author, maintainer, CI, or external proof?
## Risks and Mitigations
List only real risks for this PR. Add/remove entries as needed. If none, write `None`.
Which bot or reviewer comments were addressed?
- Risk:
- Mitigation:
<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.
| select(startswith("+") and (startswith("+++") | not))
| "\($file): \(.)"
' > "$added_lines"
if grep -En '(from|require\().*["'\''](node:)?(net|tls|http2)["'\'']|\b(net|tls|http2)\.(connect|createConnection)\b|new Socket\(|HTTP_PROXY|HTTPS_PROXY|NO_PROXY|GLOBAL_AGENT_|OPENCLAW_PROXY_' "$added_lines"; then
for key in CI GITHUB_ACTIONS GITHUB_WORKSPACE GITHUB_REPOSITORY GITHUB_RUN_ID GITHUB_RUN_NUMBER GITHUB_RUN_ATTEMPT GITHUB_REF GITHUB_REF_NAME GITHUB_SHA GITHUB_EVENT_NAME GITHUB_ACTOR RUNNER_OS RUNNER_ARCH RUNNER_TEMP RUNNER_TOOL_CACHE; do
for key in CI GITHUB_ACTIONS GITHUB_WORKSPACE GITHUB_REPOSITORY GITHUB_RUN_ID GITHUB_RUN_NUMBER GITHUB_RUN_ATTEMPT GITHUB_REF GITHUB_REF_NAME GITHUB_SHA GITHUB_EVENT_NAME GITHUB_ACTOR RUNNER_OS RUNNER_ARCH RUNNER_TEMP RUNNER_TOOL_CACHE XDG_CACHE_HOME COREPACK_HOME NODE_BIN PNPM_HOME PNPM_CONFIG_CHILD_CONCURRENCY PNPM_CONFIG_MODULES_DIR PNPM_CONFIG_NETWORK_CONCURRENCY PNPM_CONFIG_STORE_DIR PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN PNPM_CONFIG_VIRTUAL_STORE_DIR PATH; do
write_export "$key"
done
} > "${env_file}.tmp"
mv "${env_file}.tmp" "$env_file"
{
echo "# Docker containers visible from the hydrated runner"
echo "It does not sign, notarize, or upload macOS assets."
echo
echo "Next step:"
echo "- Run \`openclaw/releases-private/.github/workflows/openclaw-macos-validate.yml\` with tag \`${RELEASE_TAG}\` and wait for the private mac validation lane to pass."
echo "- Run \`openclaw/releases-private/.github/workflows/openclaw-macos-publish.yml\` with tag \`${RELEASE_TAG}\` and \`preflight_only=true\` for the full private mac preflight."
echo "- For the real publish path, run the same private mac publish workflow from \`main\` with the successful private preflight \`preflight_run_id\` so it promotes the prepared artifacts instead of rebuilding them."
echo "- For stable releases, the private publish workflow also publishes the signed \`appcast.xml\` to public \`main\`, or opens an appcast PR if direct push is blocked."
echo "- Run \`openclaw/releases/.github/workflows/openclaw-macos-validate.yml\` with tag \`${RELEASE_TAG}\` and wait for the macOS validation lane to pass."
echo "- Run \`openclaw/releases/.github/workflows/openclaw-macos-publish.yml\` with tag \`${RELEASE_TAG}\` and \`preflight_only=true\` for the full macOS preflight."
echo "- For the real publish path, run the same macOS publish workflow from \`main\` with the successful preflight \`preflight_run_id\` so it promotes the prepared artifacts instead of rebuilding them."
echo "- For stable releases, the publish workflow also publishes the signed \`appcast.xml\` to public \`main\`, or opens an appcast PR if direct push is blocked."
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.