* 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>
* fix(auto-reply): keep suppressed reply text preview truncation UTF-16 safe
String.prototype.slice at offset 160 can split surrogate pairs in
reply text previews logged for suppressed auto-replies. Replace raw
slice(0, 160) with truncateUtf16Safe — already imported in this file.
* test(auto-reply): exercise suppressed preview logging
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(acp): use truncateUtf16Safe for event mapper error text truncation
.slice(0, 100) on ACP event argument values can split surrogate
pairs from emoji or CJK text, producing U+FFFD in error/warning
notices shown to operators.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(acp): cover UTF-16-safe tool titles
* refactor(acp): keep event mapper import leaf-only
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(gateway): keep plugin approval title and description truncation UTF-16 safe
Node.js String.prototype.slice cuts at UTF-16 code unit boundaries,
which can split surrogate pairs (emoji, CJK supplementary characters)
into lone surrogates that render as U+FFFD in approval UI payloads.
Use truncateUtf16Safe for both title and description fields in
createPluginApprovalsAttachment so approval requests never deliver
broken characters to the gateway approval UI.
Fixes #???
* test(gateway): exercise UTF-16 approval payloads
---------
Co-authored-by: wm0018 <wu.min5@xydigit.com>
* fix(session-cost-usage): keep emoji / surrogate pairs intact during content truncation
.slice(0, maxLen) counts UTF-16 code units, so an emoji straddling
the cut point produces a lone surrogate that renders as � in
usage cost CLI output. Use [...str] to count full code points instead.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(usage): cover UTF-16-safe log truncation
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>