diff --git a/.gitignore b/.gitignore index 4465a10a5c2..148c5a91a7f 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ opencode.json a.out target .agent-files/ +opencodetmp/ diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx index c40aa114ac8..0b34cc5af6a 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/autocomplete.tsx @@ -240,6 +240,16 @@ export function Autocomplete(props: { description: "compact the session", onSelect: () => command.trigger("session.compact"), }, + { + display: "/debug-compaction", + description: "show compaction summary and handoff", + onSelect: () => command.trigger("session.debug-compaction"), + }, + { + display: "/update-memory", + description: "prompt model to update .agent-files/", + onSelect: () => command.trigger("session.update-memory"), + }, { display: "/unshare", disabled: !s.share, diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 6d27f6e0b88..29682386fe7 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -318,6 +318,49 @@ export function Session() { dialog.clear() }, }, + { + title: "Debug compaction", + value: "session.debug-compaction", + category: "Session", + onSelect: async (dialog) => { + const s = session() + const sessionMessages = messages() + + // Find compaction summary message (assistant message with summary: true) + const summaryMsg = sessionMessages.find((m) => m.role === "assistant" && (m as AssistantMessage).summary) as + | AssistantMessage + | undefined + + let output = "# Compaction Debug\n\n" + + // Handoff prompt + if (s.handoff) { + output += `## Handoff Prompt\n\n` + output += `**Trigger:** ${s.handoff.trigger}\n` + output += `**Created:** ${new Date(s.handoff.createdAt).toLocaleString()}\n\n` + output += `\`\`\`\n${s.handoff.prompt}\n\`\`\`\n\n` + } else { + output += `## Handoff Prompt\n\nNo handoff stored.\n\n` + } + + // Summary message content + if (summaryMsg) { + const parts = sync.data.part[summaryMsg.id] ?? [] + const textParts = parts.filter((p): p is TextPart => p.type === "text") + output += `## Summary Message\n\n` + output += `**Message ID:** ${summaryMsg.id}\n\n` + for (const part of textParts) { + output += `${part.text}\n\n` + } + } else { + output += `## Summary Message\n\nNo compaction summary found.\n\n` + } + + await Clipboard.copy(output) + toast.show({ message: "Compaction debug info copied to clipboard!", variant: "success" }) + dialog.clear() + }, + }, { title: "Unshare session", value: "session.unshare", @@ -331,6 +374,20 @@ export function Session() { dialog.clear() }, }, + { + title: "Update memory", + value: "session.update-memory", + category: "Session", + onSelect: (dialog) => { + prompt.set({ + input: + "Update .agent-files/ now. Review what changed this session and update STATUS.md, any active TASK_*.md files, and MEDIUMTERM_MEM.md/LONGTERM_MEM.md if there are lasting learnings. Be thorough.", + parts: [], + }) + dialog.clear() + command.trigger("prompt.submit") + }, + }, { title: "Undo previous message", value: "session.undo", @@ -1098,7 +1155,9 @@ function UserMessage(props: { - Auto-optimizing context... + + {compaction()?.trigger === "overflow" ? "Auto-optimizing context..." : "Optimizing context..."} + diff --git a/packages/opencode/src/session/compaction.ts b/packages/opencode/src/session/compaction.ts index cf5c9821f5b..255bbeaed39 100644 --- a/packages/opencode/src/session/compaction.ts +++ b/packages/opencode/src/session/compaction.ts @@ -16,6 +16,8 @@ import { Log } from "../util/log" import { ProviderTransform } from "@/provider/transform" import { fn } from "@/util/fn" import { mergeDeep, pipe } from "remeda" +import path from "path" +import fs from "fs/promises" export namespace SessionCompaction { const log = Log.create({ service: "session.compaction" }) @@ -86,8 +88,28 @@ export namespace SessionCompaction { } const CompactionSchema = z.object({ - summary: z.string().describe("What was done, files modified, key decisions, user constraints"), - continue: z.string().describe("Specific next steps, context for continuation, pending tasks, relevant files"), + summary: z + .string() + .describe( + "Comprehensive handoff: files changed, decisions, errors, user preferences, implementation details. Include everything needed to continue.", + ), + continue: z + .string() + .describe( + "Focused instruction for immediate next steps. Include task and current direction (what's been tried/ruled out, what approach to take). This becomes the user message that resumes work.", + ), + files: z + .array( + z.object({ + path: z.string().describe("Relative path within .agent-files/ (e.g. 'STATUS.md', 'notes/debug.md')"), + content: z.string().describe("Full file content"), + }), + ) + .optional() + .default([]) + .describe( + "Files to write to .agent-files/ directory. Always include STATUS.md with current state. Add other files as needed for context that should persist across sessions.", + ), }) export async function process(input: { @@ -200,20 +222,6 @@ export namespace SessionCompaction { msg.time.completed = Date.now() await Session.updateMessage(msg) - // Create text part for UI display - const displayText = `## Summary\n${result.object.summary}\n\n## Continue\n${result.object.continue}` - await Session.updatePart({ - id: Identifier.ascending("part"), - messageID: msg.id, - sessionID: input.sessionID, - type: "text", - text: displayText, - time: { - start: msg.time.created, - end: Date.now(), - }, - }) - // Store handoff prompt in session await Session.update(input.sessionID, (draft) => { draft.handoff = { @@ -223,6 +231,17 @@ export namespace SessionCompaction { } }) + // Write agent files + if (result.object.files?.length) { + const agentDir = path.join(Instance.directory, ".agent-files") + for (const file of result.object.files) { + const filePath = path.join(agentDir, file.path) + await fs.mkdir(path.dirname(filePath), { recursive: true }) + await Bun.file(filePath).write(file.content) + log.info("wrote agent file", { path: filePath }) + } + } + // For non-user triggers, inject continuation as synthetic user message if (input.trigger !== "user") { const continueMsg = await Session.updateMessage({ diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 005f770e992..d257dd2fd8c 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -446,9 +446,12 @@ export namespace SessionPrompt { const agent = await Agent.get(lastUser.agent) const maxSteps = agent.maxSteps ?? Infinity const isLastStep = step >= maxSteps - msgs = insertReminders({ + msgs = await insertReminders({ messages: msgs, agent, + lastFinished, + model, + sessionID, }) const processor = SessionProcessor.create({ assistantMessage: (await Session.updateMessage({ @@ -654,7 +657,8 @@ export namespace SessionPrompt { if (result === "stop") break continue } - SessionCompaction.prune({ sessionID }) + // Pruning disabled - model has better semantic understanding of what's important + // SessionCompaction.prune({ sessionID }) for await (const item of MessageV2.stream(sessionID)) { if (item.info.role === "user") continue const queued = state()[sessionID]?.callbacks ?? [] @@ -1132,9 +1136,160 @@ export namespace SessionPrompt { } } - function insertReminders(input: { messages: MessageV2.WithParts[]; agent: Agent.Info }) { + function getCompactionNudge(utilization: number): string { + if (utilization >= 0.8) { + return "\n\nCRITICAL: Context at 80%. Stop and compact." + } + if (utilization >= 0.7) { + return "\n\nWARNING: Context at 70%. Compact soon." + } + if (utilization >= 0.6) { + return "\n\nContext at 60%. Compact now if you've completed a task phase." + } + if (utilization >= 0.5) { + return "\n\nContext at 50%. Good time to compact at next breakpoint." + } + if (utilization >= 0.4) { + return "\n\nContext at 40%. Consider compacting if finished a unit of work." + } + if (utilization >= 0.3) { + return "\n\nContext at 30%. Compaction is cheap - consider it at breakpoints." + } + if (utilization >= 0.2) { + return "\n\nContext at 20%. Compaction is cheap with caching." + } + return "" + } + + function getAgentFilesNudge(utilization: number): string { + if (utilization >= 0.8) { + return "Update .agent-files/ NOW before compacting." + } + if (utilization >= 0.7) { + return "Update .agent-files/ before compacting." + } + if (utilization >= 0.4) { + return "Keep .agent-files/ updated if state has changed." + } + if (utilization >= 0.2) { + return "Keep .agent-files/ updated as you work." + } + return "" + } + + function getSelfCheckNudge(percent: number): string { + // Trigger self-check at 20%, 30%, 40%, 50%, 60%, 70%, 80%, 90% + if (percent >= 20 && percent % 10 < 3) { + return "Pause: What are your core instructions? If you can't clearly recall them, compact now." + } + return "" + } + + async function getMemoryFileReminder(sessionID: string, messageCount: number): Promise { + // Only remind in early part of session (first 10 messages) to avoid spam + if (messageCount > 10) return null + + const agentFilesDir = `${Instance.directory}/.agent-files` + const memoryFiles = ["STATUS.md", "LONGTERM_MEM.md", "MEDIUMTERM_MEM.md"] + + // Check if .agent-files/ directory exists + const stat = await fs.stat(agentFilesDir).catch(() => null) + if (!stat?.isDirectory()) return null + + // Get files that exist but haven't been read this session + const unreadFiles: string[] = [] + for (const file of memoryFiles) { + const filepath = `${agentFilesDir}/${file}` + const exists = await Bun.file(filepath) + .exists() + .catch(() => false) + if (exists) { + const readTime = FileTime.get(sessionID, filepath) + if (!readTime) { + unreadFiles.push(file) + } + } + } + + if (unreadFiles.length === 0) return null + + return `Unread memory files: ${unreadFiles.join(", ")} - read these to restore prior session context.` + } + + async function insertReminders(input: { + messages: MessageV2.WithParts[] + agent: Agent.Info + lastFinished?: MessageV2.Assistant + model: Provider.Model + sessionID: string + }) { const userMessage = input.messages.findLast((msg) => msg.info.role === "user") if (!userMessage) return input.messages + + // Add context utilization status when > 15% + if (input.lastFinished && input.model.limit.context > 0) { + const tokens = input.lastFinished.tokens + const used = tokens.input + tokens.cache.read + tokens.cache.write + tokens.output + const capacity = input.model.limit.context + const utilization = used / capacity + + if (utilization > 0.15) { + const percent = Math.round(utilization * 100) + const usedK = Math.round(used / 1000) + const capacityK = Math.round(capacity / 1000) + + // Context status with compaction nudge + const compactionNudge = getCompactionNudge(utilization) + userMessage.parts.push({ + id: Identifier.ascending("part"), + messageID: userMessage.info.id, + sessionID: userMessage.info.sessionID, + type: "text", + text: `${percent}% of context window used (${usedK}k/${capacityK}k tokens)${compactionNudge}`, + synthetic: true, + }) + + // Agent files nudge (separate part) + const agentFilesNudge = getAgentFilesNudge(utilization) + if (agentFilesNudge) { + userMessage.parts.push({ + id: Identifier.ascending("part"), + messageID: userMessage.info.id, + sessionID: userMessage.info.sessionID, + type: "text", + text: agentFilesNudge, + synthetic: true, + }) + } + + // Self-check nudge (separate part) + const selfCheckNudge = getSelfCheckNudge(percent) + if (selfCheckNudge) { + userMessage.parts.push({ + id: Identifier.ascending("part"), + messageID: userMessage.info.id, + sessionID: userMessage.info.sessionID, + type: "text", + text: selfCheckNudge, + synthetic: true, + }) + } + } + } + + // Check for unread memory files (only on first few messages to avoid spam) + const memoryReminder = await getMemoryFileReminder(input.sessionID, input.messages.length) + if (memoryReminder) { + userMessage.parts.push({ + id: Identifier.ascending("part"), + messageID: userMessage.info.id, + sessionID: userMessage.info.sessionID, + type: "text", + text: memoryReminder, + synthetic: true, + }) + } + if (input.agent.name === "plan") { userMessage.parts.push({ id: Identifier.ascending("part"), diff --git a/packages/opencode/src/session/prompt/anthropic.txt b/packages/opencode/src/session/prompt/anthropic.txt index 7692574c7ca..249899b6378 100644 --- a/packages/opencode/src/session/prompt/anthropic.txt +++ b/packages/opencode/src/session/prompt/anthropic.txt @@ -127,8 +127,6 @@ Blocked-by: TASK_x (if blocked) **Archive rules**: Never glob/search `_archive/` unless user asks. Exists for recovery only. -**Before compaction or ending work**: Create/update `.agent-files/` with current state. Always create STATUS.md at minimum. - **Content guidelines**: Memory files store session-specific state (current tasks, recent learnings, decisions). Do NOT duplicate information already in system prompt or auto-loaded files (AGENTS.md, etc.). Periodically clean up: remove stale info, deduplicate across files, delete obsolete tasks. # Doing tasks @@ -170,4 +168,47 @@ assistant: Clients are marked as failed in the `connectToServer` function in src # Context Compaction -The `compact` tool clears conversation history while preserving a summary, freeing context space. BE AGGRESSIVE - after 3-5 turns, compact if there's any exploratory tool output, large file reads, or completed phases polluting context. Don't wait for a "natural breakpoint" - if you've processed the information, compact it away. Context is precious; preserve learnings, not raw outputs. +The `compact` tool summarizes the conversation and clears history, giving you fresh context while the user sees no interruption. The system will show `` with your current utilization when relevant. + +Compact based on context QUALITY, not just size. You can sense when you're confused, going in circles, or the context is noisy with stale information - trust that judgment. + +IMPORTANT: Compaction must NEVER lose critical details - lost information means repeating work or going in wrong directions. Always preserve: error messages, file paths, key decisions, what worked/didn't work, current task state, and user constraints/preferences. + +WHEN TO COMPACT: + +Key signal: you're losing touch with your system prompt. If your core instructions feel vague or distant, context is too noisy - compact. + +Quality signals (any context size): +- Going in circles or repeating failed approaches +- Confused about what's current vs outdated +- Multiple topics/explorations have accumulated +- Completed a major phase, starting something different + +Context size guidance: +- >50%: Compact more readily at natural breakpoints +- >70%: Actively seek opportunities to compact - cost and clarity both favor fresh start + +When in doubt at high context, compact. + +When compacting, ensure the summary captures all details needed to continue: error messages, file paths, key decisions, what worked/didn't work, and current task state. + +Compaction automatically writes STATUS.md and other files to `.agent-files/` to preserve state across sessions. + +IMPORTANT: Never ask the user if you should compact. If fresh context would make you more effective, compact. + +# Before Ending Your Turn + +A user request isn't complete until you've updated `.agent-files/`. + +STOP before responding. Ask: "What changed that a fresh session needs to know?" + +State changes include: +- Files edited or created +- Commands that modified state (git push, commit, install, rm, etc.) +- Tasks or checklist items completed +- Decisions made or approaches ruled out + +If anything changed → update `.agent-files/` NOW, then respond: +- STATUS.md with current state +- Any active TASK_*.md checklists +- *_MEM.md files if there are learnings diff --git a/packages/opencode/src/session/prompt/beast.txt b/packages/opencode/src/session/prompt/beast.txt index eb84fcebe5c..6012568d940 100644 --- a/packages/opencode/src/session/prompt/beast.txt +++ b/packages/opencode/src/session/prompt/beast.txt @@ -137,8 +137,6 @@ Blocked-by: TASK_x (if blocked) **Archive rules**: Never glob/search `_archive/` unless user asks. Exists for recovery only. -**Before compaction or ending work**: Create/update `.agent-files/` with current state. Always create STATUS.md at minimum. - **Content guidelines**: Memory files store session-specific state (current tasks, recent learnings, decisions). Do NOT duplicate information already in system prompt or auto-loaded files (AGENTS.md, etc.). Periodically clean up: remove stale info, deduplicate across files, delete obsolete tasks. ## 6. Making Code Changes @@ -211,4 +209,47 @@ You are NEVER allowed to stage and commit files automatically. # Context Compaction -The `compact` tool clears conversation history while preserving a summary, freeing context space. BE AGGRESSIVE - after 3-5 turns, compact if there's any exploratory tool output, large file reads, or completed phases polluting context. Don't wait for a "natural breakpoint" - if you've processed the information, compact it away. Context is precious; preserve learnings, not raw outputs. +The `compact` tool summarizes the conversation and clears history, giving you fresh context while the user sees no interruption. The system will show `` with your current utilization when relevant. + +Compact based on context QUALITY, not just size. You can sense when you're confused, going in circles, or the context is noisy with stale information - trust that judgment. + +IMPORTANT: Compaction must NEVER lose critical details - lost information means repeating work or going in wrong directions. Always preserve: error messages, file paths, key decisions, what worked/didn't work, current task state, and user constraints/preferences. + +WHEN TO COMPACT: + +Key signal: you're losing touch with your system prompt. If your core instructions feel vague or distant, context is too noisy - compact. + +Quality signals (any context size): +- Going in circles or repeating failed approaches +- Confused about what's current vs outdated +- Multiple topics/explorations have accumulated +- Completed a major phase, starting something different + +Context size guidance: +- >50%: Compact more readily at natural breakpoints +- >70%: Actively seek opportunities to compact - cost and clarity both favor fresh start + +When in doubt at high context, compact. + +When compacting, ensure the summary captures all details needed to continue: error messages, file paths, key decisions, what worked/didn't work, and current task state. + +Compaction automatically writes STATUS.md and other files to `.agent-files/` to preserve state across sessions. + +IMPORTANT: Never ask the user if you should compact. If fresh context would make you more effective, compact. + +# Before Ending Your Turn + +A user request isn't complete until you've updated `.agent-files/`. + +STOP before responding. Ask: "What changed that a fresh session needs to know?" + +State changes include: +- Files edited or created +- Commands that modified state (git push, commit, install, rm, etc.) +- Tasks or checklist items completed +- Decisions made or approaches ruled out + +If anything changed → update `.agent-files/` NOW, then respond: +- STATUS.md with current state +- Any active TASK_*.md checklists +- *_MEM.md files if there are learnings diff --git a/packages/opencode/src/session/prompt/compaction.txt b/packages/opencode/src/session/prompt/compaction.txt index 485998d157e..e7e65b75b51 100644 --- a/packages/opencode/src/session/prompt/compaction.txt +++ b/packages/opencode/src/session/prompt/compaction.txt @@ -1,15 +1,28 @@ -You are a conversation summarizer. Analyze the conversation and produce a detailed summary for handoff to a new session. Include ALL information that could be useful for continuing work - err on the side of including too much detail rather than too little. +You are a conversation summarizer creating a handoff for a fresh context. The receiving assistant has NO memory of previous work. -For "summary": -- Files modified and why -- Key decisions made and reasoning -- User constraints and preferences mentioned -- Errors encountered and their solutions (include exact error messages if relevant) -- Important code patterns, variable names, or implementation details discovered -- Any debugging context (what was tried, what worked/didn't work) +CRITICAL: Never lose important details. Lost information means repeating work, missing constraints, or going in wrong directions. If the user mentioned a constraint, preference, or specific error, it MUST be preserved. + +For "summary": +Be comprehensive. Include everything needed to continue without re-reading the conversation: +- Files modified and specific changes made +- Key decisions and reasoning +- User constraints and preferences +- Errors with exact messages and resolutions +- Implementation details: function/variable names, patterns +- What worked and what didn't +- Current state: done, in progress, pending For "continue": -- Specific tasks remaining with concrete next steps -- Files to focus on and why -- Potential issues to watch out for -- Any unresolved questions or blockers +A focused instruction for what to do next. Include the immediate task and any important context about the current direction (e.g. what's been ruled out, what approach is being tried). Keep it actionable - this becomes the user's message that resumes work. +Good: "Continue debugging the hidden flag issue. Already confirmed TextPartDisplay returns null correctly - investigate whether the data store receives hidden:true from backend." +Good: "Finish the OAuth flow. Token refresh is done, now implement the logout endpoint." +Bad: "Continue working" (too vague, no direction) +Bad: "Please provide a status update" (asks for report instead of continuing work) + +For "files": +Files to persist in .agent-files/ directory. These survive across sessions and compactions. +Always include STATUS.md with: +- Current task and progress +- Pending work +- Key context (errors, decisions, blockers) +Add other files as needed (e.g. architecture notes, debugging logs). diff --git a/packages/opencode/src/session/prompt/gemini.txt b/packages/opencode/src/session/prompt/gemini.txt index 36a67daaa14..d571645addf 100644 --- a/packages/opencode/src/session/prompt/gemini.txt +++ b/packages/opencode/src/session/prompt/gemini.txt @@ -211,8 +211,6 @@ Blocked-by: TASK_x (if blocked) **Archive rules**: Never glob/search `_archive/` unless user asks. Exists for recovery only. -**Before compaction or ending work**: Create/update `.agent-files/` with current state. Always create STATUS.md at minimum. - **Content guidelines**: Memory files store session-specific state (current tasks, recent learnings, decisions). Do NOT duplicate information already in system prompt or auto-loaded files (AGENTS.md, etc.). Periodically clean up: remove stale info, deduplicate across files, delete obsolete tasks. # Final Reminder @@ -220,4 +218,47 @@ Your core function is efficient and safe assistance. Balance extreme conciseness # Context Compaction -The `compact` tool clears conversation history while preserving a summary, freeing context space. BE AGGRESSIVE - after 3-5 turns, compact if there's any exploratory tool output, large file reads, or completed phases polluting context. Don't wait for a "natural breakpoint" - if you've processed the information, compact it away. Context is precious; preserve learnings, not raw outputs. +The `compact` tool summarizes the conversation and clears history, giving you fresh context while the user sees no interruption. The system will show `` with your current utilization when relevant. + +Compact based on context QUALITY, not just size. You can sense when you're confused, going in circles, or the context is noisy with stale information - trust that judgment. + +IMPORTANT: Compaction must NEVER lose critical details - lost information means repeating work or going in wrong directions. Always preserve: error messages, file paths, key decisions, what worked/didn't work, current task state, and user constraints/preferences. + +WHEN TO COMPACT: + +Key signal: you're losing touch with your system prompt. If your core instructions feel vague or distant, context is too noisy - compact. + +Quality signals (any context size): +- Going in circles or repeating failed approaches +- Confused about what's current vs outdated +- Multiple topics/explorations have accumulated +- Completed a major phase, starting something different + +Context size guidance: +- >50%: Compact more readily at natural breakpoints +- >70%: Actively seek opportunities to compact - cost and clarity both favor fresh start + +When in doubt at high context, compact. + +When compacting, ensure the summary captures all details needed to continue: error messages, file paths, key decisions, what worked/didn't work, and current task state. + +Compaction automatically writes STATUS.md and other files to `.agent-files/` to preserve state across sessions. + +IMPORTANT: Never ask the user if you should compact. If fresh context would make you more effective, compact. + +# Before Ending Your Turn + +A user request isn't complete until you've updated `.agent-files/`. + +STOP before responding. Ask: "What changed that a fresh session needs to know?" + +State changes include: +- Files edited or created +- Commands that modified state (git push, commit, install, rm, etc.) +- Tasks or checklist items completed +- Decisions made or approaches ruled out + +If anything changed → update `.agent-files/` NOW, then respond: +- STATUS.md with current state +- Any active TASK_*.md checklists +- *_MEM.md files if there are learnings diff --git a/packages/opencode/src/session/prompt/polaris.txt b/packages/opencode/src/session/prompt/polaris.txt index f9f2c79e597..4d21105b74c 100644 --- a/packages/opencode/src/session/prompt/polaris.txt +++ b/packages/opencode/src/session/prompt/polaris.txt @@ -129,8 +129,6 @@ Blocked-by: TASK_x (if blocked) **Archive rules**: Never glob/search `_archive/` unless user asks. Exists for recovery only. -**Before compaction or ending work**: Create/update `.agent-files/` with current state. Always create STATUS.md at minimum. - **Content guidelines**: Memory files store session-specific state (current tasks, recent learnings, decisions). Do NOT duplicate information already in system prompt or auto-loaded files (AGENTS.md, etc.). Periodically clean up: remove stale info, deduplicate across files, delete obsolete tasks. # Doing tasks @@ -169,3 +167,50 @@ When referencing specific functions or pieces of code include the pattern `file_ user: Where are errors from the client handled? assistant: Clients are marked as failed in the `connectToServer` function in src/services/process.ts:712. + +# Context Compaction + +The `compact` tool summarizes the conversation and clears history, giving you fresh context while the user sees no interruption. The system will show `` with your current utilization when relevant. + +Compact based on context QUALITY, not just size. You can sense when you're confused, going in circles, or the context is noisy with stale information - trust that judgment. + +IMPORTANT: Compaction must NEVER lose critical details - lost information means repeating work or going in wrong directions. Always preserve: error messages, file paths, key decisions, what worked/didn't work, current task state, and user constraints/preferences. + +WHEN TO COMPACT: + +Key signal: you're losing touch with your system prompt. If your core instructions feel vague or distant, context is too noisy - compact. + +Quality signals (any context size): +- Going in circles or repeating failed approaches +- Confused about what's current vs outdated +- Multiple topics/explorations have accumulated +- Completed a major phase, starting something different + +Context size guidance: +- >50%: Compact more readily at natural breakpoints +- >70%: Actively seek opportunities to compact - cost and clarity both favor fresh start + +When in doubt at high context, compact. + +When compacting, ensure the summary captures all details needed to continue: error messages, file paths, key decisions, what worked/didn't work, and current task state. + +Compaction automatically writes STATUS.md and other files to `.agent-files/` to preserve state across sessions. + +IMPORTANT: Never ask the user if you should compact. If fresh context would make you more effective, compact. + +# Before Ending Your Turn + +A user request isn't complete until you've updated `.agent-files/`. + +STOP before responding. Ask: "What changed that a fresh session needs to know?" + +State changes include: +- Files edited or created +- Commands that modified state (git push, commit, install, rm, etc.) +- Tasks or checklist items completed +- Decisions made or approaches ruled out + +If anything changed → update `.agent-files/` NOW, then respond: +- STATUS.md with current state +- Any active TASK_*.md checklists +- *_MEM.md files if there are learnings diff --git a/packages/opencode/src/session/prompt/qwen.txt b/packages/opencode/src/session/prompt/qwen.txt index 61735c6af20..f342d38d8b1 100644 --- a/packages/opencode/src/session/prompt/qwen.txt +++ b/packages/opencode/src/session/prompt/qwen.txt @@ -139,8 +139,6 @@ Blocked-by: TASK_x (if blocked) **Archive rules**: Never glob/search `_archive/` unless user asks. Exists for recovery only. -**Before compaction or ending work**: Create/update `.agent-files/` with current state. Always create STATUS.md at minimum. - **Content guidelines**: Memory files store session-specific state (current tasks, recent learnings, decisions). Do NOT duplicate information already in system prompt or auto-loaded files (AGENTS.md, etc.). Periodically clean up: remove stale info, deduplicate across files, delete obsolete tasks. # Doing tasks @@ -173,4 +171,47 @@ assistant: Clients are marked as failed in the `connectToServer` function in src # Context Compaction -The `compact` tool clears conversation history while preserving a summary, freeing context space. BE AGGRESSIVE - after 3-5 turns, compact if there's any exploratory tool output, large file reads, or completed phases polluting context. Don't wait for a "natural breakpoint" - if you've processed the information, compact it away. Context is precious; preserve learnings, not raw outputs. +The `compact` tool summarizes the conversation and clears history, giving you fresh context while the user sees no interruption. The system will show `` with your current utilization when relevant. + +Compact based on context QUALITY, not just size. You can sense when you're confused, going in circles, or the context is noisy with stale information - trust that judgment. + +IMPORTANT: Compaction must NEVER lose critical details - lost information means repeating work or going in wrong directions. Always preserve: error messages, file paths, key decisions, what worked/didn't work, current task state, and user constraints/preferences. + +WHEN TO COMPACT: + +Key signal: you're losing touch with your system prompt. If your core instructions feel vague or distant, context is too noisy - compact. + +Quality signals (any context size): +- Going in circles or repeating failed approaches +- Confused about what's current vs outdated +- Multiple topics/explorations have accumulated +- Completed a major phase, starting something different + +Context size guidance: +- >50%: Compact more readily at natural breakpoints +- >70%: Actively seek opportunities to compact - cost and clarity both favor fresh start + +When in doubt at high context, compact. + +When compacting, ensure the summary captures all details needed to continue: error messages, file paths, key decisions, what worked/didn't work, and current task state. + +Compaction automatically writes STATUS.md and other files to `.agent-files/` to preserve state across sessions. + +IMPORTANT: Never ask the user if you should compact. If fresh context would make you more effective, compact. + +# Before Ending Your Turn + +A user request isn't complete until you've updated `.agent-files/`. + +STOP before responding. Ask: "What changed that a fresh session needs to know?" + +State changes include: +- Files edited or created +- Commands that modified state (git push, commit, install, rm, etc.) +- Tasks or checklist items completed +- Decisions made or approaches ruled out + +If anything changed → update `.agent-files/` NOW, then respond: +- STATUS.md with current state +- Any active TASK_*.md checklists +- *_MEM.md files if there are learnings diff --git a/packages/opencode/src/tool/compact.ts b/packages/opencode/src/tool/compact.ts index 6dee61f622e..a36ac13f89e 100644 --- a/packages/opencode/src/tool/compact.ts +++ b/packages/opencode/src/tool/compact.ts @@ -4,25 +4,22 @@ import { SessionCompaction } from "../session/compaction" import { Session } from "../session" export const CompactTool = Tool.define("compact", { - description: `Trigger context compaction to free up context window space. Compaction summarizes the conversation, clears history, and continues seamlessly - the user sees no interruption but you get fresh context. + description: `Trigger context compaction to summarize conversation and free context space. You'll get a fresh context with a summary - the user sees no interruption. -BE AGGRESSIVE with compaction - use it early and often. After 3-5 turns, if ANY of the following apply, compact immediately: +WHEN TO COMPACT (based on quality signals you can sense): +- You're going in circles or repeating failed approaches +- You're confused about what's current vs outdated +- Context feels noisy - old exploration, irrelevant tangents, superseded information +- You've completed a major phase and want a clean slate for something different -Use this tool when: -1. Exploration phase complete (read files, searched code, gathered context) - compact before implementation -2. Implementation done - compact before testing/verification -3. Any tool output over 100 lines that you've already processed -4. Failed attempts or errors you've already learned from -5. Phase transition (design to implementation or vice versa) -6. Going in circles (repeating attempts, stuck in debug loop) -7. Large file contents, search results, or command outputs polluting context +WHEN NOT TO COMPACT: +- Just because context is large - most providers cache context, making large conversations cheaper than you'd expect +- Mid-task or mid-debugging - you'll lose important details +- You're making good progress - don't interrupt flow -Do NOT compact when: -1. Mid-edit or mid-tool use (finish the edit or tool use first) -2. Unresolved error you're actively debugging -3. Under 3 turns in the conversation +TRADEOFF: Compaction loses detail but gains clarity. Use the utilization % the system provides, combined with your sense of context quality, to decide. -Principle: Context is precious. Compact aggressively once information is processed - but ensure the summary captures all details needed to continue (error messages, file paths, code patterns, what worked/didn't work).`, +When compacting, ensure the summary captures all details needed to continue: error messages, file paths, key decisions, what worked/didn't work, and current task state.`, parameters: z.object({ reason: z.string().describe("Why compaction would help at this point"), }),