Skip to content

fix(cli): validate gateway-rpc --timeout#54646

Closed
ruanrrn wants to merge 1 commit into
openclaw:mainfrom
ruanrrn:bughunt/fix-cli-gateway-timeout-validation
Closed

fix(cli): validate gateway-rpc --timeout#54646
ruanrrn wants to merge 1 commit into
openclaw:mainfrom
ruanrrn:bughunt/fix-cli-gateway-timeout-validation

Conversation

@ruanrrn

@ruanrrn ruanrrn commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Rejects malformed explicit gateway RPC timeout values while preserving the 30000 ms omitted-option default. Previously, invalid values like nope, 10ms, or 1.5 were silently coerced to NaN/0 and either fell back to the default or caused runtime failures.

Changes

  • Add resolveGatewayRpcTimeoutMs() to src/cli/gateway-rpc.ts for strict validation
  • Reject non-positive-integer --timeout values with a clear error message
  • Validate --timeout-seconds in cron add CLI when the user explicitly passes it (checks option source)
  • Add gateway-rpc.timeout-option.test.ts with edge case coverage
  • Carry forward fix(cli): validate gateway-rpc --timeout #54646 with adaptations for current main (ProjectClownfish upstream partial merge)

Test Plan

  • New test file: gateway-rpc.timeout-option.test.ts
  • Existing tests: register.status-health-sessions.test.ts timeout rejection tests pass
  • CI: check-lint pass, dependency-guard-detect pass

Real behavior proof

Behavior or issue addressed: The openclaw gateway-rpc CLI accepted invalid --timeout values like nope, 10ms, 1e3, and 1.5 without error. These were silently coerced to NaN or incorrect numbers, causing either obscure downstream failures or silent fallback to the 30s default. The user had no feedback that their timeout flag was ignored.

Real environment tested: Production OpenClaw CLI (v2026.6.1, Node.js v22, Linux) connected to a live Gateway instance at 127.0.0.1:18789.

Exact steps or command run after this patch:

# Valid timeout — works
$ openclaw gateway-rpc health --timeout 5000
# Request completes with 5s timeout

# Invalid timeouts — rejected with clear error
$ openclaw gateway-rpc health --timeout nope
$ openclaw gateway-rpc health --timeout 10ms
$ openclaw gateway-rpc health --timeout ""
$ openclaw gateway-rpc health --timeout "1.5"

# Omitted timeout — uses default
$ openclaw gateway-rpc health

Evidence after fix:

$ openclaw gateway-rpc health --timeout nope
Error: --timeout must be a positive integer (milliseconds)
$ echo $?
1

$ openclaw gateway-rpc health --timeout 10ms
Error: --timeout must be a positive integer (milliseconds)

$ openclaw gateway-rpc health --timeout ""
Error: --timeout must be a positive integer (milliseconds)

$ openclaw gateway-rpc health --timeout "1.5"
Error: --timeout must be a positive integer (milliseconds)

$ openclaw gateway-rpc health --timeout 10000
{ "ok": true }
$ echo $?
0

$ openclaw cron add --name "test" --cron "* * * * *" --session isolated --message "hello" --timeout-seconds 10s
Error: Invalid --timeout-seconds (must be a positive integer).

Observed result after fix:

  • All invalid timeout formats ("", "nope", "10ms", "1000abc", "1e3", "1.5") are now rejected with a clear error message before any network call
  • Valid integer strings ("5000", "1234") work correctly
  • Omitted timeout defaults to 30s as before
  • cron add --timeout-seconds similarly rejects invalid values when explicitly passed via CLI

Not tested: None for the behavior changed by this PR. Broader unrelated gateway/typecheck failures on current main are outside this PR scope.

@openclaw-barnacle openclaw-barnacle Bot added cli CLI command changes size: S labels Mar 25, 2026
@greptile-apps

greptile-apps Bot commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a silent coercion bug in callGatewayFromCli where invalid --timeout values (e.g. --timeout nope) were coerced to NaN and caused callGateway to silently fall back to 10_000ms, inconsistent with the declared CLI default of 30_000ms. The fix adds resolveGatewayRpcTimeoutMs, backed by the existing parsePositiveIntOrUndefined helper, that throws a clear error for invalid inputs and correctly defaults to 30s.

  • Core fix is correct: purely non-numeric values like "nope" or "abc" are properly rejected with a useful error message.
  • Default aligned: the constant DEFAULT_GATEWAY_RPC_TIMEOUT_MS = 30_000 now matches the Commander option default declared in addGatewayClientOptions, closing the inconsistency with the old 10_000 fallback.
  • Partial-numeric edge case: parsePositiveIntOrUndefined uses Number.parseInt, which silently truncates strings like "1000abc" to 1000 rather than rejecting them. This is a narrow gap in the validation that a follow-up could close with a stricter regex/parse guard.
  • Tests: three well-scoped unit tests cover the happy path, missing-timeout default, and the non-numeric rejection. The partial-numeric case is not covered.

Confidence Score: 4/5

  • Safe to merge; the primary bug is fixed and the only remaining gap is a narrow partial-numeric edge case in the shared parsePositiveIntOrUndefined helper.
  • The core fix is correct and well-tested. The parseInt-based partial-numeric gap ("1000abc" → 1000) is a pre-existing helper behavior, is narrow in practice, and does not introduce a regression relative to the old code. The PR is a clear net improvement.
  • No files require special attention beyond the noted parseInt edge case in src/cli/gateway-rpc.ts.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/cli/gateway-rpc.ts
Line: 26-30

Comment:
**`parseInt` silently accepts partial numeric strings**

`parsePositiveIntOrUndefined` uses `Number.parseInt(value, 10)`, which parses only up to the first non-digit character. This means `--timeout 1000abc` is silently accepted as `1000` rather than rejected — exactly the category of silent coercion this PR aims to fix.

Consider using a strict parse pattern instead:

```ts
function resolveGatewayRpcTimeoutMs(timeout: unknown): number {
  if (timeout === undefined || timeout === null || timeout === "") {
    return DEFAULT_GATEWAY_RPC_TIMEOUT_MS;
  }
  // Strict: reject strings like "1000abc" that parseInt would silently truncate
  const str = String(timeout);
  if (!/^\d+$/.test(str)) {
    throw new Error("--timeout must be a positive integer (milliseconds)");
  }
  const parsed = Number(str);
  if (!Number.isInteger(parsed) || parsed <= 0) {
    throw new Error("--timeout must be a positive integer (milliseconds)");
  }
  return parsed;
}
```

Or, if you want to keep reusing `parsePositiveIntOrUndefined`, you can guard with a regex check before calling it so the partial-numeric edge case is also caught. The current `"nope"` test passes, but `"1000abc"` would not be rejected.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(cli): validate gateway-rpc --timeout" | Re-trigger Greptile

Comment thread src/cli/gateway-rpc.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4cf4fd02d9

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/cli/gateway-rpc.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e1f5d09c40

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/cli/program/helpers.ts Outdated
@ruanrrn

ruanrrn commented Mar 25, 2026

Copy link
Copy Markdown
Contributor Author

Codex P2 (cron --timeout-seconds): good catch — after tightening parsePositiveIntOrUndefined, "--timeout-seconds 10s" would parse to undefined and silently omit payload.timeoutSeconds.\n\nFixed by validating --timeout-seconds when provided via CLI (cron add now errors on invalid values instead of silently dropping). Added unit test for "10s".\n\nCommit: e8bc4a4

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e8bc4a488e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/cli/gateway-rpc.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5362a2074e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/cli/gateway-rpc.ts Outdated
@ruanrrn
ruanrrn force-pushed the bughunt/fix-cli-gateway-timeout-validation branch from e596cb9 to 7d7bb3d Compare April 27, 2026 13:42
@clawsweeper

clawsweeper Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 1, 2026, 9:50 AM ET / 13:50 UTC.

Summary
The PR adds shared Gateway RPC timeout normalization/validation, a focused timeout-option test, and explicit cron add --timeout-seconds validation.

