Repository navigation
fix(native-chat): a new chat starts at rest; its first message starts the agent - #24450
brennanb2025 wants to merge 87 commits into
Conversation
…s the agent agentSession.create writes the record at rest (shared with /clear's founding) and opens its journal; no agent starts. The attach is now only the work-time start, which carries the create's timing span. agentSession.ensure, which no client ever called, is removed. The orchestration worker's preamble starts its agent and reports a start that is still being retried.
…le a create whose import failed
…start agents the way production does The reservation no longer founds or takes over a record: a start names the fence of the record it read, a missing record refuses, and adoption and tab checks live with the at-rest create. attachForTests now runs host.create at rest, then the delivery loop's start from the stored record. attachExistingForTests keeps the attach's own guards (stale fence, replayed operation, a lease that still admits a writer) testable. Store-level suites found records through the create and reserve at its fence, so fences sit one higher, as in production. New direct tests cover the create's refusals: sessionExists, recordUnreadable, invalid args and options, conversationHeldElsewhere, and the tab it reserves.
…n and whose start is proven gone An older host started the agent at create and left the record behind when that start failed: released, its exit proven, no conversation bound. The renderer's relaunch of that chat sends the same session id under a new operation, which the at-rest create refused as sessionExists on every message. The create now founds such a record again at rest, at its own fence, keeping its name, so the chat starts on its next message after an upgrade.
…d its history imported The create used to write its operation row as succeeded in the same transaction as the record, before it opened the journal and imported an adopted conversation. A create cut short between the two (the journal open or import throwing, a crash, a quit) replayed as an empty chat whose first message resumed a conversation the user could not see. The row now stays pending until the journal is open and the history imported; a replay of a pending row does that work again (the import is a no-op on a journal that already holds rows) and then settles it. The failure path no longer rewrites the row to failed, so a transient failure heals on the client's retry.
…onversations The at-rest create ran under the session's serialize but was not tracked with the host's attaches, so a quit could close every open conversation and then let an in-flight create open one behind the teardown. The create is now tracked from enqueue, as the agent start is.
…t agent start only Moving the start to the first message put the span around every agent start: idle-eviction resumes, start retries, restart continuations and starts an operation asks for. It now wraps only a start of a record no agent has run in Orca yet (its chain empty, or holding only the conversation it adopted), which is the start a create used to run. A failed first start retried is still a first start.
…ried start for its 60 s budget The preamble's wait for the worker's agent ended at the first failed start that is retried on its own (an account switch, a lease being settled), so the coordinator read outcome_unknown at once, and a start the 15 s retry cleared never reached ready. The wait now runs through such retries: ready if the agent takes the preamble within the budget, outcome_unknown quoting the start failure if a start is still being retried when the budget runs out, and failed at the preamble when the start is refused for the person to fix. Host-backed tests cover the three outcomes and the reason the dispatch is given.
…ost hands it to the agent A new chat now starts its agent on its first message, and the host answers the send when it accepts the message, before that start. A launch prompt counted as delivered on that answer, so "Resolve comments with AI" posted its GitHub fixing replies and resolved threads, the checks and fix actions toasted "Started an AI agent", and review notes and browser annotations were cleared, all for an agent whose start then failed (signed out, not installed, exiting as it started). The launch prompt now reads the session's own publication until the host hands the message over (delivered) or rejects it (not delivered: the claimed comments, notes and annotations are handed back for a retry). A start the host is still retrying keeps the wait open; the retry schedule ends in a verdict, and a 45-minute cap sits past the longest one. No new wire field: a host that predates the hand-over record answers a send only once it is handed over, and that answer counts.
…ay, resumes what it adopted, and recovers an older host's failed start Covers the plan items the first round found missing: a plain create replayed under its own operation id starts nothing and its first send starts the agent; agentSession.ensure answers method_not_found; an adopting create's first message resumes the adopted Codex thread rather than starting one; and a chat whose create-time start an older host lost is created again under a new operation and starts on its next message.
…est what they say The Codex reconnect pins the fence at each step again (the first start moves the create's fence 1 to 2; the start after a close moves it one past the released fence) and checks the journal survived the dead child. The cross-version stale-fence send is checked against the fence the restarted record held before the send, not a value read after it. The AI Vault ownership setup starts the chat's agent after its create, since a chat at rest owns no thread yet (this failed in the suite once it could run). The tab-table test for a create that stopped before publishing its tab goes through host.create, and the failed-start one now sends the first message and checks that its rejection leaves the tab in place.
… gone Rewords the comments the first round found false: the structured worker is ready with its preamble, not when an attach returns; the create's pre-commit half ends at the host's create; the gate lists no ensure; the cross-version fixture's supportsCreate stub is explained by the create. Test helpers that started an agent through a client's ensure now start it as the delivery loop does.
…s with its preamble
…nswers the chat it made A create whose answer was lost before its tab published leaves the renderer holding the launch as unknown; reopened more than a day later, its retry under the same operation id found the row pruned and was refused as operation_expired, and the relaunch under a new id then met sessionExists, so the chat could never start. A create starts nothing, so an aged-out row proves nothing more: when the record already there is this create's (same identity, same adopted conversation or none), the create answers it at rest under a fresh row it settles as its own. A pending row an older host left when it died mid-create is re-run and settled the same way. The crash-mid-start cases now replay through host.create.
…tate before it counts as delivered The wait now follows the first message to its final state rather than to its hand-over, and has no timer of its own: accepted (the agent took it) counts as delivered and runs the caller's outward side effects; rejected (a start that failed for good, or a chat closed mid-wait) and unknown do not. A start the host retries keeps the message queued, so the wait spans the retry schedule. Each launch prompt has one read, shared by whoever asks and tied to the launch call; a renderer reload ends it with nothing written. Tests drive a failing first start through the checks-panel acknowledgement, the notes send and the browser annotation send: a retried start the agent then takes writes exactly once and only after acceptance, a message rejected after its tries writes nothing, and a chat closed mid-wait writes nothing and keeps the notes.
Every new chat now sits at rest until its first message, and the at-rest options read named the
model catalog's default ("Opus", with its Effort) as the chat's model. Claude's settings or env
may run another, so the picker flipped to the real model once the agent started. The live
child and the client's own catalog upgrade already refuse to name Claude's default. At rest the
host now names the catalog default only for Codex; a Claude chat with no saved model answers an
unnamed model, and the client shows an unnamed model as unknown (the neutral "Model" pill, no
effort) instead of adding an empty row and naming the listing's default.
…take the launch prompt The fix-checks actions (PR page, PR dialog, check-run details, checks panel), the fix-checks dialog on the PR page, and the commit/push recovery launch said "Started an AI agent" as soon as the chat tab existed. With the agent starting on the chat's first message, that toast could appear for an agent whose start then failed. They now wait for the launch prompt's delivery (the chat's first message reaching its final state) and stay in their starting state until then; a start that fails says nothing extra here, since the failure is shown on the message.
…'s rejection to a reader Pins what ends a launch prompt's read when its chat closes mid-wait: the host withdraws the queued first message, keeping the start failure it waited out, and publishes that rejection to a subscriber. The renderer's closed-chat scenario now uses that same rejection.
…d waits for reconciliation Founding again a record an older host's failed start left replaced its lease with a fresh one that copied only the fence. That lost the floor a recovered copy of the records sets (so a fence a lost write may hold is never granted again) and skipped the reconcile gate. The create now founds over the existing lease: the fence moves on through the one mint, past any floor, and a lease this host has not yet adjudicated since load is refused as execution_owner_reconciling.
… as the worker start's timeout allows The preamble waited a fixed 60 s for the worker's agent whatever the start's own --timeout-ms, so with a short timeout or a setup wait the CLI's transport deadline (the timeout plus a fixed grace) could fire before the outcome_unknown receipt arrived. The wait is now the readiness wait capped by what the start's timeout leaves, plus the allowance a terminal worker's turn observation gets, which keeps the answer inside the CLI's grace.
…d sends at the create's fence It started the agent before sending and sent at the post-start fence, which is not what production does: the create starts nothing and the launch prompt, sent at the create's fence 1, is what starts the agent. It now creates through host.create and sends at that fence, and its header and title say the first send's start publishes the session before the delivery loop dispatches. Also corrects the settlement wait's doc for throughStartRetries.
…ad of fence args attachForTests ignores attachForTests reads a non-null fence only as "the record exists" and starts the agent at the record's own fence, so the literal fences these tests passed (2, 13, 14, a fence read beforehand) were misleading. They now call startAgentForTests with the session id.
…ic model seed A Codex chat at rest before any listing exists (its account's first chat, a WSL record, a failed catalog read) is answered with no models and no model. After the Claude naming fix the client turned that into an empty catalog, so the composer had no model picker until the first start. An empty listing is an unknown one, as the host's catalog contract says: the client now keeps its static seed's models and naming.
…swer; tests open on the host's snapshot An unknown dispatch is not final on this host: a Codex turn/start that timed out after writing the frame keeps its echo armed, and a late echo settles the message as accepted. The launch prompt's read now ends only on accepted, rejected, or the stream ending, so the caller no longer says the agent could not start (and hands the comments back) for a prompt the agent then runs. The test driver now opens with a snapshot, as the host's subscribe does, with cases where that snapshot already carries accepted or rejected, and the closed-chat case uses the rejection the host writes.
…ordinator test's RPC caller in their own modules Both files went over their line limits once this PR added the at-rest create and the attach helper. The store's death-evidence and first-record listeners, and the draft watch that feeds them, move to AgentSessionRecordStoreCommitListeners; the coordinator-mail suite's numbered RPC request and call move to a test fixture. No behavior change.
…wn lane The receipt append now queues in the host's per-session serialize, so it lands after whatever the lane is running and at the fence the chat holds then, never under a start or close that moves it. The run that leads to it was started off a publish, so no task in the lane waits on it.
…plies this account posted Before posting again, the host now matches the PR's comments on thread, text, time since the agent took the message, and author: the login the repo's GitHub writes run as (its bound gh account, else the signed-in gh user), looked up once per repo for the host's life. Where that login can't be told (SSH, WSL, nobody signed in, a failed lookup) it matches on the rest alone and logs that.
… that advertises running it With #24340's route, a structured chat now starts on whichever host owns the workspace, a paired server included, and an older server's strict send params would refuse the whole launch prompt. The host now advertises agent-session.review-reply.v1, and the checks panel plans the reply only for a chat whose host does (this client's own always does). A chat on an older paired server gets the prompt without it, and the panel writes the replies itself once the host has the message, as it did before this PR.
…t first, through a read that throws
The restart dedupe read the panel's PR comments: one oldest-first page per source, every failure
swallowed as an empty list. A busy PR hid Orca's own replies, and a failed read posted again with
nothing logged. It now reads exactly the reply threads (`node(id:)`, `comments(last: 100)`), the
conversation comments since a minute before the agent took the message (host and GitHub clocks can
disagree), and `viewer { login }` in the same GraphQL call, which replaces the separate account
lookup. A failed read is logged and the replies are posted (at worst once more); an unnamed
account matches by text alone.
…st advertises running it
…desktop's views of it to refetch The host writes through the runtime's repo methods, which never sent the `gh:workItemMutated` notice the desktop's own writes do, so the checks panel kept showing the threads open with no "Fixing" reply and invited a second launch. After a run with any successful GitHub write, main now sends that notice for the repo and PR through a new runtime notifier hook. Only this machine's windows hear it: a paired client's panel refetches on its own triggers (limit).
…aunch prompt owns its review reply If a structured launch's first send was refused or lost, or its create failed but kept the chat's outbox for the relaunch, the panel handed the comments back, said "Could not start the selected agent." and left Start enabled, while the chat still sent the message (or offered its Retry) with the review reply on it. A second Start then posted a second set of public replies. The launch's delivery result now says when the chat still holds the prompt, and the panel then treats the comments as handed off: selection cleared, dialog closed, no toast; the chat's Retry owns it. With nothing held, the comments still go back to the panel as before.
…ed chat can't reply on the PR Without the PR's repository the chat's host can resolve the threads but not post the replies, and the structured route dropped them silently where the panel used to say so. It now shows the panel's existing "Could not resolve the GitHub PR to reply on." once, as the launch hands off. The other two cases need no new line: with no resolve target, or a reply too large to carry, nothing rides the message and the panel writes and reports as before.
…ot that one started A structured chat now starts at rest and its first message starts the agent, so "Started an AI agent for ..." fired before any agent had started, and that start can still fail. The fix-checks, conflicts, recovery and check-details toasts now say "Sent to an AI agent for ...", in all six catalogs. The checks panel's comments now say who writes the review reply on each route.
…ys the agent started On a structured chat the launch opens the chat at rest, so "The agent started, but ..." was false. The messages now say only what is known: "The prompt wasn't sent to the agent.", "The notes weren't sent to the agent.", and "Couldn't confirm the prompt/notes were sent to the agent."
…ich for a chat is when it records the message
…helper in their own module The suite went over its line limit with the review-reply case; the two helpers move unchanged.
… the kept prompt without its review reply R5R1 refinement: a create that failed still hands the comments back to the panel, as before, so the prompt the chat keeps for a relaunch drops its review reply; only one owner can write it. A prompt the host refused or never answered after the chat existed is still held by the chat, and three existing tests now expect the delivery result to say so.
…red chat's message took it The panel decided the reply rode the message whenever the launch took the structured route. A paired host that declines the chat opens the same launch as a terminal, whose paste carries no reply, so nobody resolved the threads or posted. The launch prompt's delivery result now says whether the chat's message carries the reply, and the panel writes after delivery otherwise.
…ly after a restart; only a failed read is logged
…s host has written its review reply On a paired server the host's mutation notice never reaches this client, so the checks panel kept showing the threads open with no "Fixing" reply. When a launch hands its review reply to a chat, the panel now watches that chat's live stream for the message's receipt (a failure line or a tombstone) and refetches once. The watch is one-shot and dies with the panel, when the chat closes, or after the 24 h window in which the host still owes the reply. No new notice or toast.
…open, and refreshes only the review still shown Three round-7 fixes to the refetch: - The receipt notice moves from the read transport to the chat's read owner, its layer; the owner's older-page reader moves to its own module to make room. - A chat's read runs only while its pane shows or its outbox owes a delivery, and on a paired host the entry retires before the host writes, so a hidden chat heard nothing. An armed watch now holds the owner's read open, as owed work does, and lets go when it fires, with the panel, when the chat closes, or after the window. - The deferred refetch used the closure of the launch's render, possibly hours later, with no check that the panel still shows that review; a stale fetch could leave "Comments are still loading." stuck. It now calls the panel's latest refresh, and only while the panel shows the launch's review (head changes allowed). The watch lives in its own hook.
…e launch used, and lets go whenever the chat goes Round-8 fixes: - The read transport still noticed receipts after the move to the read owner, so each batch was checked twice and the wire module imported the panel's bus. The transport no longer does. - The hold's host was worked out again after delivery from what the UI showed then, which could name this machine for a paired chat and retry a refused read for 24 h. The launch now hands out the host it used (this client's own, or the paired server it asked), and the hold reads there. - The hold was let go on a user's tab close only. It now also goes on a close with no host to name, on a worktree purge, and when the host refuses the read because it holds no such chat.
…he agent's turn folds The host wrote the failure into the agent's live turn with no tone, so once the turn settled it folded under "Worked for Ns" and the person never learned the resolves and replies didn't happen. It is now written as Orca's other notes about the conversation are: outside any turn, with the error tone. The success tombstone still shows nothing.
…e agent's turn runs, and still lands outside it
…h notice's new wording The phone's notice changed to "The notes weren't sent to the agent."; the golden still held the old "The agent started, but ..." toast. Re-recorded by the corpus script; no other golden moved.
"Could not resolve the GitHub PR to reply on." read as an internal failure. It now reads "Couldn't find the GitHub PR to reply on. Reply to those comments yourself.", in every catalog.
Review status (rebuild): #24450, a new chat starts at rest; its first message starts the agentThe problem. Opening a new chat started the agent before you typed anything. If that start failed (signed out, Claude not installed, an account switch in progress, a CLI that exits), the chat showed a chat-level "Chat could not be started…" line with no message attached, no sidebar row and no automatic retry. The phone and orchestration each showed the same failure in their own shape. What changes for you. A new chat opens with no agent running, and your first message starts it. If it can't start, the reason shows on that message, with Orca's automatic retry or Retry. Launches with a prompt (fix checks, conflicts, recovery, review notes, annotations) count as sent once Orca records the message: notes clear and the toast says "Sent to an AI agent…". "Resolve comments with AI" now has the host post its GitHub/GitLab "Fixing" replies and resolve the threads only once the agent actually takes the message. The checks panel refreshes when that's done, and a failed write is said once in the chat with what to do. The first reply comes about 0.3 s later (M4 Air: 1020 → 1313 ms warm median). How the review went. The first version made each source (checks panel, notes, phone) wait for the agent to take its prompt before running its side effects. Review kept finding ways the message could move without the source knowing (Retry, Stop, reload, a re-send by the chat), so the PR was rebuilt:
Fixed during review (each with a test that fails without the fix):
Deferred, listed in the description:
Verified:
Not verified:
Next: this PR is stacked on #24340. When #24340 merges, main is merged in, the PR is retargeted to main, and it is marked ready. |
ELI5
When you opened a new chat, Orca started the agent (Claude or Codex) right away, before you typed anything. If that start failed, the new chat showed a chat-level "could not be started" error that had nothing to do with any message, and Orca never retried it.
Now a new chat opens with no agent running, the same way
/clearalready does. Your first message starts the agent. If it can't start, the reason shows on that message, with Orca's automatic retry or a Retry button.What Changed
The problem.
agentSession.createstarted the provider child before it answered, so a failed start was the create's failure. It looked different everywhere:agent.launchthrew a refused-launch error;failedat its create stage.None of these put the failure on a message.
/clear(fix(native-chat): /clear starts nothing; the new chat's first message starts its agent #23935) already created its new chat at rest, so Orca had two ways of making a chat.Before / After (what you see)
agent.launchwith a promptworker-startfailedat createfailedat the preamble stage; a start still being retried when the start's own budget (at most 60 s) runs out readsoutcome_unknown, with the start failure in the reasonMechanism
host.create(structured-agent-session-create-at-rest.ts) writes the record and its operation row in one transaction (store.createAtRest,agent-session-at-rest-create.ts), then opens the journal at rest and imports an adopted transcript. The record is founded byfoundAgentSessionRecord, the same founding/clearuses. Create refuses only admission failures: an unsupported host, a taken tab id, a refused resume source, a replayed failed operation.startAgentForTests).agentSession.ensureis deleted: the method, its phone allowlist entry, its generated params-catalog row and its cross-version manifest entry. No client in the history of the desktop renderer, phone app, CLI or preload ever called it.reviewReplyon the launch prompt's send and is stored on the message's own journal row, never sent back to clients. A host-side observer posts it once that message isaccepted(the agent took it), within 24 h of acceptance, and writes a receipt for the message in the chat's own lane. Nothing is stored per launch: what's owed is derived from the message, its state and the absence of a receipt, on every journal publish and again when a journal opens after a restart. Before posting after a restart, the host reads the PR (each thread's newest comments plus the posting account) and skips replies that account already posted after acceptance; if that read fails it posts anyway and logs a possible duplicate. Retry re-arms the same message, so the reply follows it; a message never accepted (rejected, stopped, closed) owes nothing.sendStructuredWorkerPreamblereturns{ state, startRetry? }and waits across retried starts for at most 60 s, capped by what the worker start's own timeout leaves, so a short--timeout-msgets theoutcome_unknownreceipt instead of a transport timeout. This is the worker's readiness check, the same one terminal workers get. fix(native-chat): a failed start is shown on its message and retried, and later messages go ahead #24340's background watch still fails the start and tells the run if the held preamble is later rejected for good.agentSession.createtiming span keeps its meaning: it wraps only a chat's first agent start (including/clear's new chat and automatic retries of a failed first start), not later starts after idle, a crash or an operation.Why
The common pattern creates a chat as a cheap record and starts the agent on the first send; a failed start lands on the message, never on the create; a source counts its prompt as delivered once the message is recorded. Orca already had the pieces:
/clear's at-rest chat and the delivery loop's start-on-send. Using them for every chat removes the second create model and the three different failure shapes.Orca also posts a "Fixing" reply on the review threads it hands to an agent. Running that from the host, derived from the message's own state, lets it wait for the agent to really take the message without the source having to wait, and survives reloads and restarts.
Alternatives considered:
Differences from the common pattern
outcome_unknown.agent-session.review-reply.v1gets noreviewReply. For such a chat the checks panel posts the replies itself once the message is recorded, as before this PR. Follow-up: drop the fallback once no supported host lacks the capability.relaunchFailedStructuredAgentSessionForMessage). Only an older host can do that now; the same path also covers creates that fail for other reasons. Follow-up: drop the start-failure case once no supported host starts at create.Mixed versions
startRetry).reviewReplyon the send. Sent only to a host that advertisesagent-session.review-reply.v1. This app's own host always does. A structured chat can now live on a paired server (fix(native-chat): the desktop declares structured chat support to paired Orca servers #24204 and fix(native-chat): a failed start is shown on its message and retried, and later messages go ahead #24340 create the chat on whichever host owns the workspace), so a paired server that lacks the capability gets the field omitted and the panel falls back as above. A paired server that declines the chat opens a terminal, and the panel posts after the paste, as before.agent.launchwith a prompt answersjournaledonce the message is recorded; no wire change.agentSession.ensureremoved. No released client sends it.reviewReplyis always recorded as a message, never held as a draft.Cost: first-message latency
Starting on the first message moves the agent's start in front of that message instead of overlapping it with typing.
Measured end to end in Orca on an M4 Air: open a new Claude chat, wait 3 s, send; real Claude through a rig wrapper forced to the smallest model, timed in the page (MutationObserver + animation frame on
performance.now()), interleaved batches with fresh profiles, the machine shared with other work (load average 18–22).So the first reply comes about 0.3 s later and varies more. The chat shows its working state 10–40 ms after Enter on both. Mitigation: none planned; nothing in the common pattern starts a chat's agent ahead of time.
Linked Issue
No issue. Follow-up to #24340, which carries the per-message start failure and retry this builds on.
Visual Proof
Recorded on an M4 Air with hidden windows and CDP screenshots, driven by an Electron QA agent; GitHub was a fake
ghthat logs every call, so no real PR was touched.Testing
I manually tested these changes locally
Automated tests added/updated, or explained why not below
Manual (M4 Air, hidden Electron rig with an isolated home, real Claude, a fake
ghthat logs every call so no real PR is touched): a new chat at rest; the first message starting the agent; an account switch on the first message (on-message wait, Stop, sidebar Failed, automatic retry at +15 s); Claude not installed (reason and Retry); "Resolve comments with AI" into a new chat (no writes while the start is held; exactly one set of replies and resolves about 1 s after the agent took the message; the panel refreshed in place; no toast or success line); a failed start then Retry (zero writes, then one set); every write failing (one visible failure line, also after two restarts); a quit after the agent took the message (writes already posted: no duplicates on relaunch; writes never posted: exactly one set); a lost first send while the chat tab exists (the chat re-sent, one set); notes to new and existing chats; Stop on a waiting launch prompt; latency. Not covered: browser annotations (need a real element pick; they share the notes path) and a worktree created with an agent prompt (no CLI in the rig).New and changed tests:
agentSession.ensureanswers method-not-found; an adopted create's first message resumes the adopted conversation.reviewReplyis accepted exactly where the host advertises the capability.Every new test was checked by reverting its fix and watching it fail.
CI unit, cross-version wire, static analysis, mobile and packaging. Locally:
tc:node,tc:web,tc:cliand mobiletsc, full oxlint on every touched file, the changed-code quality gate, localization checks, and named runs of every affected unit file.Not run: the real Claude CLI test files (
claude-structured-real-cli*); the real-Claude QA above covers the start path.AI Disclosure
Review
Reviewed over nine rounds by independent reviewers (host create path, clients and orchestration, tests, architecture, launch-prompt ownership, the host-run reply, the renderer revert), two readiness checklists and two rounds of M4 Air QA. The first design made each source wait for the agent to take its prompt; review showed that shape kept producing edge cases, and it was replaced by the host-run reply described above. Fixed along the way include: GitHub replies, "Started" toasts and cleared notes for an agent that never started; an empty chat after an interrupted import; a chat an older host failed to start that could never start; an aged-out create; quit not draining a create; the timing span firing on every start; orchestration reporting
outcome_unknowntoo early and outliving the CLI's timeout; the picker naming the wrong model; a second launch posting a second set of replies; the panel staying stale after the host's reply; and a failure line folded out of sight.Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
Stacked on #24340 (
brennanb2025/chat-start-failure-on-message): a failed first-send start needs its per-message failure and retry. This retargets to main once #24340 merges.The change happens on the host that runs the agent, so it is the same on macOS, Linux, Windows and SSH hosts.
Checklist
ensure, the host-run review reply, and the tests that assumed a create starts the agent)N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)Author: @BrennanKB5