7.6 KiB
Compaction
English | 中文
The compaction seam — a capability seam split like bash: interface (dsh-compact, ctx.compact), implementation (a backend such as dsh-compact-basic), and consumer (a /compact tool, deferred). Compaction is one optional capability, not part of the agent-loop spine — so its vocabulary lives here, not in core.md. A tokenizer- or template-based backend is a sibling package implementing the same interface. Unlike bash, the interface necessarily depends on dsh-session and dsh-llm: its verbs act on an agent-owned Session, and its durable summary event uses the ContentBlock vocabulary (see the compaction capability-seam Agent Note).
Source: packages/compact/compact/src/types.ts
The compact/* session events
Compaction extends SessionEventMap with three event types via declaration merging. All three are log-only — they record the compaction lock and its provenance, and never join the surface. SurfaceEventType is deliberately NOT extended (only message-producing events reach the model), so the summary itself rides on a separate user/message with surfaceOp: { op: 'replace', start, end } — the only surface mutation performed by summary compaction. See the Agent Note for why reusing user/message is honest rather than a workaround.
| Event | Payload | Role |
|---|---|---|
compact/start |
{ turn } |
acquires the log-recorded lock |
compact/summary |
{ summary, shadowedRange, shadowedSeqs, shadowedTokenCount, provider, model, maxTokens? } |
provenance: the summary blocks, the shadowed surface-boundary pair (start/end seqs — a position span, not a numeric interval), the shadowed seqs in surface order, the estimated token count, and the summarize call's envelope (provider, model, plus its generation cap when one applied) — logged so the one-shot request is reconstructable from log + code (the reconstructability Agent Note) |
compact/end |
{ turn, error? } |
releases the lock (error set when summarization threw) |
The lock brackets the whole operation: compact/start is appended first, then summarization, the compact/summary provenance record, and the user/message replacement all land, and only then compact/end. Releasing the lock last turns a crash mid-operation into a detectable orphaned lock (a compact/start with no matching compact/end) rather than a compact/end that falsely claims compaction finished.
These variants are merged inside a declare module '@deepseek-ai/dsh-session' block, so — unlike the top-level types on the other sub-pages — they are not pasted as a drift-checked ```ts type-equiv block (the verify-type-equiv extractor matches only top-level declarations by name). The payload table above is the catalog entry; follow the source link for the authoritative shapes.
CompactionResult
What a successful compaction returns to its caller: the bookkeeping-event seqs, raw summary, shadowed range and seqs, and estimated token count.
/** Result of a successful compaction operation. */
interface CompactionResult {
/** The seq of the appended `compact/start` event. */
startSeq: number
/** The seq of the appended `compact/summary` event. */
summarySeq: number
/** The seq of the appended `compact/end` event. */
endSeq: number
/** The summary content blocks produced by the backend. */
summary: ContentBlock[]
/**
* The surface-boundary pair that was shadowed: the seqs of the first
* (`start`) and last (`end`) surface nodes of the replaced range. A
* surface-POSITION span, not a numeric seq interval — after a prior replace
* lands a fresh high-seq summary node at an older range's position, `start`
* can be GREATER than `end`. {@link CompactionResult.shadowedSeqs} is the
* authoritative set of shadowed nodes, in surface order.
*/
shadowedRange: { start: number; end: number }
/** The seqs of all shadowed surface nodes, in surface order. */
shadowedSeqs: number[]
/** Estimated token count of the shadowed content. */
shadowedTokenCount: number
}
The service
Automatic callers state why policy is running; implementations may treat confirmed overflow more aggressively than ordinary pressure.
/** Why automatic policy is asking a backend to consider compaction. */
type CompactionTrigger = 'pressure' | 'context-overflow'
CompactService exposes compactIfNeeded(agent, trigger, signal) for automatic pressure or context-overflow policy, returning null when no safe work exists, and compactRegion(...) for an explicit inclusive surface range. Every backend marks its replacement user/message with the package-exported COMPACT_CHECKPOINT_SOURCE; consumers call isCompactCheckpointSource() instead of coupling checkpoint recognition to one backend. Implementations must forward the supplied signal to summarization. The seam owns no pricing API: the singleton ctx.tokenMeter directly owns estimation and replay, while dsh-compact-basic owns retention, event sequencing, routed summarization calls, and their configuration.
Pressure compaction runs at serial agent/step before request derivation. Once pressure or canonical overflow qualifies, compact-basic invokes optional ctx.toolResultPrune before range selection, remeasures through ctx.tokenMeter, and can advance the surface without a summary. Failed-request recovery runs through agent/request-error after the failed step closes and calls agent.retry() only when the surface replacement generation advances, even if later summary work throws after pruning; cancellation still wins. Region boundaries preserve tool-call/result pairing but not whole turns, allowing early closed steps of one oversized turn to compact. dsh-compact-basic owns thresholds, retained-tail policy, overflow caps, and failure handling.
The seam exports toolPairingBalancedBefore(session, seq) and toolPairingBalancedAfter(session, seq) for those edge checks. Both validate current surface membership and reject missing seqs and orphan results; the package contract owns their cache semantics.
Tool-result pruning outcomes
The optional tool-result pruning service reports each durable content replacement and the aggregate Unicode-code-point reduction. Its public result types live in compact-tool-result-prune/src/types.ts.
/** Provenance and size accounting for one landed surface replacement. */
interface PrunedEntry {
/** Full-fidelity tool-result event shadowed by the replacement. */
readonly originalSeq: number
/** Newly appended pruned tool-result event. */
readonly replacementSeq: number
/** Tool call shared by the original and replacement. */
readonly callId: CallId
/** Original text size in Unicode code points. */
readonly charsBefore: number
/** Replacement text size in Unicode code points. */
readonly charsAfter: number
}
/** Aggregate outcome of one stable-surface pruning pass. */
interface PruneResult {
/** Replacements in the snapshotted surface order. */
readonly pruned: readonly PrunedEntry[]
/** Total Unicode code points removed across replacements. */
readonly charsRemoved: number
}