First connects backed by stored auth (URL/session token, password, or an
approved device token) now paint a small wiggling OpenClaw mark (delayed
fade-in, reduced-motion aware) until the hello resolves. The login gate
still owns credential-less first opens, rejected credentials, and manual
gate submissions.
Closes#101847
The idle Talk mic button used a full-strength accent ring that visually
competed with the recording (stop) state. Mute the idle ring to 40%
accent and ramp hover to 70% so idle -> hover -> recording escalates.
Formatting proof: oxfmt --check clean on ui/src/styles/chat/layout.css
on Testbox tbx_01kwyvx4pbdmb3yhjff6gvzvny (worktree has no node_modules,
so the format hook cannot run).
* feat(cron): add headless code-mode driver and trigger-script evaluator
Part A of cron event triggers: runCodeModeScriptHeadless runs a script
to completion (exec/settle/resume, no snapshots, no session), plus the
cron-facing evaluator with per-job tool catalog cache, semaphore, 16KB
state cap, and closed failure taxonomy.
* fix(cron): correct trigger-script bootstrap flags and cache narrowing
* feat(cron): add event triggers (polled condition-watcher scripts)
Part B: trigger field on cron jobs gated by cron.triggers.enabled;
timer evaluates the script each due tick, quiet ticks leave no run
history, fired runs append the script message to the payload; once
semantics, min-interval floor, SQLite columns, RPC/CLI/agent-tool
surfaces, and docs.
* fix(cron): propagate triggerEval through startup catch-up outcomes
* fix(cron): honor cron staggering on quiet trigger ticks; fix trigger test types
* fix(cron): reject with Error reason in trigger-script abort test
* fix(cron): regenerate protocol/docs/snapshot artifacts and break madge cycle for trigger types
CI fixes for #101195: trigger evaluator result types move to the cron
types leaf (madge cycle), cron tool schema inventory gains trigger,
Swift protocol bindings + docs map + Linux prompt snapshots regenerated.
* fix(cron): drop underscore-dangle names in trigger code sync assert
* fix(cron): adopt registerHeadlessToolSearchCatalog after tool-search symbol localization
Upstream #101831 made registerToolSearchCatalog module-private; fold the
headless ref-only catalog registration into one public seam.
* 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>