PR surface: Source +25, Tests +53. Total +78 across 3 files.

Reproducibility: yes. Source inspection shows current main still treats empty shared Gateway RPC timeout strings as fallback and drops invalid cron add --timeout-seconds; the PR body also provides after-fix terminal output for those CLI paths.

Review metrics: 1 noteworthy metric.

  • CLI Validation Surfaces: 2 tightened, 0 added, 0 removed. Both shared Gateway RPC --timeout and cron add --timeout-seconds become stricter, so maintainers should review the user-facing compatibility impact before merge.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Decide whether the fail-fast timeout compatibility change is acceptable for existing scripts.
  • [P2] Optionally add a focused cron add --timeout-seconds regression test before merge.

Risk before merge

  • [P2] This intentionally changes CLI behavior for explicit empty, whitespace, and malformed timeout values that current released paths may have treated as omitted, fallback, or silently dropped; maintainers should accept that compatibility change before merge.
  • [P2] The cron add --timeout-seconds behavior has real terminal proof in the PR body, but this branch does not add a committed cron regression test for that specific path.

Maintainer options:

  1. Accept Fail-Fast Validation (recommended)
    Merge after maintainer acceptance that malformed explicit timeout inputs now error instead of falling back or being omitted.
  2. Require Compatibility Mode
    Ask for a default-compatible path plus an explicit strict mode only if existing automation with empty timeout values must keep working.

Next step before merge

  • [P2] No repair job is needed because the branch already contains the narrow fix; maintainers need to accept the fail-fast compatibility change and run normal merge validation.

Security
Cleared: The diff only changes local CLI parsing and tests; it does not add dependencies, workflows, permissions, secrets handling, or new code execution paths.

Review details

Best possible solution:

Land this branch after maintainers intentionally accept fail-fast CLI timeout validation; add focused cron coverage before merge if they want the cron add path protected the same way as Gateway RPC.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection shows current main still treats empty shared Gateway RPC timeout strings as fallback and drops invalid cron add --timeout-seconds; the PR body also provides after-fix terminal output for those CLI paths.

Is this the best way to solve the issue?

Yes, with one maintainer compatibility decision. The patch validates at the shared Gateway RPC wrapper where omitted and explicit empty inputs can be distinguished, reuses the existing strict parser, and mirrors the stricter cron edit behavior for cron add.

AGENTS.md: found and applied where relevant.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 3e592a8bd7af.

Label changes

Label justifications:

  • P2: This is a normal-priority CLI correctness fix with limited blast radius to malformed timeout flags.
  • merge-risk: 🚨 compatibility: Existing scripts that pass empty or malformed timeout values can start failing fast instead of receiving fallback behavior or silently omitted timeout fields.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body includes after-fix terminal output from a real OpenClaw CLI connected to a live local Gateway for invalid and valid timeout cases, including the cron add rejection path.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix terminal output from a real OpenClaw CLI connected to a live local Gateway for invalid and valid timeout cases, including the cron add rejection path.
Evidence reviewed

PR surface:

Source +25, Tests +53. Total +78 across 3 files.

View PR surface stats
Area Files Added Removed Net
Source 2 25 0 +25
Tests 1 53 0 +53
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 3 78 0 +78

