chore(maint): make PR changelog edits release-only#92607
Conversation
|
@clawsweeper review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Codex review: needs maintainer review before merge. Reviewed June 13, 2026, 1:57 AM ET / 05:57 UTC. Summary PR surface: Tests -59, Docs +4, Other -480. Total -535 across 9 files. Reproducibility: not applicable. this is a maintainer automation cleanup PR, not a runtime bug report. Source inspection of the PR head shows the changed prepare-gate and merge-run behavior, and live PR checks currently report no pending or failing required checks. Review metrics: 1 noteworthy metric.
Stored data model Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land this after maintainer owner review confirms the strict release-only changelog and no-automatic-credit policy; if release/changelog PRs still need Do we have a high-confidence way to reproduce the issue? Not applicable: this is a maintainer automation cleanup PR, not a runtime bug report. Source inspection of the PR head shows the changed prepare-gate and merge-run behavior, and live PR checks currently report no pending or failing required checks. Is this the best way to solve the issue? Yes, if maintainers want a strict release-owned changelog policy; deleting the old helper path and enforcing the rule in prepare gates is the cleanest normal-PR implementation. The safer alternative is an explicit release/changelog escape hatch only if release operators need this same PR script for changelog-only release work. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 9d9389bc6b7a. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Tests -59, Docs +4, Other -480. Total -535 across 9 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
There was a problem hiding this comment.
Pull request overview
This PR updates the repository’s PR maintainer workflow so that changelog edits are treated as release-only work, and the scripts/pr merge flow no longer adds or verifies contributor/reviewer credit trailers.
Changes:
- Make
scripts/prprepare gates rejectCHANGELOG.mdedits andchangelog/fragments/*changes. - Remove coauthor/reviewer/thanks trailer generation and verification from the
scripts/prmerge flow. - Update maintainer documentation/skill guidance to reflect the new rules.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| test/scripts/check-changelog-attributions.test.ts | Updates tests to validate the attribution CLI directly and asserts prepare gates reject changelog edits. |
| scripts/pr-lib/prepare-core.sh | Stops emitting coauthor email metadata into prep artifacts. |
| scripts/pr-lib/merge.sh | Removes coauthor/reviewer/thanks trailers from merge body/comment flow. |
| scripts/pr-lib/gates.sh | Enforces release-only changelog policy by rejecting changelog and fragment edits during prepare gates. |
| scripts/pr-lib/common.sh | Removes helper functions used for contributor trailer/email resolution. |
| scripts/pr-lib/changelog.sh | Deletes the legacy changelog normalization/validation helper script. |
| scripts/pr | Stops sourcing the removed changelog helper. |
| AGENTS.md | Documents maintainer policy: no routine changelog edits; no assistant/agent credit trailers unless explicitly requested. |
| .agents/skills/openclaw-pr-maintainer/SKILL.md | Adds the same maintainer policy to the PR maintainer skill documentation. |
| pr_title=$(printf '%s\n' "$pr_meta_json" | jq -r .title) | ||
| local pr_number | ||
| pr_number=$(printf '%s\n' "$pr_meta_json" | jq -r .number) |
* fix(test): bound gateway harness teardown * fix: repair update restart type checks * test: stabilize full Mac regression suite * fix(providers): preserve adaptive Claude routing * fix(foundry): scope Entra tokens by API * fix(foundry): use bearer auth for Claude * fix(foundry): keep API-key Claude auth * fix(foundry): align Claude onboarding contracts * fix(foundry): infer selected Claude routes * fix(foundry): clear API key auth on Entra setup * fix(foundry): preserve Sonnet output limits * fix(providers): apply auth patch deletions * fix(bedrock): preserve Mythos Preview thinking policy * fix(providers): preserve Mythos max routing * fix(foundry): avoid stale setup metadata * fix(types): narrow provider auth metadata * fix(foundry): clamp Mythos Preview thinking * fix(providers): bound Claude model matching * test(anthropic): type Foundry auth fixtures * fix(providers): preserve Claude output limits * fix(providers): reconcile Claude profile routing * fix(vertex): preserve Mythos thinking policy * fix(foundry): route bearer auth from headers * fix(providers): encode Mythos adaptive requests * fix(foundry): preserve bearer auth intent * fix(foundry): use bearer auth in native transport * fix(vertex): default Mythos to adaptive thinking * fix(foundry): preserve canonical thinking identity * test(foundry): type auth lookup * fix(foundry): align Claude thinking contracts * fix(bedrock): keep optional thinking opt-in * fix(bedrock): route Mythos through Mantle * fix(bedrock): guard Mantle thinking options * fix(foundry): label Anthropic Messages onboarding * fix(foundry): make API labels exhaustive * fix(foundry): expose Claude 4.6 max effort * fix(foundry): bind auth and thinking contracts * fix(foundry): type runtime auth result * fix(auth): apply runtime request overrides everywhere * fix(auth): apply runtime auth to labels * fix(bedrock): normalize Mythos minimal effort * fix(tts): use prepared completion auth * test(tts): update prepared completion contract * fix(anthropic): require Mythos adaptive thinking * fix(tts): preserve async model discovery * fix(anthropic): repair Mythos contract types * fix(discord): scope command-deploy cache by application id (#77367) * fix(discord): scope command-deploy cache by application id Multi-bot Discord setups share a single command-deploy-cache.json under the state dir. Cache keys were unscoped (`global:reconcile`, `guild:<id>`), so a later account whose command set hashed identically to an earlier account would hit the shared hash and skip its own application's command reconcile entirely — Discord's Integration panel showed 'This application has no commands' for the secondary bot even though gateway connect, application id, and token were all valid. Scope every cache key with `app:<clientId>:` so each Discord application reconciles independently. Add regression tests covering: two applications with identical command sets each call REST against their own application; a single application with the same command set still hits the persisted cache; the on-disk cache JSON contains application-scoped keys. Fixes #77359. * fix(discord): merge on-disk hashes inside persistHashes to survive concurrent writes Codex follow-up on #77359 noted that server-channels.ts can start multiple Discord deployers concurrently, so two deployers that both load the cache file before either persists end up with the second writer overwriting the first writer's app-scoped key — defeating the rate-limit cache that the file exists to provide. Inside persistHashes, re-read the on-disk cache and merge it with our in-memory entries before the rename. Our in-memory entries always win on key collisions (we just produced them); on-disk entries we don't have in memory are preserved. Refresh in-memory state after the write so future writes from the same deployer also keep entries other deployers added. This is the lighter of the two repairs the codex review suggested (re-read/merge vs serialize writes); it covers the realistic case where one deployer writes before the other persists. Add a regression test that exercises the load-then-other-deployer-writes-then-persist sequence. * fix(discord): serialize command-deploy cache persists via in-process mutex Codex follow-up on #77367 noted: re-read-before-write inside persistHashes isn't enough — two deployers running persistHashes in true parallel can both read the same snapshot before either writes, and the later rename overwrites the earlier writer's app-scoped entries. Add a module-level Map<storePath, Promise<void>> mutex and wrap the read-merge-write cycle in withCachePersistLock so concurrent persists for the same on-disk path serialize. In-process is sufficient because Discord deployers only run inside the gateway process. New regression test fires three deployers via Promise.all on the same tick and asserts all three application-scoped entries survive — pre-fix this race lost at least one entry. * fix(discord): add override modifier on StaticCommand.description to satisfy strict TS Current main enables noImplicitOverride; the StaticCommand test helper re-declares the concrete BaseCommand.description property, which now requires an explicit 'override' modifier (TS4114). * test(discord): suppress typescript/unbound-method on vitest mock refs The createRest() helper returns vi.fn() handlers cast as RequestClient, so expect(rest.get).toHaveBeenCalledTimes(...) triggers typescript/unbound-method 12 times. File-level disable: these are vitest mock identities, not unbound class methods. * fix(discord): clean up command cache lock Signed-off-by: sallyom <[email protected]> --------- Signed-off-by: sallyom <[email protected]> Co-authored-by: sallyom <[email protected]> * feat(auto-reply): deliver inter-tool commentary as standalone verbose progress messages When verbose progress is enabled, preamble item events now flush as durable standalone progress messages through the same delivery path as tool summaries, instead of living only in ephemeral channel streaming drafts. The latest text per item id is buffered so snapshot-style producers send one message per item; the buffer flushes when the producer moves on (next item, tool event, block reply, or final reply) and drains before the final answer. Verbose runs also force commentary classification on (commentaryProgressEnabled), so inter-tool text routes to the commentary lane rather than being folded into the final answer text. Dispatch additionally exposes a live verbose-progress visibility getter via the new onVerboseProgressVisibility reply option, so draft-rendering channels can route progress to the durable lane while it is active. * feat(telegram): route verbose progress payloads durably instead of into the streaming draft With streaming on, the dispatcher diverted tool-kind payloads (including the new durable commentary messages) into the ephemeral progress draft, where they were discarded when the final answer arrived - so verbose runs lost their progress record whenever streaming was enabled. While the durable verbose lane is active (per the dispatch visibility getter), tool payloads are now sent as real standalone messages and the draft yields its commentary lines; tool/plan draft lines keep the draft since they have no durable counterpart. Reasoning lane and tool status reactions are unaffected. * feat(auto-reply): emit durable tool summaries from CLI runner tool results The CLI parser already emits tool result events (name, toolCallId, isError, sanitized result), but the runner bridge dropped them, so CLI-backed runs had no durable tool record under verbose while embedded runs did. The bridge now forwards result events, and both runners feed a summary tracker that renders the same formatToolAggregate line the embedded runner emits (meta captured from the start event args), plus the tool output block when full verbose output is enabled. Delivery rides each runner's existing tool-result route, so verbose gating, ordering ahead of the final answer, and the Telegram durable routing all apply unchanged. * fix(discord): yield the commentary draft while durable verbose progress is active Discord consumes the dispatch verbose-progress visibility getter the same way Telegram does: while the durable lane is delivering commentary as standalone messages, the ephemeral progress draft skips its preamble lines so commentary renders exactly once. Covered by an active/inactive regression pair. * test(discord): add onVerboseProgressVisibility to dispatch params type * refactor(auto-reply): distill verbose commentary lane wiring * fix(sessions): preserve user model override across daily/idle rollover (#90128) User-driven /model (and sessions.patch) overrides were dropped when a session rolled over at the daily/idle reset boundary, reverting to the configured default on the next turn despite the 'Model set to ... for this session' ack. The override-preservation carryover in initSessionState was gated on resetTriggered, so implicit stale rollovers (the common case for always-on channel sessions) skipped it. Run resolveResetPreservedSelection for any rollover that mints a new session from an existing entry (explicit /new + /reset AND implicit stale daily/idle). resolveResetPreservedSelection already preserves only user-driven overrides and clears auto-fallback pins, so resets still return to the default. Adds regression tests in session.test.ts covering both cases. Fixes #90119 Filed with AI assistance (OpenClaw agent); reviewed by @Peetiegonzalez. Co-authored-by: Marvinthebored <[email protected]> * fix(clickclack): allow explicit enable through plugin allowlist (#92084) Allow an explicit canonical ClickClack enable/setup selection to record ClickClack in a nonempty plugin allowlist, while preserving unrelated allowlist rejection, denylist authority, and global plugin disablement. Validated at source head 24af9d8e751440a8954aa731c5d2c63a53094698 with focused regressions, built-CLI disposable-config E2E, security checks, and autoreview. Merged under owner authorization despite the two documented untouched-main agent-core baseline failures. * fix(auto-reply): stop dropping claude-cli narration when commentary lane is off After #91976, the claude-cli JSONL parser reclassifies assistant text that precedes a tool_use block as commentary. The classification gate (commentaryProgressEnabled !== undefined) was looser than the delivery gate (commentaryProgressEnabled === true && onItemEvent), so any channel that defined the flag as false engaged classification with no consumer wired: flushPendingClaudeCommentaryText() called an undefined onCommentaryText and silently discarded the text. On Discord with verbose off this dropped all inter-tool narration and the pre-final-answer preamble text. Two-layer fix: - Align the classify gate with the delivery gate in both CLI dispatch sites (agent-runner-execution, followup-runner) so classification only engages when a commentary consumer exists. - Defense in depth: flushPendingClaudeCommentaryText() now falls back to the assistant text lane instead of discarding when no consumer is wired, so no future gate mismatch can silently eat model output. Reported on Discord: claude-cli backend lost interleaved narration and the regular-text reasoning preamble with or without /verbose on. * refactor(agents): derive CLI commentary classification from consumer presence * perf(ci): isolate Docker tooling tests * perf(ci): isolate Docker tooling tests * test(ci): align Anthropic agent expectations * #92109: [Bug]: EmbeddedAttemptSessionTakeoverError caused by Btrfs ctimeNs instability (#92123) * fix(session-lock): remove ctimeNs from session file fingerprint comparison Btrfs background maintenance (snapshots, scrub, quota) updates ctime without any file content change. Including ctimeNs in the fingerprint causes false-positive EmbeddedAttemptSessionTakeoverError on all Btrfs filesystems, breaking cron jobs and subagent spawns with 100% failure. dev + ino + size + mtimeNs are sufficient to detect external writes — any content change will also update mtimeNs and/or size. ctimeNs only tracks metadata changes and adds no meaningful protection. Closes #92109 * test(session-lock): cover ctime-only fence drift * fix(session-lock): narrow ctime drift acceptance * fix(session-lock): trust verified ctime drift * fix(session-lock): bound ctime drift digest * fix(session-lock): skip absent ctime digest --------- Co-authored-by: Vincent Koc <[email protected]> * fix(feishu): reply inside P2P direct-message threads (#92136) * fix(feishu): keep P2P replies inside direct-message threads * fix(clownfish): address review for ghcrawl-165996-agentic-merge (1) * fix(clownfish): address review for ghcrawl-165996-agentic-merge (1) Co-authored-by: LiaoyuanNing@TTC <[email protected]> --------- Co-authored-by: vincentkoc <[email protected]> Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com> Co-authored-by: LiaoyuanNing@TTC <[email protected]> * fix(memory): preserve live SQLite index during swaps Fixes #91216. Preserve the live memory SQLite index during atomic reindex swaps by publishing the POSIX main DB with an overwrite rename, keeping target WAL/SHM sidecars rollbackable until publish succeeds, and refusing no-create post-swap reopens that would otherwise auto-create an empty DB. Verification: - node scripts/run-vitest.mjs extensions/memory-core/src/memory/manager.atomic-reindex.test.ts extensions/memory-core/src/memory/manager-db-probe.test.ts extensions/memory-core/src/memory/manager.self-heal-missing-identity.test.ts extensions/memory-core/src/memory/manager-reindex-state.test.ts extensions/memory-core/src/memory/manager.fts-only-reindex.test.ts extensions/memory-core/src/memory/manager.readonly-recovery.test.ts - git diff --check origin/main...HEAD - node scripts/run-oxlint.mjs extensions/memory-core/src/memory/manager-atomic-reindex.ts extensions/memory-core/src/memory/manager.atomic-reindex.test.ts extensions/memory-core/src/memory/manager-db.ts extensions/memory-core/src/memory/manager-db-probe.test.ts extensions/memory-core/src/memory/manager-sync-ops.ts - .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main - GitHub CI on 60df2b4178c8e16a301b715c77bc6197aced99d1 * Stabilize A2A prompt cache metadata (#90173) A2A session routing identifiers are needed for delivery provenance, but concrete session keys in extraSystemPrompt make the agent system prompt vary between otherwise identical handoffs. Keep the model-facing system context stable by describing high-cardinality session slots with placeholders while retaining concrete values in inputProvenance. Channel names stay concrete: they are low-cardinality (discord/slack/webchat/...), so they do not meaningfully fragment the cache, and they inform reply formatting on the receiving agent. Constraint: OpenClaw contributor PRs require focused behavior proof and tests for prompt/cache-facing changes. Rejected: Removing routing metadata entirely | would weaken model context for requester/target roles. Rejected: Placeholdering channel values too | drops model-visible formatting context for negligible cache benefit (reviewer feedback). Confidence: medium Scope-risk: narrow Directive: Keep concrete session identifiers out of extraSystemPrompt; preserve them in structured provenance or payload fields. Low-cardinality channel labels may stay model-visible. Tested: node scripts/run-vitest.mjs src/agents/tools/sessions-send-helpers.test.ts src/agents/openclaw-tools.sessions.test.ts Tested: corepack pnpm exec oxfmt --check src/agents/tools/sessions-send-helpers.ts src/agents/tools/sessions-send-helpers.test.ts src/agents/openclaw-tools.sessions.test.ts Tested: node scripts/run-oxlint.mjs src/agents/tools/sessions-send-helpers.ts src/agents/tools/sessions-send-helpers.test.ts src/agents/openclaw-tools.sessions.test.ts Tested: git diff --check Tested: live before/after provider cache trace (isolated local gateway, two A2A sends from distinct requester sessions; see PR Real behavior proof) Co-authored-by: Sunjae Kim <[email protected]> * fix(cli-runner): scope claude-cli queue to live-session owner identity (#91946) (#91974) * fix(cli-runner): scope claude-cli queue to live-session owner identity Fresh claude-cli runs without a stored cliSessionId previously collapsed onto a single workspace-scoped queue key, serializing all fan-out within one workspace regardless of subagent lane configuration. Replace the workspace fallback with the same owner identity that claude-live-session.ts already uses for its live-session map (agentAccountId + agentId + authProfileId + sessionId + sessionKey), keeping per-session resume safety while letting independent OpenClaw sessions in the same workspace run concurrently. Refactor buildClaudeLiveKey() to share the new buildClaudeOwnerKey() helper so the queue key and the live-session key cannot drift. Refs: #91946 * test(cli-runner): pin owner-key hash + document buildClaudeOwnerKey contract Add a golden-hash regression test for buildClaudeOwnerKey using the exact legacy fixture, so a future refactor that reorders fields or flips the JSON encoding can't silently orphan every deployed Claude live session at upgrade. Hash verified empirically against the prior inline sha256(JSON.stringify(...)) in buildClaudeLiveKey. Add a JSDoc on buildClaudeOwnerKey explaining the cross-module contract between the CLI run queue and the live-session map. Refs: #91946 * docs(cli-runner): tighten buildClaudeOwnerKey contract comment The previous comment claimed an encoding mismatch would orphan deployed live sessions across upgrades. The Claude live-session registry is process-local, so any restart already discards every entry — the real invariant is that the queue path and live-session path produce byte-identical owner keys *within a single process*, so a fresh queued turn picks up the same live session the registry already holds. Update the helper docstring and the golden-hash test description accordingly; the pinned hash and behavior are unchanged. * test(cli-runner): add owner-key concurrency demo script A pure-Node, no-test-runner demo that reproduces the PR-head queue behavior end-to-end: BEFORE-PR collapse (workspace lane), distinct-owner overlap, and identical-owner serialization, all in one run with millisecond-stamped event ordering. Useful as a low-overhead regression check for the owner-key contract and as a maintainer-runnable proof artifact for #91946. * test(cli-runner): satisfy oxlint curly + no-promise-executor-return Wrap single-statement if/for-of bodies in braces and rewrite the sleep helper so its Promise executor is a void block instead of an arrow with an implicit return. No behavior change; demo output and the byte-equivalent slice fingerprints are unchanged. --------- Co-authored-by: wanglu241 <[email protected]> * fix(thinking): apply Claude profile to anthropic-messages catalog rows (#92053) * fix(thinking): apply Claude profile to anthropic-messages catalog rows When a custom provider (e.g. `jdcloud-anthropic`) fronted Claude Opus over the native anthropic-messages adapter, `--thinking xhigh` was silently clamped to `off`. The thinking-profile dispatcher resolves bundled plugin policy surfaces by exact provider id, so a renamed Anthropic-compatible provider never reached the anthropic plugin's policy and `xhigh` was not in the resulting profile. `auto-reply/thinking.ts` already had a fallback keyed on `context.api === "anthropic-messages"` that attached `CLAUDE_FABLE_5_THINKING_PROFILE` for Fable models. Generalize it to use `resolveClaudeThinkingProfile(modelId, params)` instead — the same canonical helper the anthropic plugin uses — which still returns the Fable profile for Fable models and now returns the correct Opus 4.7/4.8 profile (with `xhigh`/`adaptive`/`max`) for Claude Opus regardless of provider id. Non-Claude models on anthropic-messages routes still get the base profile, and a Claude id on a non-Anthropic transport (e.g. an openai-completions catalog row) is unaffected. Fixes #91975 * fix(thinking): match native Anthropic includeNativeMax in fallback Address ClawSweeper P2 review on #92053. The anthropic-messages fallback in `resolveThinkingProfile` calls `resolveClaudeThinkingProfile` but omits the `{ includeNativeMax: true }` option that the bundled anthropic plugin uses (extensions/anthropic/provider-policy-api.ts:38,45). For native-xhigh Claude families (Opus 4.7/4.8) this had no effect since the native-xhigh branch already exposes `max`. But adaptive Claude families that take the adaptive-default branch (e.g. claude-sonnet-4-6, claude-opus-4-6) silently lost `max` parity on custom anthropic-messages providers compared to native Anthropic policy. Also add a regression test on `claude-sonnet-4-6` that verifies the adaptive-branch path keeps `max` for custom providers. * docs(thinking): document deliberate compat.xhigh bypass on anthropic-messages Self-review surfaced a subtle behavior change worth documenting: when the anthropic-messages fallback was generalized, non-Claude models on this transport stop honoring catalog `compat.supportedReasoningEfforts: ["xhigh"]` because they take the Claude base profile instead of falling through to the later `catalogSupportsXHigh` upgrade path. This is intentional — anthropic-messages does not carry a generic xhigh contract; xhigh on this protocol is a Claude-family capability. Add an inline comment at the resolver site and a regression test that locks the suppression so the next reader (or a future patch) doesn't accidentally restore the upgrade path. * fix(thinking): extract Claude profile to leaf to break import cycle The previous commits added a `resolveClaudeThinkingProfile` import from `auto-reply/thinking.ts` to `plugin-sdk/provider-model-shared.ts`. The shared barrel re-exports `provider-replay-helpers` and `plugins/types`, which transitively reach back into `auto-reply` via the gateway server methods chain — creating the madge cycle reported by `check:madge-import-cycles`: auto-reply/thinking.ts -> ... -> plugin-sdk/provider-model-shared.ts -> plugins/{config-schema, host-hooks, ...} -> plugins/types.ts Move `BASE_CLAUDE_THINKING_LEVELS`, `isClaudeAdaptiveThinkingDefaultModelId`, and `resolveClaudeThinkingProfile` to a new leaf module `src/plugins/provider-claude-thinking.ts` whose only imports are `@openclaw/llm-core` and the existing leaf `provider-thinking.types`. `provider-model-shared.ts` continues to re-export both helpers so existing consumers (`extensions/anthropic/*`, the public test surface) are unaffected. `auto-reply/thinking.ts` now imports the leaf directly, breaking the cycle. * test(thinking): add live proof harness for #91975 anthropic-messages clamp --------- Co-authored-by: wanglu241 <[email protected]> * Google: show detailed Gemini CLI OAuth extraction failures (#41991) * Google: preserve Gemini CLI OAuth failure context Port the diagnostics-only fix to the bundled Google OAuth implementation so user-facing setup errors explain why automatic Gemini CLI client-config discovery failed. Constraint: #54289 may remove or gate automatic Gemini CLI credential extraction Rejected: Change extraction consent behavior here | security/product decision belongs in #54289 Confidence: medium Scope-risk: narrow Tested: pnpm test -- extensions/google/oauth.test.ts Tested: pnpm check Tested: pnpm format:check extensions/google/oauth.credentials.ts extensions/google/oauth.test.ts Not-tested: full pnpm test suite * Google: clarify Gemini bundle fallback diagnostic comment Keep the follow-up limited to the explanatory comment so it matches the diagnostic error preservation added around bundle traversal failures. Constraint: comment-only cleanup after diagnostics port Confidence: high Scope-risk: narrow Tested: pnpm format:check extensions/google/oauth.credentials.ts Not-tested: tests not run; comment-only change * fix: resolve OAuth test rebase conflict * fix(qqbot): flush tool output before silent non-streaming final (#92074) * fix(qqbot): flush tool output before silent non-streaming final * fix qqbot silent final delivery * chore: drop local plugin runtime helper * fix: suppress stale qqbot tool flush * fix(qqbot): flush tool output before silent non-streaming final (#92074) (thanks @sliverp) --------- Co-authored-by: Ubuntu <[email protected]> * fix(agents): forward channel identity to CLI hooks * fix(models): clarify provider model registration hint (#89508) Co-authored-by: Cornna <[email protected]> * fix(agents): keep migrated session entry ids unique on v1 upgrade (#89085) migrateV1ToV2 assigned each entry id via generateId(ids) but never added the result back into ids, so the collision-check set stayed empty for the whole migration and generateId's check was a no-op. A v1 to v2 upgrade could then mint two entries with the same 8-hex id, and because the migration rebuilds the parent/child tree from those ids it would parent the second entry to itself, corrupting the branch. Add ids.add(entry.id) so the generator sees prior ids and retries on collision. Adds a regression test that drives the real SessionManager.open migration path with a seeded id collision. * fix(discord): clean migrated thread binding state (#89552) * fix(discord): clean migrated thread binding state * fix(discord): omit undefined thread binding fields --------- Co-authored-by: Hex <[email protected]> * fix(cron): reject durations that overflow to a non-finite value (#89448) * fix(cron): reject durations that overflow to a non-finite value parseDurationMs guarded the parsed mantissa but returned Math.floor(n * factor) with no finite check on the product. A finite mantissa times a large unit factor (e.g. "1e302d", factor 86_400_000) overflows to Infinity, which was returned as the millisecond value. Reject a non-finite result instead, matching the existing contract that already rejects non-finite / non-positive mantissas. Fixes #83906. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * ci: rerun flaky runner checks --------- Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]> * fix(doctor): warn on unsupported hook entry loaders (#89319) * fix(doctor): warn on unsupported hook entry loaders * fix(doctor): guard null hook entry configs * fix(doctor): guard null hook entry configs * fix(doctor): repair misplaced hook loader paths * fix(doctor): clarify unsupported hook entry repair --------- Co-authored-by: Vincent Koc <[email protected]> * fix(qqbot): guard silent-final tool flushing * fix(config): stop config.patch replacePaths index suffix from widening array consent (#91966) * fix(config): stop config.patch replacePaths index suffix from widening array consent normalizeConfigPatchReplacePath stripped a trailing array bracket, so an entry/index-scoped token like bindings[0] or bindings[] collapsed onto the bare whole-array token (bindings). That bare token is both the merge replaceArrayPaths key and the destructive-array gate's exact-path token, so an index-scoped consent silently authorized a full-array replacement and dropped unrelated base entries on the gateway config.patch path, and the same collapse let the agent self-edit tool truncate id-keyed arrays whenever no protected path happened to be involved. Keep the interior index normalization (agents.list[0].skills -> agents.list[].skills) but no longer collapse a trailing bracket, so a bracket/index-suffixed token never matches the bare whole-array token and the destructive-array gate stays fail-closed unless the documented exact path is passed. Update the agent-tool test whose expectation depended on the old collapse: agents.list[0] now does a non-destructive id-keyed merge that only changes model and is correctly allowed. * fix(config): distinguish indexed and array replace consent * test(config): cover replace consent syntax * fix(config): make replace path normalization idempotent --------- Co-authored-by: Vincent Koc <[email protected]> * fix(plugins): stop derived metadata snapshot rescan storm in /models (regression shipped since v2026.5.18) (#92127) Since 5734193fdf ("fix(plugins): keep metadata snapshot memo fresh", first shipped in v2026.5.18), the in-process plugin metadata snapshot memo stores derived-registry results under a key recomputed from the freshly built snapshot.index, while lookups key off the persisted-index registry state. On installs where the registry resolves as "derived" (persisted index absent or not covering the running checkout), the two keys never match. Worse, the lookup-side adoption loop returns the most recently stored registryState for the context, so two alternating call shapes (e.g. the model-catalog build mixing workspace-scoped and global lookups) each adopt the other shape's state, compute a key that was never stored, and re-run the full plugin manifest scan - on every call, forever. Chat /models (and each subsequent provider/model pick) pays multiple full manifest scans plus all downstream snapshot-identity cache invalidation per step, pinning a CPU core for seconds on every interaction, in every chat channel. Fix: store the memo under the exact memoKey/registryState the call looked up by, instead of re-deriving a second key from snapshot.index. Freshness is unchanged - the lookup context hash and the plugin metadata lifecycle clears (install/reload/doctor) still own invalidation. The now-unused index parameter of resolvePersistedRegistryMemoState is removed. Measured on the author's VPS (real plugin discovery, identical catalog output of 263 entries on both sides): the full-discovery model catalog build behind chat /models dropped from ~6.3s to ~0.3s (~21x), with repeat snapshot lookups going from full rescans to memo hits. Regression test: alternating derived call shapes must not re-scan (red on main: 4 scans; green with this fix: 2). * fix(ollama): use provider thinking default in SDK session factory (#91657) * fix(ollama): use provider thinking default in SDK session factory * fix(agents): preserve model metadata for thinking defaults * fix(agents): resolve custom Ollama thinking policy --------- Co-authored-by: Vincent Koc <[email protected]> * fix(memory): abort orphaned embedding work when memory_search times out (#91742) * fix(memory): abort orphaned embedding work when memory_search times out memory_search raced its 15s deadline with Promise.race and returned a clean timeout to the agent, but the underlying embedQueryWithRetry loop kept retrying (3 attempts x 60s) against the embedding backend with no consumer. Thread the tool-owned AbortSignal through manager.search -> embedQueryWithRetry -> runEmbeddingOperationWithTimeout so the deadline cancels in-flight embedding work, stops the retry loop, and skips fallback-provider activation for an absent caller. Fixes #91718 * fix(memory): let the deadline result win before aborting the search Abort listeners dispatch synchronously, so an abort-aware search could reject the raced task before the timeout promise resolved and replace the stable 'memory_search timed out after 15s' result with a provider-wrapped abort error. Resolve the timeout first, then abort. * fix(memory): scope deadline abort to builtin embeddings * fix(memory): preserve deadline signal across fallback --------- Co-authored-by: Vincent Koc <[email protected]> * fix(memory-core): retry narrative message reads (#89091) * fix(memory-core): retry narrative message reads * fix(memory-core): wait longer for narrative text * fix(release): gate beta publish on plugin verification Delay public GitHub release publication until postpublish verification, dependency evidence upload, proof append, and required plugin publish gates pass. Also updates release-maintainer instructions so newly publishable plugins are minted/prepublished through an owner-approved path without consuming the next auto-bumped beta version unless that path is the actual release publish. * fix(cli): validate gateway RPC timeout inputs Reject malformed or explicit empty Gateway RPC timeout values before opening Gateway calls, align the shared Gateway RPC omitted-timeout fallback with the 30000 ms CLI default, and validate explicit `cron add --timeout-seconds` values at the CLI boundary. Carries forward the useful source work from #54646 and the earlier timeout-validation context from #40953. #60661 remains separate accepted-run timeout semantics work and is intentionally not folded into this change. Validation: - `npm run review-results -- /tmp/clownfish-check-27341769444` - `git diff --check` - OpenClaw PR checks on `ce7bd8b9388a5689b14ddc2b3a984f7b4647e5ca`: 132 pass, 0 pending, 0 failing - ClawSweeper re-review: https://github.com/openclaw/clawsweeper/actions/runs/27344244608 Co-authored-by: RayRuan <[email protected]> Co-authored-by: Homeran <[email protected]> * fix(agents): retry same model across short rate-limit windows (#91911) Bound same-model rate-limit retries to explicit short-window signals or parsed short Retry-After values, honor Retry-After in the retry sleep, preserve zero-rotation fallback behavior, and record same-model rate-limit retries separately from profile rotations. Verification: - node scripts/run-vitest.mjs src/agents/embedded-agent-runner/run/assistant-failover.test.ts src/agents/embedded-agent-runner/run/helpers.test.ts - Azure Crabbox cbx_bdb5a7807a1f / coral-shrimp: OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1 OPENCLAW_CHANGED_LANES_RAW_SYNC=1 corepack pnpm check:changed - .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main * fix(agents): project thinking catalog compat * test(ci): relax docker signal wait * chore: remove redundant proof scripts * fix(cron): report SQLite storage path in cron.status instead of legacy jobs.json (#92144) * fix(cron): report SQLite storage path in cron.status instead of legacy jobs.json The `cron.status` gateway response returned `storePath` pointing to the legacy `jobs.json` path, but cron jobs are actually stored in the shared SQLite state database. This misled operators and agents into looking for a JSON file that no longer exists. - Add `storage: "sqlite"` and `sqlitePath` fields to CronStatusSummary - Mark legacy `storePath` as @deprecated (kept for backward compat) - Update CLI warning to prefer sqlitePath over storePath - Add regression assertions in read-ops test Fixes #91766 * fix(macos): prefer sqlitePath in cron status display * fix(macos): add sqlitePath to CronSchedulerStatus type * fix(channel): harden local setup trust (#92175) Summary: - The PR extends channel setup trust enforcement and trusted catalog fallback from workspace-origin plugins to ... nfigured load paths into catalog discovery, and adds focused regression plus Docker/package proof coverage. - PR surface: Source +190, Tests +892, Other +324. Total +1406 across 13 files. - Reproducibility: yes. The source PR provides a concrete clean-main Docker/package path where an explicitly t ... ns unresolved, while the patched package resolves it and still blocks untrusted module and setup execution. Automerge notes: - PR branch already contained follow-up commit before automerge: fix(channel): stabilize trusted catalog dts typing - PR branch already contained follow-up commit before automerge: fix(channel): repair trusted catalog exclusions typing - PR branch already contained follow-up commit before automerge: test(channel): cover local channel plugin trust - PR branch already contained follow-up commit before automerge: chore(deps): refresh plugin shrinkwraps - PR branch already contained follow-up commit before automerge: test(channel): route trust regression in command shard - PR branch already contained follow-up commit before automerge: test(channel): remove e2e-named trust regression Validation: - ClawSweeper review passed for head eabee04d54596790a16a70d56b97b51eddd87791. - Required merge gates passed before the squash merge. Prepared head SHA: eabee04d54596790a16a70d56b97b51eddd87791 Review: https://github.com/openclaw/openclaw/pull/92175#issuecomment-4680798117 Co-authored-by: Mason Huang <[email protected]> Co-authored-by: Copilot <[email protected]> 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 <[email protected]> * fix(installer): stop after failed Node package installs Linux Node package-manager setup/install failures now fail the installer immediately instead of falling through to a misleading success path. Adds regression coverage for NodeSource setup and apt nodejs install failures under conditional shell invocation.\n\nFixes #73837\n\nProof: bash -n scripts/install.sh; node scripts/run-vitest.mjs test/scripts/install-sh.test.ts; node scripts/run-oxlint.mjs test/scripts/install-sh.test.ts; git diff --check origin/main...HEAD; autoreview clean; Azure Crabbox check:changed cbx_6286dc1e287b passed. * fix(wizard): report keyless web search providers as ready Onboarding finalize now treats configured web search providers with requiresCredential: false as ready instead of warning that an API key is missing. This covers keyless providers such as Parallel Search (Free), DuckDuckGo, and Ollama while preserving credential-required warnings for providers that need keys.\n\nProof: focused wizard/search tests; oxlint on changed files; git diff --check; autoreview clean; Azure Crabbox check:changed cbx_b92ef084c21c passed; GitHub checks green. * fix: handle explicit silent assistant replies (#92073) Signed-off-by: sallyom <[email protected]> * feat(skills): allow trusted workshop symlink targets * fix: gate Skill Workshop symlink writes * fix: validate workshop support symlink writes * test: harden stalled websocket cleanup * refactor(whatsapp): introduce inbound message contexts * refactor(whatsapp): read inbound contexts in auto reply * test(whatsapp): update auto reply inbound fixtures * test(whatsapp): cover inbound context compatibility * docs(plugins): record whatsapp inbound compatibility * fix: keep whatsapp inbound aliases live * test: clean whatsapp alias assertions * test: narrow whatsapp mention fixture * refactor: move workspace skill writes to lifecycle * fix: preflight skill writes before rollback metadata * fix: avoid support write shadowed variable * fix: preserve utils exports in doctor health tests * fix: rely on ClawHub plugin publish checks * test(sqlite): add state perf query plan harness 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 * fix(daemon): keep unsupported service status readable 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. * fix(cron): preserve timezone on expression edits Fixes #92291. Preserves the existing cron timezone when `cron edit <id> --cron ...` replaces only the expression, while avoiding stale stagger writes and preserving API/UI omitted-timezone clearing semantics. Proof: - node scripts/run-vitest.mjs src/cli/cron-cli/register.cron-edit.test.ts src/cli/cron-cli.test.ts src/cron/service.jobs.test.ts src/cron/normalize.test.ts - node scripts/run-oxlint.mjs src/cli/cron-cli/register.cron-edit.ts src/cli/cron-cli/register.cron-edit.test.ts src/cli/cron-cli.test.ts src/cron/service/jobs.ts src/cron/service.jobs.test.ts - git diff --check origin/main...HEAD - .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main - Azure Crabbox cbx_3ce6a4e0d945 / silver-lobster: 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 * fix(docker): bundle QA Lab runtime in the image (#92087) * fix(docker): split qa lab runtime fixes * fix(docker): remove store platform selector * test(docker): assert qa lab ui copy is gated * fix(docs): remove stale ClawHub nav page * fix(release): use trusted publishing for plugin npm * refactor(telegram): centralize edit error classification in network-errors * fix(telegram): retry transient draft preview failures instead of killing the stream * fix(telegram): carry bot api error codes across the ingress worker boundary * fix(telegram): restart isolated polling on getUpdates conflict and surface it in status * fix: scope image inbound state env * test: scope reply session state env * fix: add test env delete helper * test: scope doctor missing state env * fix: restore commitment store state env * test: restore commitment runtime state env * fix: restore commitment extraction state env * test: scope commitment full chain state env * fix: scope commitment heartbeat state env * test: route state dir env helper * fix: route live env restore deletes * test: scope plugin dispatch state env * fix: route respawn hint env clears * fix(anthropic-vertex): stop re-marking cache_control on transport-budgeted payloads (#92387) 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 6ef19602bfb48a819e096b23b66ece94a8072ed4. - Required merge gates passed before the squash merge. Prepared head SHA: 6ef19602bfb48a819e096b23b66ece94a8072ed4 Review: https://github.com/openclaw/openclaw/pull/92387#issuecomment-4688955121 Co-authored-by: openperf <[email protected]> Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com> Approved-by: takhoffman Co-authored-by: takhoffman <[email protected]> * fix: stop docker build commands by pid and group * fix doctor channel SecretRef preview (#92229) * Fix disabled heartbeat one-shot cron retries (#92225) * fix: retry disabled cron wake one-shots * fix: satisfy cron retry CI checks * fix: inherit static transport for configured DeepSeek models (#92265) * fix: fail closed for cli-backed btw fallback (#92226) * fix heartbeat suppressed commitment delivery (#92231) * fix(agents): classify structured unsupported model errors (#92280) * fix(agents): classify structured unsupported model errors * test(agents): update embedded harness helper mock * Fix OTLP log trace correlation (#92276) * fix diagnostics otel log trace correlation * test diagnostics trace provenance contract * fix(update): hand off Linux service auto-updates (#92282) * fix: resolve managed secretref provider auth (#92235) * fix provider static model fallback (#92293) * fix(agent): continue after source message tool replies (#92343) * fix(codex): preserve memory prompt registration (#92350) * fix(codex): restore memory recall guidance * fix(codex): add memory recall fallback * fix(codex): preserve memory prompt registration * test(codex): expect memory slot in scoped harness load Signed-off-by: sallyom <[email protected]> --------- Signed-off-by: sallyom <[email protected]> Co-authored-by: sallyom <[email protected]> * fix: clarify gateway SecretRef auth diagnostics (#92290) * fix gateway secretref health diagnostics * fix gateway health result type narrowing * fix: repair rejected Anthropic thinking replay (#92286) * fix: repair rejected Anthropic thinking replay * fix: narrow recovered retry result * test: satisfy thinking recovery lint * test: prove thinking retry preserves fresh reasoning * test: type narrow thinking retry proof * Fix Telegram spooled buffered replay (#92281) * fix telegram spooled buffered replay * fix telegram replay type checks * fix telegram replay lint * test telegram replay visible output retry guard * fix telegram rollback failure retry * fix(doctor): show per-step progress spinners during update * test(doctor): cover update progress wiring and spinner cleanup * fix(doctor): drop redundant Boolean conversion flagged by oxlint * test: tighten doctor update progress coverage * fix(outbound): honor top-level image param as send media source (#92407) (#92416) 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. * fix(sandbox): render cli skill prompts from materialized paths (#92508) * Updated fallback logic * fix: update esbuild audit pin (#92540) * Add QA evidence artifact output (#91484) * feat: add qa evidence summary normalization * chore: rename qa evidence target environment * chore: align qa evidence profile terminology * chore: align qa evidence summary fields * chore: add qa evidence taxonomy ref * test: remove stale multipass evidence example * test(qa): normalize vitest and playwright evidence * test(qa): slim evidence summary metadata * test(qa): clarify evidence summary inputs * test(qa): rename scenario specs in evidence flow * test(qa): treat evidence profiles as mapping strings * test(qa): use neutral evidence test identity * test(qa): nest evidence summary joins * refactor(qa): normalize live evidence summaries * fix(qa): accept normalized telegram rtt summaries * fix(qa): normalize evidence lane summaries * fix(qa): align evidence summaries with requirements * refactor(qa): tighten evidence summary builders * refactor(qa): restore standard evidence ids * fix(qa): keep legacy summaries out of rtt evidence * refactor(qa): make package evidence provenance explicit * test(qa): keep script tests out of qa lab internals * refactor(qa): rename scenario evidence definitions * refactor(qa): clean evidence summary wording * test(qa): fix evidence summary test inputs * refactor(qa): simplify evidence identity fields * refactor(qa): tighten evidence summary inputs * refactor(qa): rename evidence artifact * Add QA scorecard taxonomy validation (#91500) Merged via squash. Prepared head SHA: a9aec907d4823ad24fe06edf7e66d5365a4f2343 Co-authored-by: RomneyDa <[email protected]> Co-authored-by: RomneyDa <[email protected]> Reviewed-by: @RomneyDa * fix(telegram): allow expandable blockquotes * test: cover telegram expandable blockquotes * feat(moonshot): add Kimi K2.7 Code support (#92554) * 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 * fix(moonshot): backfill reasoning_content on assistant tool-call replay messages (#92396) 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) <[email protected]> * fix(e2e): keep lifecycle timeout cleanup alive (#92566) * ci: split plugin ClawHub publishing paths * feat: partition clawhub plugin release candidates * fix: read clawhub trusted publisher config endpoint * feat: split clawhub plugin bootstrap workflow * ci: split plugin clawhub publish paths * ci: pin clawhub package publish workflow * ci: keep clawhub bootstrap token out of builds * ci: fix clawhub release dry-run gating * ci: align clawhub oidc publish refs * ci: make clawhub bootstrap recovery idempotent * ci: route clawhub repair candidates through bootstrap * ci: preserve tideclaw alpha clawhub guards * ci: simplify clawhub release ref handling * ci: extract clawhub release routing plan * ci: extract clawhub release runtime state * test: guard clawhub release helper executability * ci: pin ClawHub CLI for plugin publishing * ci: allow historical ClawHub dry-run validation * ci: fix ClawHub bootstrap token handoff * fix(gateway): mirror commentary-phase assistant events to session message subscribers 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 <[email protected]> Co-authored-by: Chisel <[email protected]> * fix: harden websocket payload handling * fix(moonshot): rewrite duplicate native Kimi tool call ids 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 <[email protected]> * Expose paged channel action results (#88993) * fix(discord): expose paged thread list results * fix(discord): keep thread pagination local * fix(discord): use archive timestamps for thread cursors * test(discord): cover thread pagination results --------- Co-authored-by: Peter Steinberger <[email protected]> * fix(fireworks): resolve catalog model params from manifest (#90326) 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 <[email protected]> * fix(doctor): diagnose blocked external channel plugins (#86629) Diagnose configured channel plugins whose installed owners are blocked by trust or activation policy, while preserving multi-owner fallback and actionable channel safety warnings. Closes #83212. Co-authored-by: luyifan <[email protected]> * fix(mistral): skip unreadable tool schemas (#90242) Skip unreadable Mistral tool schemas while preserving valid sibling tools. Omit empty tool payloads, reject forced choices for removed tools, and snapshot pinned tool names before validation and emission. Live Mistral E2E: run_9b34d30e9f5b CI: https://github.com/openclaw/openclaw/actions/runs/27459679633 Co-authored-by: Vincent Koc <[email protected]> * fix(slack): persist delivered replies in transcripts (#92498) 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 <[email protected]> * fix(channels): report progress draft startup failures (#92083) Report timer-fired channel progress-draft startup failures at the shared gate boundary instead of silently dropping them. Explicitly awaited starts still reject, and failed timer starts remain retryable. Refs #92031. Co-authored-by: Hansraj Singh Thakur <[email protected]> * fix(agents): isolate invalid plugin model catalogs (#92564) 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 <[email protected]> * chore(maint): make PR changelog edits release-only (#92607) * docs: UX-013 — design system documentation (#89827) * docs: UX-013 — shared design system documentation * docs: align design system docs with source tokens * feat(ui): hide empty workboard columns (#89615) * fix(a11y): B-1+B-2+B-3 — contrast, focus states, minimum font sizes (#89822) * 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 * fix(memory): preserve qmd startup errors (#92618) * docs(gateway): add uptime monitoring guidance to health check docs (fixes #55768) (#92608) * fix(docs): pin Windows Hub download links to v2026.6.5 (#92605) 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 * #92589: fix(internal-runtime-context): wrap prompt-preface runtime context body in delimiters (#92593) * 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 * Run Vitest and Playwright scenarios from qa suite (#92606) * 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 * Revert "chore(maint): make PR changelog edits release-only (#92607)" This reverts commit 4640baa299099df01bf58b040c8b6ae9f243713e. * feat(hooks): per-turn usageState (with provider limits + rich atoms) on reply_payload_sending 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 <[email protected]> * fix(hooks): make usageState limits credential-aware + document best-effort delivery 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 <[email protected]> * fix(usage): map auth mechanism to usage-credential type for provider limits 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 <[email protected]> * fix(usage): thread real context occupancy + last-call usage into usageState 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 <[email protected]> * fix(usage): don't resolve subscription windows for an absent auth signal 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 <[email protected]> * fix(hooks): tighten reply usage state correlation * fix(models): bound model browse discovery (#92247) 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 <[email protected]> * fix: require admin for HTTP model overrides Co-authored-by: Peter Steinberger <[email protected]> * fix(gateway): honor profile auth for SecretRef model entries (#90686) 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 <[email protected]> * feat(usage): native templated /usage full renderer (retires the footer plugin) 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 <[email protected]> * style(usage): satisfy oxfmt + oxlint for native renderer 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 <[email protected]> * fix(usage): clear type-aware oxlint in usage-bar translator - 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 <[email protected]> * feat(usage): add fixed:N decimal-format verb to usage-bar translator 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 pla…
Summary
CHANGELOG.mdand changelog fragment editsscripts/prmerge flow from adding or verifying coauthor/reviewer/thanks credit trailersAGENTS.mdand the OpenClaw PR maintainer skillVerification
bash -n scripts/pr scripts/pr-lib/*.shnode scripts/run-vitest.mjs test/scripts/check-changelog-attributions.test.tsnpx -y [email protected] exec oxfmt --check test/scripts/check-changelog-attributions.test.ts .agents/skills/openclaw-pr-maintainer/SKILL.md AGENTS.md scripts/pr scripts/pr-lib/common.sh scripts/pr-lib/gates.sh scripts/pr-lib/merge.sh scripts/pr-lib/prepare-core.shgit diff --check