Dashboard › › Session ses_3882bf5d
ses_3882bf5d7ffe23xrE7KdYTSuHZMy long-term memory has a note about the compaction overflow check, but not the full mechanism. Let me read the source directly.
/home/byk/Code/opencode/packages/opencode/src/session/compaction.ts
<path>/home/byk/Code/opencode/packages/opencode/src/session/compaction.ts</path>
<type>file</type>
<content>1: import { BusEvent } from "@/bus/bus-event"
2: import { Bus } from "@/bus"
3: import { Session } from "."
4: import { Identifier } from "../id/id"
5: import { Instance } from "../project/instance"
6: import { Provider } from "../provider/provider"
7: import { MessageV2 } from "./message-v2"
8: import z from "zod"
9: import { SessionPrompt } from "./prompt"
10: import { Token } from "../util/token"
11: import { Log } from "../util/log"
12: import { SessionProcessor } from "./processor"
13: import { fn } from "@/util/fn"
14: import { Agent } from "@/agent/agent"
15: import { Plugin } from "@/plugin"
16: import { Config } from "@/config/config"
17:
18: export namespace SessionCompaction {
19: const log = Log.create({ service: "session.compaction" })
20:
21: export const Event = {
22: Compacted: BusEvent.define(
23: "session.compacted",
24: z.object({
25: ...
Now let me also quickly check how compaction is triggered (when isOverflow is checked):
Found 20 matches
/home/byk/Code/opencode/packages/opencode/src/session/message-v2.ts:
Line 160: type: z.literal("compaction"),
Line 498: if (part.type === "compaction") {
Line 650: msg.parts.some((part) => part.type === "compaction")
/home/byk/Code/opencode/packages/opencode/src/session/prompt.ts:
Line 13: import { SessionCompaction } from "./compaction"
Line 289: const task = msg.parts.filter((part) => part.type === "compaction" || part.type === "subtask")
Line 487: // pending compaction
Line 488: if (task?.type === "compaction") {
Line 500: // context overflow, needs compaction
Line 504: (await SessionCompaction.isOverflow({ tokens: lastFinished.tokens, model }))
/home/byk/Code/opencode/packages/opencode/src/session/processor.ts:
Line 15: import { SessionCompaction } from "./compaction"
Line 274: if (await SessionCompaction.isOverflow({ tokens: usage.tokens, model: input.model })) ...
<path>/home/byk/Code/opencode/packages/opencode/src/session/processor.ts</path>
<type>file</type>
<content>265: files: patch.files,
266: })
267: }
268: snapshot = undefined
269: }
270: SessionSummary.summarize({
271: sessionID: input.sessionID,
272: messageID: input.assistantMessage.parentID,
273: })
274: if (await SessionCompaction.isOverflow({ tokens: usage.tokens, model: input.model })) {
275: needsCompaction = true
276: }
277: break
278:
279: case "text-start":
280: currentText = {
281: id: Identifier.ascending("part"),
282: messageID: input.assistantMessage.id,
283: sessionID: input.assistantMessage.sessionID,
284: ...
<path>/home/byk/Code/opencode/packages/opencode/src/session/prompt.ts</path>
<type>file</type>
<content>480: synthetic: true,
481: } satisfies MessageV2.TextPart)
482: }
483:
484: continue
485: }
486:
487: // pending compaction
488: if (task?.type === "compaction") {
489: const result = await SessionCompaction.process({
490: messages: msgs,
491: parentID: lastUser.id,
492: abort,
493: sessionID,
494: auto: task.auto,
495: })
496: if (result === "stop") break
497: continue
498: }
499:
500: // context overflow, needs compaction
501: if (
502: lastFinished &&
503: lastFinished.summary !== true &&
504: (await SessionCompaction.isOverflow({ tokens: lastFinished.tokens, model }))
505: ) {
506: await SessionCompaction.create({
507: sessionID,
508: agent: lastUser.agent,
509: ...
Here's how OpenCode compaction works:
Trigger: After each AI step finishes (in processor.ts:274), it checks isOverflow(). It also checks on the main prompt loop in prompt.ts:500-513.
Overflow condition (compaction.ts:30-38):
count = input + cache.read + output
usable = model.limit.input ?? (context - output_reservation)
overflow = count > usable
Importantly, this is different from the UI's context inspector formula (which also includes reasoning and cache.write).
What happens on overflow:
compaction task is queued via SessionCompaction.create() — this adds a synthetic compaction part to the user message.prompt.ts:488 detects the pending compaction and calls SessionCompaction.process().process() — what it actually does:
summary: true.MessageV2.toModelMessages(messages, model)) and appends a final user message with the prompt:
"Provide a detailed prompt for continuing our conversation above. Focus on information that would be helpful for continuing the conversation, including what we did, what we're doing, which files we're working on, and what we're going to do next considering new session will not have access to our conversation."
summary: true.There's also a prune() step (runs first) that clears old completed tool outputs from older parts to reduce token count before the summary is generated.
So the key difference from your eval's "default" mode: The eval simulates "last 80k tokens" by truncating, but real compaction generates a rolling LLM summary of everything before the cutoff. ...