* fix(android): keep cold-start gateway auto-connect from overriding explicit intents
NodeRuntime's init-time auto-connect now atomically claims the first gateway
lifecycle intent (CAS 0->1) and stands down permanently when any explicit
connect/disconnect/switch intent already exists, so a late discovery emission
can no longer override a user decision. This was also the root cause of the
CI-only flakes in GatewayBootstrapAuthTest (refreshGatewayConnection_* at
:616/:627 and switchToUndiscoveredGateway* on PRs #101002/#101387/#101396).
GatewaySession.Connection.sendRequestFrame now starts its response watcher
with CoroutineStart.ATOMIC: disconnect teardown cancels connectionJob right
after failPending(), and a DEFAULT-start watcher still queued for dispatch was
cancelled without running, silently dropping the terminal UNAVAILABLE onError
owed to fire-and-forget callers (root cause of the flaky
GatewaySessionInvokeTest.disconnectReportsUnknownOutcomeForFireAndForgetRpc).
Tests neutralize runtime background work (cancel+join the runtime scope)
before arming the registry and scripting lifecycle steps, and add regression
coverage for the auto-connect intent claim.
Proof: refresh flake reproduced at iteration 1 under taskset -c 0,1 on a
Blacksmith Linux box; post-fix 3 pinned stress rounds (120/150 looped
iterations) plus full :app:testPlayDebugUnitTest (109 classes, 0 failures)
and :app:ktlintCheck green on the same box.
* chore(i18n): resync native inventory line positions after NodeRuntime edits
* fix(shared): skip app-group identity migration when OPENCLAW_STATE_DIR is overridden
An unentitled process (mac app, swift test runners) with a fresh state dir
imported the machine's real app-group device identity and auth tokens into
any explicitly overridden state dir, violating the documented legacy-fallback
contract. On developer Macs this leaked real operator/node device tokens into
per-test temp state dirs, breaking the nil-token expectations in
GatewayNodeSessionTests (non bootstrap hello / untrusted bootstrap hello)
for local swift test runs while clean CI passed.
Migration now requires no OPENCLAW_STATE_DIR override, with a regression
test. StateDirectoryIsolationTrait owns per-test state-dir pinning (fresh
temp dir per gated test, env restored to launch value or a run-scoped
quarantine dir, never unset), replacing the hand-rolled setenv/defer
boilerplate in every gated test.
* test(shared): remove redundant throws annotations
* fix(gateway): bound all-agent usage.cost concurrency to prevent SQLite starvation
Replace raw Promise.all in loadAllAgentCostUsageSummary with
runTasksWithConcurrency (limit=12), matching the sibling sessions.usage
pattern. Unbounded concurrent usage-cost cache reads saturated the SQLite
connection pool for deployments with 100+ agents, causing SQLITE_BUSY
timeouts or gateway memory crashes.
Fixes#101552
* fix(gateway): share usage cache concurrency cap
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(claude-cli): surface re-auth hint when subprocess OAuth token expires
When the claude-cli subprocess emits a 401 "Failed to authenticate. API
Error: 401 Invalid authentication credentials" because its stored OAuth
token has expired, the error was surfaced as the generic provider-auth
copy instead of the targeted re-auth hint.
Two fixes:
1. Extend isOAuthRefreshFailureMessage to recognise the claude-cli
subprocess 401 error shape (requires "claude-cli" prefix + 401 +
auth failure text). extractOAuthRefreshFailureProvider returns
"claude-cli" for this pattern; classifyOAuthRefreshFailureReason
maps it to "revoked" so the "expired" copy is shown.
2. Reorder both classification sites in buildExternalRunFailureReply and
the runAgentTurnWithFallback catch block so the OAuth-refresh check
runs before classifyProviderRequestError. The typed 401 branch in
classifyProviderRequestError would otherwise intercept the FailoverError
before the OAuth hint path is ever reached.
Fixes#97553
* fix(claude-cli): use correct auth login command format in re-auth hint
--provider claude-cli is not a registered provider id; the correct
command to re-authenticate the claude subprocess is:
openclaw models auth login --provider anthropic --method cli
Add a special-case branch in buildOAuthRefreshFailureLoginCommand so
that a 'claude-cli' sanitized provider emits the two-flag form instead
of the generic --provider <id> template. Update the regression test
expectation to match.
* test(claude-cli): expect CLI auth re-login command
* fix(claude-cli): classify structured OAuth auth failures
* test(claude-cli): add real http oauth expiry proof
* test(claude-cli): add proof output to real HTTP server OAuth test
* test(claude-cli): add proof console.log to real HTTP server test
* test(claude-cli): keep real HTTP OAuth proof output explicit
* fix(agents): include Claude CLI reauth step
* test(claude-cli): expect full cli reauth command
* test(claude-cli): align reauth hint with terminal wording
* chore: retrigger claude-cli CI after opengrep fetch failure
* fix(sessions): bound trajectory snapshot and pointer reads
* test(sessions): align oversized snapshot assertion with bounded reader error message
* fix(sessions): cap follow-mode trajectory deltas
ClawSweeper review noted that the trajectory snapshot read was bounded
but the follow-mode delta read (Buffer.alloc(fileState.size - offset))
was not. A runaway trajectory append could still OOM the tail loop.
Reject deltas above TRAJECTORY_RUNTIME_FILE_MAX_BYTES and add regression
coverage for the oversized-delta path.
* test(sessions): exercise trajectory limits through command path
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(http-error-body): keep emoji / surrogate pairs intact during error body truncation
Replace .slice(0, limits.maxChars) with truncateUtf16Safe() so that
emoji straddling the maxChars boundary don't produce lone surrogates
that render as � in error diagnostics. Matches the sibling http-body.ts
which already uses truncateUtf16Safe.
Co-Authored-By: Claude <noreply@anthropic.com>
* test: cover UTF-16 HTTP body branches
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(gateway): support native Windows exec approvals
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* chore: defer changelog entry to release
* test: use tracked approvals temp directories
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Related: #101647
## What Problem This Solves
Removes stale Control UI test mocks that modeled helper exports no longer exposed by the chat display module.
## Why This Change Was Made
The chat message tests no longer reach the display module, so their mock was removed. The chat view tests retain only the fallback asset mock and exercise the canonical avatar helpers directly.
## User Impact
No user-visible behavior change. Tests now reflect the current module ownership without duplicating avatar logic.
## Evidence
- Focused Testbox run: 2 files, 209 tests passed.
- Testbox: `tbx_01kwyatxdvb64ncvbn9ertg6fa`
- Actions run: https://github.com/openclaw/openclaw/actions/runs/28868305075
- `git diff --check`: passed.
- Codex autoreview: no actionable findings.
* fix(tasks): keep emoji / surrogate pairs intact during terminal output truncation
.slice() counts UTF-16 code units; use Array.from() to count full
code points instead, preventing lone surrogates in task list output.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(tasks): preserve terminal column width
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(control-ui-assets): keep emoji / surrogate pairs intact during last-line truncation
.slice(0, 239) counts UTF-16 code units; an emoji straddling the
cut point produces a lone surrogate rendering as in WebUI assets.
Use [...last] spread to count full code points instead.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(control-ui-assets): preserve diagnostic length cap
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>