Skip to content

feat(acp): report compaction with standard session updates - #52737

Merged
nexxeln merged 2 commits into
v2from
acp-compaction-update
Oct 2, 2026
Merged

nexxeln merged 2 commits into
v2from
acp-compaction-update

Conversation

@nexxeln

@nexxeln nexxeln commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Why the change

ACP now defines standard compaction_update and compaction_summary_chunk session updates, so clients that support them can show a compaction where it happened, stream its summary, and rebuild it on session/load, instead of reading opencode's private _meta marker.

Special things to note

  • Gated on the client capability. Standard updates go out only when initialize has clientCapabilities.session.compaction set to an object. If it is missing or null, the client gets the existing _meta["opencode/compaction"] marker, unchanged, except that a compaction abandoned by a cancelled turn's wind-down now closes as failed with an aborted error instead of staying started.
  • The summary is already shown to users. The RFD says to leave the summary out when it could reveal context the user has not seen. Opencode's own clients already display it: the TUI (packages/tui/src/routes/session/index.tsx:2022-2023, 2064-2077), the mini TUI streams it from the deltas (packages/tui/src/mini/stream-v2.transport.ts:1094-1110), and the app (packages/session-ui/src/message/message-content.tsx:405, 442-450). A provider's encrypted native compaction has no summary, so it sends summary: null.
  • Child sessions. A child compaction projected onto the parent session keeps the marker, because a standard update there would read as compaction of the parent's own context. Clients that use opencode/child-session-updates get standard updates inside those notifications.

Change outline

Core to ACP mapping, all in ACPCompaction:

Core ACP
compaction message ID (inputID for /compact, otherwise derived from the event ID, the same ID the marker's messageId uses) compactionId
session.compaction.started status: "in_progress"
session.compaction.delta compaction_summary_chunk, only while the compaction is open
session.compaction.ended / completed message completed, summary: [text], or null when the summary is empty
failed with aborted or compaction.interrupted cancelled, summary: null
any other failure failed, summary: null, error: message (a defect's pretty-printed cause becomes "Compaction failed")

Live and replay:

live
  started            → in_progress
  delta              → summary chunk (dropped before in_progress or after settling)
  ended / failed     → one terminal update
  failed, not started → in_progress, then the terminal update (a live first update is never terminal)

session/load replay
  settled compaction → one terminal update at its place in the history
  running compaction → skipped, since no live turn on this connection will settle it

Cancel: if the server's wind-down reports the interruption, the compaction settles as cancelled. If the wind-down times out, abandon settles the session's open compaction through the same builder before the prompt response (cancelled in standard mode, the aborted marker otherwise). Child compactions keep going and are reported by the consumer that follows children after the turn.

 packages/cli/src/acp/
+├── compaction.ts   # compaction types, core→ACP mapping, standard vs marker choice, start/chunk/settle/abandon/replay builders
 ├── translate.ts    # routes compaction events to ACPCompaction
 ├── turn.ts         # `abandon` replaces `abandonTools`; `run` reads the capabilities
 ├── service.ts      # stores `compaction` from initialize; replay reads capabilities once
 └── agent.ts        # initial `compaction: false`

Tests in packages/cli/test/acp/compaction.test.ts run over the wire fixture:

  • Live: streaming then completion with the summary, no-summary completion, failure after partial chunks, failure before start (in_progress then failed), a defect giving the generic error, and chunks outside an open compaction being dropped.
  • Cancel: cancelled through the wind-down, and through abandon after a timeout, in both standard and marker mode.
  • Child sessions: markers on the parent, and standard updates inside child-session notifications.
  • Replay and capability: replay of failed, completed, cancelled and native compactions; a null capability falls back to the marker.
  • The existing marker tests cover clients without the capability.

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.

1 participant