Commit Graph

36296 Commits

Author SHA1 Message Date
sunlit-deng
70cc909eea fix(openrouter): send explicit auth headers (#98187)
* fix(openrouter): send explicit auth headers

* test(openrouter): type stream mock calls
2026-06-30 18:03:14 -07:00
headbouyJB
518937be4e fix(system-prompt): move exec-approval + Authorized Senders below cache boundary (#98267)
* fix(system-prompt): move exec-approval + Authorized Senders below cache boundary

buildExecApprovalPromptGuidance (channel-varying: CLI /approve vs native
approval UI) and buildUserIdentitySection / "## Authorized Senders"
(owner/identity-varying, dropped when minimal) were emitted into the static,
cacheable prefix *above* SYSTEM_PROMPT_CACHE_BOUNDARY. They fork the cacheable
prefix at ~token 1,460, invalidating client-side prefix caching for the rest of
the ~17.8K-token system prompt — causing minutes-long cold prefills on local
models (llama.cpp / MLX / Ollama) after any channel-varying cron/heartbeat turn.

Follow-on to #40256, which moved Messaging/Voice/Reactions below the boundary
but missed these two. Relocates both into the existing below-boundary
channel-guidance block. Pure cache-stability change with no behavioural change
(the guidance is position-independent for correctness). Extends the boundary
test to assert both sections sit below SYSTEM_PROMPT_CACHE_BOUNDARY.

Measured on a local deployment: shared cross-channel prefix grew ~1,460 ->
~15,486 tokens; post-cron interactive turns went from minutes to ~10s.

Fixes #98261

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H6Hz9UEpxQ4W3d8XecvupH

* fix(system-prompt): suppress relocated exec-approval line under tool_call_style override

Addresses review (clawsweeper): the exec-approval guidance lived inside the
`tool_call_style` fallback, so a provider override of that section previously
replaced it. Relocating it below the boundary emitted it unconditionally, which
changed behaviour for providers/plugins that override `tool_call_style`. Gate the
relocated line on the absence of a `tool_call_style` override, restoring the
original "override replaces the whole section" contract. Extends the
provider-override test to assert the default approval line is suppressed when
the section is overridden.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H6Hz9UEpxQ4W3d8XecvupH

* fix(system-prompt): tighten cache boundary proof

* fix(system-prompt): tighten cache boundary proof

* fix(system-prompt): tighten cache boundary proof

---------

Co-authored-by: headbouyJB <23249268+headbouyJB@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-06-30 18:00:21 -07:00
Dallin Romney
f749de4f0a fix(device-pairing): don't churn requestId on subset re-requests (#98145)
* fix(device-pairing): don't churn requestId on subset re-requests

A reconnect that re-requested a subset of an already-pending device
pairing request still superseded it with a fresh requestId. This is the
root cause of the "unknown requestId" failures during device approval:

1. A TUI connect files a broad scope-upgrade request; the owner copies its
   id from `openclaw devices list`.
2. `openclaw devices approve <id>` reconnects as a CLI probe that only needs
   `operator.pairing` — a subset of the pending scopes.
3. That subset re-request superseded the pending request with a new id, so
   the originally-listed id no longer existed and approve failed.

Refresh the existing request in place when the incoming request only asks
for roles/scopes a single pending request (same device key + role) already
covers. Escalations that request *more* than the pending request still
supersede with a fresh id, so the requestId stays bound to at least the
scope snapshot the owner saw (the existing security-motivated behavior and
its test are preserved).

AI-assisted (Claude Code).

* fix(device-pairing): align subset pairing with scope coverage
2026-06-30 18:00:12 -07:00
welfo-beo
fc97e92ddd fix: surface node approval guidance from devices CLI (#98115)
* fix: surface node approval guidance from devices CLI

* fix: preserve node approval connection guidance

* fix: handle admin retry unknown device approvals

* fix: require stable node approval matches

---------

Co-authored-by: welfo-beo <187608477+welfo-beo@users.noreply.github.com>
Co-authored-by: welfo-beo <welfo-beo@users.noreply.github.com>
2026-06-30 17:55:30 -07:00
zengLingbiao
490a4c5d9b test(gateway): add unit tests for node wake state tracking and testing seam (#98205)
Add 17 unit tests for nodes-wake-state.ts covering:
- Exported wait/poll constants
- nodeWakeById Map operations (insert, overwrite, multi-entry, inFlight)
- nodeWakeNudgeById Map operations (independent tracking)
- clearNodeWakeState function (removal, no-op, scoped deletion)
- testing seam (getNodeWakeByIdSize, hasNodeWakeEntry, resetWakeState)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 17:53:56 -07:00
ly-wang19
6285f5c776 fix(utils): keep reply directive ids unicode-safe (#96938)
* fix(utils): keep reply directive ids unicode-safe

* test(utils): catch trailing lone surrogate in hasUnpairedSurrogate helper

Per PR #96938 review: the test helper missed a high surrogate at end of
string because charCodeAt(out-of-bounds) returns NaN, and NaN comparisons
are always false. Guard bounds explicitly so the truncation test actually
proves what it claims, plus two cases pinning helper behavior.

* chore(utils): rerun QA smoke to confirm memory-index timeout flake on #96938

---------

Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
2026-06-30 17:47:39 -07:00
xingzhou
aadd57c31c fix(pdf): reject empty parsed page ranges (#97698) 2026-06-30 17:46:45 -07:00
qingminlong
e2dd5a0309 fix(sessions): avoid cross-cwd recent resumes (#97785) 2026-06-30 17:46:40 -07:00
Peter Lee
856cd1824c test: migrate leaky raw mkdtemp calls to createSuiteTempRootTracker() (#96058)
Gap finding: tempdir-8h9i0j1k, tempdir-9i0j1k2l

Replace bare fs.mkdtemp/mkdtempSync calls in 4 test files with the
shared createSuiteTempRootTracker() helper, adding proper cleanup
where none existed.

Background: the project has standardized on shared temp dir helpers
since 88b87d893d (withTempDir) -> 13df67ebc8 (createSuiteTempRootTracker)
-> 06431fd99b / #87298 (CI guard + docs guidance). This PR continues
that migration for the highest-risk files.

Files changed:
- src/skills/lifecycle/install-fallback.test.ts: variable overwrite risk
- src/auto-reply/reply/session-hooks-context.test.ts: helper leaks temp dirs
- src/auto-reply/reply/abort.test.ts: createAbortConfig leaks root dir
- src/auto-reply/inbound.test.ts: test cases without cleanup

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 17:43:28 -07:00
Wynne668
ba3f68030b fix(cron): clear agentTurn thinking override by blanking the field (#96293)
* fix(cron): clear agentTurn thinking override when patched with null

Cron agentTurn patches could clear model/fallbacks/toolsAllow overrides by
sending an explicit null, but thinking had no clear path: the patch schema and
normalizer dropped thinking:null before it reached the merge logic, and the
payload merge only handled string values. Blanking the Thinking/Effort field in
the Cron Control UI therefore silently preserved the old value.

Add thinking:null support across the patch schema, exported type, normalizer,
and payload merge (mirroring model). The Control UI now sends an explicit clear
for model/thinking when an edited job blanks a previously stored override, and
the CLI gains --clear-thinking for parity with --clear-model.

* docs(cron): document --clear-thinking beside sibling clear flags
2026-06-30 17:43:22 -07:00
Peter Lee
91d0e77e2e test: migrate src/commands tests to shared temp dir helpers (#96359)
* test: migrate src/commands tests to shared temp dir helpers

* fix(test): remove unused path import in migrate apply test
2026-06-30 17:43:16 -07:00
Yzx
16b275a0fb fix: warn when configured channel plugins cannot load (#96397)
* fix(gateway): warn for blocked configured channel plugins

* fix(gateway): preserve configured channel warning source
2026-06-30 17:43:10 -07:00
solodmd
b1fae752f8 fix(anthropic-oauth): bound OAuth token endpoint response reads (#96644)
postJson reads the Anthropic OAuth token endpoint response body with
an unbounded await response.text(). A compromised or hijacked OAuth
endpoint can stream an arbitrarily large body and force the runtime
to buffer the entire payload before parsing — an OOM/DoS vector.

Replace with readResponseWithLimit at 16 MiB cap + TextDecoder decode
to match the sibling bounded-read pattern (provider-http-errors.ts:308).

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-30 17:43:06 -07:00
xingzhou
29371cfe1f fix #96840: [Bug]: Targetless message.send fails with 'Action send requires a target' in WebChat despite docs stating source-reply sink should handle it (#97167)
* fix(message): route WebChat sends to source sink

* fix(webchat): preserve message tool source replies

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-06-30 17:34:26 -07:00
Yuval Dinodia
82871fe21b fix(doctor): merge colliding model-ref map keys instead of dropping (#96544)
* fix(doctor): merge colliding model-ref map keys instead of dropping

When two retired model-ref keys in a models map upgrade to the same
current ref, rewriteModelRefMapKeys dropped the second entry silently
and still logged a success line, so doctor --fix lost one entry's
params/streaming/alias/agentRuntime tuning while falsely reporting it
as upgraded.

Mirror the sibling provider-models-array merge path (#90047): deep-merge
the colliding entries preserving disjoint fields, keep the existing
value deterministically on a true field conflict, and push a merge or
collision change-log line instead of dropping.

* fix(doctor): block prototype keys when merging colliding model-ref map entries

mergeModelRefMapEntries copied arbitrary incoming config fields into a
plain object, so a colliding model entry carrying a __proto__ field could
mutate the merged entry prototype during legacy config migration. Skip
isBlockedObjectKey fields before assigning or recursing, matching the
shared config merge guard. Disjoint-field merging is unchanged.

* fix(doctor): preserve canonical model migration values

* fix(doctor): filter blocked keys on both merge sides and report retired source key

* fix(doctor): preserve colliding retired model settings

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
2026-06-30 17:20:49 -07:00
ZOOWH
5a73361ed2 fix(plugins): apply output text transforms to toolcall_delta and toolcall_end events (#97769)
* fix(plugins): apply output text transforms to toolcall_delta and toolcall_end events

toolcall_delta and toolcall_end events bypassed the output replacement
pipeline, leaking masked tokens (e.g. PII placeholders) into tool call
arguments and external systems.

Closes #97761

* fix(plugins): transform nested tool call arguments in output replacements

Add recursive transformToolCallArgumentText to handle strings, arrays,
and nested objects in tool call arguments, not just the name field.

* fix(plugins): keep tool names unchanged and cover result path

- Remove toolCall.name transformation to prevent breaking tool routing
- Add arguments transform to stream.result()/done.message via
  transformContentText for tool-call content blocks

* fix(plugins): narrow argument transform to toolCall content blocks only

Only transform arguments on content blocks with type=toolCall to avoid
touching non-tool-call blocks that happen to have an arguments field.

* test(plugins): assert stream.result() tool-call content block is transformed

* test(plugins): fix result-path fixture to actually contain toolCall block

The previous fixture used makeAssistantMessage('final') which produces
a plain text content block, not a toolCall block. The stream.result()
assertion was vacuously passing.

* style(plugins): fix type cast in result-path test assertion

* fix(agents): preserve tool argument transforms after repair

* refactor(agents): keep tool transform boundary narrow

---------

Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
2026-06-30 17:15:01 -07:00
Agustin Rivera
3811001d27 fix(exec): bind Windows allowlist execution path (#98260)
* fix(exec): bind windows allowlist execution path

* fix(exec): add windows shadow execution proof

* fix(exec): preserve wildcard allowlist behavior

* fix(exec): correct blocked plan test fixture
2026-06-30 14:33:08 -07:00
Dallin Romney
44ec7580e2 fix(cli): stop pairing list crashing with empty channel enum (#98142)
When no chat DM pairing channels are configured, `openclaw pairing list`
(no channel argument) threw `Channel required ... (expected one of: )`
with an empty enum that reads like a bug. Users who hit this are usually
trying to approve a TUI/device or scope-upgrade request, which lives under
`openclaw devices`, not `openclaw pairing` (which only handles chat DM
pairing).

- Guard the channel hint in help text and errors so an empty channel list
  no longer renders a bare `()` / `(expected one of: )`.
- When no pairing channels exist, redirect to `openclaw devices list` /
  `openclaw devices approve` instead of failing opaquely.

AI-assisted (Claude Code).
2026-06-30 14:15:19 -07:00
Agustin Rivera
b885c81479 fix(mcp): require owner for Claude permission replies (#98256)
* fix(mcp): require owner for Claude permission replies

* fix(mcp): prove owner-gated permission replies
2026-06-30 13:58:02 -07:00
Gio Della-Libera
9f07b21e62 Doctor: expose auth profile findings (#97125)
Expose `core/doctor/auth-profiles` as a default-disabled structured Doctor lint check.

This maps the existing auth-profile health diagnostics into structured findings while preserving legacy Doctor repair behavior. The check remains opt-in through `--only core/doctor/auth-profiles` or `--all`; default `doctor --lint` behavior is unchanged.

Validation:
- focused auth-profile/hints/contribution Vitest passed
- changed-file oxfmt and oxlint passed
- core tsgo passed
- git diff --check passed
- exact-head hosted CI/Testbox gates passed

Co-authored-by: giodl73-repo <235387111+giodl73-repo@users.noreply.github.com>
2026-06-30 13:28:26 -07:00
Alix-007
640258d7b3 fix(usage): reject inverted startDate-endDate range in usage.cost and sessions.usage (#94096)
* fix(usage): reject inverted startDate-endDate range

* chore: retrigger CI for real behavior proof check

* refactor(usage): resolve validated date ranges once

---------

Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
2026-06-30 12:55:43 -07:00
Jason (Json)
786abe78df Streamline OpenClaw onboarding (#98218)
* feat: streamline onboarding setup flow

* fix: harden onboarding preflight installs

* test: isolate gateway preflight safety env

* fix: keep local gateway probes on loopback

* fix: honor onboarding node manager installs

* docs: align setup onboarding reference

* fix: harden bare gateway probe fallback

* fix: honor env gateway auth in bare TUI probe

* test: isolate wizard TUI hatch mocks
2026-06-30 11:22:26 -07:00
Ayaan Zaidi
3f147ae5ca test(provider-transport-fetch): cover split large SSE event 2026-06-30 11:08:38 -07:00
Peter Lindsey
81d60ca30d fix(provider-transport-fetch): raise SSE sanitize buffer cap to 16 MiB
The 64 KiB inter-event SSE sanitize buffer added in #96989 rejects a single
legitimate event larger than 64 KiB — e.g. a large gpt-5.5 reasoning summary on
the openai-chatgpt-responses API — throwing "SSE response exceeded max buffer
size (65536 bytes) without event boundary" and failing the whole request. The
default ChatGPT-subscription gpt-5.5 path is unusable (present in v2026.6.11-beta.1).

Decouple the two bounds: keep the non-OK error-body cap tight at 64 KiB
(SSE_NONOK_BODY_MAX_BYTES), and raise the inter-event sanitize buffer to the same
16 MiB ceiling as the JSON-synthesis path. The guard still trips on a genuinely
boundary-less (hostile/broken) stream, just not on a real large event.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 11:08:38 -07:00
Ayaan Zaidi
68c533cfb3 fix(config): preserve unreadable write rejection for exported writes 2026-06-30 10:52:44 -07:00
Ayaan Zaidi
c1605064d0 test(config): distill unreadable config write guard cases 2026-06-30 10:52:44 -07:00
yetval
14198836b2 fix(config): fail closed when config is unreadable before a full-file write
A present-but-unreadable openclaw.json (for example EACCES after a sudo
command leaves it root-owned) returns an empty best-effort fallback
snapshot. A later full-file write (openclaw doctor, including the update
doctor pass) then serialized a skeletal config over the still-rich file,
dropping gateway.mode and bricking gateway startup.

The fallback base has no raw bytes and an empty resolved config, so the
existing size-drop and gateway-mode-removed guards never fired, and the
update path passes allowConfigSizeDrop=true.

Record the read failure on the snapshot (readError) and treat an
unreadable base as an always-blocking write reason
(unreadable-config-before-write) that allowConfigSizeDrop does not bypass.
The allowDestructiveWrite escape hatch and the rejected-artifact path are
preserved, so explicit recovery still works and the blocked payload is
saved to openclaw.json.rejected.<timestamp>.

Refs #78493.
2026-06-30 10:52:44 -07:00
Ayaan Zaidi
8829dc4aae fix(gateway): reject deleted-agent exec followups before dedupe 2026-06-30 10:49:23 -07:00
Ayaan Zaidi
35d06e6356 refactor(gateway): distill deleted-agent RPC guard 2026-06-30 10:49:23 -07:00
Pick-cat
afe137d839 fix(gateway): reject deleted-agent sessions before dedupe and attachment offload 2026-06-30 10:49:23 -07:00
Pick-cat
379756fc92 fix(gateway): guard deleted-agent sessions before agent reset commands 2026-06-30 10:49:23 -07:00
Pick-cat
8cf599cb93 test(gateway): model ops as configured in voice-wake agent fixture 2026-06-30 10:49:23 -07:00
Pick-cat
60f30be526 test(gateway): add live agent RPC deleted-agent integration proof 2026-06-30 10:49:23 -07:00
Pick-cat
71e6505fb1 fix(gateway): reject agent RPC runs for deleted-agent sessions
Related #65524
2026-06-30 10:49:23 -07:00
0668000787
b2787a1c7a fix(text): strip antml:namespaced tool call XML from visible content
When a proxied Anthropic-Messages endpoint degrades a native tool_use block
into plain text, the assistant emits antml:namespaced XML tags
(<antml:invoke>, <antml:parameter>). These were not recognized by the
visible-text sanitizer, leaking tool-call XML into user-visible replies.

Added antml: (and mm:) namespace handling to:
- TOOL_CALL_QUICK_RE regex for fast pre-check
- TOOL_CALL_TAG_NAMES set for tag name recognition
- Tag name scanner to accept ':' in tag names
- TOOL_CALL_XML_PAYLOAD_START_RE for XML payload detection
- shouldDetectXmlPayload to include antml:invoke tags

Fixes #97750.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 10:24:28 -07:00
cxbAsDev
f284ce3b4d fix(cli): bound docs search API response reads with committed test (#98188) 2026-06-30 10:00:17 -07:00
Masato Hoshino
a75431c586 fix(agents): classify Anthropic orphaned tool-use replay errors (#98163) 2026-06-30 09:57:14 -07:00
pick-cat
169acd1e4e fix(plugin-sdk): guard legacy dedupe JSON parse against malformed files (#98125) 2026-06-30 09:56:20 -07:00
Pavan Kumar Gondhi
6cb82eaab8 fix: require owner for trajectory export (#97840) 2026-06-30 16:59:50 +05:30
Pavan Kumar Gondhi
3d4b7cade9 fix: gate group activation changes by owner (#97838) 2026-06-30 13:56:56 +05:30
Kris Wu
444a093593 fix(agents): skip pre-prompt precheck when context engine owns compaction (#95342)
* fix(agents): skip pre-prompt precheck when context engine owns compaction

When a context engine advertises ownsCompaction=true (e.g. lossless-claw),
skip the pre-prompt preemptive overflow precheck entirely. The engine
already manages the context budget through assemble() and its own
compaction lifecycle — the built-in precheck is redundant and causes
false-positive overflow errors for CJK-heavy sessions due to its
conservative token estimation formula.

Safety is preserved: if the model's actual context limit is exceeded,
the model API returns an error that the outer overflow-compaction retry
loop handles normally.

* fix(agents): assert non-null context engine in precheck skip log

* test(context-engine): cover owning precheck contract

* fix(agents): preserve precheck after context assembly failure

---------

Co-authored-by: Josh Lehman <josh@martian.engineering>
2026-06-30 00:55:03 -07:00
Agustin Rivera
72f837a4a4 fix(codex): require admin for native controls (#97952)
* fix(codex): require admin for native controls

Gate Codex native session controls and bound turns on current owner or operator.admin authority. Preserve gateway scope precedence and read-only status behavior.

* fix(codex): align native authorization

* fix(codex): preserve silent bound handling

* fix(codex): narrow bound auth contract

* fix(docs): refresh generated docs map
2026-06-29 20:41:28 -07:00
dwc1997
28347ba51c test(globals): add unit tests for global CLI flag state helpers (#96735)
* test(globals): add unit tests for global CLI flag state helpers

Add unit tests for shouldLogVerbose, logVerbose, and logVerboseConsole
functions in src/globals.ts to verify verbose logging behavior.

Tests cover:
- shouldLogVerbose returns true when isVerbose is true
- shouldLogVerbose returns true when file log level is debug
- shouldLogVerbose returns false when both are false
- logVerbose does not log when shouldLogVerbose is false
- logVerbose logs to console when isVerbose is true
- logVerboseConsole does not log when isVerbose is false
- logVerboseConsole logs to console when isVerbose is true

* ci: trigger re-review

* test(globals): cover verbose logger behavior

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-06-29 17:45:30 -07:00
Agustin Rivera
4aa07513fe fix(cron): scope agent wake targets (#97949)
* fix(cron): scope agent wake targets

* fix(cron): refresh prompt snapshots
2026-06-29 17:43:43 -07:00
Jason (Json)
455f813d6e fix(telegram): deliver durable reasoning when enabled
Preserve shared reasoning suppression by default while letting Telegram opt into durable reasoning payloads only when it has a deliverable reasoning lane. Covers persistent /reasoning on, separate reasoning stream lanes, and progress-stream suppression.\n\nVerification:\n- node scripts/run-vitest.mjs src/auto-reply/reply/dispatch-from-config.test.ts extensions/telegram/src/bot-message-dispatch.test.ts\n- git diff --check upstream/main...HEAD\n- .agents/skills/autoreview/scripts/autoreview --mode branch --base upstream/main --stream-engine-output\n- CI run 28411526182 green, including QA Smoke CI and check-test-types\n- Real behavior proof run 28411676681 passed\n\nPR: #97875
2026-06-29 17:31:06 -07:00
Agustin Rivera
6ead092302 fix(acp): require owner for runtime controls (#97953) 2026-06-29 17:29:08 -07:00
Jiaming Guo
456c48f368 fix(gateway): require admin scope for browser proxy invoke (#85916)
* fix(gateway): require admin scope for browser proxy invoke

* fix(gateway): document trusted node scopes

* fix(gateway): align browser scope test

* fix(gateway): satisfy node test lint

---------

Co-authored-by: Agustin Rivera <agustin@rivera-web.com>
2026-06-29 16:23:02 -07:00
Josh Avant
052dc59526 fix: /pair qr fails to show QR code in chat surfaces (#97933)
* fix(pair): render qr per chat surface

* test(plugin-sdk): update reply helper surface budget

* fix(pair): keep qr metadata internal
2026-06-29 18:17:21 -05:00
Agustin Rivera
b7b2e1f77e fix(pdf): guard native provider requests (#97872)
* fix(pdf): guard native provider requests

* fix(pdf): preserve configured origin trust

* fix(pdf): preserve resolver compatibility
2026-06-29 15:51:26 -07:00
Dallin Romney
05d83125c9 test: add hosted media provider QA live evidence (#97900) 2026-06-29 13:01:47 -07:00