镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

feat(agents): ask to resume claude and codex turns stopped by reload or quit - #1235

Merged
kalvinnchau merged 5 commits into
mainfrom
murderbot/resume-after-reload
Oct 20, 2026
Merged

kalvinnchau merged 5 commits into
mainfrom
murderbot/resume-after-reload

Conversation

@baxen

@baxen baxen commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Cmd-R and Quit kill every process the page started, so any Claude or Codex turn in flight died with it. After this change Buzz remembers those turns. On the next start it asks whether to resume them, and nothing restarts unless the owner says so.

What changes

  • Host (Rust): the native host no longer sends a process's exit to a page that has reloaded. Before this, the old page could still be running when the SIGTERM (143) arrived. It took that as a failed turn, posted "⚠️ I couldn't finish that: Claude Code exited (143)", and forgot the turn. Quit goes through the same stop_all, so it is covered too.
  • Runner: for a type that sets resumes (Claude and Codex here), the runner saves each mention turn's event id and channel in agent storage while the turn works. Each agent has its own key (buzz.agents.interrupted.v1:<pubkey>, like catch-up progress), and a runner writes only the agent it changed, so one program never overwrites another program's agents. The entry is cleared when the turn settles.
  • Next start: the agents with saved turns appear as interrupted in the Agents snapshot, and an info toast (InterruptedNotice, built on the shared ToastNotice) asks Resume or Don't resume. There is no close button: the owner picks one.
    • Resume reads each mention again from the relay with session.readRemote once its community connects, then hands them to run oldest first with resumed: true. Oldest first is the order they were sent, so a later correction comes after the request it corrects. The relay answers newest first. Claude and Codex add a note telling the agent to carry on in its saved session and not repeat what it already did or sent.
    • Don't resume forgets the saved turns.
    • Both answers apply only to the agents the notice names. An agent left out because its community is down keeps its saved turns and gets its own prompt once it connects.
    • Only the copy of the app that holds an agent lists it in the notice and may forget its turns. A copy that does not hold the agent leaves the saved turns to the holder. When custody changes, claim and release refresh the notice, so a copy that takes the agent over lists it right away.
    • Only Resume gives a saved mention again. Replay, live delivery and catch-up skip it (heard()), both before the owner answers and after Don't resume, so it never runs as an ordinary turn without the resumed note.
  • readRemote rather than read: read merges this device's outbox into the result as copies, and a copy carries no event proof. After a reload, a mention the owner sent from the same desktop is still in the outbox, so with read it was dropped at hasEventProof. The turn was then forgotten and nothing resumed.
  • A turn stays saved until its resumed turn settles, so closing again mid-resume loses nothing. If the resumed job is dropped before it starts because the queue is full, the turn is forgotten. If it is dropped because another copy holds the agent, the turn stays saved for the holder. If the relay read fails, the turns stay saved and the next catch-up retries.

Not covered

  • Mentions queued behind a working turn but not yet started are not saved.
  • Watch and standalone turns are not saved.
  • Other agent types are not saved. Goose has not opted in.
  • If an agent is deleted, its saved entry stays in storage with no way to dismiss it. A deleted agent never appears in the notice, and both actions apply only to the agents it names. The entry is inert: nothing runs it.

Testing

Automated

  • service.test.ts, under "a turn closing or reloading the app stopped", covers:
    • The prompt waits for the owner, and the turn runs once after Resume.
    • The turn stays saved while it works, so a second close shows it again, and it is forgotten when it settles.
    • Don't resume forgets the turn.
    • A replay or live delivery of the saved mention does not run it, either before the owner answers or after Don't resume. Resume then runs it, marked resumed.
    • Resume and Don't resume apply only to the agents the notice names. The test uses two communities with one disconnected.
    • A failed relay read keeps the turn saved, and the next try runs it.
    • A copy that does not hold the agent shows no notice for it, and its Resume and Don't resume leave the holder's saved turn alone.
    • The notice appears once this copy takes the agent over on a tick, with no other refresh.
    • Two programs sharing storage, each with its own agent, keep each other's saved turns.
    • Saved mentions resume oldest first when the relay answers newest first.
    • A type that does not resume saves nothing.
  • InterruptedNotice.test.tsx covers:
    • the wording for one agent and for several;
    • Resume and Don't resume each calling the right method;
    • the toast closing once answered;
    • there being no X.
  • Claude and Codex runtime tests check that the resumed note appears only on resumed turns.
  • Rust host_process tests pass 12/12, including no exit after stop_all.
  • At the head of this PR:
    • tsc --noEmit is clean.
    • biome is clean on the changed files.
    • The branch is rebased onto main at abdc0e4.
    • vitest run src/features/agents src/bundled/agents src/bundled/claude-code src/bundled/codex src/app passes 1544 tests, with 1 skipped.
    • Rust host_process tests pass 12/12.
    • Each new regression test fails without its fix. That covers notice scoping, replay suppression, the non-holder notice, per-agent keys, ordering and the takeover refresh.

