Set TMPDIR=/tmp inside the package Telegram Docker runner so runtime scratch files are written to a writable container path.
Proof:
- pnpm test test/scripts/npm-telegram-live.test.ts
- git diff --check
Compaction summarization consumes the model stream via result() only (no
iteration), so it never emitted model.call diagnostic spans. Observe the
stream's result() in the diagnostic wrapper and wire the wrapper into the
direct compaction path so these LLM calls are traced (request/response
content, byte accounting, traceparent).
Decouple underlying-iterator cleanup from terminal-event dedup. The agent
loop awaits result() on the terminal event then abandons the iterator, so
once result() also emits the terminal event, gating safeReturnIterator on
terminalEventEmitted skipped provider cleanup (idle-timeout abort listeners
on the long-lived run signal, SSE readers). Track iterator settlement
separately so return() cleanup always runs; emit dedup stays on
terminalEventEmitted.
Parent compaction model-call spans to the active run/harness trace rather
than a phantom child trace that emits no span of its own.
Clarify that `openclaw mcp list`, `show`, `set`, and `unset` manage the OpenClaw `mcp.servers` registry and do not include the separate mcporter registry.
Co-authored-by: Alix-007 <li.long15@xydigit.com>
The generic dmPolicy/allowFrom warning read only the canonical top-level
allowFrom, so channels that keep their wildcard under the legacy dm.allowFrom
alias (e.g. Discord/Slack, mode=topOnly/topOrNested) got a false 'all DMs
dropped' warning even though runtime honors dm.allowFrom. Resolve policy and
allowFrom through the shared resolveChannelDm* helpers with the channel's
dmAllowFromMode (matching runtime and doctor), and skip nestedOnly channels
whose canonical fields live under dm.* and do not match this warning's
top-level paths. Adds a Discord legacy-alias regression test.
Addresses ClawSweeper review finding P1 (false positives on legacy dm.allowFrom).
Replace the hardcoded Mattermost-only open-DM config check with a generic,
plugin-agnostic warning driven by a single shared evaluator
(evaluateDmPolicyAllowFromDependency) reused by the Zod refinements and the
CLI validator. Surface warnings at 'config validate' and on config load.
Remove the Mattermost-specific status-issues module now covered generically;
keep the runtime drop-log diagnostic.
* fix(feishu): fetch quoted content before empty-message guard
Moves the quoted/replied message content fetching before the empty-message
early return so a reply with only @bot mention (no text, no media) is not
dropped when it quotes a message with meaningful content. The guard now also
checks that quoted text is empty before skipping.
Note: because the fetch is now unconditional on parentId after passing the
group admission/mention gate, an empty-text reply that quotes a parent in an
open group (requireMention: false) without mentioning the bot will now be
dispatched, where before it was dropped. This is the intended behavior for
open groups — any non-empty turn (including one where context comes from a
quote) should reach the agent. For requireMention:true groups, unmentioned
messages still exit at the mention gate before the fetch, so no over-fetch
occurs.
Adds group-based regression tests for the #90177 scenario:
- Positive: mention-only reply in requireMention:true group with quoted
parent — dispatches with [Replying to: "..."] in the body.
- Negative: empty reply with no bot mention in requireMention:true group —
getMessageFeishu is never called and nothing is dispatched.
* fix(feishu): fetch quoted content before empty-message guard (#90192) (thanks @bladin)
---------
Co-authored-by: 黑承亮0668000844 <bladin@users.noreply.github.com>
Co-authored-by: sliverp <870080352@qq.com>
* fix(cron): reject invalid absolute timestamps
* fix(cron): preserve ISO end of day
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(reasoning-tags): accept MiniMax mm: prefix in silent-detection and stream gates
PR #93767 added MiniMax `mm:`-namespaced reasoning-tag support across the
shared sanitizer and Telegram lane coordinator, but two production reasoning-tag
recognizers were missed and still only matched the `antml:` namespace:
- src/auto-reply/tokens.ts: `taggedReasoningPrefixRe` / `openReasoningPrefixRe`
drive `stripLeadingReasoningBlocks` and `isSilentReplyPayloadText`, which 14+
call sites use to detect NO_REPLY silent payloads. A `<mm:think>…</mm:think>NO_REPLY`
reply was not recognized as silent, leaking the wrapper into delivery.
- src/agents/embedded-agent-subscribe.handlers.messages.ts: `REASONING_TAG_RE`
gates `shouldRecomputeFullStream`. A `<mm:think>` streaming chunk failed the
test, so the visible stream was not recomputed and the hidden reasoning leaked.
Add the `mm:` alternative alongside `antml:` in all three regexes, matching the
exact `(?:antml:|mm:)?` form used by #93767. Identification-only change, no other
regex logic touched.
* test(agents): cover MiniMax reasoning regressions
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(browser): use openTab return value to prevent wsUrl race in ensureTabAvailable
When ensureTabAvailable opens a new tab on empty list, the return value
from openTab was discarded. A subsequent listTabs() call may return tabs
without webSocketDebuggerUrl populated yet, causing the wsUrl filter to
eliminate the newly opened tab and throw BrowserTabNotFoundError.
Fix: capture openTab's return value and merge it into candidates if the
wsUrl filter excluded it. openTab's internal discovery loop already
resolves wsUrl, so the returned tab is always valid.
* fix(browser): harden tab selection discovery
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(feishu): paginate wiki node and space listing (fixes#37626)
client.wiki.spaceNode.list / wiki.space.list return at most one page (max
50 items); the tool ignored has_more/page_token and silently dropped every
node past the first page. Drain both endpoints via a bounded shared helper
that loops on has_more with a 100-page safety cap.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(feishu): expose wiki pagination cursors
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
The previous `read_text(encoding="utf-8")` call left the UTF-8 byte
order mark (EF BB BF, three bytes) in the content string if the file
was saved by a tool that emits a BOM. The first line check
(`lines[0].strip() != "---"`) then saw "\ufeff---" and rejected the
file as "Invalid frontmatter format", even though the document was
otherwise valid frontmatter.
Co-authored-by: Zo Bot <github-automation@zo.computer>
When more than maxMissedJobsPerRestart cron jobs are overdue after gateway
downtime, runMissedJobs defers the overflow jobs to a near-future staggered
catch-up slot. start()'s second maintenance pass then recomputed each overflow
cron deferral to its natural schedule slot, because it ran future-slot repair
with the default-enabled flag. For a daily 0 9 * * * job the now+stagger
catch-up was clobbered to the next 09:00, dropping the missed run for a full
period.
Scope the exemption instead of disabling repair wholesale: runMissedJobs now
returns the ids it deferred this startup, recomputeNextRunsForMaintenance gains
skipFutureRepairJobIds to exempt exactly those ids, and start() threads them
into its pass. Overflow catch-up deferrals survive until their staggered tick
while ordinary stale-future cron slots are still repaired on startup.
* fix(status): show 0/1.0m instead of ?/1.0m on a fresh session
On a brand-new /new session the persisted totalTokens is absent
(undefined), so /status rendered the context numerator as ? via
formatTokens(null, ...). A fresh session with no usage is a known
zero, not an unknown total, so normalize undefined-but-not-stale
totals to 0 before formatting while leaving the intentional
totalTokensFresh === false stale guard (which must keep ?) intact.
Fixes#93771
* fix(status): persist fresh-session zero usage
* fix(status): identify fresh empty sessions
* fix(status): persist fresh empty session usage
* fix(status): preserve fork and compaction token state
* fix(status): preserve queued compaction token state
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(google): keep parallel Gemini tool responses in the turn after the model
On Gemini < 3 vision models, a parallel tool-call turn whose non-last result
returns an image split function responses across user turns. The merge heuristic
only inspected contents[last], so the separate "Tool result image:" turn landed
between two parallel responses and stranded the second one in a fresh turn. The
turn right after the model then carried fewer functionResponse parts than the
model issued functionCall parts, so Gemini returned 400 INVALID_ARGUMENT. Because
the malformed turn is persisted, every later turn re-400s and the session sticks.
Replace the contents[last] heuristic with a run-scoped accumulator: all responses
for one model turn merge into the single user turn after it, and Gemini < 3 image
turns defer to the end of the tool-result run so they trail that response turn.
Covers both google.ts and google-vertex.ts, which share this convertMessages.
* fix(google): align provider transport tool result turns
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(memory): await search-sync before returning results to prevent stale index
When the gateway process has been running for a while, memory_search
returns stale results because startAsyncSearchSync fires off the index
sync as a background task (void ... .catch()) without waiting for it
to complete. Search results are then read from the old index state.
Change startAsyncSearchSync from sync/fire-and-forget to async/await
so that the index is synced before search results are returned. This
ensures memory_search reflects the current filesystem state, matching
the behavior of the CLI command which creates
a fresh manager each time.
Fixes#52115
* test(memory): prove search waits for dirty sync
* test(memory): align search with synchronous sync
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
Treat refreshable manifest catalog rows as non-authoritative and load the owning plugin for runtime/cache-backed discovery. Adds focused regression coverage for entries-only and full discovery paths.
* fix(feishu): recover CJK filenames from JSON file_name field
Apply recoverUtf8FileNameFromLatin1Header to JSON-derived filenames in
extractFeishuDownloadMetadata, matching the behavior already present for
Content-Disposition headers in decodeDispositionFileName.
Fixes#81103
* fix(feishu): recover inbound CJK filenames
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(reasoning-tags): strip MiniMax `mm:` namespaced reasoning tags
MiniMax M3 (e.g. via Fireworks) emits its chain-of-thought inline in the
content stream wrapped in `<mm:think>…</mm:think>` rather than in a separate
`reasoning_content` field. The reasoning-tag stripper only recognized the
`antml:` namespace, so `mm:`-namespaced tags slipped through QUICK_TAG_RE and
leaked the model's hidden reasoning into visible chat output.
Accept the `mm:` prefix alongside `antml:` in the shared sanitizer
(reasoning-tags.ts) and in the Telegram reasoning-lane coordinator's tag regex
and prefix list. Adds unit tests covering mm: think/thinking/thought blocks,
truncated-open orphan close recovery, and code-fence preservation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(reasoning): handle MiniMax tags in streams
---------
Co-authored-by: DrHack1 <DrHack1@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* feat(inbound-meta): expose message_type in trusted inbound metadata (fixes#50482)
Add resolveInboundMessageType() that extracts the media type prefix
(e.g. 'audio' from 'audio/ogg') from MediaType or MediaTypes fields.
Expose it as message_type in the inbound metadata JSON so agents can
distinguish voice messages from typed text for turn-completion heuristics.
* fix(inbound-meta): preserve per-turn source modality
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* feat(memory): apply outputDimensionality truncation to local GGUF embeddings
The outputDimensionality config field was passed through to the local
embedding provider but never applied. Local GGUF models (e.g.
Qwen3-Embedding-0.6B) always returned their full dimension vector.
Apply slice(0, N) after normalization so MRL-capable models can benefit
from dimension truncation — matching the behavior already supported by
Gemini embedding-2 and OpenAI providers.
Fixes#58765
* fix(memory): preserve local embedding dimensions through worker
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
The isToolDocBlockStart function checked normalized === normalized.toUpperCase()
but normalized is already uppercased from line 24, making the condition always true.
This caused mixed-case lines ending with ':' to be incorrectly detected as doc block
starts, truncating tool descriptions unnecessarily.
Compare the original line instead to correctly detect all-uppercase headings.
Co-authored-by: Gautam Kumar <gautamkumarofficial@users.noreply.github.com>
* fix(usage): reject invalid explicit dates in usage RPC date parsing
usage.cost and sessions.usage accepted shape-valid but impossible dates such as 2026-02-30: parseDateParts validated only the YYYY-MM-DD regex, so Date.* silently rolled them over (2026-02-30 -> 2026-03-02) and the RPC returned cost/usage for the wrong day. Out-of-range parts now fail a UTC round-trip check, and an explicitly provided unparseable date (bad format or impossible calendar date) returns INVALID_REQUEST instead of silently falling back to the default range. Absent/valid dates are unchanged.
[AI-assisted]
* fix(usage): reject non-string explicit dates
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
Duplicate user-message detection ran over the full branch, so when a prompt
was re-sent within the 60s window its earlier copy in the summarized prefix
and the later copy in the kept tail were both removed: the summarized copy via
summarizedBranchIds and the kept copy as a duplicate. With
truncateAfterCompaction enabled the prompt then vanished from the successor
transcript entirely. Restrict dedup to the kept region so the first surviving
copy is preserved.
* fix: pin plugin workspace dir for sessions.list to avoid O(rows) memo busting
sessions.list was O(rows) slow under concurrent agent/cron load because
each row read a process-global active plugin-registry workspace dir
that was mutated by other turns between rows. The per-row memo key
changed every time, so loadPluginMetadataSnapshot scanned fresh
(~100ms per row).
Fix:
1. Add AsyncLocalStorage-based workspace dir pinning to
runtime-workspace-state.ts — withPinnedActivePluginRegistryWorkspaceDir()
snapshots the current workspace dir for the duration of a callback.
2. Wrap listSessionsFromStoreAsync body in the pin so all per-row
metadata lookups use a stable memo key.
Fixes#90814
* test(plugins): cover request-scoped workspace pins
* fix(plugins): pin canonical runtime workspace reads
* fix(plugins): preserve workspace pins across reloads
---------
Co-authored-by: lsr911 <lsr911@github.com>
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(ollama): preserve configured API during discovery
* fix(ollama): keep compatible discovery base URL
* fix(ollama): route compatible APIs through configured transport
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix: scope assistant avatar override to agent ID
The local assistant avatar override was stored globally in
localStorage without an agentId, causing the same avatar to
apply to all agents. Setting an avatar for agent A would
overwrite the avatar for agent B.
Fix: include agentId when saving the local avatar override,
and filter by agentId when loading. An override saved for one
agent no longer bleeds into other agents.
Fixes#90890
* fix(ui): persist assistant avatars per agent
* fix(ui): satisfy scoped avatar checks
---------
Co-authored-by: lsr911 <lsr911@github.com>
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
The 'can preserve asynchronous provider model discovery' test was
flaky because resolveModelAsyncMock in beforeEach delegates to
resolveModelMock. When useAsyncModelResolution=true, the test
asserted resolveModelMock was not called, but the delegation
caused it to be called, failing CI on two lanes.
Fix: use a standalone vi.fn() for the async resolver in this
test, and explicitly reset resolveModelMock before the assertion
to guard against mock state leakage from prior tests.
Fixes#92117
Co-authored-by: lsr911 <lsr911@github.com>
MiniMax TTS API returns HTTP 200 even on quota/billing errors, with the
error encoded in base_resp.status_code. Without this check, placeholder
audio returned alongside the error is silently accepted, preventing the
TTS dispatcher from falling back to a configured secondary provider.
This follows the same pattern used by all other MiniMax providers:
- image-generation-provider.ts
- video-generation-provider.ts
- music-generation-provider.ts
- minimax-web-search-provider.runtime.ts
Fixes#76904
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(whatsapp): extract GIF metadata and distinguish gifPlayback in media placeholders (fixes#49099)
- Add escapeAttr() helper to sanitize quotes and angle brackets in XML attribute values
- Add extractExternalAdReplyMetadata() to extract title, sourceUrl, body from contextInfo.externalAdReply
- Distinguish GIFs from videos using videoMessage.gifPlayback flag (media:gif vs media:video)
- Enrich image and video placeholders with externalAdReply metadata when available
- Add 5 test cases covering GIF detection, metadata extraction, attribute escaping, and empty fields
* fix(whatsapp): keep GIF metadata in untrusted context
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
QQBot is the only channel that root-sandboxes outbound local files. Its three
gate sites (resolveOutboundMediaPath, the voice send re-check, and
structured-payload validation) only trusted the QQ Bot media storage roots, so
framework-generated scratch media written under OpenClaw's hardened temp root
(e.g. cron auto-TTS voice files from speech-core) was rejected. The send then
returned a no-identity error, the message was silently lost, yet cron still
recorded it as delivered.
Add one shared resolver (resolveTrustedOutboundMediaPath) that also trusts the
preferred OpenClaw temp root — already a sanctioned media root in core
(buildMediaLocalRoots) — and route all three gates through it so the trust set
agrees everywhere. Fixes#92816.
Co-authored-by: zengwen <zeng_wen@foxmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
When an assistant message's `content` is a raw string at runtime (JSONL
transcript replay passes it through even though the type declares an array),
the OpenAI-compatible completions path crashes:
- `transformMessages` called `assistantMsg.content.flatMap(...)` ->
`TypeError: ... .flatMap is not a function` (first crash, always hit).
- Two `hasToolHistory` helpers (`openai-transport-stream.ts` and
`openai-completions.ts`) called `content.some(...)` -> `TypeError: ...
.some is not a function` (siblings, surface once the flatMap crash is fixed).
Normalize a string assistant content to an equivalent single text block
before transforming (matching the string->text handling already used in
anthropic-payload-policy.ts), and `Array.isArray`-guard both `hasToolHistory`
helpers so a string assistant simply does not count toward tool history.
Verified end-to-end through the real `buildOpenAICompletionsParams` and
`streamOpenAICompletions` entry points: before the fix a string-content
assistant followed by a toolResult throws TypeError; after the fix params are
produced correctly (string preserved as text, tool history detected). Normal
array content is unaffected.
* fix(respawn): rewrite pnpm versioned entry paths to stable wrapper
During self-update the pnpm versioned directory (node_modules/.pnpm/openclaw@<ver>/)
may be removed. If process.argv contains the versioned path, the respawned child
fails to start because the entrypoint no longer exists.
Detect pnpm versioned realpaths in spawnDetachedGatewayProcess and rewrite them
to the stable node_modules/<pkg>/openclaw.mjs wrapper before spawning.
Fixes#52313
* fix(respawn): scope pnpm entry rewrite to openclaw
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(wizard): preserve existing default model during setup auth choice
Without preserveExistingDefaultModel: true, the setup wizard
overwrite the user's configured default model when a new provider
auth is selected. This causes existing heartbeat turns to silently
consume paid API quota (e.g. Google Gemini) instead of the user's
original model.
The configure.gateway-auth.ts path already passes this flag; the
setup wizard path was missing it.
Fixes#64129
* fix(wizard): add type assertion for preserveExistingDefaultModel test
Summary:
- This PR changes the docs i18n Codex command-output preview to keep a short head plus retained tail, and adds Go unit coverage for stdout and stderr tails.
- PR surface: Other +20. Total +20 across 2 files.
- Reproducibility: yes. Source inspection of current main and `v2026.6.6` shows long output is truncated to the prefix only, and the PR's focused tests model the stdout/stderr tail cases that lose final API details.
Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.
Validation:
- ClawSweeper review passed for head b510b598c6.
- Required merge gates passed before the squash merge.
Prepared head SHA: b510b598c6
Review: https://github.com/openclaw/openclaw/pull/93687#issuecomment-4720840859
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: Mason Huang <8814856+hxy91819@users.noreply.github.com>
Approved-by: hxy91819
* fix(agents): handle string assistant content in getLastAssistantText
PR #93456 added an `if (!Array.isArray(message.content)) return false` guard
to hasAssistantToolCallArguments, acknowledging that a persisted/legacy
assistant message can carry a string `content` at runtime even though the
type is declared as an array. buildSessionContext pushes such entries through
unchanged, so the string can reach agent.state.messages.
getLastAssistantText() still assumed an array: iterating a string `content`
yields individual characters, none of which has `type === "text"`, so the
assistant's text was silently dropped and the function returned undefined.
Mirror extractTextContent(): when `content` is a string, treat it as the text
itself; otherwise iterate the content blocks as before. The aborted/empty
check is left untouched because `.length === 0` is already correct for both an
empty array and an empty string.
* fix(agents): safely read persisted assistant text
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
When the block reply pipeline streamed partial content, buildReplyPayloads()
unconditionally dropped all text-only final payloads. This suppressed the
complete final reply when the pipeline only streamed a partial block and
never sent the exact final text.
The fix checks hasSentPayload() for text-only payloads too, preserving
unsent finals instead of dropping them unconditionally.
The async Clipboard API is only available in secure contexts (HTTPS or
localhost). On plain-HTTP deployments navigator.clipboard is undefined, so the
code block copy button threw synchronously and silently failed. Add a shared
copyToClipboard helper that guards the secure-context path and falls back to the
legacy execCommand copy, reuse it for the code block button and the copy-as-
markdown affordance, and cover it with a unit test plus a real-browser e2e that
simulates the non-secure context.
Fixes#93628
Co-authored-by: Pick-cat <266665499+Pick-cat@users.noreply.github.com>
Summary:
- The PR changes `/status` context-window selection to ignore stale runtime snapshots after manual model switches while preserving fallback/runtime-alias context windows.
- PR surface: Source +6, Tests +128. Total +134 across 2 files.
- Reproducibility: yes. source-reproducible: current main trusts explicit runtime context before checking fall ... fer. I did not run a local failing repro, but the PR fixture models the stale prior-runtime state directly.
Automerge notes:
- PR branch already contained follow-up commit before automerge: test(status): make context fixtures type-correct
Validation:
- ClawSweeper review passed for head f14fda4279.
- Required merge gates passed before the squash merge.
Prepared head SHA: f14fda4279
Review: https://github.com/openclaw/openclaw/pull/93306#issuecomment-4708596208
Co-authored-by: Mason Huang <masonxhuang@tencent.com>
Approved-by: hxy91819
Preserve concurrent prompt-time transcript updates across stale session managers, side appends, transcript navigation, nested owned writes, and doctor repair.
Fixes#93193.
Thanks @snowzlm for the report and original fix.
Co-authored-by: snowzlm <snowzlm@noreply.codeberg.org>
* fix(reply): preserve pending thread evidence when reconciling partial send results
extractMessagingToolSendResult re-derived threadId/threadImplicit/threadSuppressed
straight from the provider result. Mattermost is the only production provider that
implements extractToolSendResult, and for an implicitly threaded send it reports only
{ to }, so the reconciler overwrote the correct pending thread evidence with undefined.
That defeated same-thread reply suppression in reply-payloads dedupe and delivered the
agent's final reply twice in the thread, on both the native and Codex harnesses.
A partial provider result now keeps the pending thread evidence it does not speak to: a
provider-reported threadId still wins (and clears the implicit flag), but an absent one
no longer erases the pending threadId/threadImplicit/threadSuppressed.
Regression introduced by c67dc59b02 (#90943).
* test(reply): use a core-local stub provider instead of the bundled Mattermost import
The reconcile-thread regression test deep-imported extensions/mattermost from a
core test, which trips the core/extension package boundary (boundary-invariants
"keeps core tests off bundled extension deep imports", extension-test-boundary,
and check-tsgo-core-boundary pulling extensions/mattermost transitively).
Replace it with a core-local channel test plugin that reproduces the same
contract: an implicit-threading extractToolSend, a partial extractToolSendResult
that reports only { to, threadId? }, and no targetsMatchForReplySuppression
matcher. The test now exercises the generic reconciler contract with no
extension dependency. It still fails on pristine main and passes with the fix.
* fix(reply): reconcile thread evidence atomically
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
Adds Codex as a selectable hosted web-search provider, routes native Codex search safely across model overrides, and isolates bounded hosted-search workers from configured tools.\n\nVerification: focused post-merge regression suite passed 202/202 tests on exact head 23824af49a.
* fix(ollama): repair retired cloud provider endpoint
Route configured Ollama Cloud provider ids through plugin doctor compatibility migrations so doctor --fix can rewrite the retired ai.ollama.com endpoint before runtime reads persisted config.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(doctor): align provider fixture with typed config
Ensure the doctor registry provider-scoped migration test uses a fully typed provider fixture so the test type-check shard validates the intended behavior.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(ollama): align doctor fixture with typed config
Use fully typed provider and model fixtures in the Ollama doctor contract tests so the extension test type-check shard validates the migration behavior.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(ollama): preserve custom cloud provider base url
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(ollama): avoid logging retired endpoint secrets
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(ollama): repair retired cloud provider endpoint
Route configured Ollama Cloud provider ids through plugin doctor compatibility migrations so doctor --fix can rewrite the retired ai.ollama.com endpoint before runtime reads persisted config.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(doctor): align provider fixture with typed config
Ensure the doctor registry provider-scoped migration test uses a fully typed provider fixture so the test type-check shard validates the intended behavior.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(ollama): align doctor fixture with typed config
Use fully typed provider and model fixtures in the Ollama doctor contract tests so the extension test type-check shard validates the migration behavior.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(ollama): preserve custom cloud provider base url
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(ollama): avoid logging retired endpoint secrets
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
A literal null `workspace` field in an agent entry failed schema validation at
startup, producing a crash loop that `openclaw doctor --fix` could not recover
from because the compatibility pipeline never normalized the malformed field.
Add a narrow doctor migration that removes null `workspace` values from
`agents.list` entries and relies on the existing fallback path (defaults or
stateDir-derived workspace) at runtime.
Fixes#77718.
PR #88496 routed /config show and /config set chat output through the
shared schema-aware redaction path, but the sibling /debug commands in
the same handler were left untouched. /debug show JSON-stringified the
full runtime override tree verbatim and /debug set echoed the raw value,
so a secret-shaped override (e.g. gateway.auth.token, channels.*.botToken)
set via /debug set was rendered in plaintext to chat-visible output.
Apply redactConfigObject(overrides, schema.uiHints) to the override tree
before rendering /debug show, and reuse formatConfigSetValueLabel for the
/debug set acknowledgement, matching the existing /config redaction
contract. Non-secret fields and env placeholders are preserved.
* fix(ui): restore provider usage pill in desktop chat composer (#93041)
Composer refactors dropped the quota pill from renderChatControls and left the
desktop renderChatSessionSelect wrapper orphaned, so it rendered nowhere on
desktop. Re-attach the existing pill, add modelAuthStatusResult to the guarded
controls dep list so it updates when usage windows arrive async, and hide it on
the 2-col mobile composer grid.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(ui): add real-browser e2e proof for chat quota pill (#93041)
Playwright/Chromium test that mocks models.authStatus usage windows and asserts
the restored provider usage pill renders in the desktop chat composer (and is
absent without usage). Skips gracefully when Chromium is unavailable.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(ui): write quota-pill e2e screenshots to ignored .artifacts path (#93041)
Match the control-ui-e2e convention (.artifacts/control-ui-e2e/...) so the proof
run does not leave untracked root-level files. Addresses ClawSweeper review.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix(discord): apply tool status emojis immediately to avoid override by thinking reactions
Tool emoji reactions (🛠️, 🌐, 🔎, etc.) during Discord tool/skill execution
were not appearing because setTool() used a 700ms debounce shared with
setThinking(). Rapid onReasoningStream calls from overlapping reasoning
would repeatedly overwrite the pending tool emoji with 🧠, so the tool
emoji never reached Discord.
Fix by making setTool() apply emojis immediately (skip debounce). Tool
transitions are user-facing state changes that should be visible without
delay, and the terminal done/error transitions already flush any pending
state.
Fixes#92715.
* fix(discord): forward quiet tool lifecycle status
* fix(slack): preserve tool status reactions
* test(channels): type quiet tool lifecycle options
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(cron): clear delivery routing fields from cron edit
cron edit could set delivery channel/to/thread-id/account but could not unset them: an empty value (e.g. --to "") builds delivery.X = undefined, which is omitted from the JSON-RPC patch, so mergeCronDelivery never sees the key and the field is silently kept. The gateway RPC already accepts an explicit null to clear each field (CronDeliveryPatchSchema + mergeCronDelivery via normalizeOptionalString); the CLI just never sent it.
Add --clear-channel/--clear-to/--clear-thread-id/--clear-account, each emitting null (mirroring the existing --clear-model), with mutual-exclusion guards against the matching set flag and against --webhook.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(cron): preserve delivery defaults when clearing routes
* fix(cron): validate cleared prefixed routes
---------
Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* [AI] fix(feishu): guard against missing inbound in channelRuntime fallback
When channelRuntime from gateway context is truthy but lacks the inbound
property, the ?? operator still selects it over getFeishuRuntime().channel,
causing TypeError at core.channel.inbound.run().
The ChannelGatewayContext types channelRuntime as ChannelRuntimeSurface
(only guarantees runtimeContexts), but channel.ts casts it to
PluginRuntimeChannel via type assertion. If a partial runtime object
without inbound is provided, the type lie becomes a runtime crash.
Fix: check channelRuntime?.inbound before using it; fall back to
getFeishuRuntime().channel when inbound is absent.
Related to #93453
* [AI] test(feishu): add regression for partial channelRuntime lacking inbound
When channelRuntime has runtimeContexts but no inbound, the guard in
bot.ts should fall back to getFeishuRuntime().channel. Add a test that
passes a partial channelRuntime and verifies dispatch does not crash.
Refs #93453
Carry prepared manifest model-id normalization records through the runtime bridge so hot callers reuse existing metadata instead of consulting the snapshot fallback.
The final change preserves the existing no-prepared-record behavior, adds focused forwarding coverage, and removes the one-off proof script before landing.
Thanks @zeroaltitude.
Verification:
- 224 focused tests
- full CI run 27594070734
- real behavior proof run 27594081022
- final whole-branch autoreview clean
Co-authored-by: zeroaltitude <zeroaltitude@gmail.com>
Suppress each raw commentary echo paired with a typed Codex item completion by protocol order, while preserving later raw-only notes and contributor-rewritten completion text.
Fixes#93296.
Thanks @Marvinthebored.
Verification:
- 95 focused projector tests
- full CI run 27593515603
- real behavior proof run 27593522821
- local and whole-branch autoreview clean
Co-authored-by: Peter Lindsey <peter@lindsey.jp>
Pinned session-extension registries now remain the owner even when empty, preventing later active registry churn from leaking agent-owned extensions into the gateway surface.
Inbound PDF/document text already flows to agents through the canonical
media-understanding pipeline (applyMediaUnderstanding -> extractFileBlocks),
but it inherited the OpenResponses input_file limits (5MB / 4 pages), so large
managed PDFs from channels/Control UI were skipped and locked-down agents saw
only an attachment marker.
- Size inbound file extraction from agents.defaults.mediaMaxMb (default 20MB,
cap 25MB) and pdfMaxPages (default 20, cap 150) via a new
resolveFileExtractionLimits; explicit gateway responses.files config still
wins per-field. (#90096)
- chat.send: let oversized (>5MB) managed inbound PDFs pass through sandbox
staging with their managed media path instead of a 4xx, so host-side
extraction reaches sandboxed agents without copying the file into every
sandbox; non-PDF oversize files are still rejected. (#90097)
Reuses the existing extraction/injection path; no parallel module or extra
prompt-injection sites.
When a child openclaw process is spawned via a backgrounded subshell that
exits before the new process reaches the stale-pid sweep, the new process
is reparented to the supervisor (PID 1 / launchd) and the ancestor walk
in getSelfAndAncestorPidsSync can no longer see the running gateway. The
running gateway then shows up on lsof as an unrelated sibling on the
port and gets SIGKILL'd by cleanStaleGatewayProcessesSync, recreating
the issue #68451 supervisor restart loop across a reparent boundary.
Real-world trigger: a user ~/.zshrc auto-start block
if ! pgrep -x openclaw-gateway >/dev/null; then
(openclaw gateway >/dev/null 2>&1 &)
fi
combined with codex per-turn `zsh -c "set -e; . shell_snapshot"` invocations
caused every chat turn on rh-bot to SIGKILL its launchd-managed gateway,
producing HTTP 000 errors and ~33 kill events captured by a forensic
launchd unified-log tracker before the zshrc was patched.
Fix: gateway-cli captures OPENCLAW_GATEWAY_SERVICE_PID from inherited env
BEFORE overwriting it with process.pid, then threads the captured PID
through cleanStaleGatewayProcessesSync into getSelfAndAncestorPidsSync's
exclusion set. The protection is opt-in per call site so existing
maintainer paths (openclaw update / openclaw doctor restart helpers) keep
their ability to terminate a running gateway intentionally.
The inherited-PID parser is strict positive-integer only: a malformed
inherited env value (`"123abc"`, `"123.4"`, `"0x7b"`, etc.) is rejected
rather than silently protecting PID 123 from cleanup and leaving the
stale listener alive. New focused unit tests cover the parser
contract.
Existing regression tests cover the reparent suicide-kill scenario and
the defensive ignore-non-positive-PID contract on the cleanup side.
Preserve rollback journaling for NFS and SMB-backed stores, refuse SSHFS after symlink-aware mount classification, and close Workboard database handles when filesystem policy rejects initialization.
Use transactionally consistent VACUUM INTO snapshots for every state-root SQLite database and exclude original journal sidecars so verified backups cannot restore torn plugin or memory state.
* test(qa): add smoke ci primary coverage evidence
* test(qa): remove overstated primary coverage claims
* test(qa): make release profile include smoke ci
* test(qa): trim taxonomy formatting churn
* test(qa): avoid hardcoded profile names in coverage test
* test(qa): make release profile cover taxonomy
* test(qa): type profile fixture all category flag
* test(qa): include channel delivery in smoke ci profile
Archive the canonical legacy database before SQLite sidecars, then detect and finish pending sidecar cleanup on retry without reopening the migrated database.
Allow the auth-profile read-only SQLite bootstrap path through the Kysely guardrail. The runtime already wraps reads with Kysely; the raw DatabaseSync boundary is the short-lived read-only bootstrap.
Co-authored-by: Alex Knight <15041791+amknight@users.noreply.github.com>
Add exec approvals artifact evidence to Policy.
- add the execApprovals policy namespace and check IDs for required artifact presence, default/per-agent security posture, autoAllowSkills, and allowlist drift
- read the active exec-approvals.json artifact only when execApprovals policy rules are configured, honoring OPENCLAW_STATE_DIR before the default ~/.openclaw path
- emit redacted posture evidence and stable oc:// references without socket tokens, command text, resolved paths, timestamps, or approval-session details
- document the public policy surface and add focused scanner, doctor, conformance, and CLI coverage
Validation:
- GitHub Actions for head b82eefe492 are green, including Real behavior proof.
- ClawSweeper re-review completed for the same head with proof: sufficient and status: ready for maintainer look.
- Maintainer artifact-boundary acceptance is recorded in the PR discussion and body.
Co-authored-by: Gio Della-Libera <235387111+giodl73-repo@users.noreply.github.com>
* fix(tui): show activity indicator for system-injected runs
System-injected runs (bridge-notify, webhook, cron) never go through the
TUI submit path, so no active/pending run id exists when their lifecycle
"start" event arrives. handleAgentEvent dropped events for untracked runs,
leaving the status bar idle until the response landed.
Adopt an untracked lifecycle "start" for the current session (lifecycle
events always carry sessionKey) so the activity indicator shows work is
happening, mirroring how chat deltas adopt runs in handleChatEvent. Local
side-question (btw) runs never claim the active slot.
Closes#51825
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(tui): preserve concurrent injected run activity
---------
Co-authored-by: zengwen <zeng_wen@foxmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(line): cap carousel column text at 60 chars with title or image
LINE limits a carousel column's text to 60 characters when the column has
a title or thumbnail image, and 120 characters otherwise. createCarouselColumn
always truncated to 120, so a column with a title/image and 61-120 char text
exceeded the limit and made LINE reject the entire carousel reply (HTTP 400).
Apply the conditional limit (mirroring the buttons template) and drop the now
redundant slice in createProductCarousel.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(line): apply conditional text limits across templates
* fix(line): truncate template text by code point
* fix(line): preserve grapheme clusters when truncating
* fix(line): apply compact limit for default actions
* fix(line): follow title and thumbnail text limits
* fix(line): truncate template text within UTF-16 limits
* fix(line): preserve required text within template limits
* fix(line): preserve carousel product prices
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
Restore readable standard Telegram text delivery by default after Bot API 10.1 rich messages rendered as unsupported in current clients. Keep native rich tables and structured messages available through the account-level richMessages opt-in, with account-aware capability advertising and documented structural limits.
Fixes#93263.
* fix(whatsapp): preserve auth on passive terminal stops
* fix(whatsapp): recover stale web auth during relink
* fix(gateway): defer channel stop until qr takeover
Apply the canonical SQLite busy timeout to short-lived read-only auth profile reads so a brief rollback-journal exclusive lock cannot make valid persisted credentials appear missing.
The atomic reindex file ops hardcoded the WAL sidecar pair (-wal/-shm)
when moving, removing, and backing up index files. NFS-backed memory
stores run SQLite under journal_mode=DELETE, which produces a
rollback-journal (-journal) sidecar instead. As a result an index swap
left the previous targets stale -journal next to the freshly published
The inline-code/fence restore step matched the placeholder index with a
greedy `(\d+)`, so a digit in user text immediately after a code span
(e.g. `code`5) was absorbed into the index, resolved to undefined, and
`?? ""` deleted both the code span and the digit. Terminate the
placeholder index with the existing NUL marker so the index boundary is
unambiguous.
Co-authored-by: Dr Rushindra Sinha <5796457+rushindrasinha@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Classify owned silent model calls as long-running until the abort threshold while preserving stalled handling for ownerless stale activity, with diagnostics tests and docs.
Stop media writes from triggering opportunistic pruning and leave retention cleanup to the configured maintenance timer. Preserve explicit cleanup options and cover shallow/root/recursive cleanup behavior.
Track platform-incompatible skills separately from missing requirements, keep doctor --fix from treating them as broken installs, and cover the status output.
Use alias-aware credential compatibility before clearing auth-profile overrides, preventing compatible CLI sessions from flapping auth profiles. Includes regression coverage.
Use the shared suppressed-control-reply detector for cron delivery so NO_REPLY, ANNOUNCE_SKIP, and REPLY_SKIP do not leak to outbound channels, with direct/text delivery coverage.
Keep workboard card titles visible when a column overflows by pinning implicit rows to content height, and add e2e coverage for the overflow case.
Fixes#91717
* fix(telegram): control group history context
* fix(telegram): keep history mode type local
* fix(telegram): respect history mode during forum recovery
Avoid repeated full JSONL parsing and cloning on every embedded-agent turn by keeping a bounded, validated transcript cache and advancing repair incrementally.
The final implementation preserves lock ownership and exact fingerprint validation, publishes only verified writes, handles header rewrites and unterminated JSONL safely, and adds focused regression coverage.
Fixes#83943.
Co-authored-by: Alix-007 <li.long15@xydigit.com>
The fresh-tokens path of runPreflightCompactionIfNeeded fed the prompt-only
entry.totalTokens snapshot straight into the budget threshold check, dropping
the current user prompt estimate and the previous turn's output. The sibling
memory-flush gate and this function's own stale branch already project
base + output + estimate via resolveEffectivePromptTokens, so the preflight
gate under-triggered and let over-budget requests through to overflow-retry.
Project the fresh persisted base the same way: read transcript output when near
the threshold (mirroring the memory-flush gate's buffer) and run the fresh base
through resolveEffectivePromptTokens before the threshold check.
Claude Code built-ins ScheduleWakeup and CronCreate schedule a deferred
re-invocation managed by the persistent CLI runtime. In OpenClaw's
one-shot `claude -p` invocations the process exits at end_turn, so any
wakeup or cron registered during the run has no host to fire into and is
silently lost. Symptom: a CLI session spawns a background sub-agent,
calls ScheduleWakeup to poll for completion, ends the turn, and never
picks up the result — the work finishes unreviewed.
Append `--disallowedTools "ScheduleWakeup,CronCreate"` to both `args`
and `resumeArgs` in the anthropic CLI backend so the model cannot reach
for tools that don't survive the run mode. The right pattern in CLI
sessions is Monitor on the background output file, or a synchronous
sub-agent.
* fix(gateway): pass managed inbound PDFs through when sandbox staging fails
chat.send force-stages offloaded non-image media into the sandbox workspace when
one exists. If that optional staging was unavailable or incomplete,
prestageMediaPathOffloads deleted the media buffers and failed the whole send
with a 5xx — even for already-managed inbound PDFs that are safe to read
host-side. A Control-UI-uploaded PDF could fail to send.
When staging throws or is incomplete, fall back to the absolute managed paths
iff every non-image offloaded ref is a managed-inbound application/pdf (reusing
the existing resolveInboundMediaReference allow-check + the PDF mime type). This
mirrors the existing no-sandbox passthrough: with MediaWorkspaceDir unset the
managed media dir is a default media-understanding local root, so the absolute
path resolves host-side. Gated all-or-nothing so a single non-managed or non-PDF
ref keeps the previous delete + 5xx behavior. Success path and oversized 4xx are
unchanged; managed buffers are not deleted on the fallback.
Fixes#90097
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(gateway): exempt managed PDFs from staging cap
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* docs(windows): fix WSL gateway-autostart recipe for WSL ≥ 2.6.1.0
Replace /bin/true with dbus-launch true to work around the WSL ≥ 2.6.1.0
idle-termination regression (microsoft/WSL #13416): the distro exits 15-20 s
after the last wsl.exe client detaches even with loginctl linger and an active
user service. dbus-launch true keeps a child-of-init process alive (workaround
from microsoft/WSL discussion #9245, validated on WSL 2.7.3.0).
Also replace /ru SYSTEM with /ru "$env:USERNAME". Per-user WSL distros (the
default setup) are not enumerable by the SYSTEM account — the task runs
silently without starting the distro. Running as the installing user account
fixes this; Windows prompts for the password at task creation time.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* docs(windows): add dbus-x11 prerequisite for WSL keepalive
dbus-launch is provided by dbus-x11, which is not installed by default
on fresh Ubuntu WSL distros. Without it the scheduled task hits
command-not-found silently. Add the apt-get install step before the
linger and gateway-install steps so the recipe is self-contained.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Rebase onto current upstream/main (head 4780546c12). Resolves the conflict from upstream's two-line Current time + Reference UTC helper output: appendCronStyleCurrentTimeLine now refreshes/collapses any prior helper-injected block via CURRENT_TIME_LINE_RE instead of returning early on a stale base.includes('Current time:') match. Preserves upstream-added doc comments. 16/16 current-time.test.ts pass; tsgo core clean.
Thread the existing agents.defaults.timeFormat setting through the Control UI
bootstrap config so WebChat/Control UI timestamps render in the configured
12h/24h clock instead of always using the browser locale default. "auto"
keeps the browser default, so existing deployments are unchanged.
Closes#58147
Co-authored-by: zengwen <zeng_wen@foxmail.com>
resolveCronChannelOutputPolicy checked deliveryRequested === false
when there is no channel. Since deliveryRequested is optional
(?: boolean), undefined and missing opts both returned false,
blocking the hasRecoveredToolWarning rescue path for --no-deliver
cron runs whose agent recovered successfully.
Change === false to !== true: when no channel exists, prefer the
agent's final visible text unless delivery was explicitly
requested.
Fixes#90664
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
cron list/show printed "idle" for a job whose status is ok/error/skipped
when only lastRunStatus (the primary field) was set: formatStatus used
`lastStatus ?? "idle"` and omitted lastRunStatus, diverging from computeStatus
(the --json status resolver) whose JSDoc says it mirrors the human output.
Delete the duplicate formatStatus and render via the canonical computeStatus.
Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Guard memory index identity resolution against empty or whitespace provider models by falling back to fts-only, and use fts-only as the fallback source model when an adapter fallback cannot resolve a model.
This prevents empty expectedModel mismatch reasons that can leave memory search dirty while preserving registered adapter default-model resolution.
Refs #90787
Two code-review findings. (1) gateway taskMatchesAgent fell through to a requester/owner/child session-key scan even when the task had an explicit agentId, so a worker subagent task owned by agent:main:main also matched agentId:main; make explicit task.agentId authoritative and keep the session-key fallback only for legacy records without an agentId, with a gateway tasks.list regression. (2) the cross-agent attribution test passed async (root) to the zero-arg withTaskRegistryTempDir helper (TS2345/TS7006); drop the unused parameter and redundant env assignment.
Summary:
- The PR adds artifact, installed skill-file, source URL, and verification-envelope fields to ClawHub skill origin/lock metadata while keeping install telemetry restricted to the older version/registry shape.
- PR surface: Source +144, Tests +139. Total +283 across 2 files.
- Reproducibility: not applicable. as a bug reproduction. Source inspection shows current main lacks the richer `.clawhub` provenance fields, and the PR body provides after-patch live output from a ClawHub install.
Automerge notes:
- PR branch already contained follow-up commit before automerge: Persist ClawHub skill install provenance
Validation:
- ClawSweeper review passed for head 65774f4f4b.
- Required merge gates passed before the squash merge.
Prepared head SHA: 65774f4f4b
Review: https://github.com/openclaw/openclaw/pull/93283#issuecomment-4707787041
Co-authored-by: momothemage <niuzhengnan@163.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: momothemage
cleanupAgedMemoryReindexTempFiles only removed WAL sidecars (-wal/-shm) of orphaned reindex temp DBs. On NFS-backed stores configureMemorySqliteWalMaintenance -> requireRollbackJournalMode forces journal_mode=DELETE, so the reindex temp DB uses a rollback journal; a hard crash leaves an orphaned .tmp-<uuid>-journal that leaked forever (cleanup neither deleted nor even discovered it). Add -journal to both the delete set (memoryIndexFileSuffixes) and the discovery set (reindexTempEntrySuffixes), with regression tests for the temp-plus-journal and stranded-journal cases.
Summary:
- The PR filters stale session/live context-token values when rendering `/status`, threads existing per-agent/default context caps into status rendering, and adds regression tests for status message and summary output.
- PR surface: Source +107, Tests +155. Total +262 across 7 files.
- Reproducibility: yes. Source inspection shows current main forwards stale live and persisted context-token v ... atus`, and the PR comments include live gateway output validating the Kimi/DeepSeek mismatch after the fix.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(status): avoid stale session context windows
Validation:
- ClawSweeper review passed for head 4a8e9299a3.
- Required merge gates passed before the squash merge.
Prepared head SHA: 4a8e9299a3
Review: https://github.com/openclaw/openclaw/pull/93220#issuecomment-4705953238
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>
Summary:
- This PR changes pinned-session `/status` guidance, model-selection docs, and status tests to recommend `/model default` instead of `/model <configured>` or `/reset` for clearing a session model pin.
- PR surface: Source 0, Tests 0, Docs +4. Total +4 across 7 files.
- Reproducibility: yes. from source inspection. Current main and v2026.6.6 emit the old `/reset` hint, while `/model default` clears persisted model overrides and `/reset` intentionally preserves user-selected overrides.
Automerge notes:
- PR branch already contained follow-up commit before automerge: docs: align model clear hint docs
- PR branch already contained follow-up commit before automerge: fix(status): correct pinned model clear hint
Validation:
- ClawSweeper review passed for head 1181624daa.
- Required merge gates passed before the squash merge.
Prepared head SHA: 1181624daa
Review: https://github.com/openclaw/openclaw/pull/93231#issuecomment-4706327717
Co-authored-by: masonxhuang <masonxhuang@tencent.com>
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>
In dispatchReplyFromConfig the user-message success branch ran
throwIfDispatchOperationAborted() *before* clearPendingFinalDeliveryAfterSuccess().
If stuck-session recovery aborted the run in the window between the final reply
shipping and the clear, the message was delivered but pendingFinalDelivery stayed
true forever — the get-reply redelivery short-circuit then silently blocked every
future inbound and the agent "went silent" (#89115).
Reorder so the durable pending-final bookkeeping is cleared first, then honor the
abort afterwards (preserving abort reporting). Also clear the stranded
pendingFinalDeliveryIntentId field — agent-command.ts already clears it but the
success helper did not.
Keep the setup TUI parent stdin paused after its inherited-stdio child exits so Docker and PTY setup parents terminate cleanly. Align pre/post setup terminal cleanup with the cleanup-then-exit contract and add lifecycle regression coverage.
Thanks @fuller-stack-dev.
Recover assistant turns that complete tool work without producing a visible final answer, while preserving intentional silent replies.
Use concrete tool-instance replay safety across embedded, Codex, and Copilot runtimes so unknown, mutating, async-started, and durable recall operations fail closed. Preserve genuine empty Codex final items without promoting commentary or tool-progress echoes.
Supersedes #90872. Thanks @fuller-stack-dev.
Co-authored-by: fuller-stack-dev <263060202+fuller-stack-dev@users.noreply.github.com>
* fix(agents): resolve "current" session alias locally without gateway round-trip
The system prompt tells agents to use sessionKey="current" to refer to
their own session. Previously, resolveSessionReference sent the literal
string "current" to the gateway sessions.resolve action, which rejected
it with INVALID_REQUEST and logged a noisy error line on every tool call.
The wrapper fell back to requesterInternalKey and succeeded, so the tool
worked — but the gateway error was spurious.
Add "current" to the well-known client alias check in
resolveCurrentSessionClientAlias so it is resolved locally to the
requester's session key, matching how TUI/CLI/WebChat client labels are
handled. This eliminates the unnecessary gateway round-trip and the
error log line.
Fixes#78424
* test: update session_status tests for local current-key resolution
* test: update session_status tests for local current-key resolution
* Revert "test: update session_status tests for local current-key resolution"
This reverts commit d9f6c8b5248921c99f43dc222667ffa429b34401.
* Revert "test: update session_status tests for local current-key resolution"
This reverts commit 40bf77d06711833c1beaeedf562b60a765a559d6.
* Revert "fix(agents): resolve "current" session alias locally without gateway round-trip"
This reverts commit d92bc9b91e0840ea5823cd44223c139e434c5ec4.
* fix(agents): preserve literal current session resolution
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(feishu): pass card_msg_content_type to get full card content
When reading Feishu interactive card messages via getMessageFeishu,
the API returns a degraded structure (title + 'upgrade client' prompt)
unless card_msg_content_type=user_card_content is passed in params.
Fixes#78289
* fix(feishu): request full card content for message reads
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
Telegram's rich-markdown renderer treats a lone "\n" as a soft break
(rendered as a space), so streamed tool-progress draft lines joined by a
single newline collapsed onto one line. Pass "\n\n" as the progress-draft
line separator for Telegram; it renders a blank line as a single break, so
each tool/thinking/commentary line gets its own line again. Other channels
keep the single-newline default, so Discord and the rest are unaffected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a required database-first legacy-store guard and regression coverage for legacy runtime state write patterns.
The guard is wired into architecture/preflight/changed checks, narrows the documented guard contract to the implemented filesystem-write scope, and tightens extension migration exemptions to explicit owner APIs. Also includes a small memory-core lint unblocker after current CI flagged an unnecessary non-null assertion.
Verification:
- pnpm check:database-first-legacy-stores
- pnpm lint:scripts
- node scripts/run-vitest.mjs test/scripts/check-database-first-legacy-stores.test.ts -- --reporter=verbose
- node scripts/run-oxlint.mjs extensions/memory-core/src/memory/manager-embedding-ops.ts
- git diff --check
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- GitHub CI green for PR head 34dde2c620Closes#91628.
Summary:
- The PR updates the voice-call plugin to preserve live `speaking`/`listening` calls without `answeredAt`, backfill max-duration enforcement for live/restored call paths, and add regression tests.
- PR surface: Source +90, Tests +223. Total +313 across 9 files.
- Reproducibility: yes. source-level: current main and v2026.6.6 still reap aged non-terminal calls solely bec ... king` or `listening` without setting it. I did not run a live Twilio carrier call in this read-only review.
Automerge notes:
- Ran the ClawSweeper repair loop before final review.
- Included post-review commit in the final squash: fix(voice-call): preserve live Twilio streams in stale reaper
- Included post-review commit in the final squash: fix(clawsweeper): address review for automerge-openclaw-openclaw-9062…
Validation:
- ClawSweeper review passed for head 5fee2ff7a1.
- Required merge gates passed before the squash merge.
Prepared head SHA: 5fee2ff7a1
Review: https://github.com/openclaw/openclaw/pull/90812#issuecomment-4637047870
Co-authored-by: Sahibzada Allahyar <sahibzada@fastino.ai>
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 an abort-signal-specific timeout classifier, switches two embedded attempt abort handlers to it, and adds focused failover tests.
- PR surface: Source +5, Tests +32. Total +37 across 3 files.
- Reproducibility: yes. from source inspection and a focused Node abort-reason check, but not from a live 180- ... ault AbortController abort reason through the broad timeout classifier used by the embedded abort handlers.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(agents): do not misclassify client-disconnect abort as run timeout
Validation:
- ClawSweeper review passed for head 2708b0a37d.
- Required merge gates passed before the squash merge.
Prepared head SHA: 2708b0a37d
Review: https://github.com/openclaw/openclaw/pull/90936#issuecomment-4638919394
Co-authored-by: openperf <16864032@qq.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
* fix(memory): accept local default model path migration
Treat the official local default embedding model's hf URI and downloaded GGUF path identities as equivalent so upgraded local memory indexes do not pause solely on path-format changes.
* fix(memory): satisfy local identity lint
Avoid filtered array tail access in the local model filename helper while preserving the same compatibility behavior.
* fix(memory): preserve local embedding identity aliases
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
Fix gateway-routed one-shot Codex app-server teardown so owned shared clients are retired after run cleanup. Verified with focused tests, Showboat proof, and green PR CI.
Register OpenCode Go's provider-owned static catalog so lifecycle cache warmup supplies the correct context window to memory flush and compaction without persisting catalog rows in user config.
Fixes#92912.
Co-authored-by: kumaxs <45620232+kumaxs@users.noreply.github.com>
Avoid assuming every runtime model exposes a string `baseUrl` before provider attribution checks. Preserve OpenRouter and Cloudflare attribution behavior while allowing Bedrock session setup to reach provider routing.
Fixes#92974.
Co-authored-by: Sami Rusani <sr@samirusani>
Prevent duplicate `before_tool_call` execution when an already wrapped tool passes through schema normalization and coding-tool assembly. Preserve the normalized schema while replacing stale wrapper context with the current agent/session/run context.
Fixes#92973.
Co-authored-by: zengLingbiao <zeng.lingbiao@xydigit.com>
Resolve explicit relative SQLite DB paths before caching handles and centralize durable SQLite connection pragmas so busy_timeout is applied before WAL/NFS negotiation.
Use the active runtime snapshot for Discord and Slack native command routing and Discord autocomplete after config hot writes.
Fixes#39605
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(reply): deliver final reply when queued follow-up claims session; scope dedupe to routed thread
Two core bugs caused composed replies to be silently dropped (no delivery,
no error) when a second message arrived in the same thread mid-run:
1. dispatch-from-config: ensureDispatchReplyOperation only kept the
dispatch-owned operation authoritative while it had no result. Once
runReplyAgent completed the operation to drain queued follow-ups, a
second same-thread inbound could claim the session and the first final
reply would try to re-acquire the lane instead of finishing delivery,
deadlocking behind the queued work. Keep the dispatch-owned operation
authoritative through final delivery.
2. reply-payloads-dedupe: messaging-tool reply dedupe compared only the
channel target, not the routed thread, so a send in one thread could
suppress a later reply in a different thread. Thread the routed thread
id through buildReplyPayloads + follow-up delivery and only fall back to
channel-only matching for providers without a thread-aware suppression
matcher when neither side carries thread evidence.
Adds regression tests; existing Telegram topic-suppression behavior is
preserved by gating the thread guard to providers lacking a plugin matcher.
* fix(reply): preserve threaded message delivery evidence
* fix(reply): dedupe final payloads by delivery route
* fix(slack): preserve native send thread evidence
* fix(reply): preserve explicit reply thread evidence
* fix(reply): align explicit reply route dedupe
* fix(reply): preserve delivery lane through final dispatch
* fix(mattermost): preserve threaded tool send routes
* chore(plugin-sdk): refresh API baseline
* fix(reply): align final delivery route dedupe
* fix(reply): gate followups on final delivery
* fix(reply): keep send receipts private
* fix(reply): infer implicit message provider
* fix(reply): align routed threading policy
* fix(reply): preserve queued delivery context
* fix(reply): hydrate queued system event routes
* fix(reply): hydrate queued execution routes
* fix(reply): scope final delivery barriers
* fix(slack): preserve DM target aliases
* fix(reply): mirror resolved source thread routes
* fix(mattermost): retain delayed delivery barrier
* fix(codex): separate message routing from tool policy
* fix(reply): consume normalized Slack DM targets once
* fix(slack): remove stale target alias
* style(reply): satisfy changed lint gates
* fix(mattermost): preserve explicit reply targets
* test: align Slack reply branch checks
* fix(reply): persist overflow summaries to admitted session
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(codex): wait for native subagent completion
Codex native subagent lifecycle status is only a progress signal; the task row should not report success until the transcript or native completion result is available.
* fix(codex): preserve later native subagent failures
* test(codex): freeze authoritative subagent results
* fix(codex): preserve remote V1 completion fallback
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(elevenlabs): use current TTS model ids
* fix(elevenlabs): preserve served legacy model choices
---------
Co-authored-by: Ariel Bravy <ariel@vortexradar.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(tui): keep spinner active when toggling tools
* fix(tui): preserve finishing status when toggling tools
---------
Co-authored-by: zengwen <zeng_wen@foxmail.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Use PASSIVE for periodic SQLite WAL checkpoints while keeping explicit checkpoint() and close() on TRUNCATE by default.
Preserve the old interval export as a compatibility alias, add the neutral interval export, and update the task storage docs contract.
Fixes#81715.
The standalone loopback HTTP API only starts when
OPENCLAW_EAGER_BROWSER_CONTROL_SERVER=1 is set in the gateway
service environment. Without it, browser control works via CLI and
agent tools but nothing listens on the loopback control port.
Fixes#92841
Drop assistant replay turns that ended at the token limit with only incomplete hidden reasoning while preserving visible text, tool calls, empty turns, and unknown content shapes. Apply the same classification to embedded replay and public transport transforms, with focused regression, live OpenAI/Anthropic provider proof, docs, autoreview, Testbox, and green CI.
Co-authored-by: clawstation <abel@stationzero.ai>
Recover invalid Anthropic thinking replays when provider details survive genericization in SDK, failover, cause-chain, or terminal stream error fields.
The recovery matcher now uses cycle-safe named error carriers, avoids scanning assistant content and tool arguments, and retains one retry per provider call. Focused regressions cover each carrier, cyclic causes, terminal errors, and false-positive payload text.
Addresses the recovery path in #92201. The separate root cause that creates or persists invalid signatures remains open for investigation.
Co-authored-by: wlzeng0668001202 <ceng.wenlong@xydigit.com>
Clarify that interim assistant text remains visible under message_tool_only delivery while the final answer must use the message tool, and forward progress for channel message-tool turns once the message tool has delivered the final reply.
Co-authored-by: Forge <forge@psiclawops.dev>
Co-authored-by: Chisel <chisel@psiclawops.dev>
Fixes#92207.
Normalize public memory artifacts at the memory host boundary so providers that omit agentIds produce an empty list instead of throwing during artifact cloning, sorting, or memory-wiki bridge import. The bridge now renders those artifacts with unknown agents while downstream consumers still receive stable array-shaped metadata.
Verification:
- node scripts/run-vitest.mjs src/plugins/memory-state.test.ts extensions/memory-wiki/src/bridge.test.ts --maxWorkers=1
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- Crabbox run_2a30de5d0a00 / cbx_3684cb0b7ea5: OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1 OPENCLAW_CHANGED_LANES_RAW_SYNC=1 corepack pnpm check:changed
- GitHub PR checks clean on 19678ed60f
Refs #92057.
Build the request-scoped row metadata context for every non-empty sessions.list result, including limit=1, so single-row lists use the shared subagent metadata read index instead of direct per-row registry snapshot lookups. This keeps the existing single-row store child-session candidate optimization intact while removing the single-row metadata-cache gap.
Verification:
- node scripts/run-vitest.mjs src/gateway/session-utils.single-row-cache.test.ts --maxWorkers=1
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- Crabbox run_f89b56ffea83 / cbx_f1b1f5013225: OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1 OPENCLAW_CHANGED_LANES_RAW_SYNC=1 corepack pnpm check:changed
- GitHub PR checks clean on 1ba6619f2e
Fixes#92851.
When native filesystem watching exhausts its retry budget, the gateway config reloader now falls back to polling instead of disabling hot reload for the rest of the process. The watcher state tracks the effective Chokidar polling mode, including CHOKIDAR_USEPOLLING overrides, so forced polling avoids a redundant native phase and forced native mode reports an accurate native-mode disable.
Verification:
- node scripts/run-vitest.mjs src/gateway/config-reload.test.ts --maxWorkers=1
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- Testbox-through-Crabbox tbx_01kv2xvbqkv4dmvvvsswzm75hz: OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1 OPENCLAW_CHANGED_LANES_RAW_SYNC=1 corepack pnpm check:changed
- GitHub PR checks clean on c9762c5159
Fixes#92412.
Subagent spawns that request an unsupported explicit thinking level now clamp through the existing provider/model thinking fallback instead of hard-failing after the orchestrator has already received an accepted ack. The exception is limited to trusted subagent spawn runs by requiring both the subagent lane and a subagent-shaped session key, so interactive and non-subagent explicit `--thinking` validation still fails loudly.
Verification:
- `node scripts/run-vitest.mjs src/agents/agent-command.live-model-switch.test.ts --maxWorkers=1`
- `.agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main`
- Testbox-through-Crabbox `tbx_01kv2wt0nqavsmnvzzzy2antrc`: `OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1 OPENCLAW_CHANGED_LANES_RAW_SYNC=1 corepack pnpm check:changed`
- GitHub PR checks clean on `c71186863337d9dfb9a18e5349ebef634a7d5ccd`
Fix GPT-5.5 Chat Completions tool requests by omitting the incompatible reasoning effort only on verified OpenAI and Azure routes. Preserve no-tool requests and nonblank custom OpenAI-compatible providers; add official regional endpoint metadata plus OpenAI and Anthropic live regression proof.
Co-authored-by: Thomas Krohnfuß <thomas.krohnfuss@stud.th-luebeck.de>
* fix(feishu): re-resolve route when dynamic agent binding already exists in runtime config
When dynamicAgentCreation is enabled and a binding was previously written
to the config file (e.g. from a prior message), the in-memory cfg may be
stale and not contain the binding. Previously, maybeCreateDynamicAgent
returned { created: false, updatedCfg: cfg } with the stale cfg, and
bot.ts only re-resolved the route when created === true. This caused
subsequent messages to still route to agent:main.
Fix: check runtime.config.current() for the binding when it is missing
from the in-memory cfg. When found, return the runtime's current config
so the caller can re-resolve the route with up-to-date bindings.
Fixes#42837
* fix(feishu): serialize dynamic agent config updates
* fix(feishu): route with refreshed runtime config
* fix(feishu): use current dynamic-agent policy
* fix(feishu): reauthorize refreshed dynamic routes
* fix(feishu): authorize dynamic agent mutations
* fix(feishu): complete account-scoped dynamic routing
* fix(feishu): revalidate current direct routes
* fix(feishu): isolate named-account dynamic agents
* fix(feishu): bound named dynamic agent ids
* docs(feishu): explain legacy dynamic agent cap
* test(feishu): fix dynamic routing check types
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
Fall back to valid reset transcript archives when active async session transcripts are missing, while keeping active transcript priority and choosing the newest valid archive across roots.
Validation:
- node scripts/run-vitest.mjs src/gateway/session-utils.fs.test.ts src/gateway/sessions-history-http.test.ts src/gateway/sessions-history-http.revocation.test.ts src/gateway/session-history-state.test.ts src/gateway/server.chat.gateway-server-chat-b.test.ts src/gateway/managed-image-attachments.test.ts src/agents/tools/embedded-gateway-stub.test.ts src/tui/embedded-backend.test.ts
- node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.test.src.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/test-src-pr92879.tsbuildinfo
- git diff --check origin/main...HEAD && git diff --check
- autoreview --mode branch --base origin/main: clean
Direct-landed from #92879 because the source branch has maintainer edits disabled and the landed diff needed maintainer repair before merge.
Co-authored-by: Masato Hoshino <246810661+masatohoshino@users.noreply.github.com>
Co-authored-by: Hu Yitao <39733381+CadanHu@users.noreply.github.com>
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
Guard OpenAI post-hook tool inspection and code-mode filtering against unreadable accessors and asynchronous payload replacements. Preserve valid official `exec` and `wait` function tools across Responses and Chat Completions paths.
Supersedes #89703.
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Route implicit OpenAI image understanding through the Codex app-server for eligible OpenAI OAuth profiles. Preserve scoped and persisted credential ownership plus the rotating-token refresh lifecycle for isolated clients.
Fixes#87168
Thanks @bek91.
Snapshot unreadable OpenAI tool descriptors and schemas before payload construction, preserve healthy siblings, and reconcile hard tool choices with the surviving function inventory.
Adds live-tested Responses and Chat Completions coverage, including allowed_tools, while keeping Anthropic regressions green.
Related: #89413, #89013, #89016, #89378, #89543, #90200, #90283, #90286, #90397
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Quarantine unreadable and invalid Anthropic-family tool schemas before OpenAI-compatible serialization, keep tool choices aligned with surviving tools, and preserve provider metadata.
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Quarantine unreadable and structurally invalid direct/custom Anthropic tool schemas in both canonical request builders while preserving healthy siblings, forced-choice semantics, OAuth name mapping, and official OpenAI behavior.
Supersedes #89418, #89221, #90228, #89622, #89229, and #90278.
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Merge adjacent Anthropic assistant turns before dangling tool-use validation so signed tool calls remain immediately paired with their tool results. Preserve contributor credit. Fixes#87329.
Preserve signed thinking for active Anthropic tool-result continuation while omitting native thinking from completed history when the new request disables or omits thinking. Applies the same replay rule to the legacy SDK provider and managed Anthropic transport. Fixes#92360.
Clarifies cron edit --at help after maintainer rebase and preserves the Gateway-host timezone wording for cron --tz help.
Validation:
- git diff --check
- node scripts/run-vitest.mjs src/cli/cron-cli.test.ts
- local Codex autoreview clean, no actionable findings
Co-authored-by: rrrrrredy <rrrrrredy@users.noreply.github.com>
Surface missing bare Lobster workflow file paths instead of silently falling through to inline pipeline parsing.
The runner now treats plain workflow file inputs as file paths, keeps inline commands with file-like arguments as pipelines, and preserves existing workflow file paths that contain spaces. Regression coverage covers missing bare workflow paths, inline false positives, and spaced workflow filenames.
Fixes#68101.
Based on and credits #68106 by @vvitovec. This replacement branch carries the focused fix forward because #68106 is dirty against current main and could not be repaired on the fork branch with available bot permissions.
Validation:
- node scripts/run-vitest.mjs extensions/lobster/src/lobster-runner.test.ts
- autoreview clean: no accepted/actionable findings after the spaced-path fix
- GitHub checks: 127 pass, 0 fail, 0 pending
Co-authored-by: Viktor Vítovec <230458341+vvitovec@users.noreply.github.com>
Target Feishu Typing reactions at the inbound message id while preserving reply and thread routing to the topic root.
This keeps the fallback to replyToMessageId for flows without a separate inbound target, and adds regression coverage for topic/replyInThread behavior and synthetic Feishu turn sources.
Based on and credits #67783 by @huiwen01. This replacement branch carries the same user-visible fix forward because #67783 is dirty against main and earlier automation could not update the fork branch with available permissions. This intentionally does not reuse or expand #73958; root_id routing remains separate.
Validation:
- pnpm check:changed
- pnpm -s vitest run extensions/feishu/src/bot.test.ts extensions/feishu/src/reply-dispatcher.test.ts extensions/feishu/src/monitor.reaction.test.ts
- autoreview clean: no accepted/actionable findings
- GitHub checks: 127 pass, 0 fail, 0 pending
Co-authored-by: huiwen01 <89329207+huiwen01@users.noreply.github.com>
* fix(update): continue after package doctor warnings
* fix(update): type advisory step rendering
* fix(update): preserve advisory doctor step state
* fix(update): share advisory doctor state
* fix(update): keep timed-out doctor failures blocking
* fix(update): require explicit doctor advisory result
* fix(update): reject malformed doctor advisory results
* fix(update): bound doctor advisory diagnostics
* fix(update): keep doctor advisory restart-neutral
* fix(update): protect doctor advisory IPC
* fix(update): scope doctor advisories to converging updater
* fix(update): scope doctor advisories to deferred repairs
* fix(update): secure doctor advisory IPC
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(status): render sub-1000 token counts as plain integers
formatKTokens always divided by 1000 and appended "k", so token counts
below 1000 rendered as misleading fractional k in `openclaw status`
output (e.g. 999 rounded up across the boundary to "1.0k", 420 -> "0.4k",
a 300-token cache write -> "write 0.3k").
Guard value < 1000 to render the plain rounded integer, matching the
canonical formatTokenCount convention (src/utils/usage-format.ts). The
>=1000 "k" behavior is unchanged. Adds focused regression tests for the
0/420/999/1000/12000 boundary and small-session/small-cache status lines.
Fixes#89735
* fix(status): reuse canonical token formatter
* refactor(status): extract lightweight token formatter
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
Add clawsweeper:queueable-fix, clawsweeper:source-repro, and
clawsweeper:fix-shape-clear to exempt-issue-labels in all 4 stale
workflow steps and the backfill-closures script's issueExemptLabels
set.
Previously, issues classified by ClawSweeper as actionable fix
candidates could still be marked stale and auto-closed, creating
a conflict between the two automation systems (e.g. #78640,
#81078, #81122 had both 'stale' and 'clawsweeper:queueable-fix').
Fixes#89564
Remove replayed thinking and redacted-thinking blocks from GitHub Copilot Claude history and final Anthropic payloads while preserving visible content, tool turns, and non-empty assistant structure.
Fixes#81520
Supersedes #87060 and #81534
Co-authored-by: Gio Della-Libera <giodl73@gmail.com>
Cap configured session context overrides by the selected model's known context window, refresh provider/model metadata consistently, and preserve the fixed Anthropic 1M context contract.
Fixes#39857
Co-authored-by: Kros Dai <7087+xdanger@users.noreply.github.com>
Fixes#54909.
Repair #54962 by preserving raw slash-command callbacks while routing generic callback data to agents as `callback_data: <value>`.
Validation:
- pnpm check:changed
- pnpm -s vitest run extensions/telegram/src/bot.create-telegram-bot.test.ts
- Codex /review
- Real behavior proof
- GitHub PR checks
Source PR credit: continues @hnshah's work from #54962 and preserves @timt80's report credit from #54909.
Co-authored-by: Hiten Shah <3155200+hnshah@users.noreply.github.com>
* fix(gateway): preserve active run during plugin finalization
* fix(ui): skip session.message history reload while gateway reports active run
* fix(ui): remove unused eslint-disable directive
* fix(ui): preserve active runs through finalization
---------
Co-authored-by: scotthuang <scotthuang@tencent.com>
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(gateway): use resolveNonNegativeNumber for totalTokens to display 0 instead of ?
resolvePositiveNumber requires value > 0, filtering out the valid
totalTokens = 0 case (new session, no usage yet). This caused the TUI
to display 'tokens ?/200k' instead of 'tokens 0/200k (0%)'.
Use resolveNonNegativeNumber (>= 0) for the final totalTokens value
used in session display. The needsTranscriptTotalTokens check at line
2041 still correctly uses resolvePositiveNumber to decide whether to
fetch transcript data.
Fixes#43009
* fix(gateway): preserve fresh zero-token sessions
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(macos): defer isOverflowing mutation to break SwiftUI render loop
measuredHeight() mutated model.isOverflowing synchronously during a SwiftUI
view update cycle. The onChange(of: attributed) handler triggered
updateWindowFrame → targetFrame → measuredHeight, which set isOverflowing,
invalidating the view and re-triggering onChange — an infinite render loop
causing 100% CPU pinwheel.
Fix: defer the isOverflowing mutation via DispatchQueue.main.async with an
equality guard to prevent redundant updates. The frame calculation itself
remains synchronous so the window size is correct immediately.
Fixes#43480
* fix(macos): preserve latest overflow measurement
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(telegram): skip IPv4 fallback when user configures non-ipv4first dnsResultOrder
When the user explicitly configures channels.telegram.network.dnsResultOrder
to a non-ipv4first value (e.g. verbatim), the sticky IPv4 fallback dispatcher
should not be armed. Forcing autoSelectFamily=false + dnsResultOrder=ipv4first
overrides the user's explicit IPv6-friendly config, causing media downloads to
fail on hosts where IPv4 is broken but IPv6 works.
Fixes#41671
* fix(telegram): respect explicit DNS fallback policy
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(doctor): avoid false-positive legacy cron store warning when store was already migrated
When rawJobs.length > 0 and other issues exist (notifyCount, dreamingStaleCount)
but legacyStoreDetected is false (file already removed after migration), the doctor
unconditionally printed 'Legacy cron job storage detected at ...' — misleading users
into thinking the migration was incomplete.
Fix: conditionally use 'Cron store issues detected' heading when no legacy store file
exists, reserving 'Legacy cron job storage detected' for actual legacy store presence.
Fixes#92683
* test(doctor): add test for false-positive legacy cron store warning (#92683)
Follow-up to #92745 after maintainer autoreview found that the skipped recall event widened the shipped MemoryHostEvent union and changed limited legacy reads.
Keep readMemoryHostEvents() source-compatible by filtering diagnostic records before applying limits, and expose skipped recall diagnostics through the opt-in MemoryHostEventRecord/readMemoryHostEventRecords path.
Original skipped-recall behavior landed in #92745 by @mushuiyu886.
Fixes#42156.
Answer Telegram callback queries before per-chat/topic sequentialize can queue the handler behind an active turn, and carry the in-flight answer promise on the grammY context so the normal handler reuses it instead of double-answering.
Proof:
- node scripts/run-vitest.mjs extensions/telegram/src/bot.create-telegram-bot.test.ts -- -t "answers callback queries before same-chat sequentialize delays handlers|sequentializes updates by chat and thread|routes callback_query payloads as messages"
- node scripts/run-vitest.mjs extensions/telegram/src/bot.test.ts extensions/telegram/src/bot-handlers.runtime.test.ts
- node_modules/.bin/oxfmt --check extensions/telegram/src/bot-core.ts extensions/telegram/src/bot-handlers.runtime.ts extensions/telegram/src/callback-query-answer-state.ts extensions/telegram/src/bot.create-telegram-bot.test.ts
- git diff --check origin/main...HEAD
- .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
- Azure Crabbox cbx_cba20c462ad5 / silver-barnacle: OPENCLAW_TESTBOX=1 node scripts/crabbox-wrapper.mjs run --provider azure --class Standard_D4ads_v6 --idle-timeout 90m --ttl 240m --timing-json -- env OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1 OPENCLAW_CHANGED_LANES_RAW_SYNC=1 corepack pnpm check:changed
Proof gap: live Telegram Desktop/burner-account proof was not run because openclaw-telegram-user-crabbox-proof is not installed in this shell.
Reject voice media stream start frames when no acceptance validator is configured, preventing fail-open STT/TTS session creation. Verified locally, with autoreview, in a remote Linux dev box, and by green CI.
* fix(gateway): accept image-only input on /v1/responses
The OpenResponses endpoint rejected requests whose `input` contained only
an `input_image` (no `input_text`) with `400 Missing user message in
input.`, even though the image was parsed and collected into `images`.
The guard only checked `prompt.message` and ignored `images`, unlike the
equivalent /v1/chat/completions guard which uses
`!prompt.message && images.length === 0`.
Align the OpenResponses guard with Chat Completions so image-only turns
are forwarded to the agent. Empty input (no text and no image) still
returns 400.
Adds regression tests: image-only base64 input -> 200 with image reaching
the agent, and empty content -> 400.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(gateway): pass image-only /v1/responses turns to the agent
The one-line guard alone was insufficient: even after letting image-only
input past the `Missing user message` check, the downstream agent command
(`prepareAgentCommandExecution`) throws `Message (--message) is required`
for an empty message, so image-only `/v1/responses` returned 500.
Mirror the /v1/chat/completions prompt builder: substitute the shared
IMAGE_ONLY_USER_MESSAGE placeholder for the active image-only user turn so
the turn is not dropped and the real image is still attached via `images`.
Promote the placeholder constant to the shared gateway agent-prompt module
so both endpoints stay in sync, and revert the responses guard back to the
original `!prompt.message` check (responses images are not scoped to the
active turn, so the placeholder is the correct, single source of truth).
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore: retrigger CI (flaky startup-core test timeout, unrelated to change)
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: songwendong <songwendong@shuidi-inc.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Record diagnostic events when memory_search returns durable memory hits that are intentionally excluded from short-term promotion, so users can distinguish eligibility decisions from recall tracking failures.
Keep cron-local transient auth failures from polluting shared cooldowns while preserving real auth/billing/rate-limit propagation. Verified with focused auth/cron tests, type proof, autoreview, and clean CI.
Normalize Windows schtasks default gateway names without hiding similarly prefixed duplicate tasks. Verified with focused daemon tests, type proof, autoreview, and clean CI.
Keep the Tavily public artifact lightweight while lazily executing through the provider runtime. Verified with focused Tavily/provider artifact tests and clean CI.
Limit SSH tunnel classification to actual queried-port forwards and keep SSH-like non-gateway listeners out of stale gateway cleanup. Verified with focused port/restart tests and clean CI.
Validates Matrix CLI numeric option ranges before invoking setup or verification side effects.
`--initial-sync-limit` must now be non-negative, and `--timeout-ms` must now be positive.
Original PR by @rohitjavvadi.
Verification:
- `node scripts/run-vitest.mjs extensions/matrix/src/cli.test.ts --maxWorkers=1`
- autoreview clean
- Crabbox AWS `cbx_5c32f138ab3a` / `swift-lobster`, run `run_6e133b8b82e7`: `check:changed` passed
- exact PR head CI green: `d75f118299029b0516311646276cd2d6582379c5`
Preserves provider-compatible CLI runtime session pins across reply execution, follow-up execution, dispatch visibility, preflight compaction, and memory flush.
This keeps sessions pinned to compatible CLI runtimes such as `claude-cli` from leaking into embedded OpenClaw maintenance paths while still rejecting cross-provider runtime pins.
Original PR by @yu-xin-c; includes maintainer follow-up for the sibling memory paths.
Verification:
- `node scripts/run-vitest.mjs src/auto-reply/reply/agent-runner-execution.test.ts src/auto-reply/reply/agent-runner-memory.test.ts src/agents/model-runtime-aliases.test.ts --maxWorkers=1`
- autoreview clean
- Crabbox AWS `cbx_44400b494e97` / `coral-prawn`, run `run_69dd43475e39`: `check:changed` passed
- exact PR head CI green: `303b2f794f6c01fcf21b62b27c536b5f6eceb421`
Updates the macOS Swift package resolution for patch releases of Peekaboo, Sparkle, and swift-log.
Verification:
- `swift package describe --type json`
- `swift build --target OpenClawIPC`
- `swift build --target OpenClawDiscovery`
- upstream tag/revision checks for Peekaboo 3.4.1, Sparkle 2.9.3, and swift-log 1.13.2
- autoreview clean
- exact PR head CI green for macOS, dependency, and security checks
Remove the stale nested openclaw package, its .bin shim, and the pnpm virtual-store copy from the runtime Docker image before final runtime assets are copied.
Run the package dist import-closure check after the cleanup so the check validates the final runtime-assets tree that the image ships.
Compatibility note: private Docker paths under /app/node_modules/openclaw and /app/node_modules/.bin/openclaw are removed; downstream images should use the documented /usr/local/bin/openclaw launcher or /app/openclaw.mjs.
Fixes#92551.
Thanks @lzyyzznl for the fix and @fxstein for the report.
Derive the channel from canonical account-scoped DM session keys when resolving session.sendPolicy, so channel-scoped allow/deny rules apply to per-account-channel-peer sessions.
Keep derivation limited to canonical channel peer key shapes and add malformed-key regressions so incomplete or non-channel keys do not accidentally match channel rules.
Compatibility note: existing channel-scoped send-policy rules can now block account-scoped DM sends that were previously allowed by this bug.
Thanks @yetval for the fix.
Route OpenAI/OpenAI-compatible request_headers_too_large embedding failures into the existing memory-core batch splitter instead of aborting bulk memory indexing.
Tighten the classifier to require header-too-large wording rather than a bare 431 status token, so unrelated provider errors do not fan out into recursive requests.
Fixes#92465.
Thanks @mushuiyu886 for the fix and @BrettHamlin for the report and proof.
Add a Nodes overview `openclaw.json` example for node pairing, command allow/deny policy, node exec routing, and per-agent node pinning.
Also clarifies exact `denyCommands` matching and links readers to the config reference for pairing and command-policy field details.
Fixes#92662.
Thanks @liuhao1024 for the fix and @ZengWen-DT for the parallel docs wording on exact node command policy.
Correct the exported agent defaults type comments for `maxConcurrent` and `subagents.maxConcurrent` so they match the runtime defaults of 4 and 8.
No runtime behavior changes.
Thanks @ArielSmoliar for the fix.
Fix render-aware markdown chunking so `Number.POSITIVE_INFINITY` is treated as an explicit unbounded chunk limit instead of falling back to `1`.
This preserves full Signal media captions and disabled Signal text chunking while keeping invalid non-finite limits on the existing fallback path.
Fixes#92734.
Thanks @yhterrance for the report and fix.
Summary:
- The branch changes diagnostic stuck/long-running warning backoff so recovery-eligible classifications are still returned during throttled warning ticks and updates the diagnostic tests.
- PR surface: Source +17, Tests +48. Total +65 across 2 files.
- Reproducibility: yes. Current main source shows logSessionAttention can return undefined during stuck or lon ... g backoff before the heartbeat reaches requestStuckSessionRecovery; I did not run a live QQ gateway replay.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(diagnostics): keep recovery scheduling out of the stuck-session w…
Validation:
- ClawSweeper review passed for head f61ec3a33f.
- Required merge gates passed before the squash merge.
Prepared head SHA: f61ec3a33f
Review: https://github.com/openclaw/openclaw/pull/92752#issuecomment-4699298908
Co-authored-by: Gnanam <gnanasekaran.sekareee@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>
Uses the canonical model ref returned by `sessions.patch` for the TUI `/model` confirmation so alias inputs report the model that was actually applied. The fallback still shows the raw input when a backend does not return `resolved`, and the display path uses `modelKey` so nested model ids keep the provider prefix without double-prefixing self-prefixed ids.
Proof: local focused TUI Vitest/format/lint; autoreview clean; Crabbox AWS focused TUI test run `run_7d7cc5b040e8`; exact-head GitHub CI green on `6db4acfb08f9d477ee1bdab429bd7189b78ffc92`.
Detects NFS-backed SQLite database paths in the shared WAL helper and uses rollback journaling for those paths while preserving WAL/checkpoint maintenance on local filesystems. The NFS path now verifies SQLite's effective journal mode before disabling WAL maintenance, and core/memory/proxy-capture callers pass database path context into the centralized helper.
Fixes#90491.
Proof: local focused Vitest/format/lint; autoreview clean after fixing the journal-mode verification finding; Crabbox AWS focused test run `run_2ea7014350da`; Crabbox AWS changed gate `run_c828bbfe7d23`; exact-head GitHub CI green on `59674305ecd863d4815eec6098ccd3daab79ca4f`.
Exposes Telegram's thread-create CLI remap through the exported Telegram channel action adapter, preserving the existing plugin-owned mapping to topic-create before gateway dispatch.\n\nFixes #81581.\n\nProof: local focused format/lint/Vitest and dry-run; autoreview clean; Crabbox AWS run_07b98c939fce focused tests; Crabbox AWS run_1b7b35ce1de1 check:changed; exact-head GitHub CI green on 16f6afbdd7.
Preserves the selected Control UI session as the parent when creating dashboard child sessions even if the session list is stale or filtered, while avoiding the synthetic unknown session as a parent.\n\nFixes #90623.\n\nProof: local focused format/lint/Vitest/browser test; autoreview clean; Crabbox AWS run_a2bfdcd2315a UI proof; Crabbox AWS run_ce60fdc546ff check:changed; exact-head GitHub CI green on 03d1c6f646.
Adds the OpenClaw Parallel User-Agent to free Parallel Search MCP requests so the zero-config web_search path is identifiable at the HTTP layer, matching the paid REST transport.\n\nProof: local focused format/lint/Vitest; live anonymous Parallel MCP handshake; autoreview clean; Crabbox AWS run_bf41ce86e862 focused regression; Crabbox AWS run_ee9b8954b081 check:changed; exact-head GitHub CI green on b7e45e3bfc.
* Mark active main sessions during restart shutdown
* Type restart marker mock in close tests
* fix(gateway): preserve active run ownership across restart
* fix(gateway): preserve active runs across restart
* fix(gateway): close restart recovery edge cases
* fix(cron): preserve lifecycle ownership across restart
* fix(gateway): release rejected run contexts
* fix(gateway): preserve restart lifecycle ownership
* fix(cron): retain overlapping run ownership
* fix(agents): preserve restart terminal precedence
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Summary:
- Strip the redundant `google/` provider prefix before embedding Google Vertex model ids under `/publishers/google/models/`.
- Keep bare Vertex model ids unchanged.
- Add regression coverage for the provider-qualified Vertex path.
Verification:
- `node_modules/.bin/oxfmt --check --threads=1 extensions/google/transport-stream.ts extensions/google/transport-stream.test.ts`
- `node scripts/run-oxlint.mjs extensions/google/transport-stream.ts extensions/google/transport-stream.test.ts`
- `node scripts/run-vitest.mjs extensions/google/transport-stream.test.ts --maxWorkers=1 -t 'strips redundant google provider prefixes from Google Vertex model paths'`
- Autoreview clean
- AWS Crabbox `run_649b209478d2` focused Node 24 regression proof
- AWS Crabbox `run_e193db2707ad` remote `check:changed`
- Exact-head CI green for `23aca6f46f596e220df37d939317b433f7044ec6`
- Contributor live Google Vertex proof recorded in the PR body
* fix(ui): restore sidebar session picker interactivity above desktop workbench
The collapsed sidebar session picker was covered by the chat content
area when the workspace rail was visible at wider viewports. Two
issues caused this:
1. .sidebar-session-select--collapsed .chat-session-picker used
var(--z-dropdown) which was never defined, creating an invalid
z-index declaration (falls back to auto).
2. .shell-nav and .content--chat are grid siblings with equal
z-index (auto), and .content--chat (later DOM) paints above
.shell-nav, covering the session picker that extends from the
nav column into the content column.
Fix: add position:relative + z-index:10 to .shell-nav so it stacks
above .content--chat; change overflow from hidden to visible so
the session picker extends beyond the nav rail; replace undefined
var(--z-dropdown) with z-index:100.
* fix(ui): keep sidebar picker z-index tokenized
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Fixes#91003
Add explicit generated-media directives to completion handoff prompts and treat real attachment payloads as visible session-only delivery evidence for dashboard/webchat completions. Hardened maintainer follow-up keeps malformed attachment arrays from masking failed delivery and keeps generated MEDIA directive values single-line sanitized.
Proof: focused local format/lint/Vitest, clean final autoreview, Crabbox AWS focused proof run_32499eb46b33, Crabbox AWS check:changed run_af46879ffbd1, and exact-head GitHub CI green for f8e6f4a04e.
Fixes#44922
Preserve heartbeat-owned cron reminders as a single model input during heartbeat runs while keeping normal-turn fallback delivery when a heartbeat is skipped.
Proof: focused local Vitest/oxlint/format, clean autoreview, Crabbox AWS run_67abc286250a, Crabbox AWS check:changed run_bddebf014d58, and exact-head GitHub CI green for 341e807d7a.
Clear cached Feishu clients when the test runtime replaces the SDK, preventing stale clients from leaking across test setup. Adds regression coverage for the SDK swap path. Fixes#83911.
Reuse one module-level ANSI/OSC scanner during visible-width truncation and reset scanner state between calls. Keeps styled, plain, and OSC-8 truncation behavior covered by regression tests.
- messages.usageTemplate now layers OVER the built-in default (objects
merge by key, arrays/scalars replace), like other openclaw config
objects, so a user template only needs the delta it adds/changes.
- Default ships the full scale palette (braille/block/shade/moon/level/
weather/plants/moons6); users add more by name.
- Document the template format end to end (the "default" sentinel, merge
behavior, the contract paths, verb table, piece forms, a worked example)
in docs/concepts/usage-tracking.md — previously unauthorable from docs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Set messages.usageTemplate to the sentinel "default" to render a
good-looking built-in /usage full footer without supplying a template.
Intentionally undocumented in the config schema/help for now; a path or
inline object still overrides, and unset keeps the built-in line.
The default lives in source (default-template.ts) rather than a shipped
JSON so it stays in lockstep with the renderer. It keeps the 📚
context-window bar; it does not render limits/reset windows (the merged
PluginHookReplyUsageState carries no limits data).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Round MiB before selecting the display unit so low-disk warnings do not render boundary values as 1024 MiB. Adds regression coverage for the GiB boundary. Fixes#90245.
Remove the unused requireRef import and void anchor from Browser navigation command registration while keeping navigate/resize registration covered by regression tests. Fixes#83878.
Reparse nested lazy commands from the Commander root so unknown options keep the original argv and exit non-zero. Adds nested lazy-command coverage for the root rawArgs path. Fixes#92069.
Emit terminal Slack message_sent and message:sent hooks across normal, streaming, preview, fallback, slash, failure, and TTS reply paths with canonical session/target correlation and one outcome per logical payload.
Fixes#89942
Co-authored-by: Rishi Tamrakar <rishi.ktamrakar@gmail.com>
Expose session key and stable session id in Runtime prompt metadata for embedded, CLI-backed, and command-generated agent prompts so agents do not infer session identity. Fixes#92453.
Limit sessions_send active-run queue delivery to run-scoped targets, keep stranded cron-run fallback for valid cron run keys, and report unsafe queue rejections without rerouting through durable sessions. Fixes#91420.
Add both official Claude Haiku 4.5 API identifiers to the Anthropic static catalog and cover their metadata with a focused regression test.
Fixes#90088
Co-authored-by: Jasmine Zhang <jasminezhang@JasminedeMac-mini.local>
Normalize provider-qualified OpenRouter model IDs before capability lookup and transport while preserving native OpenRouter namespace IDs.
Fixes#92611.
Co-authored-by: liuhao1024 <sunsky.lau@gmail.com>
Stabilizes model auth marker tests against the current manifest-metadata discovery seam and isolated Vitest environment.
Runtime behavior is unchanged; provider-owned non-secret markers remain declared in plugin manifests.
The usage-bar subdirectory was not covered by any full-suite shard glob
(template.test.ts is disqualified from unit-fast by filesystem-state),
failing the shard coverage check. Extend autoReplyCoreTestInclude to the
subdir; unit-fast-eligible files are auto-excluded by the scoped config.
Also create the temp dir explicitly in the missing-file test instead of
binding an unused tmpFile result, which tripped oxlint no-unused-vars.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
loadUsageBarTemplate ran a synchronous statSync on every reply (and readFileSync
on mtime change) to check template freshness — synchronous filesystem I/O in the
latency-sensitive reply path, which on a slow / networked / blocked filesystem
can stall /usage full delivery (and the single-threaded event loop with it).
Read the template once into memory and keep it fresh with a filesystem watcher
(persistent: false), so the per-reply path is filesystem-free. A missing file is
not cached (a later-created template is still picked up on a subsequent call); a
watch failure leaves the one-time load in place. Adds template.test.ts covering
inline / file / invalid-JSON / missing-then-created and the FS-free hot path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Consume the usageState contract's contextUsedTokens (populated by #89629) so
the context gauge reflects real end-of-turn window occupancy instead of the
multi-call turn aggregate, which overstates it (often past 100%) and pins the
meter full while /status shows the true figure. Fall back to the aggregate
when contextUsedTokens is absent (single-call turns, where they coincide).
Also expose the final model call's usage as usage.last.* (input/output/cache +
cache_hit_pct) so a template can render the last exchange vs the turn
aggregate.
Adds the consumed fields (contextUsedTokens, lastUsage) to
PluginHookReplyUsageState as the renderer's type dependency; their population
lands in #89629.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
num truncates sub-1000 values to integers, so monetary fields like
cost.turn_usd rendered as 0 (or with full float noise when piped raw).
Add a fixed:N verb that formats a number to N decimal places (default 2),
returning empty string for non-numeric input — matching the other
formatters' guard style.
{cost.turn_usd|fixed:4} 0.03771985 -> 0.0377
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- no-misused-spread: use Array.from for code-point glyph split (same
behavior, astral glyphs intact) instead of string spread.
- no-base-to-string: return the case glyph only when it is a string.
- no-unnecessary-type-assertion: drop redundant cast already narrowed
by isObject.
No behavior change (render output byte-identical; tsgo clean).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Formatting (import order, indentation) and bracket-notation access for
reserved template keys (_aliases/_default) to satisfy no-underscore-dangle.
No behavior change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Render the per-reply /usage full footer in core from a declarative template
(the openclaw.usageBar.v1 format) when messages.usageTemplate is set; fall back
to the built-in line otherwise. Ports the reference usage_bar.py engine to TS so
no external process is involved (the external surface is just template data).
- usage-bar/translator.ts: engine (verbs num/dur/pct/inv/alias/meter, segment
forms text/when/map/each, output.surfaces, item_scales). Codepoint-correct
glyph indexing; fail-open (empty render -> boring fallback).
- usage-bar/contract.ts: buildUsageContract (snapshot -> openclaw.usageLine.v1).
- usage-bar/template.ts: resolve from a path (mtime-cached) or inline object.
- agent-runner: capture the per-turn usage snapshot and render the template at
the /usage full branch in place of the built-in line when configured.
- messages.usageTemplate config (string path | inline object) + strict schema.
- translator.test.ts: verb parity, segment forms, astral glyphs, e2e render.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fixes#90685 by allowing models.list availability to use matching auth-profile credentials when provider config contains a non-env SecretRef, while preserving unavailable results for unresolved SecretRef-only providers.
Adds isolated regression coverage for file SecretRefs and secretref-managed provider markers.
Co-authored-by: Rohit <rohitjavvadi2@gmail.com>
Bounds default model browsing to configured/read-only discovery while preserving explicit full-catalog browsing. Reuses prepared plugin metadata and auth state without triggering external CLI discovery on the picker hot path, while retaining provider normalization and canonical runtime aliases.
Verified with focused model tests, official OpenAI and Anthropic transport suites, fresh live tool calls for both providers, a full build, AWS check:changed, remote Docker OpenAI tools E2E, and green PR CI.
Fixes#91809.
Co-authored-by: samson1357924 <98934496+samson1357924@users.noreply.github.com>
A missing per-turn authMode was mapped to "oauth", so an OpenAI api-key turn
that arrived without an explicit auth mechanism could resolve and display
ChatGPT subscription windows that aren't its own — served straight from the 60s
limits cache, which the "re-checked at fetch time" guard does not cover.
Treat a genuinely absent signal as non-eligible (same as api-key): no usage
provider resolves and the footer omits limit windows. Present mechanisms are
unchanged — oauth/auth-profile/token stay eligible, and only OpenAI is gated on
the credential type so other providers are unaffected. A real oauth/profile turn
always carries its mechanism; one arriving blank is an upstream tagging bug to
fix at the source.
Inverts the now-incorrect "absent => oauth-eligible" test into regression
coverage for the absent/api-key case.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
context.used_tokens / pct_used were derived from the snapshot's aggregate
prompt total (cacheRead+cacheWrite+input). Over a multi-call tool-loop turn
that is the run AGGREGATE, overstating window occupancy (often past 100%) so a
footer's context gauge pins full while /status shows the true ~7%.
Add two optional fields to PluginHookReplyUsageState and populate them in the
reply path:
- contextUsedTokens: the final call's prompt size (agentMeta.promptTokens) =
real end-of-turn occupancy, a point-in-time state, not the aggregate.
- lastUsage: the final model call's usage only (vs `usage`, the turn
aggregate), so a footer can render the last exchange's i/o + cache.
Both optional and additive; consumers fall back to the aggregate when absent
(correct for single-call turns). Renderer consumption lands separately (#89835).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
requestShaping.authMode is the auth *mechanism* (e.g. "auth-profile" for a
configured auth profile), not the credential *type* resolveUsageProviderId
expects. Gating limits on it === "oauth"/"token" dropped 📊 for legit OAuth
(profile-based) turns. Map it: api-key/aws-sdk -> no usage provider (cannot
borrow cached oauth windows); oauth/token/auth-profile/absent -> usage-eligible,
with the real credential re-checked at fetch time.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses review feedback on #89629.
1) Provider-limit resolution no longer defaults to OAuth. getProviderUsageLimits
and getProviderUsageLimitsCached resolved with `credentialType ?? "oauth"`, and
the agent-runner snapshot call passed no credential type, so an api-key OpenAI
turn could borrow cached OAuth/ChatGPT usage windows. Drop the "oauth" default
(missing credential type => no OpenAI usage provider) and thread the turn's
authMode through at the call site. Adds provider-usage.limits.test.ts covering
api-key/no-credential (no fetch), oauth/token (resolves), and non-OpenAI.
2) usageState is documented as best-effort, present only on live dispatcher
delivery. Routed durable and recovered queue replays re-run this hook as a
stateless transform over the original payload (see QueuedDeliveryPayload); a
point-in-time usage snapshot is not stateless and would replay stale after a
restart, so it is intentionally omitted there. Consumers must treat the field
as optional.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Attach a per-turn execution snapshot to the reply_payload_sending hook as
`usageState`, so a plugin (or the future in-core /usage renderer) can render a
per-response usage readout as a pure consumer of the contract — no side calls.
Recorded in agent-runner, consumed in dispatch. Fields: provider, model,
resolvedRef/requested, reasoningEffort, fastMode, fallbackUsed, is_override
(overrideSource), authMode, compactionCount, contextTokenBudget, token usage,
turn cost (USD), duration, owning agentId/sessionId, chatType, the agent
identity (name/emoji), and the active provider's subscription `limits` windows.
reply_payload_sending is the one reply hook universal across every surface
(incl. the Codex app-server, which emits no llm_output/agent_end), so it is the
correct harness-agnostic place for per-turn usage. Limits are resolved by a
core-internal non-blocking SWR helper (src/infra/provider-usage.limits.ts) and
attached to the snapshot — no new plugin-SDK accessor. All fields optional/additive.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(qa): run vitest and playwright scenarios from qa suite
* fix(qa): harden scenario suite dispatch
* refactor(qa): share scenario path utilities
* refactor(qa): share test file scenario runner
* refactor(qa): route test file scenarios through suite runtime
* refactor(qa): use explicit suite runtime result kind
* test(qa): write suite evidence artifact
* refactor(qa): clarify suite execution dispatch
* fix(qa): keep test-file scenarios out of flow-only runners
* refactor(qa): export mixed scenario suite runner
* fix(internal-runtime-context): wrap prompt-preface runtime context body in delimiters
When buildRuntimeContextMessageContent constructs the hidden
runtime context prompt block, the body (which may contain
sensitive metadata like relevant-memories, sender info, and
conversation metadata) was not wrapped in the standard
INTERNAL_RUNTIME_CONTEXT_BEGIN/END delimiters. If the model
echoed this context back in its reply, stripInternalRuntimeContext
could only remove the header and notice lines — the sensitive
body leaked through to user-visible surfaces like Feishu
streaming cards.
Wrap the runtime context body in BEGIN/END delimiters so the
existing stripInternalRuntimeContext (which handles delimited
blocks first) can fully remove the entire block.
Closes#92589
* chore: retrigger CI for proof check
* chore: retrigger CI with corrected proof format
* chore: retrigger CI with corrected proof field format
The Windows Hub companion installers are promoted to the main OpenClaw
release via a manual workflow_dispatch, not every release includes them.
The /releases/latest/download/ links resolved to v2026.6.6 which does not
have the OpenClawCompanion assets, causing 404 errors.
Pin the links to v2026.6.5 (the latest release that has the assets) and
add a fallback note directing users to the releases page when a release
is missing the companion installers.
Fixes#92470
* fix(a11y): B-1 — raise muted text contrast to ≥4.8:1 WCAG AA
* fix(ui): C-1 — ChatSidePanel joins glass surface language
* feat(mobile): D-1 — hamburger overlay nav below 900px
- Esc key now closes nav drawer (globalKeydownHandler)
- Nav item tap targets bumped to min-height: 44px + padding: 10px 16px
in the ≤1100px drawer breakpoint (was 40px / 0 12px)
- Hamburger toggle + overlay drawer were already wired in app-render.ts;
this completes close-on-Esc and ensures accessible tap targets
* fix(a11y): B-2 — consistent focus-visible states distinct from hover
* fix(a11y): B-3 — lift all sub-12px text to 12px minimum
* fix(a11y): narrow focus and CSS scope
* fix(a11y): finish focus-visible selectors
Keep valid root and plugin models available when one generated plugin catalog is invalid, while retaining and logging the catalog error.
Fixes#92553.
Co-authored-by: tangtaizong666 <tangtaizhong792@gmail.com>
Persist successful same-channel Slack and CLI assistant replies exactly once in the owning transcript. Preserve delivery-hook output, routed/runtime ownership, custom stores, and authoritative reset/session rotation bindings.
Fixes#92489
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Resolve bundled Fireworks manifest models through core's static catalog so Kimi K2.6 keeps its 262,144-token context limit and nested model compatibility metadata.
Keep the existing dynamic fallback for uncataloged Fireworks IDs and align bundled Kimi reasoning metadata with existing runtime behavior.
Verified with focused tests, extension/core type checks, lint/format, full build, fresh autoreview, required CI, and a live Fireworks Kimi K2.6 embedded run using a real key.
Co-authored-by: Evgeni Obuchowski <evgeni@obukhovski.com>
Preserve the first native Kimi tool-call ID while rewriting repeated replay occurrences to deterministic OpenAI-style IDs and keeping paired tool results aligned. Moonshot responses-family behavior and providers that do not opt in remain unchanged.
Closes#51593
Co-authored-by: Pluviobyte <Pluviobyte@users.noreply.github.com>
Non-control-UI-visible runs previously dropped assistant commentary on the
floor for session message subscribers. Mirror those events to exact session
subscribers, gated strictly on phase === "commentary" so untagged text or
delta frames and final-answer streaming never dual-lane into channel
surfaces. Dialects that emit commentary as untagged deltas should tag the
phase at provider normalization instead.
Co-authored-by: Forge <forge@psiclawops.dev>
Co-authored-by: Chisel <chisel@psiclawops.dev>
Moonshot/Kimi requires reasoning_content on all assistant tool-call messages
when thinking is enabled. After LCM compaction, cross-model fallback, or
session repair, the replayed history may be missing this field, causing a
400 error from the Moonshot API.
Backfill an empty string to satisfy the API schema contract without
fabricating semantic reasoning content. Follows the same provider-owned
backfill pattern already used by Kimi Coding (extensions/kimi-coding/stream.ts)
and DeepSeek V4 (provider-stream-shared.ts).
Fixes#71491
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(moonshot): add Kimi K2.7 Code support
* test(moonshot): surface K2.7 live provider errors
* ci(live): accept Kimi key for Moonshot sweeps
* test(moonshot): verify K2.7 across API regions
When a message send action included an `image` media-source param, the shared outbound runner recognized it for sandbox validation and media-access hints but then omitted it from the generic send payload, causing text-only delivery with a silent ok:true result.
Add `image` to the mediaHint resolution chain in buildSendPayloadParts so it is treated as a first-class media source for send only, preserving action-specific image semantics for non-send actions. Add regression coverage.
Fixes#92407.
Summary:
- The PR removes the Anthropic Vertex adapter’s redundant cache-control payload-policy pass, forwards caller payload hooks unchanged, and adds regressions for preserving transport-budgeted payloads.
- PR surface: Source -35, Tests -11. Total -46 across 2 files.
- Reproducibility: yes. at source level. Current main reapplies cache policy to a finalized, fully budgeted pa ... ion logs show the corresponding five-marker rejection; this review did not run a live post-fix GCP request.
Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.
Validation:
- ClawSweeper review passed for head 6ef19602bf.
- Required merge gates passed before the squash merge.
Prepared head SHA: 6ef19602bf
Review: https://github.com/openclaw/openclaw/pull/92387#issuecomment-4688955121
Co-authored-by: openperf <16864032@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>
Fixes #25621.\n\nKeep gateway status readable on unsupported service-manager platforms by returning a conservative read-only service adapter, while lifecycle mutations still reject clearly. Includes regression coverage for resolver, status, summary, and lifecycle behavior.\n\nVerified with focused Vitest/oxlint/diff checks, autoreview, and Azure Crabbox check:changed on lanes core/coreTests.
Adds a SQLite state query-plan regression test and smoke benchmark, wires the smoke artifact into source performance evidence, validates SQLite smoke output in the performance summary, and removes a retired ClawHub nav entry that broke docs link checks.
Fixes#91616
- Prefer small fixes at the right ownership boundary; no refactor unless it clearly improves the bug class.
- When an accepted finding shows a bug class or repeated pattern, inspect the current PR scope for sibling instances before fixing.
- Fix the scoped bug class at once when practical; stop at touched surfaces, owner boundaries, and clear follow-up territory.
- Keep going until structured review returns no accepted/actionable findings.
- Keep going until structured review returns no accepted/actionable findings only while the work remains inside the original task scope.
- 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.
@@ -43,6 +43,42 @@ Use when:
- 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.
## Scope Governor
Autoreview is a closeout gate, not permission to rewrite the task.
Before the first review, freeze a scope baseline: original request or issue, target branch, intended behavior, owner boundary, changed files, and non-test LOC. For inherited or already-bloated branches, use the intended PR diff as the baseline rather than accepting all existing branch drift.
Before patching a finding, classify it:
- **In-scope blocker**: the finding is introduced by the current diff, affects the same owner boundary, and can be fixed without changing the task's contract.
- **Follow-up**: the finding is real but belongs to an adjacent bug class, sibling surface, cleanup, or broader hardening track.
- **Stop-and-escalate**: the finding requires a new protocol/config/storage/public API contract, a different owner boundary, a release-process change, or a design choice outside the original request.
Stop patching and report the scope break instead of continuing when:
- a narrow PR turns into an architecture change, protocol change, migration, or release-process change;
- the diff grows past 2x the original files or non-test LOC without explicit approval to expand scope;
- two review-triggered patch cycles have not converged; pause and reclassify every remaining finding before another edit;
- the best fix is "define the canonical contract first" rather than another local inference layer;
- fixing the accepted finding would make the PR no longer describe the same behavior, issue, or owner boundary.
After the two-cycle pause, continue only when every remaining accepted finding is still an in-scope blocker. Otherwise preserve the useful analysis, identify the smallest safe landed subset if one exists, and open or request a follow-up for the larger fix. Do not keep committing speculative fixes just to satisfy the reviewer.
Do not stack or push review-triggered fix commits while scope classification or focused proof is unresolved. Keep exploratory edits local until the cycle is proven in scope; if scope breaks, remove them from the landing lane instead of preserving them as branch history.
Critical exceptions must be explicit: active data loss, crash, broken install/upgrade, release blocker, or concrete security exposure. If the exception is not one of those, it is not critical enough to blow up scope.
## Release Branches And Release Process
On release, beta, stable, hotfix, signing, notarization, appcast, package-publish, or release-check work, use freeze discipline even when the branch name is not release-like:
- Fix only release blockers, failed release infrastructure, exact backports, install/upgrade breakage, data loss, crashes, or concrete security exposure.
- Treat non-blocking autoreview findings as follow-ups for `main`, not reasons to broaden the release branch.
- Do not introduce new product behavior, config surface, protocol shape, migration, plugin ownership, docs narrative, or process policy unless it directly unblocks the release.
- Keep proof tied to the release target: exact branch/ref, failing check or shipped-risk reason, smallest command/proof, and whether the fix must also forward-port to `main`.
- If review discovers a real but non-critical design problem during release closeout, stop with a follow-up issue/PR plan; do not use the release branch as the refactor lane.
description: Post an approved message as the logged-in Discord user through the Discord desktop app. Use for release announcements or other direct user-authored Discord posts; not for OpenClaw channel sends, bots, webhooks, relays, agent sessions, or archive search.
---
# Discord User Post
Use `$computer-use` to operate `/Applications/Discord.app` in the user's
existing logged-in session. This workflow represents the user directly.
## Prepare
1. Draft the complete final message outside Discord.
2. Confirm the intended server and channel with the user when either is
ambiguous.
3. Open Discord and navigate to the exact destination without entering the
message.
4. Verify the visible server name, channel header, and logged-in account.
Do not infer the target from unrelated Discord content. Stop if Discord is not
logged in, the account is wrong, or the exact destination cannot be verified.
## Confirm and Post
Posting is representational communication. Follow the `$computer-use`
confirmation policy even when the user previously asked for an announcement:
1. Show the user the exact final body and verified destination.
2. Request action-time confirmation before typing into Discord.
3. After confirmation, enter the approved body unchanged.
4. Visually inspect the composed message and destination again.
5. Send once.
If the body or destination changes after confirmation, request confirmation
again before sending.
## Verify
- Confirm the message appears once, from the user's account, in the intended
channel.
- Report the server, channel, and visible send result.
- Do not edit, delete, react, or send a follow-up without the corresponding
user instruction and confirmation.
## Guardrails
- Never use `openclaw message`, an OpenClaw agent, a Discord bot, webhook, relay,
or token for this workflow.
- Never expose private Discord content or account details in public output.
- Never send a draft, partial message, duplicate, or unreviewed attachment.
- For Discord archive/history/search, use `$discrawl` instead.
`This audited record covers the complete ${base}..${target} history: ${pullRequests.length} PRs and ${issues.length} linked issues. The grouped notes above prioritize user impact; this ledger preserves every contribution reference and eligible human credit.`,
- 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.
- When landing or merging any PR, follow the global `/landpr` process.
- When an agent is landing or merging a PR targeting `main`, use only the repo-native `scripts/pr` wrapper: run `scripts/pr review-init <PR>`, follow its emitted checkout/guard guidance, initialize and complete review artifacts with `scripts/pr review-artifacts-init <PR>`, validate them with `scripts/pr review-validate-artifacts <PR>`, then run `scripts/pr prepare-run <PR>` and `scripts/pr merge-run <PR>`.
- Use `scripts/committer "<msg>" <file...>` for scoped commits instead of manual `git add` and `git commit`.
- Keep commit messages concise and action-oriented.
- Group related changes; avoid bundling unrelated refactors.
- Judges default to `openai/gpt-5.4,thinking=xhigh,fast` and `anthropic/claude-opus-4-6,thinking=high`.
- Report includes judge ranking, run stats, durations, and full transcripts; do not include raw judge replies. Duration is benchmark context, not a grading signal.
- Candidate and judge concurrency default to 16. Use `--concurrency <n>` and `--judge-concurrency <n>` to override when local gateways or provider limits need a gentler lane.
- Scenario source should stay markdown-driven under `qa/scenarios/`.
- Scenario source is YAML-only under `qa/scenarios/`: use `index.yaml` and
per-scenario `*.yaml` files with top-level `title`, `scenario`, and optional
`flow`. Never add fenced `qa-scenario` / `qa-flow` Markdown files.
- For isolated character/persona evals, write the persona into `SOUL.md` and blank `IDENTITY.md` in the scenario flow. Use `SOUL.md + IDENTITY.md` only when intentionally testing how the normal OpenClaw identity combines with the character.
- Keep prompts natural and task-shaped. The candidate model should receive character setup through `SOUL.md`, then normal user turns such as chat, workspace help, and small file tasks; do not ask "how would you react?" or tell the model it is in an eval.
- Prefer at least one real task, such as creating or editing a tiny workspace artifact, so the transcript captures character under normal tool use instead of pure roleplay.
@@ -234,7 +236,8 @@ pnpm openclaw qa manual \
## Repo facts
- Seed scenarios live in `qa/`.
- Seed scenarios live in `qa/scenarios/index.yaml` and
`qa/scenarios/<theme>/*.yaml`.
- Main live runner: `extensions/qa-lab/src/suite.ts`
@@ -16,6 +16,10 @@ Use this with `$release-openclaw-maintainer` and `$openclaw-testing` when a rele
- 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.
- A model-list response proves authentication, not billing or inference
entitlement. Mandatory live providers must pass a real completion probe
before release dispatch. Fix the credential first; do not add an alternate
auth path merely to bypass a failed release credential.
- Full Release Validation parent monitors fail fast: once a required child job
fails, the parent cancels the remaining child matrix and prints the failed
job summary. Inspect that first red job instead of waiting for unrelated
@@ -36,6 +40,8 @@ git rev-parse HEAD
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.
The Anthropic check performs a tiny message completion so exhausted or
non-billable credentials fail before the expensive release matrix.
## Dispatch
@@ -65,6 +71,13 @@ gh workflow run openclaw-performance.yml \
Prefer the trusted workflow on `main`, target the exact release SHA:
- Keep trusted-workflow checks compatible with frozen release targets. If
`main` adds a target-owned guard script or package command after the release
branch cut, make the trusted workflow skip only when that target surface is
absent. Heal the trusted workflow before rerunning validation; do not port an
unrelated runtime refactor or mutate the release candidate just to satisfy a
newer `main`-only check.
```bash
gh workflow run full-release-validation.yml \
--repo openclaw/openclaw \
@@ -106,7 +119,10 @@ Stop watchers before ending the turn or switching strategy.
--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.
4. For secret-looking failures, validate a real completion from the same secret source before editing code. A successful model-list request is insufficient.
Claude CLI subscription credentials are a separate native auth path; prove
them in a clean-home CLI probe, never as a substitute for a required
Anthropic API-key lane.
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.
workflow_run: # zizmor:ignore[dangerous-triggers] trusted PR commenter; job gates repository, source event, workflow name, live open PR, and exact current head before reading artifacts or writing comments
if (escaped.length + encoded.length > maxEncodedLength) {
break;
}
escaped += encoded;
}
return `\`${escaped || "-"}\``;
};
const rows = (findings ?? []).map((finding) => {
const location = String(finding.location ?? "");
const [file, line] = location.split(":");
return {
file: file ? `apps/ios/${file}` : "",
line: line || "",
kind: String(finding.kind ?? ""),
name: String(finding.name ?? ""),
};
});
let mode = "failure";
let body = `${marker}\n`;
if (scanSkipped) {
mode = "skipped";
body += [
"### iOS Periphery",
"",
"Periphery scan skipped because the pull request is a draft or no longer touches iOS scan scope.",
].join("\n");
} else if (findings === null) {
body += [
"### iOS Periphery",
"",
"Periphery did not complete or its report could not be safely read. Check the workflow run for details.",
].join("\n");
} else if (rows.length === 0 && status === 0) {
mode = "success";
body += [
"### iOS Periphery",
"",
"No dead Swift code found.",
].join("\n");
} else if (rows.length > 0) {
const shown = rows.slice(0, 50);
body += [
"### iOS Periphery",
"",
`Found ${rows.length} dead Swift code ${rows.length === 1 ? "symbol" : "symbols"}. Remove the code or add a narrow Periphery exemption with a comment explaining why it must stay.`,
rows.length > shown.length ? `Showing first ${shown.length}; full JSON is in the workflow artifact.` : null,
].filter(Boolean).join("\n");
} else {
body += [
"### iOS Periphery",
"",
"Periphery exited with a non-zero status before producing findings. Check the workflow artifact for stdout/stderr.",
].join("\n");
}
body += "\n";
const maxCommentChars = 60_000;
if (body.length > maxCommentChars) {
body = [
marker,
"### iOS Periphery",
"",
`Found ${rows.length} dead Swift code ${rows.length === 1 ? "symbol" : "symbols"}. The rendered report exceeded the safe comment limit; use the workflow artifact for details.`,
Additional gateway boot, memory, plugin pressure, mock hello-loop, and CLI startup numbers are in [source/index.md](source/index.md).
Additional gateway boot, memory, plugin pressure, mock hello-loop, CLI startup, and SQLite state smoke numbers are in [source/index.md](source/index.md).
clawhub_line="- plugin ClawHub publish: dispatched separately, not awaited by this proof: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${plugin_clawhub_run_id}"
windows_line="- Windows Hub promotion: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${windows_node_run_id} from openclaw/openclaw-windows-node@${WINDOWS_NODE_TAG}"
if approve_child_publish_environment plugin-clawhub-release.yml "${plugin_clawhub_run_id}"; then
:
else
echo "- plugin-clawhub-release.yml: child environment gate not ready; publish was left dispatched (${plugin_clawhub_run_id})" >> "$GITHUB_STEP_SUMMARY"
if approve_child_publish_environment plugin-clawhub-release.yml "${plugin_clawhub_run_id}"; then
:
else
echo "- plugin-clawhub-release.yml: child environment gate not ready; publish was left dispatched (${plugin_clawhub_run_id})" >> "$GITHUB_STEP_SUMMARY"
fi
echo "- plugin-clawhub-release.yml: publish not awaited (${plugin_clawhub_run_id})" >> "$GITHUB_STEP_SUMMARY"
else
echo "- plugin-clawhub-release.yml: no normal OIDC publish to await" >> "$GITHUB_STEP_SUMMARY"
fi
if [[ -n "${plugin_clawhub_bootstrap_run_id}" ]]; then
if [[ "${plugin_clawhub_bootstrap_completed}" == "true" ]]; then
echo "- plugin-clawhub-new.yml: bootstrap already completed before continuing" >> "$GITHUB_STEP_SUMMARY"
if approve_child_publish_environment plugin-clawhub-new.yml "${plugin_clawhub_bootstrap_run_id}"; then
:
else
echo "- plugin-clawhub-new.yml: child environment gate not ready; bootstrap was left dispatched (${plugin_clawhub_bootstrap_run_id})" >> "$GITHUB_STEP_SUMMARY"
fi
echo "- plugin-clawhub-new.yml: bootstrap not awaited (${plugin_clawhub_bootstrap_run_id})" >> "$GITHUB_STEP_SUMMARY"
fi
else
echo "- plugin-clawhub-new.yml: no bootstrap publish to await" >> "$GITHUB_STEP_SUMMARY"
fi
echo "- plugin-clawhub-release.yml: publish not awaited (${plugin_clawhub_run_id})" >> "$GITHUB_STEP_SUMMARY"
fi
openclaw_result=""
@@ -996,6 +1351,7 @@ jobs:
failed=0
openclaw_failed=0
windows_node_run_id=""
if [[ -n "${openclaw_pid}" ]] && ! wait "${openclaw_pid}"; then
failed=1
openclaw_failed=1
@@ -1011,6 +1367,12 @@ jobs:
if [[ -f "${clawhub_result}" && "$(cat "${clawhub_result}")" != "success" ]]; then
failed=1
fi
if [[ -n "${clawhub_bootstrap_pid}" ]] && ! wait "${clawhub_bootstrap_pid}"; then
failed=1
fi
if [[ -f "${clawhub_bootstrap_result}" && "$(cat "${clawhub_bootstrap_result}")" != "success" ]]; then
failed=1
fi
if [[ -n "${openclaw_npm_run_id}" && "${openclaw_failed}" == "0" ]]; then
clawhub package trusted-publisher set --help 2>&1 || true
)"
printf '%s\n' "${help_output}"
if ! grep -Fq "Usage: clawhub package trusted-publisher set" <<<"${help_output}"; then
echo "::error::CLAW-277 03 - Split OpenClaw plugin ClawHub publishing into OIDC release and token bootstrap workflows requires ${CLAWHUB_CLI_PACKAGE} to expose 'package trusted-publisher set' before token bootstrap publish can run. The pinned CLI returned parent help or no set command, so this workflow is stopping before creating a ClawHub package row."
exit 1
fi
for required_flag in --repository --workflow-filename; do
if ! grep -Fq -- "${required_flag}" <<<"${help_output}"; then
echo "::error::CLAW-277 03 - Split OpenClaw plugin ClawHub publishing into OIDC release and token bootstrap workflows requires ${CLAWHUB_CLI_PACKAGE} trusted-publisher set help to include ${required_flag}."
echo "::error::One or more ClawHub packages exist but do not have trusted publishing configured. Configure trusted publishing before running the normal OIDC publish workflow."
echo "::error::One or more ClawHub packages do not exist yet and require the token-gated Plugin ClawHub New bootstrap workflow before normal OIDC publish can run."
jq -r '.bootstrapCandidates[]? | "::error::Bootstrap required: \(.packageName)@\(.version). Dispatch plugin-clawhub-new.yml for this package, then rerun the normal release."' .local/plugin-clawhub-release-plan.json
exit 1
- name:Fail manual publish when target versions already exist
@@ -172,7 +172,7 @@ Skills own workflows; root owns hard policy and routing.
- PR artifacts/screenshots: attach to PR/comment/external artifact store. Never push screenshots, videos, proof images, or proof assets to OpenClaw or any product repo branch, including temp artifact branches. Use Crabbox artifact publishing plus the manifest URL. Do not commit `.github/pr-assets`.
- CI polling: exact SHA, relevant checks only, minimal fields. Skip routine noise (`Auto response`, `Labeler`, docs agents, performance/stale). Logs only after failure/completion or concrete need.
- OpenClaw write-access maintainers may skip `Real behavior proof` when local tests or Crabbox verified behavior; record proof in PR verification.
-`/landpr`: use `~/.codex/prompts/landpr.md`; do not idle on `auto-response` or `check-docs`.
-Agent PR landing to `main`: use only the repo-native `scripts/pr` wrapper: run `scripts/pr review-init <PR>`, follow its emitted checkout/guard guidance, initialize and complete review artifacts with `scripts/pr review-artifacts-init <PR>`, validate them with `scripts/pr review-validate-artifacts <PR>`, then run `scripts/pr prepare-run <PR>` and `scripts/pr merge-run <PR>`; do not idle on `auto-response` or `check-docs`.
## Code
@@ -214,6 +214,7 @@ Skills own workflows; root owns hard policy and routing.
- Vitest. Colocated `*.test.ts`; e2e `*.e2e.test.ts`; example models `sonnet-4.6`, `gpt-5.5`; test GPT with 5.5 preferred, 5.4 ok; no GPT-4.x agent-smoke defaults.
- Prefer behavior tests over workflow/docs string greps. Put operator policy reminders in AGENTS/docs.
- QA scenario sources are YAML only: `qa/scenarios/index.yaml` and `qa/scenarios/<theme>/*.yaml`. Do not add fenced `qa-scenario`/`qa-flow` Markdown files under `qa/scenarios/`.
<li>Telegram and WhatsApp channel delivery are richer and less brittle: Telegram can send structured rich text with tables, lists, expandable blockquotes, preserved intentional line breaks, prompt-preserving CLI backend delivery, retired native draft migration, and safer rich-media boundaries, while WhatsApp now honors configured ACP bindings. (#92679, #93164, #84082, #89421, #92513) Thanks @obviyus, @jzakirov, @spacegeologist, and @TurboTheTurtle.</li>
<li>Agent and Gateway recovery is sharper across account-scoped DM sends, generated media completions, auto-reply message-tool final replies, reset archive fallback reads, restart shutdown aborts, yielded subagent pauses, trusted subagent thinking override fallback, yielded cron media, heartbeat dedupe, session identity prompts, and unknown OpenAI agent selector rejection. (#92788, #91246, #92879, #91357, #92631, #92412, #92146, #91287, #92468, #92510) Thanks @yetval, @TurboTheTurtle, @masatohoshino, @CadanHu, @ooiuuii, @openperf, @IWhatsskill, @ZengWen-DT, and @zhangguiping-xydt.</li>
<li>Provider/model handling expands and tightens with GLM-5.2, Claude Haiku 4.5 catalog rows, OpenRouter and Google Vertex provider-prefix normalization, managed SecretRef auth, OAuth image-default routing through Codex, bounded model browse discovery, LM Studio binary thinking-off delivery, storeless OpenAI Responses replay gating, invalid OpenAI reasoning-signature and genericized Anthropic thinking-signature recovery, Claude 4.5 Copilot tool-streaming safety, and OpenAI/Anthropic-family payload quarantine for unreadable or post-hook tool schemas. (#92796, #90116, #92627, #91218, #90686, #92824, #92247, #92002, #90706, #92941, #92201, #92916, #75393, #92908, #92921, #92928) Thanks @arkyu2077, @liuhao1024, @bymle, @rohitjavvadi, @nxmxbbd, @bek91, @samson910022, @mmyzwl, @CarlCapital, @snowzlm, @Kailigithub, and @vincentkoc.</li>
<li><code>/usage</code> and reply payload hooks now have a native full footer renderer, default template, fixed-decimal formatting, credential-aware limits, better partial-count handling, and warnings for broken templates instead of silent bad output. (#92657, #89835, #89629) Thanks @Marvinthebored.</li>
<li>UI and mobile flows are steadier: workspace files can collapse and start collapsed, WebChat backscroll survives streaming, the sidebar session picker remains interactive above the desktop workbench, reset soft args survive UI dispatch, stale dashboard session parent lineage is preserved, and iOS reconnects stale foreground gateways. (#92779, #92622, #92705, #91353, #90658, #92552) Thanks @shakkernerd, @TurboTheTurtle, @NianJiuZst, @zhouhe-xydt, @luoyanglang, and @Solvely-Colin.</li>
<li>Memory, state, and diagnostics recover cleaner: oversized OpenAI embedding batches split before 431s, QMD memory search stays available in transient mode, SQLite avoids WAL on NFS state volumes, stuck-session recovery scheduling no longer resets warning backoff, full memory reindexes preserve rollback/cache recovery, raw Memory Wiki source pages stop looking malformed, and Infinity chunk limits stay genuinely unbounded. (#92650, #92618, #92639, #91247, #92752, #92881, #59137, #92876, #69700, #92735) Thanks @mushuiyu886, @TurboTheTurtle, @849261680, @gnanam1990, @TSHOGX, @arlen8411, and @yhterrance.</li>
</ul>
<h3>Changes</h3>
<ul>
<li>Providers/models: add GLM-5.2 support and Claude Haiku 4.5 catalog entries while keeping provider-qualified model IDs normalized across OpenRouter and Google Vertex paths. (#92796, #90116, #92627, #91218) Thanks @arkyu2077, @liuhao1024, and @bymle.</li>
<li>Web search: keep key-free providers such as Parallel Free, DuckDuckGo, Ollama, and Codex Hosted Search as explicit opt-ins instead of selecting them automatically when no API-backed provider is configured. (#93616) Thanks @davemorin and @vincentkoc.</li>
<li>Channel plugins: ship Telegram rich-message delivery and WhatsApp ACP binding support, including preserved intentional line breaks, rich prompt handoff to CLI backends, and transport fixtures for richer drafts. (#92679, #93164, #92513) Thanks @obviyus and @TurboTheTurtle.</li>
<li>Agent commands: support <code>/btw</code> in CLI-backed sessions and keep CLI usage-error exits classified as usage failures instead of successful runs. (#92669, #92162) Thanks @joshavant and @Pandah97.</li>
<li>Usage hooks: add built-in full footer rendering, default footer templates, per-turn usage state, credential-aware limits, and fixed-decimal formatting for usage-bar templates. (#92657, #89835, #89629) Thanks @Marvinthebored.</li>
<li>Docs and operator guidance: document node config examples, clarify before-install hook scope, correct agent default concurrency comments, refresh ZAI provider docs, and update channel/group docs for current Telegram and WhatsApp behavior. (#92677, #92766, #92695) Thanks @liuhao1024, @sallyom, and @ArielSmoliar.</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Channels and delivery: preserve account-scoped DM channel send policy, intentional rich-message line breaks in Telegram and status output, rich Telegram final replies, rich Telegram tables and lists, Telegram thread-create CLI remapping, Feishu dynamic-agent routes after persisted binding reuse, Slack outbound <code>message_sent</code> hooks, contributed message-tool schema optionality, same-channel generated media completions, and channel chunking around surrogate pairs and Infinity limits. (#92788, #93164, #92679, #89421, #89943, #42837, #92814, #91137, #91246, #92735) Thanks @yetval, @obviyus, @spacegeologist, @rishitamrakar, @liuhao1024, @lundog, @TurboTheTurtle, and @yhterrance.</li>
<li>Discord: give generated auto-thread titles a 60-second timeout and 4,096-token reasoning-model output budget, clamped to the selected model output cap. (#64734) Thanks @hanamizuki.</li>
<li>Agent, cron, and Gateway runtime: mark active main sessions before restart shutdown aborts, pause yielded subagent runs whose terminal also signals abort, clamp trusted subagent thinking overrides through provider/model fallback, preserve yielded media completions, deliver channel message-tool final replies through auto-reply while hiding internal delivery hints, restore reset archive fallback reads when active async transcripts are missing, de-duplicate main-session heartbeat events, expose session identity in runtime prompts, reject unknown OpenAI agent selectors, keep generated media completions, slash-command block replies, and trajectory export commands in WebChat, and require admin privileges for HTTP session/model override surfaces. (#91357, #92631, #92412, #92146, #92879, #91287, #92468, #92510, #91246, #92651, #92646) Thanks @ooiuuii, @openperf, @IWhatsskill, @masatohoshino, @CadanHu, @ZengWen-DT, @zhangguiping-xydt, and @TurboTheTurtle.</li>
<li>Providers and model replay: preserve storeless OpenAI Responses replay compatibility, recover invalid OpenAI reasoning signatures and genericized Anthropic thinking-signature replay errors, route OAuth image defaults through Codex for eligible OpenAI profiles, avoid eager tool streaming for Claude 4.5 in Copilot, quarantine unreadable and post-hook OpenAI/Anthropic-family tool schemas without broadening allowed tool choices, deliver explicit thinking-off requests to LM Studio binary-thinking models, honor profile auth for SecretRef model entries, bound model browsing, strip provider prefixes where runtimes need bare IDs, and surface nested embedding fetch failures. (#90706, #92941, #92201, #92916, #92824, #75393, #92908, #92921, #92928, #92002, #90686, #92247, #92627, #91218, #92628) Thanks @snowzlm, @mmyzwl, @CarlCapital, @bek91, @Kailigithub, @vincentkoc, @rohitjavvadi, @samson910022, @nxmxbbd, @liuhao1024, @bymle, and @mushuiyu886.</li>
<li>Memory, state, diagnostics, and config: split header-too-large embedding batches, keep QMD memory search enabled in transient mode, avoid SQLite WAL on NFS volumes, preserve recovery scheduling outside stuck-session warning backoff, preserve full-reindex rollback/cache recovery, treat raw Memory Wiki source pages as source evidence, and keep shell environment fallbacks contained in config write tests. (#92650, #92618, #92639, #91247, #92752, #92881, #59137, #92876, #69700) Thanks @mushuiyu886, @TurboTheTurtle, @849261680, @gnanam1990, @TSHOGX, and @arlen8411.</li>
<li>Plugins and updates: repair missing required platform packages during managed plugin installs and updates, including omitted Codex platform binaries.</li>
<li>Dependencies: update Hono to 4.12.25 so published OpenClaw and ACPX packages use the patched runtime.</li>
<li>Release and test reliability: extend slow Gateway/full-suite watchdogs, split local full-suite shards when throttled, stabilize plugin auth marker fixtures, avoid brittle provider-ref error text, fold Telegram RTT sampling into live QA evidence, simplify QA scorecard mappings around canonical coverage IDs, keep QA Lab bootstrap selection assertions aligned with flow-only scenarios, skip QA coverage artifact consumers when runtime parity producer status is not green, keep Feishu lifecycle release checks pointed at the active fixture config, isolate trajectory-export live seed turns from Codex-native shell approvals, preserve release-check child refs while pinning expected SHAs, widen live OpenAI TTS budgets for slower provider responses, and avoid false downgrade prompts for unresolved latest-tag updates. (#92652, #92550, #92558, #92911) Thanks @RomneyDa and @Andy312432.</li>
</ul>
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
<li>Agent and Codex runtime recovery is steadier: subagents keep cwd/workspace separation, hook context stays prompt-local, session locks release on timeout abort while live OpenClaw locks survive cleanup, stale restart continuations are avoided, and Codex app-server/helper failures no longer tear down shared runtime state. (#87218, #86875, #87409, #87399, #87375, #88129)</li>
<li>Channel delivery and session identity got safer across outbound plugin hooks, Matrix room ids, iMessage reactions/approvals, Slack final replies, Discord recovered tool warnings, runtime-config message actions, WhatsApp profile auth roots, Telegram polling, and Microsoft Teams service URL trust checks. (#73706, #75670, #87366, #87451, #87334, #84535, #82492, #83304, #87160)</li>
<li>Mobile and chat surfaces got a broader refresh: the iOS Pro UI, hosted push relay default, realtime Talk tab playback, Gateway chat transport, onboarding, Talk permissions, WebChat reconnect delivery, and session picker behavior now preserve more state across reconnects and empty searches. (#87367, #87531, #87682, #88096, #88105) Thanks @ngutman.</li>
<li>Browser, channel, and automation inputs are stricter: Browser tool timeouts, viewport/tab indices, Gateway ports, cron retry handling, Discord component ids, schema array refs, Telegram callback pages, and channel progress callbacks now reject malformed values earlier and preserve the intended delivery context. (#82887)</li>
<li>Provider, media, and document coverage expands with Claude Opus 4.8, Fal Krea image schemas, NVIDIA featured models, MiniMax streaming music responses, encrypted PDF extraction, voice model catalogs, GitHub Copilot agent runtime support, and a Codex Supervisor plugin path for delegated Codex workflows. (#87845, #87890, #80775, #84764, #87751, #87794)</li>
<li>CLI, auth, doctor, and provider paths fail faster and recover more clearly: malformed numeric/version options are rejected, workspace dotenv provider credentials are ignored, heartbeat defaults, OAuth/token lifetimes, and local service startup requests are bounded, agent auth health labels are clearer, legacy <code>api_key</code> auth profiles migrate to canonical form, and restart guidance is actionable. (#87398, #86281, #87361, #88133, #83655, #87559, #88088, #85924) Thanks @vincentkoc and @giodl73-repo.</li>
<li>Plugin and Gateway hot paths do less repeated work while preserving cache correctness for install records, config JSON parsing, tool search catalogs, session stores, manifest model rows, auto-enabled plugin config, browser tokens, viewer assets, and release-split external plugin packages. (#86699)</li>
<li>Release, QA, and E2E validation now bound more log, artifact, harness, and cross-OS waits so failing lanes produce proof instead of hanging or false-greening.</li>
</ul>
<h3>Changes</h3>
<ul>
<li>Status: show active subagent details in status output.</li>
<li>Diffs: split the default language pack and expand default Diffs language coverage while keeping the host floor aligned. (#87370, #87372) Thanks @RomneyDa.</li>
<li>ClawHub: add plugin display names plus skill verification and trust surfaces. (#87354, #86699) Thanks @thewilloftheshadow and @Patrick-Erichsen.</li>
<li>iOS: refresh the dev app with Pro Command, Chat, Agents, Settings, hosted push relay defaults, and realtime Talk playback wired to gateway sessions, diagnostics, chat, and realtime Talk. (#87367, #88096, #88105) Thanks @Solvely-Colin and @ngutman.</li>
<li>PDF/tools: use ClawPDF for PDF extraction, support encrypted PDF extraction, and surface MCP structured content in agent tool results. (#87670, #87751)</li>
<li>Providers: add Claude Opus 4.8 support, Fal Krea image model schemas, NVIDIA featured model catalogs, MiniMax streaming music responses, and provider-backed voice model catalogs. (#87845, #87890, #80775, #84764, #87794) Thanks @eleqtrizit and @vincentkoc.</li>
<li>Codex/GitHub: add the GitHub Copilot agent runtime and the Codex Supervisor plugin package.</li>
<li>Plugins: externalize GitHub Copilot and Tokenjuice as official install-on-demand plugins with npm and ClawHub publish metadata.</li>
<li>Workboard: add agent coordination tools for tracking and handing off active agent work.</li>
<li>Discord: show commentary in progress drafts so live Discord runs expose useful in-progress context. (#85200)</li>
<li>Plugin SDK: add a reply payload sending hook for plugins that need to deliver channel-owned replies and flatten package types for SDK declarations. (#82823, #87165) Thanks @RomneyDa.</li>
<li>Agents: fall back to local config pruning when the optional <code>agents delete</code> Gateway probe cannot authenticate, so offline installs can still delete agents without removing shared workspaces.</li>
<li>Agents/Codex: keep spawned agent cwd/workspace state separated, forward ACP spawn attachments, keep hook context prompt-local, release session locks on timeout abort and runtime teardown without deleting live OpenClaw-owned locks during cleanup, avoid session event queue self-wait, clean up exec abort listeners, stream assistant deltas incrementally, recover raw missing-thread compaction failures, preserve rotated compaction session identity, keep compaction-timeout snapshots continuable, preserve shared app-server state across startup or helper failures, keep native hook relay alive across restarts and prune stale bridge files, close native hook relay replacement races, keep Claude live tool progress visible for watchdog recovery, suppress abandoned requester completion handoff, route workspace memory through tools, resolve Codex runtime models first, report quarantined dynamic tools, format <code>skills</code> command output, bind node auto-review to prepared plans, retry Claude CLI transcript probes, and bound compaction/steering retries. (#87218, #86875, #86123, #88129, #87399, #87375, #72574, #87383, #87400, #83022, #87671, #87738, #87747, #87706, #87546, #87541, #81048) Thanks @mbelinky, @Alix-007, @luoyanglang, @yetval, @sjf, @joshavant, and @benjamin1492.</li>
<li>Codex Supervisor: keep real-home app-server MCP session listing on the loaded state path, bound stored history scans, and close WebSocket probes cleanly.</li>
<li>Channels: thread canonical session keys into outbound hooks, preserve Matrix room-id case, keep fallback tool warnings mention-inert, retain delivered Slack final replies during late cleanup, continue iMessage polling after denied reactions, suppress duplicate native exec approvals, resolve Gateway message actions against the active runtime config, preserve Telegram SecretRef prompt config and polling keepalives, preserve WhatsApp profile auth roots, QR display, document filenames, and plugin hook config, suppress Discord recovered tool warnings, preserve the Discord voice outbound helper, cap Discord/Signal/Zalo channel request and container timeouts, and block untrusted Teams service URLs while keeping TeamsSDK patterns aligned. (#73706, #75670, #87366, #87451, #87465, #87334, #84535, #76262, #83304, #82492, #87581, #77114, #86426, #85529, #87160) Thanks @zeroaltitude, @lukeboyett, @xiaotian, @funmerlin, @joshavant, @eleqtrizit, @heyitsaamir, @amittell, @liorb-mountapps, @masatohoshino, @bladin, and @giodl73-repo.</li>
<li>CLI/auth/doctor/providers: reject malformed numeric/timeout/subcommand-version inputs, ignore workspace dotenv provider credentials, wait for respawn child shutdown, bound heartbeat defaults plus Codex, GitHub Copilot, OpenAI, Anthropic, Google, Feishu, LM Studio, MiniMax, Xiaomi TTS, and local-provider OAuth/token/model requests, harden Codex auth probes, label auth health by agent, preserve explicit agentRuntime pins during Codex model migration, warm provider auth off the main thread, honor Codex response timeouts, stop migrating current Claude Haiku 4.5 profiles to Sonnet, bound local service startup, resolve GPT-5.5 without cached catalog, migrate legacy memory auto-provider config, rewrite non-canonical <code>api_key</code> auth profiles, and make doctor restart follow-ups actionable. (#87398, #86281, #87361, #88133, #83655, #87559, #87719, #88088, #85924, #84362) Thanks @Patrick-Erichsen, @samzong, @giodl73-repo, @alkor2000, @mmaps, @nxmxbbd, and @vincentkoc.</li>
<li>Gateway/security/session state: expire browser tokens after auth rotation, scope assistant idempotency dedupe, drain probe client closes, avoid stale restart continuation reuse, preserve retry-after fallbacks and stale rate-limit cooldown probes, bound webchat image and artifact transcript scans, include seconds in inbound metadata timestamps, clear completed session active runs, clear stale chat stream buffers, and evict current plugin-state namespaces at row caps. (#87810, #87833, #75089) Thanks @joshavant and @litang9.</li>
<li>Config/parsing/network: reject partial numeric parsing, parse provider/Discord retry headers and dates strictly, honor IPv6 and bare IPv6 <code>no_proxy</code> entries, preserve empty plugin allowlists, canonicalize secret target array indexes, and reject malformed media content lengths, inspected TCP ports, marketplace content lengths, cron epochs, sandbox stat fields, unsafe duration values, empty config path segments, noncanonical schema array refs, unsafe Telegram callback pages, and invalid Teams attachment-fetch DNS targets. (#87883) Thanks @zhangguiping-xydt.</li>
<li>Cron/automation: retry recurring jobs after transient model rate limits before waiting for the next scheduled slot, and preflight model fallbacks before skipping scheduled work. (#82887)</li>
<li>Auto-reply/directives: respect provider and relayed channel metadata during directive persistence so channel-originated decisions keep their intended context. (#87683)</li>
<li>WhatsApp: resolve the auth directory from the active profile so profile-scoped WhatsApp installs do not drift to the wrong credential root. (#82492)</li>
<li>Gateway/session state: clear completed session active runs, avoid cold-loading providers for MCP inventory, cache single-session child indexes, cap handshake timers, and bound preauth, auth-guard, media, transcript, readiness, and port options.</li>
<li>Channels/replies: preserve channel-owned progress callbacks when verbose output is off, keep group-room progress suppression intact, prefer external session delivery context, escape Discord component id delimiters, force final TUI chat repaints, show Slack reasoning previews, and normalize Discord/Matrix/Mattermost channel numeric options. (#87476, #87423)</li>
<li>Agents/tool args: harden smart-quoted argument repair for edit arrays and exact escaped arguments so model-produced tool calls recover without corrupting valid input. (#86611)</li>
<li>Providers/agents: preserve seeded Anthropic signatures, preserve signed thinking payloads, concatenate signature-delta chunks, preserve DeepSeek <code>reasoning_content</code> replay across tier suffixes, apply OpenRouter strict9 ids to Mistral routes, promote Ollama plain-text tool calls, load NVIDIA featured model catalogs, stream MiniMax music generation responses, and recover empty preflight compaction. (#87593, #87493, #80775, #84764) Thanks @eleqtrizit.</li>
<li>Media/images: skip CLI image cache refs when resolving generated images, allow trusted generated HTML attachments, and bound generated video downloads so stale refs and slow providers fail cleanly. (#87523, #87982)</li>
<li>File transfer: handle late tar stdin pipe errors after archive validation or unpacking has already settled.</li>
<li>Performance: trust install-record caches between reloads, prefer native JSON parsing, reuse unchanged tool-search catalogs, reuse gateway session and plugin metadata paths, skip unchanged store serialization, patch single-entry session writes, add precomputed session patch writers, reduce store clone allocations, cache manifest model catalog rows and auto-enabled plugin config, avoid full session snapshots for entry reads, defer configured Slack full startup, prefer bundled plugin dist entries, and slim current metadata identity caches. (#87760)</li>
<li>Docker/release/QA: package runtime workspace templates, stream cross-OS served artifacts, preserve sparse Crabbox run artifacts, isolate npm plugin installs per package, reject incompatible package plugin API installs, drop the leftover root Sharp dependency from package manifests after the Rastermill migration, bound OpenClaw instance logs, plugin gauntlet relay logs, MCP channel buffers, kitchen-sink scans, agent-turn assertions, QA-Lab credential broker calls, QA Matrix substrate requests, and release scenario logs, and keep release/google live guards current. (#87647, #87477) Thanks @rohitjavvadi and @vincentkoc.</li>
<li>Release/CI: bound manual git fetches, ClawHub verifier responses, ClawHub owner metadata, dependency-guard error bodies, Parallels limits, startup/test/memory budget parsing, and diffs viewer build warnings so release lanes fail with useful proof instead of hanging. (#87839)</li>
</ul>
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
Maintenance update for the current OpenClaw Android release.
## 2026.6.2 - 2026-06-02
OpenClaw is now available on Android.
Connect to your OpenClaw Gateway to chat with your assistant, use realtime Talk mode, review approvals, and bring Android device capabilities like camera, location, screen, and notifications into your private automation workflows.
`bun run android:bundle:release` auto-bumps Android `versionName`/`versionCode` in `apps/android/app/build.gradle.kts`, then builds two signed release bundles:
Android release archives use the pinned version in `apps/android/version.json`. Update it with:
- Play build: `apps/android/build/release-bundles/openclaw-<version>-play-release.aab`
The signing sync pulls encrypted Android upload-key assets from the shared `apps-signing` repo and materializes decrypted files under `apps/android/build/release-signing/`.
Generate raw Google Play screenshots:
```bash
pnpm android:screenshots
```
`pnpm android:release:archive` builds signed release artifacts into `apps/android/build/release-artifacts/` and writes `.sha256` checksum files:
- Play build: `openclaw-<version>-play-release.aab`
When generating `apps/android/fastlane/metadata/android/en-US/release_notes.txt`, the tooling reads the first available changelog section in this order:
1. exact pinned version, for example `## 2026.6.2`
2.`## Unreleased`
Recommended workflow:
- while iterating on a Play internal testing train, keep pending notes under `## Unreleased`
- before the production release, move or copy the final notes under `## <pinned version>` and run sync again
## Release Workflow
1. Pin Android to the intended release version.
2. Run `pnpm android:version:sync`.
3. Update `apps/android/CHANGELOG.md`, then run `pnpm android:version:sync` again if needed.
4. Run `MATCH_PASSWORD=<signing repo password> pnpm android:release:signing:sync:pull` to materialize encrypted Android signing assets from `apps-signing`.
5. Run `pnpm android:release:preflight` to validate Play auth, signing, synced versioning, and release notes.
6. Run `pnpm android:screenshots` to refresh raw Google Play screenshots.
7. Run `pnpm android:release:archive` to produce the signed Play AAB and third-party APK.
8. Run `pnpm android:release:upload` to upload metadata, screenshots, and the Play AAB to Google Play internal testing.
9. Promote to production manually in Google Play Console.
The third-party flavor is archived as a signed APK for non-Play distribution. It is not uploaded by the Play release lane.
## Signing model
`apps/android/Config/ReleaseSigning.json` pins the Android signing assets in the shared private `apps-signing` repo. The Android pipeline uses the same `MATCH_PASSWORD` release-owner secret as iOS, but the Android files are managed by `scripts/android-release-signing.mjs` instead of Fastlane `match`.
`sync:pull` decrypts the Play upload keystore and Gradle signing properties into `apps/android/build/release-signing/`. That directory is gitignored, and Fastlane exports the materialized values as Gradle project properties for the current release command.
If `MATCH_PASSWORD` is not set, the existing manual Gradle-property signing path still works: provide `OPENCLAW_ANDROID_STORE_FILE`, `OPENCLAW_ANDROID_STORE_PASSWORD`, `OPENCLAW_ANDROID_KEY_ALIAS`, and `OPENCLAW_ANDROID_KEY_PASSWORD` through your local Gradle user properties before running release tasks.
Create a Google Play service account JSON key with Google Play Developer API access, then grant that service account access to the OpenClaw app in Play Console.
Android release signing uses the same private `apps-signing` repository and `MATCH_PASSWORD` secret as iOS, but with Android-specific encrypted assets. Pull the shared upload key before release validation:
The pull command materializes decrypted signing files under `apps/android/build/release-signing/`, which is gitignored. Later Fastlane release commands reload those materialized values and export them to Gradle for the current process.
For the first setup or rotation, provide the Play upload keystore and a local signing properties file, then push encrypted assets to `apps-signing`:
Store the Google Play upload key, not the irreplaceable app signing key, when Play App Signing is enabled.
Validate auth:
```bash
cd apps/android
fastlane android auth_check
```
Archive locally without upload:
```bash
pnpm android:release:archive
```
Generate deterministic Google Play screenshots:
```bash
pnpm android:screenshots
```
Upload metadata, release notes, and the Play AAB to the internal testing track:
```bash
pnpm android:release:upload
```
Direct Fastlane entry point:
```bash
cd apps/android
fastlane android release_upload
```
Release rules:
-`apps/android/version.json` is the pinned Android release version source.
-`apps/android/Config/Version.properties` is generated from that source and read by Gradle.
-`apps/android/CHANGELOG.md` is the Android-only changelog and release-note source.
-`apps/android/fastlane/metadata/android/en-US/release_notes.txt` is generated from that changelog by `pnpm android:version:sync`.
-`apps/android/Config/ReleaseSigning.json` pins the encrypted Android signing assets in the shared signing repo.
-`MATCH_PASSWORD` enables Fastlane to pull encrypted Android signing assets into `apps/android/build/release-signing/` before release validation or archive builds.
- Supported pinned Android versions use CalVer: `YYYY.M.D`.
-`versionCode` uses `YYYYMMDDNN`, where `NN` is a two-digit build number for the pinned version.
-`pnpm android:version:pin -- --from-gateway` promotes the current root gateway version into the pinned Android release version.
-`pnpm android:version:pin -- --version 2026.6.5 --version-code 2026060502` increments another build on the same Android release train.
-`pnpm android:version:sync` updates generated version artifacts.
-`pnpm android:version:check` validates checked-in Android version artifacts.
-`pnpm android:release:preflight` validates Google Play auth, Android release signing, synced versioning, release notes, and prints the package/track/version/versionCode that will be uploaded.
-`pnpm android:release:signing:sync:pull` pulls encrypted Android signing assets from `apps-signing`.
-`pnpm android:release:signing:sync:push` creates or refreshes encrypted Android signing assets in `apps-signing`.
-`pnpm android:screenshots` builds and installs the Play debug app, launches deterministic screenshot scenes, and captures raw PNGs.
-`pnpm android:release:archive` builds the signed Play AAB and third-party APK into `apps/android/build/release-artifacts/`.
-`pnpm android:release:upload` uploads the Play AAB to the configured Google Play track. The default track is `internal`.
- Production promotion remains manual in Google Play Console.
Screenshots:
- Android screenshot capture writes raw Play screenshots under `apps/android/fastlane/metadata/android/<locale>/images/phoneScreenshots/`.
- Set `SUPPLY_UPLOAD_SCREENSHOTS=1` to include those screenshots in `fastlane android metadata`.
- Do not commit generated screenshot captures unless they become intentional store metadata assets.
Connect to your OpenClaw Gateway to chat with your assistant, use realtime Talk mode, review approvals, and bring Android device capabilities like camera, location, screen, and notifications into your private automation workflows.
OpenClaw is a personal AI assistant you run on your own devices.
Pair this Android app with your OpenClaw Gateway to use your phone as a secure node for chat, voice, approvals, and device-aware automation.
What you can do:
- Pair with your private OpenClaw Gateway by QR code or setup code
- Chat with your assistant from Android
- Use realtime Talk mode and push-to-talk
- Review Gateway action approvals from your phone
- Enable device capabilities such as camera, screen, location, and notifications when you choose
- Receive push wakes and node status updates for connected workflows
OpenClaw is local-first: you control your gateway, keys, configuration, and permissions. Device access is managed by Android permissions and can be enabled only for the capabilities you want to use.
Getting started:
1) Set up your OpenClaw Gateway
2) Open the Android app and pair with your gateway
3) Start using chat, Talk mode, approvals, and automations from your phone
Connect to your OpenClaw Gateway to chat with your assistant, use realtime Talk mode, review approvals, and bring Android device capabilities like camera, location, screen, and notifications into your private automation workflows.
- Apple account signed into Xcode for automatic signing/provisioning
-App Store Connect API key set up in Keychain via `scripts/ios-asc-keychain-setup.sh` when auto-resolving a beta build number or uploading to TestFlight
- Apple account signed into Xcode for the canonical OpenClaw team (`FWJYW4S8P8`)
-Fastlane Apple Developer Portal session for the canonical OpenClaw team when creating bundle IDs or enabling services
- Release-owner access to the encrypted signing repo password (`MATCH_PASSWORD`)
- App Store Connect app already created for `ai.openclawfoundation.app`
- App Store Connect API key set up in Keychain via `scripts/ios-app-store-connect-keychain-setup.sh` when auto-resolving a build number or uploading to App Store Connect
Release behavior:
- Local development keeps using unique per-developer bundle IDs from `scripts/ios-configure-signing.sh`.
-Beta release uses canonical `ai.openclaw.client*` bundle IDs through a temporary generated xcconfig in `apps/ios/build/BetaRelease.xcconfig`.
-Beta release also switches the app to `OpenClawPushTransport=relay`, `OpenClawPushDistribution=official`, and `OpenClawPushAPNsEnvironment=production`.
-The beta flow does not modify `apps/ios/.local-signing.xcconfig` or `apps/ios/LocalSigning.xcconfig`.
- Local development uses the canonical `ai.openclawfoundation.app*` bundle IDs when the OpenClaw team is available, and unique `ai.openclawfoundation.app.test.*` bundle IDs only for non-canonical fallback teams.
-App Store release uses canonical `ai.openclawfoundation.app*` bundle IDs through a temporary generated xcconfig in `apps/ios/build/AppStoreRelease.xcconfig`.
-App Store release uses manual `Apple Distribution` signing with profile names pinned in `apps/ios/Config/AppStoreSigning.json`.
-Fastlane owns one-time Developer Portal setup, encrypted `match` signing sync to the repo/branch pinned in `apps/ios/Config/AppStoreSigning.json`, and release handling.
- App Store release also switches the app to `OpenClawPushTransport=relay`, `OpenClawPushDistribution=official`, `OpenClawPushAPNsEnvironment=production`, and a production `aps-environment` entitlement.
-`pnpm ios:release:upload` generates App Store screenshots and uploads release notes before archiving and uploading the IPA.
-`pnpm ios:release` remains a compatibility alias for `pnpm ios:release:upload`; prefer the explicit upload command in new release docs and automation.
- App Review submission is manual in App Store Connect. The release lane uploads a build and metadata, but does not submit for review.
- The release flow does not modify `apps/ios/.local-signing.xcconfig` or `apps/ios/LocalSigning.xcconfig`.
-`apps/ios/version.json` is the pinned iOS release version source.
-`apps/ios/CHANGELOG.md` is the iOS-only changelog and release-note source.
- The pinned iOS version must use CalVer like `2026.4.10`.
- That pinned value becomes:
-`CFBundleShortVersionString = 2026.4.10`
-`CFBundleVersion = next TestFlight build number for 2026.4.10`
-`CFBundleVersion = next App Store Connect build number for 2026.4.10`
- Changing the root gateway version does not change the iOS app version until you explicitly pin from the gateway.
- See `apps/ios/VERSIONING.md` for the full workflow.
Relay behavior for beta builds:
Relay behavior for App Store builds:
-Beta builds default to `https://ios-push-relay.openclaw.ai`.
-Release builds default to `https://ios-push-relay.openclaw.ai`.
- App Store Connect API auth uses Keychain for private key material plus non-secret `apps/ios/fastlane/.env` variables.
- The encrypted signing repo password lives outside this repo in the release-owner vault and is exposed locally as `MATCH_PASSWORD`.
- Apple Distribution private keys, certificates, provisioning profiles, and decrypted signing sync output stay under `apps/ios/build/` or Keychain and are gitignored.
- Rotating release signing means refreshing Fastlane `match` assets and pushing a fresh encrypted sync state.
Prepare the generated release xcconfig/project without archiving:
```bash
pnpm ios:release:prepare -- --build-number 7
```
Archive without upload:
```bash
pnpm ios:beta:archive
pnpm ios:release:archive
```
Archive and upload to TestFlight:
Archive and upload to App Store Connect:
```bash
pnpm ios:beta
pnpm ios:release:upload
```
If you need to force a specific build number:
```bash
pnpm ios:beta -- --build-number 7
pnpm ios:release:upload -- --build-number 7
```
### Maintainer Quick Release Checklist
Use this when a clone is missing local iOS release setup and you want the shortest path to a TestFlight upload.
Use this when a clone is missing local iOS release setup and you want the shortest path to an App Store Connect upload.
1. Confirm Fastlane auth is set up:
@@ -111,46 +142,58 @@ fastlane ios auth_check
2. If auth is missing, bootstrap it once on this Mac:
Use `pnpm ios:release:signing:setup` for the initial portal setup, then `MATCH_PASSWORD=... pnpm ios:release:signing:sync:push` to publish encrypted Fastlane match assets to the shared private repo.
4. Optional: set a custom official relay URL for the build. If unset, the release flow uses `https://ios-push-relay.openclaw.ai`.
- Fastlane log line like `Uploaded iOS App Store build: version=<version> short=<short> build=<build>`
8. If this is a fresh clone on a maintainer machine that already works elsewhere, it is OK to copy the non-secret `apps/ios/fastlane/.env` from another trusted local clone on the same Mac. The Keychain-backed private key remains machine-local and is not stored in the repo.
9. If this is a fresh clone on a maintainer machine that already works elsewhere, it is OK to copy the non-secret `apps/ios/fastlane/.env` from another trusted local clone on the same Mac. The Keychain-backed private key remains machine-local and is not stored in the repo.
## iOS Versioning Workflow
@@ -176,7 +219,7 @@ Recommended flow:
1. Keep `apps/ios/version.json` pinned to the current train version.
2. Update `apps/ios/CHANGELOG.md`, usually under `## Unreleased` while iterating.
3. Run `pnpm ios:version:sync` after changelog changes.
4. Upload more TestFlight builds with `pnpm ios:beta`.
4. Upload more TestFlight builds with `pnpm ios:release:upload`.
5. Let Fastlane bump only the numeric build number.
-`apps/ios/Sources/OpenClaw.entitlements`derives `aps-environment`from the active build configuration/signing override.
- APNs token registration to gateway happens only after gateway connection (`push.apns.register`).
- Local/manual builds default to `OpenClawPushTransport=direct` and`OpenClawPushDistribution=local`.
- Local/manual builds default to `OpenClawPushTransport=direct`,`OpenClawPushDistribution=local`, and a development `aps-environment` entitlement.
- Your selected team/profile must support Push Notifications for the app bundle ID you are signing.
- If push capability or provisioning is wrong, APNs registration fails at runtime (check Xcode logs for `APNs registration failed`).
- The gateway host also needs direct APNs auth configured separately with `OPENCLAW_APNS_TEAM_ID`, `OPENCLAW_APNS_KEY_ID`, and either `OPENCLAW_APNS_PRIVATE_KEY_P8` or `OPENCLAW_APNS_PRIVATE_KEY_PATH`.
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.