Repository navigation
buzz-acp: resume agent sessions via session/load (SessionStore keyed by channel + thread root) #6
Description
TL;DR: buzz-acp never restores an agent session: after the pool sleeps (15 min), Stop/Start, Restart or a Desktop restart, the agent gets a new session and loses all accumulated context. The adapters (claude-agent-acp, codex-acp) support session/load, and the transcripts are on disk. We propose porting upstream PR block#6088 and extending the session key to the thread.
Problem
buzz-acp only ever creates sessions: the single ACP session call is session/new (crates/buzz-acp/src/acp.rs:758). The map of live sessions sessions: HashMap<SessionScope, String> lives inside the AgentPool (crates/buzz-acp/src/pool.rs:133) and is wiped by SessionState::invalidate_all (pool.rs:219) whenever the pool is torn down. That happens on:
- idle pool sleep (
BUZZ_ACP_IDLE_POOL_SLEEP=900, see the companion issue on idle teardown); - Stop → Start or Restart of the agent in Desktop;
- Desktop quit/restart (
shutdown: waiting for in-flight prompts→ grace period → abort); - worker crash / cold respawn after a failed turn (upstream buzz-acp: failed/steered turns respawn cold sessions with ~2.4s capped backoff (token-burn storm); ephemeral event kinds bypass respond_to gate block/buzz#5327).
The next mention opens a fresh session ([session/query] … resume=none in the agent log) and the harness injects only the thread root plus the last BUZZ_ACP_CONTEXT_MESSAGE_LIMIT (12) replies as <thread-context>. Everything the model had reasoned about, intermediate decisions, file paths it discovered, subagent results — gone.
Evidence
- 2026-09-29, one agent (session policy
thread), harness logagents/logs/<agent>__<session>.log: turn dispatched 07:11:01, last message 07:25:13,idle pool sleep bound reached — tearing pool back to lazy stateat 07:26:16, new session59aac60b…withresume=noneat 07:44:54 on the next mention. Old transcript~/.claude/projects/<workspace>/<session>.jsonl(2.4 MB) was intact on disk. - The owner's expectation (2026-09-29): "I set each thread to start its own session with the agent and expected it to persist until I close Buzz." Since then the team runs a manual ritual in every long thread: "let's pause, we'll continue in the next session", "write a prompt to start the next session" — handoff files and restart prompts written by hand because the harness cannot resume.
What already exists
@agentclientprotocol/claude-agent-acp≥ 0.79 implementsloadSession→createSession(..., { resume: sessionId }), i.e. native Claude Code resume from the on-disk transcript.codex-acpalso advertisesloadSession: truein itsinitializeresponse.- Upstream PR buzz-acp: resume channel sessions across a restart, and name the channel on session/new block/buzz#6088 ("buzz-acp: resume channel sessions across a restart, and name the channel on session/new", head
313d5fd4, +1753/−52, filesacp.rs,config.rs,lib.rs,pool.rs, newsession_store.rs): aSessionStoreat<state-dir>/sessions-<pubkey>.json,session/loadwhen there is no live session, fallback tosession/newon JSON-RPC error, flagsBUZZ_ACP_STATE_DIR/BUZZ_ACP_NO_RESUME_SESSIONS. Maintainer confirmed the basic scenario and requested changes (2026-08-18); author replied 2026-08-23; no activity since,mergeable_state=dirty. Still OPEN as of 2026-10-09.
Proposal
- Port buzz-acp: resume channel sessions across a restart, and name the channel on session/new block/buzz#6088 onto
fork/mainas a registered fork patch (scripts/fork/patches.json, perdocs/fork/MAINTENANCE.md). - Extend the store key from
channel_idto the fullSessionScope(crates/buzz-acp/src/scope.rs:71):Conversation { channel_id }andThread { channel_id, root_event_id }. Thread sessions landed in upstream main on 2026-09-18, after the PR was written. - Cover the teardown path explicitly: lazy-pool sleep → empty
AgentPool::from_slots→ next dispatch must callsession/loadwith the stored id, notsession/new. Add a test for it. - Gate on the runtime capability from
initialize(loadSession); runtimes without it keep today's behaviour. - On successful load, skip the full
<thread-context>re-injection and deliver only the delta (thedelivered_event_idsbookkeeping would also need to be persisted, or the harness re-sends the window once and accepts the duplication). - Document the limitation: resume restores conversation history, not in-flight turn state (background subagents, scheduled wakeups do not come back).
Benefit
- Agents survive the 15-minute idle teardown, Desktop restarts and Restart buttons with their context intact; no more hand-written handoff prompts between "sessions" of the same thread.
- Fewer tokens: a resumed session does not re-read the thread, the repo and the feature directory from scratch.
- Thread-scoped sessions (the policy the owner relies on) finally mean what the UI promises.
References
- upstream: buzz-acp: resume channel sessions across a restart, and name the channel on session/new block/buzz#6088, [Bug] buzz-acp loses channel sessions on restart and creates duplicate Codex cloud threads block/buzz#5342 (loses channel sessions on restart), buzz-acp: failed/steered turns respawn cold sessions with ~2.4s capped backoff (token-burn storm); ephemeral event kinds bypass respond_to gate block/buzz#5327, buzz-acp: bound ACP session + provider-resource lifecycle (no eviction today) block/buzz#6958 (session lifecycle)
- fork:
crates/buzz-acp/src/acp.rs:758,pool.rs:133,219,scope.rs:71
Activity
docs(fork): design visible and controllable agent sessions
spiritabsolute commentedon Oct 20, 2026
Implemented and accepted in #15, now squash-merged into
mainas258fba8eb89a67451806b4ff4e984981d0432d13. This completes session continuity and the scoped visibility/context controls. The owner rebuilt and accepted the app; candidate CI passed. Detailed evidence and retained limitations are recorded in the merged PR.