What I checked:

  • Repository policy read: Root AGENTS.md was read in full and applied; there is no scoped AGENTS.md under src/cli, and the only maintainer note found is Telegram-specific. (AGENTS.md:1, 3e592a8bd7af)
  • Current shared Gateway RPC wrapper lacks validation: Current main declares the shared --timeout <ms> default as 30000, but callGatewayFromCli forwards opts directly to the runtime without distinguishing omitted timeout from explicit empty input. (src/cli/gateway-rpc.ts:22, 3e592a8bd7af)
  • Current runtime fallback behavior: Current main's runtime uses parseTimeoutMsWithFallback(opts.timeout, 10_000), while the parser returns the fallback for empty or whitespace strings and throws only for non-empty malformed parsed values. (src/cli/gateway-rpc.runtime.ts:43, 3e592a8bd7af)
  • Current cron add silently omits invalid timeoutSeconds: Current main parses opts.timeoutSeconds inside the agent payload and writes undefined when parsing fails, so invalid explicit values can be dropped before this PR's validation. (src/cli/cron-cli/register.cron-add.ts:192, 3e592a8bd7af)
  • Sibling cron edit validates this flag: cron edit already validates explicit --timeout-seconds with an error before accepting the patch, supporting the PR's owner-boundary choice for cron add. (src/cli/cron-cli/register.cron-edit.ts:244, 3e592a8bd7af)
  • PR head implements the missing Gateway RPC validation: The PR adds resolveGatewayRpcTimeoutMs, rejects empty or malformed timeout strings, normalizes omitted timeouts to 30_000, and forwards the normalized value to the runtime. (src/cli/gateway-rpc.ts:31, 4dadf7e3b890)

Likely related people:

  • vincentkoc: Current main blame points the touched shared Gateway RPC, parser-helper, and cron-add lines at 2dcb681f38, and the PR discussion says Vincent pushed a narrow repair to keep this contributor branch canonical. (role: recent area contributor; confidence: high; commits: 2dcb681f38d3, dfdc281f554d; files: src/cli/gateway-rpc.ts, src/cli/gateway-rpc.runtime.ts, src/cli/program/helpers.ts)
  • steipete: History shows Peter introduced the shared Gateway timeout parser in 4e055d8df2 and carried substantial cron CLI extraction and maintenance around register.cron-add.ts. (role: adjacent owner; confidence: medium; commits: 4e055d8df2fb, bcbfb357bec7, f9409cbe43d2; files: src/cli/parse-timeout.ts, src/cli/cron-cli/register.cron-add.ts, src/cli/cron-cli.ts)
  • shakkernerd: The lazy Gateway RPC runtime split in 23422ccb68 moved the timeout parsing into src/cli/gateway-rpc.runtime.ts, which is the main path this PR tightens. (role: introduced runtime seam; confidence: medium; commits: 23422ccb6842; files: src/cli/gateway-rpc.ts, src/cli/gateway-rpc.runtime.ts)
  • Tyler Yust: Cron history includes timeout and scheduling work in early February 2026, making this a secondary routing signal for cron-specific timeout behavior. (role: adjacent cron timeout contributor; confidence: low; commits: 3a03e38378e2, ab9f06f4ffae; files: src/cli/cron-cli/register.cron-add.ts, src/cron/service/state.ts, src/cron/service/store.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@vincentkoc

Copy link
Copy Markdown
Member

ProjectClownfish pushed a narrow repair to this branch so the original contributor path can stay canonical.

Source PR: #54646
Validation: pnpm check:changed
Contributor credit is preserved in the branch history and PR context.

@vincentkoc
vincentkoc force-pushed the bughunt/fix-cli-gateway-timeout-validation branch from 8cda661 to 705b1e8 Compare April 28, 2026 07:55
@vincentkoc vincentkoc added clownfish:human-review clawsweeper Tracked by ClawSweeper automation and removed clownfish:merge-ready labels Apr 28, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels May 19, 2026
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 19, 2026
@clawsweeper clawsweeper Bot added P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels May 19, 2026
@clawsweeper

clawsweeper Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

🎁 Pass real behavior proof to wake the egg and unlock a hatchable treat.

Where did the egg go?
  • The egg game starts only after the PR passes the real-behavior proof check.
  • Before that, no creature or rarity is rolled. The treat waits for real proof.
  • This is still just collectible flavor: proof affects review readiness, not creature quality.

ruanrrn added a commit to ruanrrn/openclaw that referenced this pull request Jun 1, 2026
Reject malformed explicit gateway RPC timeout values while preserving the
30000 ms omitted-option default. Keep cron agent timeout validation from
silently dropping invalid inputs.

- Add resolveGatewayRpcTimeoutMs() to gateway-rpc.ts for strict validation
- Reject non-positive-integer --timeout values instead of silently falling back
- Validate --timeout-seconds in cron add CLI (checks option source)
- Add gateway-rpc.timeout-option.test.ts with edge case coverage
- Carry forward openclaw#54646 with adaptations for current main

Co-Authored-By: ProjectClownfish <[email protected]>
@ruanrrn
ruanrrn force-pushed the bughunt/fix-cli-gateway-timeout-validation branch from 705b1e8 to 4bd4356 Compare June 1, 2026 10:11
@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 1, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 1, 2026
Reject malformed explicit gateway RPC timeout values while preserving the
30000 ms omitted-option default. Keep cron agent timeout validation from
silently dropping invalid inputs.

- Add resolveGatewayRpcTimeoutMs() to gateway-rpc.ts for strict validation
- Reject non-positive-integer --timeout values instead of silently falling back
- Validate --timeout-seconds in cron add CLI (checks option source)
- Add gateway-rpc.timeout-option.test.ts with edge case coverage
- Carry forward openclaw#54646 with adaptations for current main

Co-Authored-By: ProjectClownfish <[email protected]>
@ruanrrn
ruanrrn force-pushed the bughunt/fix-cli-gateway-timeout-validation branch from 2b73e1b to 4dadf7e Compare June 1, 2026 13:43
@openclaw-clownfish

Copy link
Copy Markdown
Contributor

Clownfish 🐠 reef update

Thanks for the work here. Clownfish could not write to the source branch, so it opened a replacement PR rather than letting the fix drift. attribution still points back here.

Replacement PR: #92158
Source PR: #54646
This source PR stays open so maintainers and the original contributor can compare the paths.
Contributor credit is carried into the replacement PR body and changelog plan.

fish notes: model gpt-5.5, reasoning xhigh; reviewed against aeda3bc.

vincentkoc pushed a commit that referenced this pull request Jun 11, 2026
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]>
@vincentkoc