Human testing (native, just desktop)

The test used one Claude agent and one Codex agent. Both were given the same mention: count to 60, posting each multiple of 10 as its own message, with sleep 15 between posts.

  • Cmd-R mid-turn, before the readRemote fix:
    • No "couldn't finish" message was posted.
    • The "Resume interrupted agents?" toast named both agents.
    • Nothing restarted on its own.
    • Clicking Resume cleared the saved turns, but neither agent ran. Logging showed the relay read returned the mention, and hasEventProof rejected it. That led to the fix above.
  • Cmd-R mid-turn, after the fix:
    • The toast appeared, and nothing ran for 34s before the click.
    • Clicking Resume restarted both agents. Each continued from its last number without starting over.
    • The saved turns were removed once both turns settled. That run used the old shared key.
  • Cmd-Q and relaunch with just desktop, after the fix: tested and verified.
  • These native runs were done on the commit that switched to readRemote. They have not been repeated on the later review-fix commits: per-agent scoping, replay suppression, per-agent keys, the holder-only notice, ordering and the custody refresh.
  • The toast was seen in the native app in one theme at one window width. Other themes, narrow widths and enlarged text have not been checked.
  • Deferred: keyboard checks. F6 to reach the toast, Tab between Resume and Don't resume, activating each from the keyboard, and where focus returns afterwards have not been checked.

@kalvinnchau kalvinnchau left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 Review findings from my AI agent at f9c3ea0: one P2 and two optional P3 improvements, detailed inline. The P2 scenario concerns multiple communities with different connection states; it was established by static tracing, not live reproduction. TypeScript passed, but the test runs did not establish a clean full-package pass. Browser validation was blocked before launch by a Keychain-dependent startup path; no browser scenarios ran.

Comment thread src/features/agents/runner.ts Resolved
Comment thread src/features/agents/runner.ts Resolved
Comment thread src/features/agents/runner.ts Resolved

@salman1993 salman1993 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 Reviewed f9c3ea0 on Salman’s behalf, limited to significant behavior issues. One recovery-decision bug is detailed inline; I have not duplicated the existing findings.

Validation: 322 existing focused service, notice and Claude/Codex runtime tests passed in a temporary source snapshot. Two additional clock-controlled service cases reproduce the finding, both before answering the notice and after choosing Don’t resume. Native Cmd-R/Quit and visual acceptance were not exercised.

Goose scope: Goose remains excluded from interrupted-turn recovery. It also stops with the page, but its conversation/session mappings currently live only in memory, so supporting it requires more than setting resumes: true.

Comment thread src/features/agents/runner.ts Resolved
@kalvinnchau
kalvinnchau force-pushed the murderbot/resume-after-reload branch from f9c3ea0 to eadd3a2 Compare October 20, 2026 18:27
@kalvinnchau kalvinnchau changed the title Ask to resume agent turns a close or reload stopped feat(agents): ask to resume claude and codex turns stopped by reload or quit Oct 20, 2026
@kalvinnchau
kalvinnchau marked this pull request as ready for review October 20, 2026 19:35
@kalvinnchau
kalvinnchau requested a review from a team as a code owner October 20, 2026 19:35

@wesbillman wesbillman 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.

🤖 Star Lord’s automated source review via Wes’s account, integrating independent Rocket (systems/lifecycle), Mantis (correctness/security/concurrency), and Groot (product/accessibility) reviews.

One P2 correctness finding is inline: resumed mentions need chronological delivery. Source assessment: 8.5/10; resolving that ordering defect is the concrete path to the repository’s 9/10 bar. The earlier per-agent consent and replay-suppression findings are addressed in this revision. The documented decisions on retry backoff and deleted-agent cleanup are preserved.

Minor description correction: the “Not covered” bullet says a deleted agent’s saved entry remains until Don’t resume is chosen. Deleted agents are absent from the notice, and the action now only covers named agents, so that entry actually remains without a dismissal path. Please align the sentence with the limitation already explained in the author’s reply; this does not request a cleanup behavior change.

Reviewed head: 2b7bb99
Target base: bc921c5
Feature merge-base: 724ae9c

Validation: immutable source and callers reviewed; diff whitespace check passed. Public PR description and commit messages inspected; no attached images or disclosure finding. No tests, builds, app launches or CI assessment performed in this source-only review, and the ordering scenario was not reproduced at runtime. Native acceptance documented in the PR predates this head’s consent/replay changes. Both themes, narrow/wide widths, enlarged text and keyboard F6/Tab/action/focus-return coverage remain unverified here; please name completed keyboard checks or list them as deferred. Automated counts in the description were not independently verified. This COMMENT is not an approval or merge-readiness claim.

Comment thread src/features/agents/runner.ts Resolved
@kalvinnchau

Copy link
Copy Markdown
Contributor

🤖 [P2] Another app copy can erase a live turn’s recovery record

At 2b7bb99de5708226b58537f67840f829e04ed12e, interrupted.ts:38 stores every agent under one shared key, and runner.ts:1022 rewrites that blob from each runner’s startup snapshot. Notice eligibility at service.ts:3036 does not require custody.

When one copy is executing a mention, a second copy sharing storage but denied the agent’s claim still shows its interruption notice. Choosing Don’t resume erases the working copy’s recovery marker; reloading the holder then offers no recovery, despite no provider completion. Separate programs holding different agents can also overwrite each other’s entries.

Reproduced with shared-store service probes and the actual frontend/production broker in Chromium and WebKit. Browser claims, providers and upstream were modeled; two native processes were not exercised.

Use per-agent persistence keys following catchUpKey, and restrict notices/actions/writes to the agent’s holder. Replace the erasure expectation in service.test.ts:4490 with preservation, and add shared-storage regressions for non-holders and disjoint agents. Recommend fixing this before merge.

@kalvinnchau

Copy link
Copy Markdown
Contributor

🤖 Addressed in 38b2688.

Another app copy can erase a live turn's recovery record (P2)

  • Each agent's saved turns now live in their own key, buzz.agents.interrupted.v1:<pubkey>, following catchUpKey. A runner writes only the agent it changed (saveTurns(pubkey, …)), so a program can no longer overwrite other agents from its startup snapshot. The key has not shipped, so there is no migration from the old shared key.
  • The notice lists an agent only when this copy holds it (runners.runs). dismissInterrupted also checks custody before it forgets anything.
  • When a resumed job is dropped because another copy holds the agent, its saved turn is no longer forgotten (dropMentions(runner, forget)). It is still forgotten when the agent is retired.
  • The erasure test is now "is left to the copy of the app that holds the agent". It checks that a non-holder shows no notice, and that its Resume and Don't resume leave the saved turn in place.
  • New test: "saves each agent's turns apart, so another program's agents keep theirs". Two programs share one store, each with a disjoint agent. It fails on 2b7bb99 and passes now.
  • Two native processes have not been exercised.

Description notes from the Star Lord review

  • The deleted-agent bullet now says the entry stays with no way to dismiss it, since a deleted agent never appears in the notice. The entry is inert.
  • Keyboard checks (F6, Tab, activating each action, focus return) are now listed as deferred.

@wesbillman wesbillman 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.

🤖 Star Lord’s automated source review via Wes’s account, integrating independent Rocket (lifecycle/storage/performance), Mantis (correctness/security/concurrency), and Groot (product/accessibility) reviews.

One new P2 is inline: refresh the interrupted-turn snapshot when agent ownership changes. Source assessment: 8.5/10; fixing that recovery-notice defect is the concrete path to the repository’s 9/10 bar.

The prior chronological-delivery finding is addressed: resumed mentions now sort oldest first, with deterministic ID ties and a newest-first regression fixture. The per-agent persistence and non-holder preservation changes address the shared-storage concern in source. This follow-up checks those fixes and their introduced behavior; the previously declined retry/backoff and deleted-entry cleanup changes remain outside scope.

Reviewed head: 38b2688
Target base: bc921c5
Feature merge-base: 724ae9c
Follow-up comparison: 2b7bb99 → reviewed head.

Validation: immutable source, callers and regression-test source reviewed; follow-up diff whitespace check passed. Public PR description and follow-up commit material inspected; no attached images or disclosure finding. No tests, builds, app launches, performance measurements or CI assessment were performed, and the ownership scenario was not reproduced at runtime. The description correctly identifies current-head native acceptance, two-process behavior, themes/widths/enlarged text, and F6/Tab/action/focus-return checks as deferred. Shared ToastNotice/Button ownership is preserved; live error/retry focus remains unverified. This COMMENT is not an approval or merge-readiness claim.

Comment thread src/features/agents/service.ts Resolved
baxen and others added 5 commits October 20, 2026 13:32
Cmd-R and Quit kill every process the page started, so a Claude or Codex
turn in flight died with it. Now the runner saves each mention turn of a
type that `resumes` in localStorage while it works. On the next start
Agents lists those agents as `interrupted` and a toast asks the owner to
Resume or not; nothing restarts unless they choose to. Resume reads each
mention again from the relay and gives it to `run` marked `resumed`,
which Claude and Codex turn into a note to carry on in the saved session.

A turn stays saved until its resumed turn settles or is dropped, so
closing again first loses none, and a failed read is retried.

The host no longer sends a process's exit to a page that reloaded. That
page could still run until it unloaded, take the SIGTERM (143) for a
failed turn, post "I couldn't finish that", and forget the turn.

Co-authored-by: murderbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Bradley Axen <baxen@squareup.com>
read() merges this device's outbox into the result as copies, and a
copy has no event proof. the owner's mention sent from the same
desktop is still in the outbox after a reload, so resumeTurns dropped it
at hasEventProof and forgot the turn. readRemote returns only the
verified relay answer.

Co-authored-by: peon <9ac6794b000690b7e814eb1805ad32405d0bec7d52838de3a86cf967565dacc0@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kalvin Chau <kalvin@block.xyz>
a saved mention could still run as an ordinary turn: a replay or live
delivery of it before the owner answered, or after don't resume, went
through heard() without the resumed flag. heard() now drops mentions the
page started with saved, so only resumeTurns gives them.

resume and don't resume applied to every saved agent, including ones
whose community was down and so missing from the notice. both now take
the notice's agents; the rest wait for their own prompt.

Co-authored-by: peon <9ac6794b000690b7e814eb1805ad32405d0bec7d52838de3a86cf967565dacc0@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kalvin Chau <kalvin@block.xyz>
each agent's saved turns now live in their own key
(buzz.agents.interrupted.v1:<pubkey>), and a runner writes only the agent
it changed. one shared blob let a program overwrite another's agents
from its startup snapshot.

only the copy that holds an agent lists it in the notice and may forget
its turns, and losing custody no longer forgets a resumed turn, so a
non-holder can no longer erase the holder's recovery record.

resumed mentions are queued oldest first. the relay answers newest
first, which put an earlier request after its correction.

Co-authored-by: peon <9ac6794b000690b7e814eb1805ad32405d0bec7d52838de3a86cf967565dacc0@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kalvin Chau <kalvin@block.xyz>
the notice lists an agent only while this copy holds it, but claim and
release change that asynchronously without rebuilding the snapshot. a
copy that took an agent over on a tick offered no resume until some
unrelated update. claim and release now tell the service when holding
an agent with saved turns changes.

Co-authored-by: peon <9ac6794b000690b7e814eb1805ad32405d0bec7d52838de3a86cf967565dacc0@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kalvin Chau <kalvin@block.xyz>
@kalvinnchau
kalvinnchau force-pushed the murderbot/resume-after-reload branch from 38b2688 to 6a54927 Compare October 20, 2026 20:35

@wesbillman wesbillman 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.

🤖 Star Lord’s automated source review via Wes’s account, integrating independent Rocket (systems/lifecycle), Mantis (correctness/security/concurrency), and Groot (product/accessibility) reviews.

No new actionable findings in this follow-up. Source assessment: 9/10. The prior custody-refresh P2 is addressed: claim completion (including its error path) and release notify the service when holding an agent with saved turns changes; the service recomputes the interrupted list. The new controlled-clock regression covers takeover on a later tick without an unrelated refresh. The four earlier feature patches are unchanged by the rebase, as checked with range-diff. Accepted retry/backoff and deleted-agent limitations remain unchanged.

Reviewed head: 6a54927
Target base: abdc0e4
Follow-up: custody-refresh fix after previously reviewed 38b2688.

Validation: immutable source, callers and regression-test source reviewed; diff whitespace check passed. Public PR description and follow-up commit inspected; no attached images or disclosure finding. One hosted-check snapshot showed CI required successful, with Windows native validation and browser flake checks skipped. We ran no tests, builds, apps or performance measurements. Current-head native two-process takeover, reload/quit acceptance, themes/widths/enlarged text, and keyboard/action/error-retry focus behavior remain unverified here and are explicitly deferred in the description. Shared ToastNotice/Button ownership is preserved. This COMMENT is not an approval or merge-readiness claim.

@kalvinnchau
kalvinnchau merged commit e9f5d57 into main Oct 20, 2026
37 checks passed
@kalvinnchau
kalvinnchau deleted the murderbot/resume-after-reload branch October 20, 2026 21:07
wpfleger96 pushed a commit that referenced this pull request Oct 20, 2026
…-sessions

* origin/main: (23 commits)
  Add Marketplace tab and shared agent profile panels (#1411)
  fix: publish scoped typing indicators from conversation composers (#1435)
  feat: add configurable composer and pane focus shortcuts (#1418)
  perf(messages): reuse captured video opening frames on remount (#1437)
  FOR OCT 22: let agents clone and push their channel's repository (#1324)
  perf(relay): keep row profile map identity while new authors load (#1431)
  perf: avoid redundant relay channel derivation (#1412)
  Normalize dialog sizing, headers, spacing, and motion (#1403)
  Polish agent activity pills and popover actions (#1429)
  feat(agents): ask to resume claude and codex turns stopped by reload or quit (#1235)
  test(sign-out): join listed paths with / so the tests pass on Windows (#1247)
  fix(deps): remove distrusted emsign certificate roots (#1436)
  fix(plugins): distinguish usage and costs from agent activity (#1392)
  refactor(design-system): extract named Appearance palettes and revise plugin gradients (#1339)
  fix(channels): cancel inert activity popover opens (#1426)
  fix(codex): set up Codex in a dialog from the agent creation card, as Claude does (#1419)
  Add community-local settings (#1363)
  Fix sidebar and pane header alignment (#1409)
  Show recorded Codex scripts in Activity (#1413)
  fix(agents): restore emoji avatars in the agent avatar picker (#1404)
  ...

# Conflicts:
#	crates/agent-controller/src/app_agent.rs
wpfleger96 added a commit that referenced this pull request Oct 20, 2026
…-sessions

* origin/main: (23 commits)
  Add Marketplace tab and shared agent profile panels (#1411)
  fix: publish scoped typing indicators from conversation composers (#1435)
  feat: add configurable composer and pane focus shortcuts (#1418)
  perf(messages): reuse captured video opening frames on remount (#1437)
  FOR OCT 22: let agents clone and push their channel's repository (#1324)
  perf(relay): keep row profile map identity while new authors load (#1431)
  perf: avoid redundant relay channel derivation (#1412)
  Normalize dialog sizing, headers, spacing, and motion (#1403)
  Polish agent activity pills and popover actions (#1429)
  feat(agents): ask to resume claude and codex turns stopped by reload or quit (#1235)
  test(sign-out): join listed paths with / so the tests pass on Windows (#1247)
  fix(deps): remove distrusted emsign certificate roots (#1436)
  fix(plugins): distinguish usage and costs from agent activity (#1392)
  refactor(design-system): extract named Appearance palettes and revise plugin gradients (#1339)
  fix(channels): cancel inert activity popover opens (#1426)
  fix(codex): set up Codex in a dialog from the agent creation card, as Claude does (#1419)
  Add community-local settings (#1363)
  Fix sidebar and pane header alignment (#1409)
  Show recorded Codex scripts in Activity (#1413)
  fix(agents): restore emoji avatars in the agent avatar picker (#1404)
  ...

Co-authored-by: Will Pfleger <wpfleger@block.xyz>
Signed-off-by: Will Pfleger <wpfleger@block.xyz>
ngoodger-block added a commit to ngoodger-block/buzz-app that referenced this pull request Oct 21, 2026
* origin/main: (56 commits)
  Polish huddle portraits and thread layout (block#1406)
  fix(goose): keep long model names inside the Settings model row (block#1432)
  feat(agents): show a timestamp on each activity feed entry (block#1443)
  feat(agents): add buttons to compact and start new session (block#1434)
  feat(activity): request and show Claude Code and Codex thinking (block#1452)
  add inline renaming for Solo sidebar chats (block#1345)
  fix(goose): say why the profile has no model to choose (block#1445)
  Add Marketplace tab and shared agent profile panels (block#1411)
  fix: publish scoped typing indicators from conversation composers (block#1435)
  feat: add configurable composer and pane focus shortcuts (block#1418)
  perf(messages): reuse captured video opening frames on remount (block#1437)
  FOR OCT 22: let agents clone and push their channel's repository (block#1324)
  perf(relay): keep row profile map identity while new authors load (block#1431)
  perf: avoid redundant relay channel derivation (block#1412)
  Normalize dialog sizing, headers, spacing, and motion (block#1403)
  Polish agent activity pills and popover actions (block#1429)
  feat(agents): ask to resume claude and codex turns stopped by reload or quit (block#1235)
  test(sign-out): join listed paths with / so the tests pass on Windows (block#1247)
  fix(deps): remove distrusted emsign certificate roots (block#1436)
  fix(plugins): distinguish usage and costs from agent activity (block#1392)
  ...

Signed-off-by: Nikolaj Goodger <ngoodger@squareup.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants