You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
opencode acp creates and persists a durable session in SQLite the moment an ACP client (Zed, Neovim, etc.) issues session/new, before any prompt is ever sent. If the user then closes the editor panel, switches agents, or the client disconnects without prompting, the empty session row is orphaned forever — there is no cleanup on close and no session/delete in ACP 0.21.
Zed's agent panel triggers this routinely: opening the panel or picking the opencode agent from the dropdown provisions a new session eagerly. Every such interaction that the user abandons before typing leaves behind a New session - <ISO> row that keeps reappearing in the session picker.
Verified against upstream/dev at 849c2598abc7d2b40261e74b5826bc74ffc78308 (post-v1.18.4).
Reproduction
opencode auth login and configure a working model.
In Zed (or any ACP client), open the agent panel and pick opencode.
Do not send any prompt. Close the panel, or switch to another agent, or quit the editor.
Reopen the agent panel and list sessions (Zed's session history, or opencode TUI's session list).
An empty session titled New session - 2026-07-21T... (zero messages, zero cost) appears and persists across restarts.
Repeat a few times → the picker fills with orphaned stubs.
constcloseSession= ...(function*(params){constremoved=yield*session.remove(params.sessionId)// in-memory Map onlyregisteredMcp.delete(params.sessionId)sessionSnapshots.delete(params.sessionId)if(!removed)return{}yield*abortBackingSession(removed)// POST /session/abortreturn{}})
session.remove (in packages/opencode/src/acp/session.ts) only clears the in-process Map<string, Info>. abortBackingSession calls session.abort, not session.delete. Nothing removes the row.
No session/delete in ACP. The SessionCapabilities schema in @agentclientprotocol/sdk@0.21.0 only exposes list, resume, fork, close. SessionCloseCapabilities docs are explicit: "By supplying {} it means that the agent supports closing of sessions." Close ≠ delete. Even if a client had a "delete" UI action, it would still resolve to session/close.
listSessions surfaces them forever.service.ts:244-288 merges sdk.session.list() (durable) with in-memory ACP sessions and returns both. Orphaned stubs from prior runs keep showing up.
Contrast with the TUI
opencode's own TUI creates the session lazily inside SessionV2.prompt(...) on first prompt, so no empty rows accumulate. Only the ACP entry point has this eager-persist behavior.
Suggested fixes (cheapest → most correct)
Cleanup on close. In closeSession, if the backing session has zero messages, call sdk.session.delete(...) after abortBackingSession. Handles graceful disconnect (Zed closing the panel).
Cleanup on connect. At the start of each ACP connection, sweep sessions under the project directory that have zero messages and were created by ACP. Cheap heuristic (SessionMessage count == 0 + a "created via ACP" marker). Handles crash/restart.
Defer persistence (the real fix). Have newSession allocate a session ID without projecting Session.Created. Only persist on the first session/prompt. Aligns ACP with the TUI's lazy-persist behavior and eliminates the class of bug rather than sweeping it up. Requires plumbing an "unprojected" state through SessionV2.prompt.
(1) + (2) together are a solid stopgap needing no protocol changes. (3) is the correct long-term fix.
Client: Zed (agent panel); reproducible with any ACP client that eagerly calls session/new.
Related
Port ACP support to V2 core and APIs #35457 (Port ACP support to V2 core and APIs) — umbrella for the V2 port; doesn't mention this specific empty-session behavior. Regardless of the V2 migration, both newSession and closeSession on the current V2-compat code path exhibit this bug.
Summary
opencode acpcreates and persists a durable session in SQLite the moment an ACP client (Zed, Neovim, etc.) issuessession/new, before any prompt is ever sent. If the user then closes the editor panel, switches agents, or the client disconnects without prompting, the empty session row is orphaned forever — there is no cleanup on close and nosession/deletein ACP 0.21.Zed's agent panel triggers this routinely: opening the panel or picking the opencode agent from the dropdown provisions a new session eagerly. Every such interaction that the user abandons before typing leaves behind a
New session - <ISO>row that keeps reappearing in the session picker.Verified against
upstream/devat849c2598abc7d2b40261e74b5826bc74ffc78308(post-v1.18.4).Reproduction
opencode auth loginand configure a working model.opencodeTUI's session list).New session - 2026-07-21T...(zero messages, zero cost) appears and persists across restarts.Repeat a few times → the picker fills with orphaned stubs.
Mechanism
newSessionpersists eagerly.packages/opencode/src/acp/service.ts:161-207callsinput.sdk.session.create({...})unconditionally. That resolves toV2Session.createwhich:ProjectTablerow,SessionInfowithtitle: "New session - ${new Date(now).toISOString()}"(session.ts:228),SessionV1.Event.Created, which theSessionProjectorsynchronously inserts intoSessionTable.By the time
session/newreturns, the row is durable — zero messages, zero cost, timestamp-placeholder title.closeSessiondoesn't delete — it only aborts.service.ts:339-347:session.remove(inpackages/opencode/src/acp/session.ts) only clears the in-processMap<string, Info>.abortBackingSessioncallssession.abort, notsession.delete. Nothing removes the row.No
session/deletein ACP. TheSessionCapabilitiesschema in@agentclientprotocol/sdk@0.21.0only exposeslist,resume,fork,close.SessionCloseCapabilitiesdocs are explicit: "By supplying{}it means that the agent supports closing of sessions." Close ≠ delete. Even if a client had a "delete" UI action, it would still resolve tosession/close.listSessionssurfaces them forever.service.ts:244-288mergessdk.session.list()(durable) with in-memory ACP sessions and returns both. Orphaned stubs from prior runs keep showing up.Contrast with the TUI
opencode's own TUI creates the session lazily inside
SessionV2.prompt(...)on first prompt, so no empty rows accumulate. Only the ACP entry point has this eager-persist behavior.Suggested fixes (cheapest → most correct)
closeSession, if the backing session has zero messages, callsdk.session.delete(...)afterabortBackingSession. Handles graceful disconnect (Zed closing the panel).SessionMessage count == 0+ a "created via ACP" marker). Handles crash/restart.newSessionallocate a session ID without projectingSession.Created. Only persist on the firstsession/prompt. Aligns ACP with the TUI's lazy-persist behavior and eliminates the class of bug rather than sweeping it up. Requires plumbing an "unprojected" state throughSessionV2.prompt.(1) + (2) together are a solid stopgap needing no protocol changes. (3) is the correct long-term fix.
Environment
sst/opencode@849c2598ab(upstream/dev, post-v1.18.4)@agentclientprotocol/sdk@0.21.0session/new.Related
newSessionandcloseSessionon the current V2-compat code path exhibit this bug.