Copy link
Copy Markdown
Member

Thanks again for the useful timeout-validation work here.

This is now landed via the replacement PR: #92158
Landed commit: 99d0bdc

I am closing this source PR as superseded because the replacement carries the same Gateway RPC timeout-validation path, keeps the cron timeout input validation, removes the release-owned changelog entry, and preserves contributor credit in the squash commit.

#60661 remains open because accepted-run timeout semantics are related but separate product behavior, not the same fix.

@vincentkoc vincentkoc closed this Jun 11, 2026
wangmiao0668000666 pushed a commit to wangmiao0668000666/openclaw that referenced this pull request Jun 12, 2026
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 openclaw#54646 and the earlier timeout-validation context from openclaw#40953. openclaw#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]>
wangmiao0668000666 pushed a commit to wangmiao0668000666/openclaw that referenced this pull request Jun 12, 2026
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 openclaw#54646 and the earlier timeout-validation context from openclaw#40953. openclaw#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]>
wangmiao0668000666 pushed a commit to wangmiao0668000666/openclaw that referenced this pull request Jun 12, 2026
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 openclaw#54646 and the earlier timeout-validation context from openclaw#40953. openclaw#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]>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 12, 2026
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 openclaw#54646 and the earlier timeout-validation context from openclaw#40953. openclaw#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]>
michmill1970 added a commit to michmill1970/openclaw that referenced this pull request Jun 13, 2026
* 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…
wangmiao0668000666 pushed a commit to wangmiao0668000666/openclaw that referenced this pull request Jun 16, 2026
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 openclaw#54646 and the earlier timeout-validation context from openclaw#40953. openclaw#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]>
wangmiao0668000666 pushed a commit to wangmiao0668000666/openclaw that referenced this pull request Jun 17, 2026
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 openclaw#54646 and the earlier timeout-validation context from openclaw#40953. openclaw#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]>
badgerbees pushed a commit to badgerbees/openclaw that referenced this pull request Jul 8, 2026
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 openclaw#54646 and the earlier timeout-validation context from openclaw#40953. openclaw#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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clawsweeper Tracked by ClawSweeper automation cli CLI command changes merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants