Skip to content

fix(tui): defer agent reload until the reactor's message run ends - #1486

Merged
corbits-triage[bot] merged 3 commits into
mainfrom
cl-10766-dont-reload-the-agent-while-an-approval-resumed-run-is-still
Oct 17, 2026
Merged

corbits-triage[bot] merged 3 commits into
mainfrom
cl-10766-dont-reload-the-agent-while-an-approval-resumed-run-is-still

Conversation

@bwachman

@bwachman bwachman commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • A pending agent reload, such as the one queued when MCP servers finish connecting, now waits for the reactor's current message run to end, not only for the send or approval hand-over to settle. An approval-resumed tool call and the inference after it are no longer torn down mid-run.
  • Every agent build logs its reason (initial, reload, interrupt, session-rotation), so a rebuild is visible in the log.
  • Newly connected MCP tools take effect after the current run ends rather than partway through it.
  • Approvals resumed by a reactor rebuilt after a restart do not open a message run yet, so this deferral does not cover them; CL-10777 tracks that.

Verification

  • bun run check and bun run test pass (9198 tests) on top of the run.json snapshot changes in main
  • The new runner test fails on main (the agent rebuilds during the approval-resumed run) and passes on this branch
  • Existing reload, interrupt, /clear and /new runner tests pass unchanged

Fixes CL-10766
Part of CL-10763

@linear-code

linear-code Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

CL-10763

CL-10766

An approval resume resolves once the decision is handed to the reactor,
while the resumed tool and the next inference still run inside the
parked message run. Gating reload on send occupancy alone let a pending
reload close and rebuild the agent mid-run, killing the approved call
and rehydrating its pending operation on the new reactor. Each agent
build now also logs its reason so a rebuild is visible in the log.

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review · Request changes

A pending reload now waits out the same open message run, including an approval resume. A synchronous handoff to the next run still rebuilds before that run is tracked.

Findings

  • src/tui/runner/exit.ts:484 — message.run.ended drops the run and calls reloadIfIdle before the following message.run.started is applied. The reactor emits that pair with no await when it dequeues an uncorrelated message.received (vendor/intx-inference/src/reactor.ts:1635). The stream buffers the start (vendor/intx-agent/src/stream.ts:69). enqueue schedules the rebuild as an earlier microtask than that start (src/tui/delivery-queue.ts:437), so both busy checks see an empty openMessageRuns and closeAgentForRebuild runs. Compaction continuation, a queued steer, or any other uncorrelated deliver during a deferred MCP reload tears the agent down at the start of the next run. If the superseded run had committed a pending approval, the new reactor can re-arm it.

Notes

  • The same-run case holds. closeMessageRun is not called on suspend or between the resumed tool and the following inference. A regression that feeds message.run.ended and the next message.run.started in one burst, and asserts no rebuild until the successor run ends, would lock this.

@bwachman
bwachman force-pushed the cl-10766-dont-reload-the-agent-while-an-approval-resumed-run-is-still branch from 463c4de to d65bded Compare October 17, 2026 03:41
When the reactor dequeues an uncorrelated message while a run is still
open, it emits message.run.ended and the next message.run.started with
no await between them. The run-end path queued the rebuild op as a
microtask ahead of the stream consumer handing the buffered start to
the sink, so both busy checks saw no open run and the agent was torn
down at the start of the successor run.

The rebuild op now waits one macrotask before its busy re-check. The
reactor moves from closing one run to opening the next without leaving
the microtask queue, and the stream delivers buffered events through
resolved promises, so by the next macrotask any such start has been
applied and the reload re-defers to that run's end.

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review · Request changes

A pending agent reload waits until the reactor's message run ends, and a one-macrotask re-check keeps a buffered successor run from being torn down at the hand-off.

Findings

  • src/tui/runner/exit.ts:477 — buildLiveAgent clears openMessageRuns so a run abandoned by interrupt or rotation cannot keep the session busy, but it leaves pendingReload set and does not call reloadIfIdle. Interrupt (exit.ts:679) and session rotation (exit.ts:785) both rebuild through that helper. A reload that already deferred because the run was open (reloadIfIdle returns at exit.ts:502 while agentBusy() is true, and only clears the flag at exit.ts:503 once it proceeds) stays armed. message.run.ended (exit.ts:464-466) is the path that would have consumed it, and reactor shutdown does not emit that event for the abandoned run. The next message.run.ended or inFlight drop therefore sees an idle agent and builds again, closing the agent interrupt or /clear just created — including one that already picked up the tools the reload was waiting on. Clear pendingReload in buildLiveAgent when it drops those runs, since that build is the reload.

The interrupt and session-rotation builds already construct the agent
a deferred reload was waiting on, so leaving pendingReload set rebuilds
again at the next idle edge.
@corbits-triage

Copy link
Copy Markdown
Contributor

Triage: Pass

No changes needed from you. A maintainer will review this pull request.

@corbits-triage
corbits-triage Bot merged commit 6d1e8b2 into main Oct 17, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants