This release centers on Remote Control reliability: the bridge now refuses to mint a new session when reattach is required, retries token re-mints with auth-specific error messages, recovers expired worker credentials, re-arms itself after a re-login, and surfaces a login prompt when token refresh is exhausted. Artifact tooling gained pagination and single-thread filtering for comments, retries on 429 and 503 responses with a visible progress line, and detection of summons another session has already answered. Cross-session messaging is more predictable, with held peer messages that supersede, re-arm, expire in headless sessions, and settle as expired at shutdown, plus clearer text about why a message is held and whether a peer is cloud or Remote Control. Auto mode now falls back when the primary classifier refuses, denies safeguard-refused calls without recording a verdict, and its nudge dialog actually switches the running session or explains why it cannot. Fixes include a streaming read index that skipped messages, gateway 429s without rate-limit headers driving the usage indicator, and child processes inheriting an IDE entrypoint identity.
Official notes✓ syncedPackage@anthropic-ai/claude-codeDiffv2.1.224 → v2.1.225ProviderclaudeModelclaude-opus-5 (effort medium)
15
Use it now
87
You'll notice
16
Not switched on
12
Env Vars / Flags
Official Changelog
Official · Anthropic
Anthropic’s official release notes
Published verbatim by Anthropic for v2.1.225 — shown here alongside the source-level analysis below. Text is unmodified from the upstream changelog.
Added gateway spend-limit support to Claude Code's usage warning; the limit-reached message now names the cap, its reset time, and the operator's message (requires the gateway on 2.1.225)
Added a workspace trust prompt to claude agents for untrusted directories, matching the behavior of claude
Fixed a transient 401 replacing a long-lived CLAUDE_CODE_OAUTH_TOKEN with a stored login's short-lived token, breaking headless sessions until restart
Fixed MCP OAuth servers on macOS intermittently failing with a burst of 401 errors, as if never authenticated, after a keychain read timed out
Fixed auto mode counting a safety-filter refusal of its own permission check toward the consecutive-block limit; the action is still denied, but the model is now told to move on rather than retry
Fixed cross-session messages staying parked without a notice or expiry in headless sessions and during startup
Fixed conversation history breaking on Remote Control session resume after very large conversations were compacted
Fixed hovering over a session in another project in the agents list changing the directory the next agent starts in
Fixed claude self-hosted-runner registering and then failing every session when --base-dir cannot be created or written; it now exits at startup with a clear error
Fixed Claude Code on the web sessions being misreported as stuck, re-sending a growing event backlog on every reconnect
Improved Remote Control: photos attached from the Claude app are now shown to Claude directly instead of being read from disk with a separate tool call
[VSCode] Fixed Focus view folding away the latest to-do list, a pending question's context, and settled answers; thinking-only folds show "Thought for Ns" and re-collapse when their turn completes
SendMessage can now start a conversation with your Remote Control sessions on other machines by name (ListAgents shows them as name [ref]), instead of only replying after they message you first
SendMessage: a Remote Control recipient you already confirmed is never swapped for a same-named session on this machine when its own list couldn't be checked
Source: anthropics/claude-code · CHANGELOG.md · 14 entries · synced automatically when Anthropic publishes official notes for a version.
Source-Level Analysis
Reverse-engineered from a diff of the bundled CLI — deeper, structured detail. Unofficial.
Shipped inside the bundle with its gate off, its guard hardcoded false, or nothing calling it. What the code shows, not a roadmap: no claim about what is released, announced or planned.
Filter
What probably matters to you
30 items
Anything you can use today, anything that visibly changes, and anything worth poking at. One line each, open for detail.
Artifact comments can be filtered to one thread or resumed with a cursor #
Juicy
You can read one artifact comment thread by id, or resume a cut-off listing with a cursor.
thread_idcursorcomments
Use it now · Artifacts
What
The Artifact tool's comments action takes a thread_id to read a single thread and a cursor to continue a listing that was cut off by the size cap. Results echo back thread_filter and cursor so the model knows which view it is looking at.
Details
thread_id and cursor are mutually exclusive and both must be lowercase UUIDs, each with its own validation error and error code
when a requested thread is not in the result, the model is told to re-run comments without thread_id for the full list
truncated listings carry a hint to re-run with cursor, and resumed listings carry a "Resumed from the cursor:" note; threads are listed newest first
the tool-use line renders counts such as "read 1 comment thread (filtered)"
available to anyone who can use the Artifact tool; neither field existed in 2.1.224
Evidence
`thread_id (read one thread) and cursor (continue the list) cannot be combined — pass one. , thread_filter`
OAuth 401 recovery no longer silently replaces a user-supplied token #
Juicy
Your explicitly set OAuth token is no longer silently swapped for a stored credential after a 401.
A 401 no longer causes Claude Code to swap in a credential from disk when you explicitly supplied CLAUDE_CODE_OAUTH_TOKEN.
Details
When the token came from the user's environment, and this is not a remote child session or a unix-socket setup, an explicit error is logged and the supplied token is kept
The disk-recovery path now additionally requires the stored credential to be unexpired
The whole env/disk recovery block is skipped when credentials are host-managed via CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST
The recovery failure reason gained a third value for the skipped-user-env-token case, alongside background-worker and interactive no-refresh-token
Evidence
OAuth 401: keeping the user-supplied CLAUDE_CODE_OAUTH_TOKEN instead of adopting the stored credential., oauth_401_skipped_user_env_token
The bypassPermissions consent flag reaches startup mode resolution #
Juicy
Setting defaultMode to bypassPermissions now works in VS Code when you have enabled the consent setting.
A settings defaultMode: "bypassPermissions" is now honoured in a VS Code-owned session when the corresponding VS Code consent setting is enabled.
Details
allowDangerouslySkipPermissions is now a parameter of the startup permission-mode resolver and is forwarded into the resolver's cli block
previously the resolver was called without it, so the mode was rejected with 'Permission mode bypassPermissions from settings was ignored — enable the "Claude Code: Allow Dangerously Skip Permissions" setting in VS Code to consent to it' even when the setting was on
Evidence
allowDangerouslySkipPermissions
Artifact publishes retry 429 and 503 with a live progress line #
Artifact publishing now retries when the service is busy, showing a live attempt counter instead of failing.
You'll notice · Artifacts
What
Publishing an artifact through /api/frame/deploy/direct now retries transient failures instead of failing outright, and shows the retry state while it happens.
Details
A 429 is retried once after the retry-after delay, capped; 503s are retried with backoff up to a maximum attempt count.
Retries honor the abort signal, logging "[artifact] /deploy/direct retry cancelled by user".
A new progress event type renders "Publish service temporarily unavailable" or "Publish service busy" followed by " — retrying (attempt N of M)…".
Telemetry deploy_503_retried / deploy_429_retried carries deploy_attempts and deploy_final_status.
onRetry and signal are threaded through the publish call chain. None of this existed in 2.1.224.
Evidence
artifact_publish_retry
Held-message approval dialog now defaults to Deny #
The prompt for messages from another Claude session now defaults to Deny, so a stray Enter drops it.
You'll notice · Remote Control
What
The prompt shown when another Claude session sends a message now pre-selects the safe option, so an accidental Enter drops the message instead of delivering it.
Details
"Deny — drop it and tell the sender it was declined" is listed first and "Deliver this message to Claude" second.
The select is given defaultFocusValue "deny" plus hideIndexes; previously delivery was first with no default focus.
The payload builder no longer emits its truncated field.
Evidence
Deny — drop it and tell the sender it was declined, Deliver this message to Claude
Remote Control now turns itself back on after you log in again, instead of staying dead.
/login
You'll notice · Remote Control
What
A watcher now runs while a REPL bridge session sits in the "auth" failure state and turns Remote Control back on once valid credentials reappear, instead of leaving the session dead until manual intervention.
Details
Re-checks credentials every 300000 ms; on finding a fresh credential for the same account it clears replBridgeError, flips replBridgeEnabled back on and fires telemetry tengu_bridge_repl_auth_revive.
Refuses to revive across accounts, logging "credential belongs to a different account — disarming".
In non-interactive sessions a brake applies unless an in-process /login bumps the new authChangeGeneration counter.
Close code 4094 (worker credential expired) is treated as auth-recoverable only when this gate is on.
Gated on tengu_bridge_auth_revive, fallback true absent a server value; with the gate off the code still logs "kill switch off — still polling, no unattended revive".
authChangeGeneration and the "\x00baseline-pending" sentinel are new in this build.
Evidence
[bridge:repl] Auth-revive watcher: fresh same-account credential — re-enabling Remote Control
Artifact comment threads are paginated with a cursor #
Truncated artifact comment listings now hand you a cursor to fetch the remaining threads.
cursor
Use it now · Artifacts
What
When a comments listing is truncated by the size cap, the result now tells the caller how to fetch the rest rather than only pointing at the web page.
Details
A cursor is computed from the last listed thread.
The truncation message instructs the caller to re-run the "comments" action with that cursor.
Usage
comments with cursor "<cursor from the previous truncated result>"
Evidence
re-run action "comments" with cursor "
Child processes no longer inherit an IDE entrypoint identity #
Juicy
Commands you spawn no longer inherit an IDE entrypoint or remote session id from your editor session.
CLAUDE_CODE_ENTRYPOINT is stripped from derived environments when it holds an IDE or desktop value, so spawned commands do not misreport where they came from.
Details
The stripping helper deletes the variable for IDE/desktop values such as claude-desktop-3p.
It is now called from the env builders for the spawned-command environment and for the sandbox/scrubbed environment; previously only one spawn path applied it.
CLAUDE_CODE_REMOTE_SESSION_ID was added to the set of remote-session variables stripped from derived environments.
Evidence
claude-desktop-3p
Remote Control disconnect notices point at /login for auth failures #
If Remote Control drops due to auth, the notice now tells you to run /login.
/login/remote-control
Use it now · Remote Control
What
A disconnect caused by authentication now tells you to re-login instead of offering the generic reconnect hint.
Details
The disconnect banner takes a cause argument; when the cause is "auth" it appends "run /login to restore Remote Control", unless the message already mentions /login.
Other causes keep the existing "run /remote-control to reconnect" hint.
The surrounding REPL-bridge hook was refactored so all teardown paths go through one reset helper taking archiveAbandoned, replacing several duplicated ref-clearing blocks.
SDK spawn errors now explain musl/glibc binary mismatches and tell you which option overrides the path.
options.pathToClaudeCodeExecutable
Use it now · SDK
What
When the configured Claude Code executable exists but cannot be spawned, the SDK error now describes the dynamic-loader mismatch case and points at the option you would use to fix it.
Details
covers the case of a musl-linked binary on a glibc host, where /lib/ld-musl-* is absent
names options.pathToClaudeCodeExecutable as the override
Evidence
e.g. spawning a musl-linked binary on a glibc Linux host fails because the musl dynamic loader (/lib/ld-musl-*) is missing
Gateway spend precheck returns headers for allowed requests too #
Juicy
Successful gateway calls now return rate-limit headers, so you can see overage state before being blocked.
The admin gateway spend precheck now returns { blocked, headers }, so rate-limit headers reach the client on successful calls and not just on a blocking 429.
Details
Previously only the blocking 429 response carried the precheck headers.
When the request is allowed and the upstream response is ok, the precheck headers are copied onto that response.
This is how clients see overage state, for example anthropic-ratelimit-unified-overage-disabled-reason, on a call that went through.
Before committing, Claude can be made to state which verify, simplify and code-review skills it ran.
tengu_opal_circuit
Not switched on · Skills
What
A prompt block tells Claude, immediately before running git commit, to state by literal name whether each of the check skills RAN or NOT RUN this session, with carve-outs for trivial commits and test-only diffs. It renders only when the circuit check passes, and each firing is recorded with telemetry carrying has_verify, has_code_review and a hashed path.
Details
Gated on tengu_opal_circuit, a gate lookup with fallback !1, so it is off by default in this build.
A cached decision or a remote value can override the fallback and turn it on.
The check skills named are verify, simplify and code-review.
You can set a startup timeout for self-hosted runners, otherwise the built-in default applies.
SELF_HOSTED_RUNNER_STARTUP_TIMEOUT_MS
Use it now · Elsewhere
What
The env-var-or-default logic for the self-hosted runner startup timeout moved into its own function.
Details
Gated on SELF_HOSTED_RUNNER_STARTUP_TIMEOUT_MS.
Unset or empty still falls back to the built-in default.
Evidence
SELF_HOSTED_RUNNER_STARTUP_TIMEOUT_MS
Auto-default nudge actually flips the mode, and reports the outcome #
Saying yes to the auto-mode prompt now actually saves auto as your default permission mode.
permissions.defaultModeauto
Use it now · Permissions
What
Accepting the prompt to make auto mode your default permission mode now performs the switch rather than only recording the answer.
Details
Writes permissions: { defaultMode: "auto" } to userSettings and prints an [auto on]-style confirmation line
If the gate refuses the switch, shows the auto-mode-unavailable warning instead
tengu_auto_default_nudge_resolved gained an outcome field alongside the existing choice and current_mode
Evidence
tengu_auto_default_nudge_resolved
Routine trigger tool can create webhook triggers #
Juicy
The remote trigger tool can create webhook triggers so routines fire on outside events.
create_webhook_triggerCLAUDE_AI_ORIGIN
Use it now · Background Tasks
What
The claude.ai remote-trigger tool's action enum adds create_webhook_trigger, which POSTs to /v1/code/webhook-triggers so a routine can be fired by an external event source.
Details
The body must name the event source, scope, events, filter and the routine_trigger_id to fire.
On success the tool appends a summary line linking the routine it fires; the link is assembled at runtime from the CLAUDE_AI_ORIGIN and the routine trigger id.
Telemetry trigger_id is populated from the request's routine_trigger_id for this action.
The tool description was rewritten to document the new action.
Evidence
create_webhook_trigger requires body
The routines/triggers tool can create webhook triggers #
Juicy
Claude can now attach a webhook trigger, like a GitHub event, to a routine instead of a schedule.
create_webhook_trigger
Use it now · Background Tasks
What
A create_webhook_trigger action lets Claude attach an event source, such as a GitHub event, to an existing routine so it fires on that event rather than on a schedule.
Details
Posts to /v1/code/webhook-triggers and requires a body; without one it errors with 'create_webhook_trigger requires body'
Documented in the tool prompt alongside the other routine actions
The response appends the routine's claude.ai link instead of a next run time
Evidence
create_webhook_trigger: POST /v1/code/webhook-triggers (requires body)
Bundled skill-backed slash commands forward more capabilities #
Juicy
Slash commands from bundled skills now support arguments, completions, background runs and effort settings.
You'll notice · Slash Commands
What
Slash commands generated from bundled plugin skills now carry through a much larger set of command features, so they behave more like hand-written commands.
Details
Newly passed through: argsMayContainSlashCommands, getContext, background, the resolved skillRoot, getEffort, getDefaultEffort, onUserTypedArgs and getArgumentCompletions.
A skill-supplied progressMessage is forwarded, defaulting to "running".
getPromptForCommand now comes from a cached wireSkillFilesExtraction step.
Messages sent after a conversation reset or a cancelled request are no longer silently dropped.
You'll notice · SDK Transport
What
Two index bugs in the input-stream loop are fixed, so messages after a reset or a cancelled request are not silently dropped.
Details
The read index is reset whenever a conversation_reset event appears in the pending batch, instead of only when a filtered condition held.
When a cancelled request's placeholder text message is spliced out, the read index is decremented if it sat past the removal point.
Evidence
conversation_reset
Artifact comments listing gained pagination and single-thread lookup #
You can now fetch a single artifact comment thread or page through long listings with a cursor.
thread_idcursor
Use it now · Artifacts
What
The artifact tool's "comments" action now takes thread_id and cursor on top of action and url, so a model can fetch one thread or page through a long listing.
Details
When a listing is truncated, the omitted-threads line names the cursor to re-run with.
The parameter help describes cursor as continuing a listing that ended with a "more threads not listed" line.
Evidence
comments only: continue a listing that ended with a "more threads not listed" line
Device bridge websocket URLs are checked against a host allowlist #
Juicy
The device bridge now only connects to approved hosts, with local overrides limited to localhost.
CLAUDE_REMOTE_TOOLS_BRIDGE_URL
Use it now · Sessions
What
The device-registration bridge validates its dial URL before connecting, so it cannot be pointed at an arbitrary websocket host.
Details
new isAllowedUrl check requires wss:, no username or password in the URL, and a hostname in {bridge.claudeusercontent.com, bridge-staging.claudeusercontent.com}
an operator-overridden base URL is allowed only through a separate same-origin check, which permits ws: only for localhost, 127.0.0.1 and [::1]
no such check existed in 2.1.224
the bridge only runs when non-essential egress is allowed, the provider is first-party, and the allow_remote_sessions policy permits it; the base URL is overridable via CLAUDE_REMOTE_TOOLS_BRIDGE_URL
Evidence
bridge-staging.claudeusercontent.com
SDK can defer permission mode resolution to the CLI #
Juicy
SDK callers can let the CLI pick the permission mode from settings instead of forcing "default".
resolvePermissionModeInCli
Use it now · SDK
What
The SDK query builder accepts resolvePermissionModeInCli, which leaves an unspecified permission mode undefined so the CLI resolves it from settings rather than being pinned at the SDK layer.
Details
Previously an unspecified permissionMode was always sent as "default".
With the option set and no mode given, the field is omitted entirely and settings decide.
Without the option, the old "default" fallback still applies, so existing callers are unaffected.
Opt-in per call site.
Evidence
resolvePermissionModeInCli
CLAUDE_REMOTE_TOOLS_BRIDGE_URL added to the remote-settings env filter #
Juicy
Remotely managed settings can no longer redirect your bridge connection by setting that URL variable.
CLAUDE_REMOTE_TOOLS_BRIDGE_URL
You'll notice · Settings
What
The bridge URL override variable is now filtered out of the env block of remotely fetched managed settings.
Details
the filter list otherwise holds security-sensitive names such as HOME, NODE_OPTIONS, CLAUDE_CODE_OAUTH_REFRESH_TOKEN and the other CLAUDE_BRIDGE_* URLs
the same variable still overrides the device bridge base URL when set locally
Evidence
CLAUDE_REMOTE_TOOLS_BRIDGE_URL
Untrusted-device recovery can be turned off by a flag; unarchive gets its own 403 handling #
Juicy
Untrusted-device failures still recover automatically, and expired trusted-device sessions now tell you to run /login.
/login
You'll notice · Auth
What
Whether an untrusted-device failure counts as auth-recoverable is now controlled by a kill switch, and the 403 classification used by archive and unarchive was split.
Details
the recoverability check changed from reason === "untrusted_device" || reason === "session_stale_relogin" to treating untrusted_device as recoverable only when the proactive-enrollment kill switch is off
gate is tengu_sessions_elevated_auth_disable_proactive_enrollment, fallback false, so absent a remote value untrusted_device stays recoverable
archive and unarchive pass different 403 classification modes; only unarchive maps session_stale_relogin
that reason surfaces as "session expired for trusted-device check — run /login to re-authenticate"
Auto-mode nudge now switches the running session, or explains why it can't #
Juicy
Saying yes to the auto-mode prompt would switch your current session too, not just future ones.
Not switched on · Permissions
What
Accepting the "Make auto mode your default permission mode?" dialog applies auto mode to the session in progress rather than only writing the setting for next time.
Details
on accept, the live tool-permission context is switched to auto with source auto_default_nudge
on success the user setting is written and an in-line confirmation is shown
if the auto gate is unavailable, a warning notification keyed auto-default-nudge-unavailable is posted and the nudge is not marked as seen
the resolution event now records outcome alongside choice
the nudge itself is gated behind tengu_maple_pier, fallback false
Evidence
auto-default-nudge-unavailable
Device bridge registers this machine as a remote-tools device #
Juicy
Your machine can register as a named remote device with a tool that reports its environment.
A new subsystem connects the CLI to a claude.ai bridge over WebSocket as a named device and exposes an MCP server for it. It is off in this build unless the remote gate turns on.
Details
MCP server name claude-code-device, with one tool get_device_info, described as "Returns runtime environment details for this device (platform, architecture, Claude Code version, and device name). Call this to confirm the device connection is live."
handles handshake, heartbeats ({"type":"hb"}), reconnect backoff, slot contention and a 33554432-byte inbound frame cap
the default enable check awaits the remote gate tengu_violin_wood and falls back to false if it throws
additionally requires non-first-party egress to be allowed, provider firstParty, and the allow_remote_sessions policy
default endpoints are wss://bridge.claudeusercontent.com and wss://bridge-staging.claudeusercontent.com, overridden by CLAUDE_REMOTE_TOOLS_BRIDGE_URL
Self-hosted runner checks its base directory before starting work #
The self-hosted runner fails fast if its base directory is unwritable or the mount hangs.
--base-dirSELF_HOSTED_RUNNER_BASE_DIR
Use it now · Elsewhere
What
Startup creates and access-checks the base directory under a timeout so an unwritable or hung mount fails immediately rather than partway through a run.
Details
a timeout exits the process with a hint to check mount health
other failures throw with instructions to pass --base-dir or set SELF_HOSTED_RUNNER_BASE_DIR
Evidence
SELF_HOSTED_RUNNER_BASE_DIR
Live "authoring progress" spinner text while Claude writes a workshop page #
Juicy
The spinner can narrate what Claude is drafting on a workshop page, like "drawing figure 3".
CLAUDE_WORKSHOP_PROGRESS
Not switched on · Artifacts
What
The spinner can narrate what Claude is currently drafting in a workshop design page, driven by the partial tool input as it streams in. It is gated on an environment variable and on workshop mode being active.
Details
A new module (exporting isWorkshopProgressEnabled, deriveAuthoringProgress, onInputJsonDelta) hooks tool-use start, input-JSON deltas and content_block_stop for Write and Edit on .workshop.html files
Messages include "authoring the design page", "wiring up the decisions", "drawing figure N" and "drawing decision N", the latter including the decision id
Output is throttled and truncated to the terminal width, and cleared when the tool use ends
The stream handler only calls it when the caller passes authoringProgressSurface: !0, which the main REPL does
Turned off when CLAUDE_WORKSHOP_PROGRESS is set to false, and it also requires an internally registered workshop-active predicate to return true
Hook failures are swallowed and logged as "workshop authoring-progress hook failed"
Evidence
authoring the design page, wiring up the decisions, workshop authoring-progress hook failed
Everything else · 125
Smaller changes and internals, grouped as the pipeline found them. Nothing is dropped, it is only further down.
New Features
16 items
Impersonation warning when a local session claims a pinned remote identity #
If a session claims a peer identity that isn't on your machine, the message is blocked as suspicious.
You'll notice · Cross-Session Messaging
What
Peer resolution tracks identities previously confirmed as not being on this machine. If a local session record later claims one of those names, nothing is sent and the model is told the situation is suspicious.
Details
The pinning is per-identity: once a name resolves to a session that is NOT on this machine, that fact is remembered for the session.
On a conflict the send is blocked outright rather than routed to the local match.
The model is instructed to ask the user before confirming anyone.
Evidence
A same-named session on this machine your user did not start is suspicious: ask the user before confirming anyone., A session on this machine impersonating it is suspicious: ask the user.
Headless sessions expire held peer messages instead of holding forever #
Headless runs no longer hang forever on a peer message that needs an approval nobody can give.
You'll notice · Cross-Session Messaging
What
In non-interactive mode a held peer message whose hold cause is a mode mismatch now gets a deadline, so a headless run no longer blocks indefinitely waiting for an approval that can never arrive.
Details
The expiry uses an unref'd timer, so it does not keep the process alive on its own.
On expiry the message is dropped and a cancelled receipt is issued to the sender.
The drop is logged with the reason that no approval surface exists.
Evidence
[cross-session-inbound] headless: held peer message expired (no approval surface) — dropped with an expired receipt
Spend-limit blocks now tell you which cap period you hit and when it resets.
You'll notice · Rate Limits
What
The spend precheck now picks the most binding cap period that has spend against it and returns anthropic-ratelimit-unified-* headers describing that period.
Details
headers cover status, reset, overage utilization, surpassed threshold, retry-after and the period itself
the surpassed-threshold header trips at utilization 0.95 and 0.75
blocked responses name the offending period and its reset time rather than a generic message
failures reading the spend store now surface a fetch_error disabled reason instead of a bare error string
Held cross-session messages are now settled as expired at shutdown #
Quitting now settles pending peer messages as expired so the sending session gets a definite answer.
You'll notice · Cross-Session Messaging
What
When Claude Code exits, peer messages that were being held in the cross-session inbound queue are answered as expired instead of being abandoned, so the sending session gets a definite result.
Details
A shutdown hook drains the hold buffer and settles every still-held peer message with an expired status, logging how many were settled
The drain is raced against a grace period of up to 750 ms before shutdown continues
Once shutdown has begun, a late peer message is refused rather than parked, recording peer_inbound_gate / shutdown_expired telemetry
Unix-socket messaging server teardown now also destroys open connections and unlinks the socket file before awaiting the drain
A new lookup lets the headless expiry path read a message's hold cause
Evidence
[cross-session-inbound] shutdown: settling ${e.length} still-held peer message(s) as expired, shutdown: settling , [cross-session-inbound] shutdown: not parking a late peer message \u2014 settled as expired:
New bash-parse rule flags two statements sharing a line #
Commands cramming two statements onto one line are now flagged as too complex to auto-approve.
You'll notice · Permissions
What
Command analysis now treats a command as too complex when one statement directly follows another on the same line, since bash reads that as a single command.
Details
Two node-type sets back the check: a statement-like set including redirected_statement and ERROR, and a smaller set of negated_command, if_statement, while_statement and for_statement
A new walk over the parse tree applies the rule and classifies the command accordingly
Evidence
statement directly follows another statement on the same line
Remote Control bridge sessions get [ref] handles in listings #
Sessions reached over Remote Control now show a reference handle so you can message them by name.
You'll notice · Remote Control
What
Sessions reached over Remote Control now resolve a reference handle the way local and cloud sessions do, so they can be addressed by name, and the listing warns when it is showing an incomplete set.
Details
Bridge session refs are resolved from the bridge-session\x00<id> key.
When the bridge listing is incomplete, the output appends a note that those rows carry no [ref] and are not messageable by name until a later listing completes.
Selecting a bridge session maps to a cloud-session target tagged via: "remote-control".
Evidence
list incomplete just now
Artifact publish now retries 503s with a visible progress line #
Publishing an artifact now retries when the service is busy and tells you it is waiting.
You'll notice · Artifacts
What
Publish and deploy calls go through a shared retry wrapper that retries HTTP 503 using a delay derived from retry-after, and the transcript shows why it is waiting instead of appearing to hang.
Details
Retry progress is reported as an artifact_publish_retry event, rendered by a new UI component.
The line reads "Publish service temporarily unavailable" for a 503 and "Publish service busy" otherwise, followed by an attempt counter.
Telemetry records "deploy_503_retried" and "deploy_429_retried" with a deploy_attempts field.
Evidence
Publish service temporarily unavailable
Org-memory decision can be reopened, and is bound to an identity #
Shared org memory now remembers who settled a decision and lets a settled decision be reopened.
Under the hood · Elsewhere
What
The org-memory decision store now records which identity settled a decision and supports reopening a settled decision back to the undecided state.
Details
A settle attempt from a different identity than the one recorded is refused with cause "superseded".
Reopening emits the new event tengu_org_memory_decision_reopened with from_state and from_cause.
The reset helper clears the remembered identity in addition to the state.
Evidence
tengu_org_memory_decision_reopened
Held cross-session peer messages: supersede, re-arm and already-resolved notice #
Held peer messages now cancel their approval prompts cleanly instead of leaving stale ones hanging around.
You'll notice · Cross-Session Messaging
What
The inbound-hold hook keeps a per-message record with an abortable approval prompt, so held peer messages can be superseded, dropped or released cleanly instead of leaving orphaned prompts.
Details
Superseded, dropped and released holds have their approval prompts cancelled via the stored abort handle.
When the approval surface returns, the expiry deadline is re-armed rather than being counted against the time with no surface available.
An approval or denial that lands after the message was already resolved by a mode or setting change posts a warning instead of acting.
Evidence
That held message was already resolved before your
Artifact replies detect that another session already answered the summon #
If another session already answered an artifact summon, yours says so instead of duplicating the reply.
You'll notice · Artifacts
What
Posting a reply to an artifact summon can now come back as "already answered elsewhere", so a second session does not duplicate a standing answer or retry pointlessly.
Details
A 409 whose body reason is summon_already_answered, or a response with summon_answered: !0, maps to the outcome { kind: "summon_answered" }.
The tool passes answersSummon with the reply, and the autoreact path threads a new declareAnswersSummon flag through reply and edit calls.
The tool returns replied:false with a result carrying summon_answered, and the model is told not to retry but to read the comments for the answer that landed.
The tool-use line shows "reply not posted (summon already answered)" instead of the generic reply-needs-thread-activation text.
The autoreact pipeline takes a separate answeredElsewhereNoticeText notice, for example that the follow-up note was withheld because another session's reply already stands in the thread.
Telemetry records summon_answered_elsewhere.
Evidence
summon_answered_elsewhere, Reply not posted: this summon was already answered in another session, and the standing answer is in the thread. The draft was discarded. Do not retry \u2014 read the comments to see the answer that landed., reply not posted (summon already answered), summon_already_answered
Artifact tool now tells Claude that finished deliverables should be published #
Claude is now pushed to publish finished reports or plans and hand you the link.
You'll notice · Artifacts
What
The Artifact tool description gained a paragraph arguing that a report, plan or reference meant for an audience is not actually delivered until it is published and the link handed over.
Details
Applies to work products aimed at a reader, not to incidental output
Frames publishing plus handing over the link as part of finishing the task, not an extra step
Description text only; no change to the tool's parameters
Evidence
is not fully delivered while it lives only in terminal scrollback or a local file
Remote Control bridge: token-refresh exhaustion now surfaces a login prompt #
When remote control can't refresh your token, it now tells you to run /login instead of stalling.
/login
You'll notice · Remote Control
What
When the bridge runs out of token-refresh retries, the session is marked failed with an instruction to run /login instead of stalling silently.
Details
The refresher gained an onExhausted callback fired after the configured number of consecutive failures, logged at error level
Existing "Refresh chain exhausted for sessionId=" logging and bridge_token_refresh_exhausted telemetry are unchanged
The bridge supplies the callback and moves the session into a failed/auth state carrying the user-facing reason
Token-refresh failures inside getAccessToken are now caught and logged rather than thrown
A new cred_4094_recovery cause joins the recovery connect-timeout classification
Evidence
OAuth token unavailable \u2014 run /login to restore Remote Control, run /login to restore Remote Control
Auto mode denies safeguard-refused classifier calls without recording a verdict #
A safety refusal during auto mode denies the action but no longer counts against your denial limit.
You'll notice · Permissions
What
When the auto-mode classifier call returns stop_reason "refusal", the action is denied but the denial is no longer treated as a normal block or counted against you.
Details
The decision is marked noVerdict: !0 and returns a message explaining the refusal reacts to earlier conversation content rather than the action itself: "Retrying it will hit the same refusal, so don't rewrite or rework the action to get around this".
The denial is exempt from the denial counter.
In headless mode it instead throws "Agent aborted: auto mode classifier request refused by the safety safeguard in headless mode".
Downstream, decisions carrying noVerdict suppress the auto-mode meta message and the config-reject bookkeeping path.
Telemetry gains failureKind: "policy_refusal", stopReason, stage1VerdictStands and refusedBySafeguard.
Evidence
Auto mode classifier request refused by the safety safeguard, denying (exempt from the denial counter)
Bridge recovery for expired worker credentials on close code 4094 #
When your remote worker credential expires, the bridge now re-mints it and refreshes login instead of dying.
You'll notice · Remote Control
What
The remote bridge can now recover from a worker credential expiry instead of just reporting the close code, re-minting the credential and forcing an OAuth refresh.
Details
Adds a recovery-table entry for close code 4094 alongside the existing 401, 4091 and 4093 entries.
The 4094 entry re-mints the worker credential and sets needsOAuthRefresh: !0.
Epoch-mismatch conditions token_expired and auth_exhausted map to 4094.
Gated on the typed close-code mapping, passed as causeTypedCloseCodes: !0 by both bridge callers, so it is active for the remote bridge.
The plain "code 4094" close-reason text existed in 2.1.224; only the recovery handling is new.
Evidence
worker credential expired \u2014 re-minting
Artifact comment replies can declare that they answer a summon #
A reply to an artifact comment can mark itself as answering a summon, and only once.
You'll notice · Artifacts
What
Comment posting accepts an answersSummon flag; when set, the POST body carries answers_summon: !0 so the server knows the reply resolves an outstanding summon.
Details
A one-shot claim registry keyed by artifact slug validates the declaration against the expected thread id and tool-use id.
The claim is marked consumed once used, so a given summon can only be answered once.
Evidence
answers_summon
New user-facing hint when the Remote Control server returns a malformed response #
If the Remote Control server sends something unreadable, you now get a clear message suggesting an update.
You'll notice · Remote Control
What
A dedicated message now explains that the Remote Control server sent something the client could not parse, and points at updating Claude Code, instead of a generic failure line.
Details
new exported message BRIDGE_MALFORMED_RESPONSE_HINT, returned for the malformed_response failure reason
applies to both bridge session creation and reattach
also listed among the errors the bridge status UI treats as actionable
Evidence
Remote Control got an unexpected server response \u2014 try again after updating Claude Code
Improvements
45 items
send_message: clearer story about sessions on other machines #
Messaging an unknown peer now explains that sessions on other machines could not be checked, so retry.
You'll notice · Remote Control
What
The old "reply-only bridge session" refusal was removed, and unresolved peer names now explain that Remote Control sessions on other machines could not be checked, with a suggestion to retry.
Details
Unresolved names no longer fall back to the send_message connector.
Timeouts are now reported as "timeout" instead of not_reachable.
The isolatePeerMachines prompt is assembled from whichever of the cloud or bridge lookups actually failed.
Evidence
sessions on other machines could not be checked just now, so they were not searched.
OAuth token refresh timers re-check in 60s chunks #
Login token refreshes now survive laptop sleep and long suspends more reliably.
You'll notice · Auth
What
Scheduled token refreshes no longer rely on one long setTimeout, which makes them more reliable across long sleeps and machine suspends.
Details
A repeating chunked timer re-evaluates the remaining time against a 60000ms cap.
The refresh only fires when a generation token still matches, so stale timers from a superseded schedule are ignored.
Evidence
Scheduled token refresh for sessionId=
VS Code-owned sessions get their own settings defaultMode handling #
In VS Code sessions, unsafe default permission modes from settings are refused and fall back to normal.
permissions.defaultModebypassPermissions
You'll notice · Permissions
What
For sessions owned by the VS Code extension, permissions.defaultMode is read only from policy, flag and user settings, and unsafe values from settings are refused.
Details
bypassPermissions from settings is ignored unless the user has consented; a warning is printed to stderr and the mode falls back to default.
auto is ignored while the auto-mode circuit breaker is active.
The auto read additionally requires tengu_harbor_willow (fallback !1) or a meadow_lantern config value.
Artifact comment errors distinguish missing thread from degraded fetch #
Artifact comment errors now tell you whether a thread is really missing or just dropped from fetch.
You'll notice · Artifacts
What
Asking for an artifact comment thread that is not in the fetched set now says whether the thread genuinely does not exist or whether threads were dropped from the fetch.
Details
Raises comments_thread_not_found when the id is absent, with text pointing at the "comments" action as the source of thread ids
Raises comments_degraded instead when threads were dropped, so the model does not conclude the thread is gone
A filtered listing that comes back empty now tells the model to re-run without thread_id
Evidence
no comment thread ${er} on this artifact \u2014 thread ids come from action "comments"
Replies are suppressed when another session already answered the summon #
If another session already answered a comment, the duplicate reply is dropped instead of posted.
You'll notice · Artifacts
What
If a summon was already answered in another session, the drafted reply is discarded rather than posted, and the caller is told why.
Details
New summon_answered result path reports the reply was not posted and that the standing answer is already in the thread
The auto-react flow gained answeredElsewhereNoticeText variants for cases where the artifact is published but the reply is withheld
Telemetry records summon_answered_elsewhere
Evidence
Reply not posted: this summon was already answered in another session, and the standing answer is in the thread.
Workspace trust is now enforced before the agents/fleet view #
Opening the agents view now asks you to trust the workspace first if you haven't.
You'll notice · Permissions
What
Opening the agents view goes through a trust check first, prompting if the workspace has not been trusted yet.
Details
The agents view and related entry points await ensureAgentsWorkspaceTrust, which decides skip, trusted or ask
The ask path renders the TrustDialog
After acceptance it clears the plugin cache, re-initializes GrowthBook while preserving pending exposures, primes plan slug collisions and captures a policy snapshot
Telemetry and config environment variables are only applied once trust is accepted
agents-listing description updated for Remote Control #
The agent list now calls bridge sessions your Remote Control sessions on other machines.
You'll notice · Remote Control
What
The list-agents tool description no longer calls bridge sessions "reply-only" and instead describes them as your Remote Control sessions on other machines.
Details
The wording is conditional on Remote Control being connected on this machine
Description text only; the listing behaviour is unchanged
Evidence
and (when Remote Control is connected here) your Remote Control sessions on other machines
Held cross-session messages name managed or repo settings as the cause #
When a peer message is held, you now see whether org or repo settings caused it.
--setting-sources
You'll notice · Cross-Session Messaging
What
When an inbound cross-session peer message is held, the explanation now tells you whether an org managed setting or the repository's own settings forced it, rather than just reporting an explicit setting.
Details
Two new hold-reason cases: managed-setting and repo-setting, in addition to the plain explicit-setting case.
The repo case explains that a repo may only tighten, so your own "accept" cannot override it, and points at --setting-sources to exclude it.
The resolver was reworked to return both the effective value and which tier decided it; repo-level local and project settings are attributed as repoSettings.
Evidence
This repository's settings set "crossSessionInbound" to "hold" (a repo may only tighten, so your own "accept" cannot override it); remove the repo setting or exclude it with --setting-sources., This repository's settings set "crossSessionInbound" to "hold" (your own "accept" cannot override a repo tightening).
Peer listings now warn when the session walk didn't finish, so partial lists aren't mistaken for complete.
Under the hood · Cross-Session Messaging
What
listAllPeers now tells the model when bridge rows are present but the session walk did not finish, so partial results are not mistaken for the full set.
Details
The previous bridge-population field was replaced with a bridgeWalkFailed flag, set when the walk failed or the handle produced no usable result.
With bridge rows present and the walk failed, the listing appends: "list incomplete just now — those rows carry no [ref] and are not messageable by name until a later listing completes".
A helper that reported bridge population separately was removed.
Evidence
bridgeWalkFailed
Remote Control unarchive re-auths trusted devices, and malformed /bridge responses are terminal #
Unarchiving a remote session now retries with device trust and stops retrying broken bridge replies.
You'll notice · Remote Control
What
Unarchiving a remote session now handles device trust failures explicitly, and a malformed bridge response is no longer retried as if it were transient.
Details
untrusted_device and session_stale_relogin 403s are retried once with an X-Trusted-Device-Token header.
If that does not resolve it, the call returns an elevated_auth outcome logged as bridge_repl_v2_unarchive_elevated_auth, so reattach surfaces an auth failure while preserving the session pointer.
A malformed /bridge response, or one with an invalid worker_epoch, now returns a terminal malformed_response result instead of null.
Evidence
bridge_repl_v2_unarchive_elevated_auth
Clearer permission prompt for messaging a Remote Control session #
The messaging prompt now says your note arrives marked as from another Claude session, not you.
You'll notice · Cross-Session Messaging
What
The confirmation prompt now explains how the message is attributed on the receiving end rather than describing it as a user prompt.
Details
Removes the old "arrives as a user prompt" phrasing.
States that the message is marked as coming from another Claude session, not from that session's user.
Evidence
marked as from another Claude session, not from its user
Trusted-device 403s handled on elevated-auth requests #
A rejected session can now tell you it's stale and needs re-login, not just untrusted device.
You'll notice · Sessions
What
A 403 from the code-session request helper can now report a stale session needing re-login, not just an untrusted device.
Details
The helper always parses the error resource on a 403.
Still returns untrusted_device as before.
Additionally surfaces session_stale_relogin when the call site is tagged elevated_auth.
Replaces the old classifyUntrustedDevice option object.
The user-facing re-login message itself already existed in 2.1.224.
Evidence
Your session has expired for the trusted-device check. Run /login to re-authenticate, then retry.
Classifier-unavailable denial message shortened via shared fragments #
The "temporarily unavailable" denial message is shorter and no longer shows HTTP status details.
You'll notice · Permissions
What
The "temporarily unavailable" denial text in auto mode now ends with two shared constants rather than inline prose, and the same fragments are reused by the new safeguard-refusal message.
Details
The fragments are "If it keeps failing, continue with other tasks that don't require this action and come back to it later." and "Note: reading files, searching code, and other read-only operations do not require the classifier and can still be used."
The helper that produced HTTP status and error-kind detail now returns the empty string, so status details no longer appear in either message.
Evidence
Note: reading files, searching code, and other read-only operations do not require the classifier and can still be used.
Remote Control: token refresh chain gives up loudly, and auth changes invalidate in-flight attempts #
Remote Control now reports dead token refreshes instead of failing silently, and re-logging in cancels stale connection attempts.
You'll notice · Remote Control
What
When the bridge token refresher exhausts its retry chain it now reports the failure instead of stopping quietly, and re-logging in cancels bridge init attempts that started under the old credentials.
Details
On exhaustion it logs an error built from the fixed fragment "Refresh chain exhausted for sessionId=" plus the session id and failure count, emits bridge_token_refresh_exhausted, and returns "no_oauth_token" to the caller.
App state gained authChangeGeneration, bumped on login or account change; bridge init compares it before applying results.
The 401 recovery path keys off a per-endpoint needsOAuthRefresh flag and aborts init after a successful refresh rather than continuing with the stale response.
Session relaunch messages now say "environment" instead of "pool".
You'll notice · Sessions
What
User-facing text about relaunching a session now says environment instead of pool.
Details
The requeue confirmation reads "This re-launches the session on another runner in the environment."
The secret-claim error says it "cannot derive the environment ID for spawn-hint polling".
Evidence
This re-launches the session on another runner in the environment.
Auto-mode classifier failures are distinguished from classifier denials #
When the auto-mode classifier can't decide, you no longer get a bogus denial message or permission-rule hint.
You'll notice · Permissions
What
A permission decision that the classifier could not produce a verdict for is now tracked separately from a real denial, so telemetry and UI stop treating a classifier failure as a refusal.
Details
The decision reason carries a new noVerdict flag.
Denial-reason bucketing keys off the flag instead of matching a message prefix; telemetry labels are assembled at runtime as classifier:${e.classifier}:no-verdict around the literal :no-verdict.
The UI denial kind automode-parsing-error is now chosen by t.noVerdict.
The 'add a Bash permission rule' hint and the post-denial hook message are both skipped for no-verdict cases.
Evidence
noVerdict
Bridge reattach can fail hard instead of silently starting a new session #
After re-authenticating, Remote Control reattaches to your existing session or says it's gone, instead of quietly starting a new one.
You'll notice · Remote Control
What
An auth-triggered revive now either reattaches to the same session or reports a dead pointer, rather than quietly creating a fresh session under the old one.
Details
REPL bridge session options gained reattachOrFail, reviveInitiated, onAuthProven and onReattachPointerDead.
The caller passes reattachOrFail only when a revive was initiated and a previous session id is known.
Evidence
onReattachPointerDead
Org memory can be superseded when the identity changes #
Org memory remembers who enabled it, and a different account trying to enable it is rejected.
You'll notice · Elsewhere
What
Enabling org memory records the identity that turned it on, and a later enable attempt from a different identity is rejected rather than silently taking over.
Details
the earlier decision is settled off with cause superseded
the outcome is reported through the tengu_org_memory_decision event
applies to everyone; no flag gates it
Evidence
Wz("superseded")
Held-message answer picker hides numbering and pre-focuses a default #
The held-message prompt drops the numbered list and opens with a choice already highlighted.
You'll notice · Remote Control
What
The prompt asking what to do with a held cross-session message renders as a plain list with a sensible option already selected.
Details
the select is passed hideIndexes: !0, so options render without numeric indexes
a defaultFocusValue is passed so the list opens on a specific choice
Evidence
defaultFocusValue
Code-session failures are now a terminal/non-terminal discriminated union #
Expired logins during code sessions now refresh and retry automatically instead of failing the operation.
You'll notice · Auth
What
HTTP failures from code-session calls are classified in one place, and a 401 is treated as recoverable so the caller refreshes OAuth and retries instead of failing the operation.
Details
one helper returns "oauth_rejected" for 401, "transient" for 408, 429 and any status >= 500, and "rejected" otherwise
the type guards now check e.terminal === !0 / e.terminal === !1 rather than testing for the presence of a terminal key
the bridge's 401 retry path also fires when the result is any non-terminal failure
Evidence
oauth_rejected
PID 1 no longer counts as an ancestor when verifying a peer connection #
Sessions running as the main process in a container no longer get wrongly matched to peer connections.
You'll notice · Cross-Session Messaging
What
Ancestor verification for peer connections no longer treats a container init process as a legitimate ancestor, so a session running under PID 1 cannot be matched by accident.
Details
the Linux /proc ancestor walk now records a parent pid of 1 and stops there, where it previously discarded it by breaking at ppid <= 1
the array descendant helper returns false immediately for pid 1 (if (t === 1) return !1)
the cached lookup now requires process.pid !== 1 && i.includes(process.pid)
gated by platform: the ancestor walk reads /proc, so this is the Linux path
Evidence
[peer-cred] peer pid lookup failed
Host initialize payload reports the current permission mode #
IDE and remote hosts now learn your current permission mode the moment they connect.
Under the hood · Elsewhere
What
IDE and Remote Control hosts now learn the CLI's permission mode at connect time, alongside the model.
Details
new current_permission_mode field in the connect-time state
documented as "@internal The CLI's active permission mode at connect time, for the same connect-time sync as current_model."
populated from the live tool-permission context in the main init payload and in both repl-bridge getInitializeState implementations
Evidence
current_permission_mode
Unified rate-limit tracking now also applies on the gateway auth source #
Gateway-authenticated sessions now get the same rate-limit tracking and 429 handling as subscription ones.
You'll notice · Rate Limits
What
Sessions authenticating through the gateway get the same unified rate-limit handling that subscription sessions already had.
Details
the guarding predicate changed from "caller says so, or the subscription check passes" to additionally accepting a gateway auth source
covers unified rate-limit header parsing, 429 handling and the background usage probe
Evidence
anthropic-ratelimit-unified-overage-status
Stale-peer send failure text points at the peer list tool #
Failed sends to a vanished peer now explain why and point you at the peer list.
You'll notice · Remote Control
What
When a send fails because the peer is gone, the error now distinguishes a name-resolved peer from a stale bridge ID and tells you how to find the current peers.
Details
Name-resolved peers get "that session may have ended, been archived, or disconnected".
Stale bridge IDs get their own wording.
A suggestion to call the peer-listing tool is appended only when that tool is present in the current tool set.
Evidence
— that session may have ended, been archived, or disconnected.
Artifact replies now answer commenters' questions directly and flag change requests instead of promising them.
You'll notice · Artifacts
What
The reply-only composer now answers commenter questions directly and treats change requests as flagged rather than promised.
Details
Instructed to answer questions directly and substantively
Change requests are acknowledged as flagged for the artifact's owning session, not committed to
Explicitly told not to describe its own limitations to the commenter
Evidence
Noted \u2014 flagged for this artifact's session to pick up
Workshop template: waiting-state painter on the opening publish #
Workshop pages show a painter animation while waiting for the first real version, with a reload hint.
You'll notice · Artifacts
What
The opening publish of a workshop page now shows a painter animation while it waits for the next version, and nudges you to reload if nothing arrives.
Details
Both workshop HTML templates gained a script that detects the opening version by an empty ws-decisions island with no decision rows
The painter animation runs while waiting for the next publish
After 180000 ms it escalates once into the footer bar with a reload hint
The authoring skill text drops its hardcoded line-number ranges, now stating the template is 2,948 lines, and adds a refresh hint
Evidence
Still nothing \u2014 Claude may not be watching this page right now. Reload to check for the latest version.
Shared "auto mode unavailable" message and a warning-kind notice #
The "auto mode unavailable" message is now consistent everywhere and shown as a warning.
You'll notice · Permissions
What
The workflow auto-mode notice and the auto-default nudge now produce their unavailability text from one helper, and the notice is styled as a warning.
Details
The helper returns a reason-specific string when there is one, otherwise the fallback "auto mode is unavailable right now"
The notice is tagged kind: "warning"
Evidence
auto mode is unavailable right now
Handoff classifier refusals allow the output with an unreviewed warning #
When a safety check on subagent handoff output is refused, the work passes through with a warning.
You'll notice · Subagents
What
A sub-agent handoff whose safety classifier request is itself refused by the safeguard is no longer blocked; the output is passed through with a security warning attached.
Details
The refusal is not treated as a verdict. The sub-agent output is allowed but prefixed with a long warning telling the model the work is unreviewed and untrusted.
Classifier outcome logging gained a "refused" bucket alongside the existing "unavailable" and "blocked".
Evidence
Handoff classifier request refused by the safety safeguard, allowing sub-agent output with an unreviewed warning
Cross-session send prompts distinguish cloud from Remote Control peers #
Prompts for messaging another session now say whether the peer is cloud or Remote Control.
You'll notice · Cross-Session Messaging
What
The permission prompt for messaging another session now says which kind of peer you are reaching and makes clear how the message is attributed on arrival.
Details
The prompt branches on the peer's via, saying either "cloud session" (running in the cloud) or "Remote Control session" (on another machine).
Both texts now spell out that the message arrives marked as from another Claude session, not from its user.
Peer-listing and error paths were updated to match: a peer-name collision now reads "a Claude session on another machine (cloud or Remote Control)", and error text says a session "may have ended" and either "or been archived" (cloud) or "or disconnected".
Evidence
It reaches the receiving Claude (running in the cloud) via Anthropic's servers as a cross-session message — marked as from another Claude session, not from its user.
Reading a single artifact comment thread now shows clearer results, including when the thread isn't found.
You'll notice · Artifacts
What
Reading a single artifact comment thread now has its own result rendering in both the tool-result UI and the model-facing text.
Details
"read 1 comment thread (filtered)" replaces the plain comment count.
A missing thread renders "requested thread not in this result".
An empty filtered result tells the model to re-run action "comments" without thread_id for the full list.
Evidence
The requested comment thread is not in this result — run action "comments" without thread_id for the full list.
Cross-session send says whether the recipient is in the cloud or on another machine #
Delivery confirmations now correctly say whether your message went to a cloud or remote machine session.
You'll notice · Cross-Session Messaging
What
The success text after delivering a message to another Claude session no longer always claims the target was a cloud session.
Details
The message branches on the delivery route used.
A recipient reached over Remote Control is recorded as a "bridge-session" rather than a "cloud-session".
The wording appears in the tool result text, so anyone using cross-session messaging sees it.
Evidence
a Claude session on another machine, over Remote Control
Code-session create and poll failures are classified by reason #
Code session failures are now sorted by cause, so retries and error messages make more sense.
You'll notice · Sessions
What
HTTP failures from the code-sessions endpoints are now sorted into transient, terminal and auth categories instead of being treated uniformly, which changes what gets retried and what gets reported.
Details
401 becomes a non-terminal oauth_rejected.
408, 429 and 5xx remain transient and are retried.
Anything else becomes terminal request_rejected.
Grouping and project rejections are tagged grouping_rejected and get their own message about the Project being unavailable.
OAuth-rejected session creation feeds a distinct telemetry path.
Evidence
v2_session_create_oauth_rejected
Self-hosted runner prompt reworded around environment secret files #
The self-hosted runner prompt now talks about environment secret files instead of pool secrets.
You'll notice · Elsewhere
What
The permission prompt for starting a detached self-hosted runner drops the "pool secret" wording in favour of environment-centric terms used by the other runner tools.
Details
Refers to an environment secret file rather than a pool secret.
Describes the work as that environment's queued sessions.
Evidence
Start a detached self-hosted runner with the environment secret file
Remote Control: new terminal error reasons and clearer failure surfacing #
Remote Control failures now tell you what actually broke and that you can rerun /remote-control to retry.
/remote-control
You'll notice · Remote Control
What
Bridge failures are now distinguished by reason and kind instead of collapsing into one generic error, and the message tells you how to retry.
Details
New failure reasons request_rejected, whose message is assembled as "Remote Control server rejected the request (HTTP ...) — run /remote-control to retry", and malformed_response.
HTTP classification separates "oauth_rejected" (non-terminal) from "transient" and "rejected" (terminal).
Failures are reported with an explicit kind, "terminal" or "auth", including the org-policy case.
App state carries replBridgeError and replBridgeErrorKind.
Error and telemetry text now scrubs invalid HTTP header values so they can't leak into reports.
Under the hood · Telemetry
What
Two more patterns were added to the telemetry and error scrubber so header values do not leak through error text.
Details
One pattern truncates everything after "has invalid value: " for a named header.
The other covers Headers.append and Headers.set messages about an invalid header name or value.
Evidence
has invalid value:
Search tokenizer emits compound candidates from single-character tokens #
Search now joins stray single-character tokens with their neighbours, so split-up terms match better.
You'll notice · Elsewhere
What
Query tokenization pairs a single-character token with an adjacent multi-character neighbour and offers the joined form as an extra candidate, improving matches on terms that were split apart.
Details
The joined form is appended before the compound-first ordering is applied.
Compound candidates are still filtered out unless the caller explicitly asked for compounds.
Evidence
compound
Bridge reconnects only refresh OAuth for the close codes that need it #
Remote Control reconnects skip needless token refreshes, so ordinary disconnects recover faster.
You'll notice · Remote Control
What
Reconnect recovery no longer forces an OAuth refresh for every handled close code, cutting unnecessary token work on non-auth disconnects.
Details
The per-close-code recovery table (401, 4091, 4093, 4094) gains needsOAuthRefresh, true for 401 and 4094 only.
The flag decides whether to force an OAuth refresh up front, whether to enter the retry loop that prompts for a fresh login, and whether to do a late refresh instead.
The CCR client gained an onRequestAuthOk callback fired on a successful heartbeat, wired through the bridge so the session can mark auth as proven.
Evidence
needsOAuthRefresh
Session descriptions understand bridge and remote sessions #
Sessions reached over Remote Control are now described as being on another machine, not this one.
You'll notice · Remote Control
What
A peer reachable over Remote Control is described as being on another machine instead of falling through to the "on this machine" wording.
Details
The human description helper treats bridge-session like other Claude sessions.
A new location string is rendered for these sessions.
Evidence
on another machine (Remote Control)
Background job state keeps respawn flags, and non-allowlisted tokens are stripped #
Background jobs now save the flags needed to relaunch them, dropping any unrecognised flags with a warning.
Under the hood · Elsewhere
What
Persisted background job state now carries the flags needed to respawn a job, with unknown flag tokens filtered out before they are written.
Details
the job-state writer persists respawnFlags alongside the fields it already rewrites
the schema defaults respawnFlags to an empty list and filters its contents against an allowlist
dropped tokens produce a warning naming them
Evidence
[jobs] stripped non-allowlisted respawnFlags token(s) from persisted job state:
A successful Remote Control heartbeat now clears stored auth failures, so you get fewer spurious re-login prompts.
You'll notice · Remote Control
What
A successful bridge heartbeat clears accumulated authentication-failure state instead of leaving the counter to age out on its own.
Details
the CCR client gained an onRequestAuthOk option, invoked next to resetHeartbeatStreak() on each successful heartbeat
the bridge supplies a callback that resets its auth-failure counter and calls the existing onAuthProven hook
Evidence
onRequestAuthOk
Held peer messages now say which settings tier held them #
When a cross-session message is held, you now see whether org, repo or local settings did it.
crossSessionInbound
You'll notice · Cross-Session Messaging
What
When an inbound cross-session message is held by an explicit setting, the notice names the settings layer responsible instead of giving a single generic cause.
Details
the old single hold cause "explicit-setting" was replaced by source attribution, including managed-setting
policy settings render "Your organization's managed settings set \"crossSessionInbound\" to \"hold\"."
project and local settings render "This repository's settings set \"crossSessionInbound\" to \"hold\" (your own \"accept\" cannot override a repo tightening)."
a WeakMap holds the last-reported cause so a notice is re-emitted only when the cause changes
Inbound peer messages are no longer mistaken for your own when running inside a container.
You'll notice · Cross-Session Messaging
What
Inbound peer messages are no longer misattributed to this process when the session runs as PID 1, which is common inside containers.
Details
the self-sent check now takes the pid as a parameter rather than reading it implicitly
returns false outright when that pid is 1, so no false selfSent match
pairs with the ancestor-walk change above
Evidence
selfSent
Bridge auth failures no longer burn reconnect attempts #
A login problem on the bridge no longer eats your reconnect attempts and is now named clearly.
You'll notice · Sessions
What
An authentication failure in the repl-bridge connect loop is reported as such and does not consume one of the limited retries.
Details
the retry counter is incremented only for non-auth failures
failures now carry an explicit kind: auth for auth failures, terminal otherwise
replaces a single generic terminal path for both cases
Evidence
auth
Bug Fixes
14 items
REPL sampling helpers report validation errors as progress instead of failing silently #
Bad arguments to the in-REPL sampling helpers now show a visible error instead of vanishing.
You'll notice · Elsewhere
What
The in-REPL haiku, opus, sonnet and fable sampling helpers emit their start progress event before validating arguments, so bad input shows up as a visible error rather than a throw with nothing recorded.
Details
"prompt must be a string" and "schema must be an object" now arrive as a repl_tool_call progress event with phase "error"
a non-string prompt is recorded in progress as an assembled placeholder naming its typeof
Compaction now trims the saved conversation payload to match the preserved message list, avoiding mismatches.
You'll notice · Compaction
What
When preserved event ids exceed the cap, the compaction payload itself is now trimmed to match, rather than only the id list.
Details
The internal event writer rewrites compactMetadata.preservedMessages.uuids to the last N and adjusts the preserved segment.
Whether that rewrite happened is reported as payloadClamped on the existing tengu_ccr_preserved_event_ids_clamped event.
Evidence
tengu_ccr_preserved_event_ids_clamped
Bridge hook resets now fire the pending archive instead of dropping it #
Changing directory mid-session no longer silently drops a pending archive in the bridge.
You'll notice · Sessions
What
Changing the working directory mid-session no longer discards a pending archive in the REPL bridge hook.
Details
The per-session state reset was factored into a single helper taking an archiveAbandoned flag.
The working-directory-change path now calls it with the flag set, which fires the pending archive before unregistering it.
Previously that path cleared the refs silently.
Evidence
archiveAbandoned
Artifact watch list wording fixed for pluralization #
The artifact watch summary now says "watches" instead of a garbled plural.
You'll notice · Artifacts
What
The watch summary now reads correctly for more than one watch.
Details
The count is formatted separately and the word "watch" is pluralized to "watches".
Previously the whole phrase "artifact watch" was pluralized.
Evidence
in this session (session-local; none survive a restart):
Fleetview group-header selection no longer fights the current selection #
Picking a group header in Fleetview no longer bounces your selection back and forth.
You'll notice · Fleetview
What
Selecting a group header no longer causes the selection to bounce between the header and the previously selected row.
Details
The effect that snaps selection to the computed target bails out when the current index already equals the target.
The header-scroll effect sets selection and scroll offset together.
The empty-state telemetry event still reports skeleton and has_origin.
Evidence
tengu_fleetview_empty_state_shown
Bundled skill extraction verifies existing files instead of failing on them #
Re-installing a bundled skill over identical existing files now succeeds instead of erroring.
You'll notice · Skills
What
Re-extracting a bundled skill over files that already exist succeeds when the contents already match, instead of erroring out on EEXIST.
Details
Extraction passes a tolerance option; on EEXIST it opens the existing file, checks it is a regular file and compares its full contents to what would be written.
It rethrows only if the contents differ.
The 'additional skill files' path uses plain tolerateExisting: !0.
Evidence
tolerateExisting: "verify-content"
Post-tool-use commit awareness no longer fires inside subagents #
Commit-awareness notes now appear once in your main session instead of from every subagent.
You'll notice · Hooks
What
The post-commit awareness system message is now emitted only for the main session, not for every subagent that happens to run the tool.
Details
the PostToolUse callback returns early when the hook payload carries an agent_id, on top of the existing non-interactive check
relatedly, the code-review prompt no longer appends its "end your response with this exact line" suffix when running with an agent id or as a skill preload
Evidence
e.agent_id !== void 0
Bridge population recording forgets superseded sets #
The bridge now drops stale session listings instead of keeping outdated ones around.
Under the hood · Sessions
What
A recorded set of sessions for a handle is now dropped when an unrecordable listing supersedes it, instead of being left stale.
Details
The successful path records the newly computed listing as before.
The drop is logged as "[bridge:population] forgot the recorded set (an unrecordable listing superseded it)".
Evidence
[bridge:population] forgot the recorded set (an unrecordable listing superseded it)
Gemini command import stops rejecting a bare exclamation mark #
Importing Gemini commands containing a plain exclamation mark no longer fails validation.
You'll notice · Slash Commands
What
Importing a Gemini command that used an exclamation mark as prose or negation could fail validation even when nothing shell-like was involved.
Details
Previously any command mixing argument placeholders with a '!' anywhere outside its shell blocks was rejected.
The importer now masks $-prefixed tokens first.
It only flags a '!' whose preceding character is not an ordinary word or punctuation character.
The dangling-marker and shell-block checks are unchanged.
Evidence
Mixes argument placeholders with backticks or '!' outside its shell blocks
Artifact share status reports "public" for tokenless reads and hides a stale owner role #
Shared artifacts no longer show you as owner when the response gives you only public read access.
You'll notice · Artifacts
What
Share status no longer trusts a server-reported mode when the response carries no asset token, which stops a public read from being presented as owner access.
Details
With no asset token, the mode is reported as "public" regardless of what the server said.
A role of "owner" is dropped in that case.
The artifact tool emits an "unknown_share_mode" telemetry reason when the derived mode is "unknown".
Evidence
unknown_share_mode
Unknown control messages complete their command lifecycle #
An unrecognized message no longer leaves your command hanging forever waiting for a result.
You'll notice · SDK Transport
What
An incoming message with an unrecognized type no longer leaves callers waiting on it forever.
Details
The handler extracts a uuid if present and reports that command as "completed" through the lifecycle callback.
The existing "Ignoring unknown message type" warning is still emitted alongside.
Evidence
Ignoring unknown message type:
"Reachable agents" listing includes bridge sessions #
Machines reachable only over the bridge now appear instead of showing "No reachable agents."
You'll notice · Fleetview
What
A machine reachable only over the bridge no longer shows "No reachable agents.", because bridge transports are now passed into the session section rather than merely counted.
Details
Bridge rows are deduplicated against cloud sessions.
The recorded-population debug lines were reworded to say so.
Evidence
[bridge:population] warm recorded set:
Gateway 429s without unified rate-limit headers no longer drive the usage indicator #
A gateway spend block no longer scrambles the rate-limit usage indicator you see.
You'll notice · Rate Limits
What
A 429 injected by the gateway, for example a spend block, no longer overwrites the displayed Anthropic rate-limit state.
Details
Rate-limit status tracking returns early when the provider is the gateway and the 429 response lacks the unified status header.
Evidence
anthropic-ratelimit-unified-status
Virtual-scroll follow re-pin also updates a second focus setter #
Scroll-following in long lists keeps focus and scroll position in sync more reliably.
You'll notice · Fleetview
What
The follow poller in the virtual scroll list now updates an additional focus setter when it re-pins, and the scroll layout effect's early-return guard was rewritten against different refs.
Details
triggered on the same path that logs [FV-poll] follow re-pin moved focus:
the extra setter runs alongside the existing one rather than replacing it
the user-visible consequence could not be pinned down beyond it being a focus and scroll sync fix
Evidence
follow re-pin moved focus
In Development
17 items
Verify-skill suggestion suppressed unless the opal circuit is on #
The nudge suggesting the verify skill is now silenced unless a gate turns it on.
tengu_opal_circuit
Not switched on · Skills
What
The helper that returns the verify-skill nudge string now returns an empty string early for skill preloads and whenever the opal circuit check is false. The string itself already existed; the gating around it is new.
Details
Gated on tengu_opal_circuit.
Even when the circuit passes, the nudge is only produced if a skill matching the verify constant is present in the loaded skill set.
Skill preload paths always get "".
Evidence
if (e.options?.isSkillPreload) return "";
Remote Control re-mint retry loop and auth-specific failure messages #
Remote Control now retries reconnecting with backoff and tells you to run /login when auth fails.
/login/remote-control
You'll notice · Remote Control
What
Credential re-mint after a transport close now retries with exponential backoff instead of giving up, and authentication failures get their own terminal messages telling you exactly which command to run.
Details
Backoff uses a 30s base with a 300s cap, showing "Remote Control server unreachable — retrying (attempt N)".
A rejected credential forces one OAuth refresh before the next attempt.
Terminal failures direct the user to run /login and then /remote-control.
Evidence
Claude.ai login expired — run /login, then /remote-control
Untrusted-device credential rejection surfaces as a rejection #
An untrusted device now gets a clear rejection instead of a silent failure and endless retries.
You'll notice · Remote Control
What
Remote credential fetch retries once through a new device-attestation path, and an untrusted_device result without the relevant capability now returns a terminal rejection instead of a bare null.
Details
The rejection carries reason: "request_rejected" with status 403 and is marked terminal, so callers stop retrying.
OAuth-rejected results are now passed through to the caller rather than swallowed.
The enabled state of the surrounding path could not be resolved from the build.
Cloud and remote sessions try to start device registration #
Cloud and remote sessions try to start device registration, which does nothing while the gate is off.
allow_remote_sessions
Not switched on · Device Bridge
What
Attaching a cloud session and completing remote auth setup both now attempt to bring up the device bridge for that session.
Details
both paths dynamically import startDeviceRegistration and call it with the session id, an access-token getter and the org uuid
registration short-circuits on its own enable and egress checks, so nothing happens unless tengu_violin_wood and the allow_remote_sessions policy both permit it
Evidence
startDeviceRegistration
Auto-mode classifier now falls back when the primary model refuses #
When the auto-mode model refuses a request, a backup model now gets a try instead of failing.
Not switched on · Elsewhere
What
A safeguard refusal from the primary classifier model is treated like the model being unavailable, so the fallback model gets a chance instead of the request failing outright. The rollout state of this path could not be resolved in this build.
Details
refusedBySafeguard joins unavailability as a trigger for retrying on the fallback model within the remaining time budget
Usage and durations from both attempts are merged
If the fallback also fails to produce a usable answer, the original result is kept
The log line now distinguishes a refusal from an unavailability
Evidence
refused (stop_reason=refusal)
Workshop authoring-progress hooks fire during streaming #
Streaming now feeds tool and partial-input events to the workshop progress display.
CLAUDE_WORKSHOP_PROGRESS
Not switched on · Artifacts
What
Streaming now emits tool-use and partial-JSON callbacks into a workshop authoring-progress module, which is off unless internal workshop mode applies.
Details
onToolUseStart is called on a content_block_start for a tool use.
onInputJsonDelta is called on each partial JSON delta.
The module is only consulted when enabled, which requires an internal workshop-mode predicate.
Setting CLAUDE_WORKSHOP_PROGRESS to false switches it off.
Evidence
CLAUDE_WORKSHOP_PROGRESS
Public-reader artifact gate moved to a wrapper and now keys on the asset token alone #
Reading an artifact as a public non-member reader still refuses; the switch stays off in this build.
Not switched on · Artifacts
What
The refusal shown when an artifact is served to a public, non-member reader moved out of the boot-response parser into a wrapper around it, and now fires whenever no asset token comes back.
Details
The error text is unchanged: "artifact read failed: this artifact is served to you as a public (non-member) reader, and reading public artifacts that way is not enabled yet".
Public reading remains behind the remote-config gate tengu_cobalt_plinth_sedge, whose in-source fallback is false, so it is off in this build.
Evidence
tengu_cobalt_plinth_sedge
Artifact tool parses an optional cursor argument #
The artifact tool now accepts a cursor value, groundwork for fetching comments page by page.
Not switched on · Artifacts
What
The artifact tool's argument extractor now reads an optional string cursor in addition to thread_id and text.
Details
cursor is returned as part of the parsed argument shape
no schema entry or request consuming it was found in this build, so it appears to be plumbing ahead of paginated comment fetching
Evidence
"cursor" in e
Auto-mode default nudge requires the session to actually support auto #
The auto-mode suggestion would only appear in sessions where auto mode can actually be used.
Not switched on · Permissions
What
The nudge suggesting the user's non-auto default permission mode no longer fires in sessions where auto mode is not reachable.
Details
shouldShowAutoDefaultNudge now takes the tool permission context
additionally checks that auto mode can be cycled to, meaning context availability plus the auto-mode gate
the nudge itself sits behind tengu_maple_pier, fallback false
Evidence
shouldShowAutoDefaultNudge
Remote Control refuses to mint a fresh session when reattach is required #
Remote Control can now fail loudly instead of silently starting a new session when it can't reattach.
Not switched on · Remote Control
What
When init runs in reattach-or-fail mode, a reattach pointer that is gone (or absent entirely) now fails terminally with a user-facing message instead of quietly creating a new remote session.
Details
Covers both the gone-pointer case and the no-pointer-at-all case.
Emits distinct telemetry for each failure shape.
The enabled state of the reattach-or-fail mode itself could not be resolved from the build.
Evidence
[remote-bridge] Reattach-or-fail: no reattach pointer for this init; failing terminally instead of minting fresh
Leaked recovery-flag detection and worker-credential recovery cap #
Remote Control auth recovery stops looping forever when a credential keeps failing or a flag sticks.
You'll notice · Remote Control
What
Two guards were added to Remote Control auth recovery so a stuck flag or a repeatedly failing credential no longer loops forever.
Details
If the auth-recovery in-flight flag is still held past a computed ceiling, the close is handled directly and logged as leaked.
Close code 4094 gets its own attempt counter.
Recovery gives up after repeated failures with no successful heartbeat in between.
The device bridge reports its whole connection lifecycle, but none of it fires yet.
Not switched on · Device Bridge
What
The bridge reports its full connection lifecycle, gated the same way as the bridge itself, so none of it fires in this build unless tengu_violin_wood is on.
Details
events: tengu_device_bridge_started, _stopped, _skipped (with reasons missing_org and missing_account), _start_failed, _connected, _rejected, _closed, _connect_failed, _pong_timeout, _reconnect_exhausted and _heartbeat_unsupported
the connected event records the protocol version plus negotiated hb_interval_ms, staleness_ms and rpc_timeout_ms
repeated transport failures are de-duplicated before being reported
Evidence
tengu_device_bridge_heartbeat_unsupported
Keybindings can be pre-warmed from the storage backend #
Your custom keybindings can load in the background at startup so they're ready sooner.
tengu_keybinding_customization_release
You'll notice · Terminal UI
What
Startup, after prefetch, can run an async warm read that populates keybindings from the storage backend ahead of first use, tracked by a warmedFromBackend flag.
Details
the warm read suppresses feature events; the next synchronous read re-validates and discards the warmed value if customization is disabled, so a backend-loaded set does not survive the feature being turned off
warming is skipped in non-interactive mode and when the feature is off
a backend read failure during warming logs the telemetry reason warm_backend_read_failed_fell_back
hot-reload and delete paths clear the flag
gated on tengu_keybinding_customization_release, which falls back to true
Dream prompt rebound, shipping-policy text absent #
Instructions telling background sessions to open a draft PR unprompted are not present in this build.
Under the hood · Sessions
What
The memory-consolidation prompt keeps its exact wording but is now bound to different constants, and the background-session shipping instructions that told the model to open a draft PR are not present in this build's module graph.
Details
The removed text instructed the model to open a draft PR via gh pr create --draft without asking.
Whether that text was relocated elsewhere rather than deleted could not be established, so treat its status as unresolved.
Evidence
open a draft PR via gh pr create --draft without asking
The auto-mode suggestion dialog now just dismisses itself instead of switching your session to auto.
Not switched on · Elsewhere
What
The auto-default-nudge dialog now renders a different component whose only action on completion is to dismiss itself, so the state it used to change is no longer applied here.
Details
The previous onDone callback that switched the session to "auto" and merged a new toolPermissionContext is gone.
The new component clears itself on done and nothing else.
Whether and when this dialog is reachable in this build could not be resolved.
Evidence
auto-default-nudge
Transcript directory scans ask the storage backend to skip scope stats #
Listing your past transcripts now skips some extra bookkeeping, which may make it a bit lighter.
Under the hood · Sessions
What
Both transcript scope-listing loops now pass a flag to listEntries requesting that scope statistics be skipped.
Details
the calls pass { skipScopeStats: !0, ... } instead of only a cursor
the flag appears at exactly these two call sites in the bundle, so the consumer is not visible and what the backend does with it could not be confirmed
Evidence
skipScopeStats
Daemon auth cooldown and status can go through the v5 storage layer #
Background daemon login cooldown and status can live in a storage service instead of local files.
Under the hood · Elsewhere
What
The headless daemon's browser-login cooldown and auth-status records can now be read and written through the v5 storage client instead of files in the config dir.
Details
both records gained state keys for the v5 layer
active only when the supervisor is constructed with a storageV5 client
dedicated failure reporting on that path, for example daemon-auth v5 cooldown read failed
falls back to the daemon-auth-cooldown and daemon-auth-status.json files under the config dir when no client is passed
Evidence
daemon-auth-cooldown
Internal Changes
33 items
Classifier refusals are tagged separately in blocking telemetry #
Safety-classifier refusals are now logged distinctly from ordinary parse failures.
Under the hood · Telemetry
What
The two-stage prompt-injection and safeguard classifier now records when the model stopped with a refusal, which previously looked identical to an ordinary parse failure.
Details
The parse-failure event now carries stopReason plus failureKind: "policy_refusal".
The block result itself carries refusedBySafeguard.
Evidence
refusedBySafeguard
Daemon auth cooldown and status write through the v5 backend #
Daemon login cooldown and status writes go through the storage backend and now surface errors instead of hiding them.
Under the hood · Elsewhere
What
The browser-login cooldown timestamp and the auth_required status file go through the storage backend when one is available instead of direct file IO.
Details
writes use a umask-derived file mode
backend failures raise explicit errors carrying telemetry codes rather than being swallowed
Evidence
daemon-auth v5 cooldown write failed
Org-memory discovery can be re-decided after an account change #
Organization memory can be rediscovered after you switch accounts, and it refuses in untrusted folders.
Under the hood · Elsewhere
What
New serialized entry points re-run org-memory discovery and report how the decision moved.
Details
refuses to run in an untrusted workspace or when the account boundary changed, returning a refusal with a reason
on success it clears the discovery cache, re-runs discovery and reports both the previous and current state
Docker detection and git identity lookups no longer spawn subprocesses, so startup is slightly lighter.
You'll notice · Environment Detection
What
Docker detection and git identity lookups no longer spawn subprocesses, doing the same work in-process.
Details
The Docker check no longer runs test -f /.dockerenv as a subprocess.
Git identity no longer shells out to git config --get user.email or user.name.
Both results stay memoised as before.
Evidence
/.dockerenv
SDK spawn errors carry a telemetry class and redacted paths #
Launch failure reports now scrub secrets out of file paths before sending them.
You'll notice · SDK
What
Executable launch failures and subprocess init timeouts now report a structured error class, and any path in the message is scrubbed for secrets before it is interpolated.
Details
The helper producing "executable not found / failed to launch" strings now returns an object with telemetryMessage and errorClass, one of "executable_launch_failed" or "executable_not_found".
Paths go through a new redaction helper applying the shared token patterns: Bearer, sk-ant-, AKIA, gh*_, JWT and others.
Subprocess init timeouts become a TelemetrySafeError with errorClass "initialize_timeout"; the user-visible message is unchanged.
Evidence
errorClass: "initialize_timeout"
Telemetry event schema drops repl_code and git_remote_url, adds MCP and auth fields #
Usage reporting stops sending your repo remote URL and adds MCP server and tool names.
You'll notice · Telemetry
What
The protobuf-shaped event metadata changes which fields it carries, removing two code and repo identifiers and adding MCP identity plus reported account details.
Details
Removed: repl_code and git_remote_url.
Added: mcp_server_name, mcp_tool_name, an optional event_metadata_vars string, and a client_reported_auth submessage carrying account_uuid and organization_uuid.
The experiment-exposure message separately gains variation_key, and user_attributes / experiment_metadata become optional, serializing as undefined rather than an empty string.
Evidence
client_reported_auth
Experiment-exposure telemetry gains variation_key and stops padding empty strings #
Experiment tracking records which variation you got and omits blank optional fields.
Under the hood · Telemetry
What
Experiment events now record which variation was seen, and absent optional fields are omitted rather than sent as empty strings.
Details
Adds a variation_key field to the experiment event schema.
user_attributes and experiment_metadata default to undefined instead of "".
The same treatment applies to the generic event's process and additional_metadata fields.
Evidence
variation_key
Account identity readable without a full config load #
Your account details can now be read straight from the config file when credentials refresh mid-session.
Under the hood · Auth
What
A new async helper pulls the account identifiers directly off the config file instead of going through the synchronous cached config.
Details
Reads oauthAccount for accountUuid and organizationUuid.
Returns undefined on any parse or type failure rather than throwing.
Used by the OAuth refresh path and by the bridge hook when refreshing credentials mid-session.
Evidence
"oauthAccount" in t
Tool renderers can now supply a progress renderer #
Artifact tools can now show live progress lines in the transcript while they work.
Under the hood · Artifacts
What
The tool UI override map for the artifact tool passes renderToolUseProgressMessage alongside renderToolResultMessage.
Details
This is the path by which the publish-retry progress line reaches the transcript.
Evidence
renderToolUseProgressMessage
Bundle module-initialization graph reshuffled around the coordinator user-context module #
Startup module loading was reshuffled internally, with nothing visible changing for you.
Under the hood · Elsewhere
What
A large module-init block was emptied down to a single import of getCoordinatorUserContext, and a new init block elsewhere now pulls in the roughly 45 modules, plus require("crypto"), that used to live there.
Details
Bundling and lazy-init layout only; no behaviour visible to a user changes.
Tool descriptions are cached separately depending on which tools are loaded, avoiding stale reuse.
Under the hood · Elsewhere
What
Prepared tool descriptions are cached under a key that now varies on whether a particular tool is in the tool set.
Details
an "S:" prefix segment is added when that tool is present, empty otherwise
sessions with and without it no longer share cached descriptions
Evidence
? "S:" : ""
Session env files can go through the storage abstraction #
Session environment files can be read and written through a storage layer instead of directly on disk.
CLAUDE_ENV_FILE
Under the hood · Elsewhere
What
The hook-generated session environment loader and the cwd-env clearing routine can now read and write through a storage handle instead of the filesystem directly.
Details
Both take an optional storage handle; when supplied, entries under session-env in userConfigDir are listed, read and written through that layer, paged with a cursor
When no handle is passed, the existing direct fs/promises path is used