Commit Graph

66940 Commits

Author SHA1 Message Date
Vincent Koc
bbd01c169e test(update): seed extended-stable install preflight 2026-07-07 06:35:44 +02:00
Vincent Koc
84e5327720 fix(text): keep bounded outputs UTF-16 safe (#101355)
Co-authored-by: Alix-007 <li.long15@xydigit.com>
2026-07-06 21:33:24 -07:00
lzw112
da20b65231 fix(telegram): add UND_ERR_CONNECT_TIMEOUT to PRE_CONNECT_ERROR_CODES (#101258)
* fix(telegram): add UND_ERR_CONNECT_TIMEOUT to PRE_CONNECT_ERROR_CODES

UND_ERR_CONNECT_TIMEOUT occurs during TCP/TLS connect handshake,
before any HTTP request data is sent. Adding it to the pre-connect
set allows isSafeToRetrySendError to safely retry sendMessage when
undici's connect timeout fires — the message was never transmitted.

* test(telegram): cover connect-timeout retry funnels

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-07 05:33:02 +01:00
Peter Steinberger
db42f05efe perf(test): batch source directory routing assertions 2026-07-07 00:27:23 -04:00
cxbAsDev
325cdb7f1a fix(infra): swallow mid-stream read errors in session-cost readJsonlRecords (#101062)
* fix(infra): swallow mid-stream read errors in session-cost readJsonlRecords

* fix(infra): keep usage cache strict on read errors

---------

Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-06 21:23:30 -07:00
Buddy Hadry
964fd53076 fix(update): keep self-updates on the running install's global root (#101228)
* fix(update): keep self-updates on the running install's global root

Package self-updates resolved their install target from `npm root -g`
even when the running install's package root was already known. In an
environment where PATH's npm does not belong to the running install's
Node tree (a launchd/systemd service PATH, or mixed Homebrew + nvm
setups), the probe points at a different global root — or derives a
brand-new one — so the update installs there, reports success, and
leaves the running install stale.

Observed in the wild: a gateway-managed `openclaw update --yes` on
2026.5.28 ran nvm's npm under Homebrew's node, npm derived its default
prefix from the realpathed execPath, and the update installed a full
copy into /opt/homebrew/Cellar/node/26.3.1/lib/node_modules/openclaw
("before": null — a tree that had never existed) while the running nvm
install stayed on the old version until a manual shell update.

resolveGlobalInstallTarget now prefers the package root's own global
root for npm targets; the `npm root -g` probe only decides the target
when no package root is known, and an ephemeral per-Node probe result
(Homebrew Cellar, nvm/n/asdf/volta version dirs — the same set
#93650 filters npm commands against) is never adopted.

* fix(update): infer global roots for scoped package roots

The running-root fallback missed scoped installs because
inferGlobalRootFromPackageRoot only accepted an immediate node_modules
parent. Hop over an @scope segment so node_modules/@scope/name resolves
to the same global root. Regression test: scoped package root with an
ephemeral probe now falls back to the running root.

* fix(update): scope running-root selection

* docs(changelog): note npm self-update root fix

* chore(changelog): defer release note

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-07 05:21:14 +01:00
Jason (Json)
9b2bb9c0a6 fix(models): refresh provider auth after CLI login (#101256)
* fix(models): refresh gateway auth after login

* docs(changelog): note model auth live refresh

* chore(changelog): defer release note

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-07 05:20:35 +01:00
Vincent Koc
835e6a7532 refactor(ui): localize unused module exports (#101339) 2026-07-06 21:20:18 -07:00
Marcus Castro
31432bf101 fix(reply): preserve steered audio for inbound TTS (#95596)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-07 05:18:36 +01:00
Vincent Koc
a7031832ec fix(browser): refresh extension relay token rotation 2026-07-07 06:12:50 +02:00
Peter Steinberger
7a5e5e1852 fix(codex): register the ring-zero crestodian tool directly instead of relying on a dead per-run plugin config override (#101281) 2026-07-07 05:08:08 +01:00
Vincent Koc
1d128b4dd2 fix(whatsapp): gate poll sends during reachout timelock 2026-07-07 06:03:09 +02:00
Peter Steinberger
b337164916 ci: fail Android PRs on ktlint formatting drift (#101275)
* style(android): fix ktlint drift in ShellScreen.kt

Formatting drift landed via #101161; :app:ktlintCheck fails on main
without this. Generated with ./gradlew :app:ktlintFormat.

* ci(android): add ktlint lane to the Android CI job

Android formatting drift kept landing on main because CI never ran
ktlintCheck (see the #100304 manual sweep). Adds an android-ktlint
matrix lane mirroring pnpm android:lint.

* chore(i18n): sync native inventory after ShellScreen.kt format

The ktlint format fix shifted line numbers tracked in
apps/.i18n/native-source.json; regenerated via pnpm native:i18n:sync.

* style(android): fix new ktlint drift in NodeRuntime and ChatHardwareKeyTest

Import-order/blank-line drift landed via the hardware-Enter changes
(#101321 and follow-up) while this PR was open — the exact recurrence
the new android-ktlint lane prevents. Generated with ktlintFormat;
native i18n inventory resynced for the shifted line numbers.
2026-07-07 05:01:26 +01:00
cxbAsDev
08349608f2 QA-lab credentials-admin response bound (#101131)
* fix(qa-lab): bound credentials admin response reads

* test(qa-lab): verify streamed response cancellation

Co-authored-by: 陈宪彪0668000387 <chen.xianbiao@xydigit.com>

* test(qa-lab): satisfy cleanup callback lint

Co-authored-by: 陈宪彪0668000387 <chen.xianbiao@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-07 04:56:07 +01:00
Marcus Castro
6ba19c5ad0 fix: block WhatsApp sends during reachout timelock (#101264) 2026-07-07 00:54:43 -03:00
Ayaan Zaidi
dc5d4138f9 test(gateway): isolate reload handler plugin records
The reload handler no-op config test enables fake timers and drains all timers. In cross-file non-isolated runs, server-startup-post-attach closes the shared state DB first, so this test reopens it through the managed config reloader's default installed-plugin record read.

That DB open schedules SQLite WAL maintenance at src/infra/sqlite-wal.ts:345 while fake timers are active, and vi.runAllTimersAsync spins the interval until Vitest aborts after 10000 timers. Mock installed plugin record reads in this handler test so the test owns only the reload debounce timer it is trying to drain.
2026-07-06 20:52:42 -07:00
Peter Steinberger
ad5b81e884 fix(memory): trust structured cron archive provenance (#101322)
Prevent user-authored [cron:] text from clearing reset or deleted session memory while preserving structured cron-run opacity.

Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
2026-07-07 04:52:21 +01:00
Yuval Dinodia
7900ff4731 fix(macos): preserve device identity storage (#101105)
* fix(macos): preserve device identity storage

Avoid selecting App Group identity storage when the running app lacks the matching application-groups entitlement, so unentitled macOS builds fall back to the legacy writable identity directory. Existing unrecognized identity files are now preserved instead of being replaced by a newly generated identity, matching the no-overwrite behavior already used for recognized invalid identity data.

* fix(macos): repair identity CI coverage

Limit the App Group entitlement probe to macOS builds so iOS keeps its existing App Group path behavior. Update the state-dir identity regression to assert the new no-overwrite contract for invalid identity-shaped files.

* fix(macos): migrate existing app group identity on legacy fallback

Unentitled macOS builds now migrate a readable App Group device identity
into the selected legacy store before falling back, so an upgrade preserves
the existing device id instead of minting a new one.

* fix(macos): migrate device auth store with identity on app group fallback

The app group to legacy identity migration now carries the sibling
device-auth store file, so an unentitled build keeps its stored device
tokens (keyed by the migrated deviceId) instead of re-pairing. The
process-immutable entitlement check is resolved once per process rather
than creating a SecTask on every state-dir lookup, and the migration
source API returns an identity+auth pair struct.

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-07 04:49:46 +01:00
Jesse Merhi
4112e3a0f3 Add native Signal reply quotes (#95718)
* feat(signal): quote native replies

* Fix Signal native reply edge cases

* fix(signal): tighten native reply quotes

* fix(signal): harden native reply quotes

* fix(signal): preserve approval reaction state

* fix(signal): target native replies to edited messages

* fix(signal): preserve plugin approval reaction kind

* fix(signal): keep edit event reply ids distinct
2026-07-07 13:49:13 +10:00
Vincent Koc
de8db74058 fix(android): handle hardware enter in chat sheet 2026-07-07 05:48:25 +02:00
Peter Steinberger
1834592be1 fix(auth): use OpenClaw lobster logo on OAuth callback pages
The shared provider OAuth success/error page (OpenAI/Codex and Anthropic
login flows) still embedded the legacy Pi logo SVG. Swap it for the
OpenClaw lobster mascot, matching ui/public/favicon.svg.
2026-07-07 04:48:01 +01:00
Vincent Koc
0fd69dc3d2 fix(wizard): honor configured auth dir in finalize 2026-07-07 05:41:02 +02:00
Peter Steinberger
780ca1d253 fix(android): send chat with hardware Enter (#101321) 2026-07-07 04:36:06 +01:00
Ayaan Zaidi
107a9d6d9b fix(qa-lab): skip runtime-context carrier in mock last-user matching 2026-07-06 20:34:01 -07:00
Ayaan Zaidi
c718afe247 refactor(agents): thread runtime-context carrier indexes 2026-07-06 20:34:01 -07:00
Peter Lindsey
f5931f5516 fix(agents): carry current-turn inbound metadata in a tail runtime-context message for byte-stable prompt caching
The active user turn was decorated at request-build time with an inbound
metadata block (Conversation info / reply-target / sender / forwarded /
chat-history), but the session store persists the bare text and the LLM boundary
strips that block from historical replay. So a user message's serialized bytes
changed retroactively — decorated as the active turn, bare as history — on every
turn, invalidating any prefix/exact-match provider prompt cache from that point
onward, every turn. (#90811 fixed the timestamp half of this same asymmetry.)

Rework of the earlier "strip the active turn" approach (which lost model-visible
context — ClawSweeper's P1) into one that keeps the context AND recovers the
cache cost:

- Route current-turn inbound metadata out of the user prompt and into the hidden
  runtime-context custom message (the mechanism from openclaw#89428), and
  relocate that carrier to the ABSOLUTE TAIL of the wire request (after the
  active user turn and any tool-call scaffolding). The user turn stays bare and
  byte-identical in both the active and historical positions; the volatile
  carrier vanishes next turn exactly where the assistant reply begins anyway, so
  the request is an append-only prefix-extension through the active user turn.
- A durable marker (UserMessage.runtimeContextCarrier, set by convertToLlm from
  the carrier's details) lets the Anthropic SDK transport, the managed Anthropic
  transport (anthropic-transport-stream / anthropic-payload-policy), and
  OpenAI-completions skip the carrier when selecting the deepest cache_control
  breakpoint, keeping the anchor on the last stable user turn.

Storage stays BARE (invariant preserved); runtime-only turns (room events) keep
their existing inline behavior, which is byte-stable because room-event/system
context is not strip-eligible; legacy bare-stored sessions are unchanged.

Reviewed with adversarial gpt-5.5/codex sweeps (correctness, byte-identity,
compatibility, test adequacy): found + fixed the managed Anthropic transport
cache-anchor path and added the missing OpenAI-completions/managed-transport
breakpoint tests; a theoretical runtime-only mutation was verified unreachable
and pinned with a test. Final sweep clean.

Co-authored-by: openclaw#89428 (runtime-context handoff approach)
2026-07-06 20:34:01 -07:00
Ayaan Zaidi
f7df9536b0 docs(agents): note codex session-id header naming divergence 2026-07-06 20:33:51 -07:00
Peter Lindsey
06061c4fe3 fix(agents): treat caller session_id headers as case-insensitive
buildOpenAIClientHeaders only skipped generating a session_id header
when the exact lowercase key was already present, so a caller-supplied
Session_ID or SESSION_ID header would end up on the wire alongside our
generated one instead of winning per the caller-wins header contract.
Check all resolved header keys case-insensitively before injecting.
2026-07-06 20:33:51 -07:00
Peter Lindsey
3d87932541 fix(agents): send session_id affinity header to ChatGPT Responses backend
The ChatGPT backend routes requests by session_id (codex-cli sends it); without it each request lands on an arbitrary machine and the prompt cache misses. Measured on live traffic: 56.0% -> 81.8% TRUE cache hit rate (12-turn A/B, identical prompts).
2026-07-06 20:33:51 -07:00
Peter Steinberger
654029892d feat(browser): pair the Chrome extension directly to a remote gateway (#101127)
* feat(browser): direct extension→gateway relay path for remote Chrome (#53599)

Let the OpenClaw Chrome extension pair directly to a remote gateway over
wss:// with no OpenClaw node host on the browser machine — the managed-hosting
path from #53599 (extension is the only thing installed on the laptop).

- Gateway route /browser/extension registered by the browser plugin with
  auth:"plugin" + no nodeCapability, so the gateway does not pre-enforce token
  auth (browser WebSockets cannot send an Authorization header). The upgrade
  handler self-validates the host-local relay secret from ?token=, origin-checks
  chrome-extension://, resolves the extension profile, then attaches the socket
  to the same ExtensionRelayBridge the loopback relay uses. All CDP synthesis,
  tab-group scoping, and the in-process Playwright /cdp client are unchanged.
- `openclaw browser extension pair --gateway-url wss://host` prints a
  wss://host/browser/extension#<secret> string; the path ends in /extension so
  the extension's existing pairing parser accepts it with zero extension code
  changes.
- relay-server: extract attachExtensionWebSocket + export requestToken /
  isAllowedExtensionOrigin / EXTENSION_RELAY_MAX_PAYLOAD_BYTES so loopback and
  gateway paths share one bind + one frame cap.
- runtime-lifecycle: dispose the shared gateway WebSocketServer on shutdown.
- docs: three remote topologies (same host / direct-to-gateway / via node host).

Coverage: 6 unit tests for the handler's path/503/403/404/401/attach branches.
The full extension→bridge→CDP→Chrome loop over /browser/extension was live-proven
with a real Chrome + the built extension. The real gateway upgrade→handleUpgrade
dispatch for an auth:"plugin" unprotected route is verified against core
(server-http.ts, plugins-http.ts, route-auth.ts).

* fix(browser): harden remote extension pairing
2026-07-07 04:31:52 +01:00
Alex Knight
87a17920a0 fix(diagnostics-otel): surface error message on run/harness error spans (#101244)
* fix(diagnostics-otel): surface error message on run/harness error spans

Errored openclaw.run / openclaw.harness.run spans only carried a
low-cardinality errorCategory (or a hardcoded "error"), so trace UIs
showed "outcome error" with no message. Thread the redacted error
message through run.completed / harness.run.completed / harness.run.error
onto an openclaw.error span attribute + span status, mirroring
recordWebhookError. The raw message stays off metric attrs to preserve
cardinality; support-bundle redaction covers the new error field by name.

* fix(diagnostics-otel): harden run failure telemetry

Co-authored-by: Alex Knight <aknight@atlassian.com>

* test(diagnostics-otel): assert wire-level redaction

* chore(changelog): defer release note to release process

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-07 04:29:39 +01:00
Peter Steinberger
118e5bd762 fix(agents): harden tool search child streams (#101295)
Co-authored-by: 陈宪彪0668000387 <chen.xianbiao@xydigit.com>
2026-07-07 04:25:16 +01:00
Peter Steinberger
ae16704864 fix(proxy): handle aborted upstream responses (#101315)
Co-authored-by: Sebastien Tardif <sebtardif@ncf.ca>
2026-07-07 04:24:23 +01:00
Peter Steinberger
a78538dc2a perf(test): reuse extension test inventory 2026-07-06 23:23:09 -04:00
cxbAsDev
d90c9a1ede fix(agents): handle stdout/stderr stream errors in ssh sandbox commands (#101031)
* fix(agents): handle stdout/stderr stream errors in ssh sandbox commands

* fix(agents): also handle stdin stream errors in ssh sandbox command

* fix(agents): handle tar/ssh pipeline stream errors in sandbox upload

* fix(agents): kill ssh child on stdout/stderr/stdin stream errors in sandbox

* fix(agents): move upload fail helper before use to avoid TDZ in ssh sandbox

* docs(proof): note ssh-sandbox upload TDZ fix in proof header

* fix(agents): harden SSH sandbox stream errors

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-07 04:20:23 +01:00
Vincent Koc
62e5d4406d refactor(ui): remove unused module exports (#101262) 2026-07-06 20:20:06 -07:00
Ayaan Zaidi
b2baf799b4 fix(agents): bound finalize hook hangs after compaction retry
Behavior: a plugin before_agent_finalize hook that never resolves could previously freeze an agent run forever after a successful compaction retry, with no errors and no recovery from a gateway restart; this was the frozen-runner mechanism behind #84777. before_agent_finalize now has the same 15s default budget as sibling modifying hooks and fails open with the original final answer, converting the freeze into a bounded delay.

Surface: plugin hook runner defaults (src/plugins/hooks.ts), docs/plugins/hooks.md.

Refs #84777.
2026-07-07 03:16:57 +00:00
Blaine Perry
06c18e232d feat(android): add chat agent selector (#80422)
* feat(android): add live chat agent selector

Co-authored-by: Blaine Perry <blainecperry@gmail.com>

* fix(android): keep agent selector on canonical session

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-07 04:14:55 +01:00
Peter Steinberger
21d919deb8 fix(onboard): keep the wizard alive through provider auth failures and polish standalone install UX (#100632)
Fixes rough edges in the standalone install flow (install.sh -> openclaw onboard), found and verified by running the flow in a clean container and on a clean macOS Tahoe VM:

- Provider auth setup failures (e.g. the preselected "Anthropic Claude CLI" option on a host without a Claude CLI login) no longer kill the whole wizard. The interactive wizard notes the error and returns to the provider picker; explicit --auth-choice automation still fails fast.
- Onboarding config now persists before the channel/search/skills steps, so a crash or cancel during channel pairing no longer loses auth + gateway decisions.
- With model auth skipped, finalize no longer auto-sends the "Wake up, my friend!" message (which always failed with a provider auth error). The hatch seed is gated on usable model credentials and a "Model auth missing" note explains the next step.
- Search provider picker no longer labels non-key credentials (e.g. SearXNG base URL) as "API key required".
- install.sh no longer warns "PATH missing npm global bin dir" with manual fix steps after it already persisted the export line; it reports the PATH was updated and how to reload the current shell.
- Removed the dead interactive hooks onboarding step (setupInternalHooks); quickstart enables default hooks silently.

Verified live per fix in a clean Debian/Node 24 container and on a clean macOS 26.5 Parallels VM (wizard re-prompt, SearXNG label), plus wizard/onboard test suites and tsgo:core.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-07 04:11:23 +01:00
Peter Steinberger
95f46743e4 fix(discord): prioritize eligible default account (#101292)
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
2026-07-07 04:10:32 +01:00
Peter Steinberger
d86bb60fba fix(skills): avoid map spread in curator status 2026-07-06 22:58:21 -04:00
Kevin Lin
e3c40efeb0 feat(update): notify extended-stable availability (#100438)
* docs(update): clarify extended-stable notifications

* feat(update): notify extended-stable availability

* fix(update): isolate notification state

* fix(docs): align update channel table

* fix(update): refresh after channel switch

* fix(update): satisfy notification checks

* test(update): prove loopback notification path

* test(update): fix loopback server lifecycle

* fix(update): refresh extended stable without cache

* fix(update): retain extended stable throttle marker
2026-07-06 19:57:03 -07:00
Peter Steinberger
ad833d7883 fix(protocol): regenerate swift curator models 2026-07-06 22:52:15 -04:00
Vincent Koc
084cf2a43d fix(ci): refresh deprecated plugin sdk budgets 2026-07-07 04:46:49 +02:00
Peter Steinberger
4fa8af486f perf(test): drop redundant benchmark cli spawn 2026-07-06 22:44:30 -04:00
Peter Steinberger
5df7942f70 fix(ci): refresh plugin sdk export budget 2026-07-06 22:44:30 -04:00
Peter Steinberger
0e3ce24cc8 fix(ui): resolve sidebar merge markers 2026-07-06 22:44:30 -04:00
Colin Johnson
632efa2d73 fix ios qr scanner lifecycle (#101235)
* fix ios qr scanner lifecycle

* fix(ios): tighten QR scanner lifecycle change

---------

Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
2026-07-06 21:34:44 -05:00
Darren2030
ceb7a4adba fix(codex): project guardianWarning circuit-breaker notification (#101220)
* fix(codex): project guardianWarning circuit-breaker notification

Codex emits a `guardianWarning` notification from its rejection
circuit-breaker (e.g. 3 consecutive or 10 total denials in a turn)
right before ending the turn as interrupted. The app-server event
projector had no case for it, so the rejection-limit reason was
silently dropped and the turn degraded to a generic interruption.

Project it on the `codex_app_server.guardian` stream with phase
"warning", alongside the existing autoApprovalReview handling. The
exact v2 param shape is not bundled in OpenClaw's protocol schemas,
so the human-readable reason is read defensively.

Closes #101207

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(codex): route guardianWarning through thread-scoped filter

`guardianWarning` is thread-scoped — codex emits it with only
{message, threadId} (no turnId). The turn-strict pre-switch filter
therefore dropped it before it reached the projection switch, making
the handler added in the previous commit dead code for real traffic.

Correlate it on the thread alone (mirroring hook notifications) so it
reaches the switch, and read the `message` field per the codex schema.
The regression test now sends the real {threadId, message} shape
instead of a synthetic turnId, so it actually exercises the routing.

Refs #101207

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* repro(codex): add real behavior proof for guardianWarning projection

* fix(codex): narrow guardian warning projection

Co-authored-by: 曾文锋0668000834 <zeng.wenfeng@xydigit.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-06 19:33:24 -07:00
Masato Hoshino
c0b6b846ac fix(acp): record cancelled background turns as cancelled, not succeeded (#101245)
* fix(acp): record cancelled background turns as cancelled, not succeeded

A cancelled parented ACP turn resolves without throwing (only failed turns
throw), so the turn-runner success path recorded the detached task as
succeeded. Thread the terminal status out of consumeAcpTurnStream and branch
at the runner; widen markBackgroundTaskTerminal to accept cancelled, routed
through the existing failTaskRunByRunId sink. The runTurn fallback infers
cancellation from done.stopReason, mirroring the acpx adapter.

* fix(acp): narrow cancelled task status handling

Co-authored-by: masatohoshino <g515hoshino@gmail.com>

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-06 19:32:40 -07:00