From ce0449c57fc0dba87523fd040cbc6cfeae7890d9 Mon Sep 17 00:00:00 2001 From: Jeff Blenman Date: Tue, 10 Mar 2026 22:47:17 -0400 Subject: [PATCH] feat: preserve AGENTS.md/CLAUDE.md instructions across compaction Currently, when a session is compacted, the compaction LLM call receives an empty system prompt (system: []), meaning it has no visibility into which project instructions (AGENTS.md, CLAUDE.md) are important to preserve. This causes instruction context to be silently lost after compaction, leading to degraded agent behavior (ignoring git safety rules, coding conventions, etc.). This change adds three layers of instruction preservation: 1. Pass project instructions into the compaction system prompt so the summarizer knows which rules matter 2. Add "Active Project Instructions" section to the compaction summary template, explicitly asking the summarizer to capture behavioral rules that must carry forward 3. After compaction, inject project instructions as a synthetic message in the conversation context (similar to how Claude Code re-injects CLAUDE.md after compression) All changes are backward-compatible - if no instruction files exist, the new code paths are no-ops. Co-Authored-By: Claude Opus 4.6 --- .../opencode/src/agent/prompt/compaction.txt | 3 ++ packages/opencode/src/session/compaction.ts | 46 ++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/agent/prompt/compaction.txt b/packages/opencode/src/agent/prompt/compaction.txt index 3308627e153c..278a48f1cbed 100644 --- a/packages/opencode/src/agent/prompt/compaction.txt +++ b/packages/opencode/src/agent/prompt/compaction.txt @@ -8,7 +8,10 @@ Focus on information that would be helpful for continuing the conversation, incl - What needs to be done next - Key user requests, constraints, or preferences that should persist - Important technical decisions and why they were made +- Any active rules, guidelines, or behavioral instructions from AGENTS.md, CLAUDE.md, or the system prompt that were being followed during the conversation (e.g. git safety rules, coding conventions, reasoning requirements, project-specific constraints) Your summary should be comprehensive enough to provide context but concise enough to be quickly understood. +IMPORTANT: If the system prompt contains project instructions (from AGENTS.md, CLAUDE.md, or similar), you MUST preserve the key rules and constraints in your summary. These instructions guide agent behavior and must carry forward across compaction boundaries. + Do not respond to any questions in the conversation, only output the summary. diff --git a/packages/opencode/src/session/compaction.ts b/packages/opencode/src/session/compaction.ts index 79884d641ea0..99fcef01d402 100644 --- a/packages/opencode/src/session/compaction.ts +++ b/packages/opencode/src/session/compaction.ts @@ -14,6 +14,7 @@ import { Agent } from "@/agent/agent" import { Plugin } from "@/plugin" import { Config } from "@/config/config" import { ProviderTransform } from "@/provider/transform" +import { InstructionPrompt } from "./instruction" export namespace SessionCompaction { const log = Log.create({ service: "session.compaction" }) @@ -174,12 +175,18 @@ export namespace SessionCompaction { 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. The summary that you construct will be used so that another agent can read it and continue the work. +IMPORTANT: If the system prompt contains project instructions (from AGENTS.md, CLAUDE.md, or similar instruction files), you MUST preserve any key rules, constraints, and behavioral guidelines in the "Active Project Instructions" section below. These instructions guide agent behavior and must carry forward across compaction boundaries. Without them, the next agent will lose critical context about how to behave. + When constructing the summary, try to stick to this template: --- ## Goal [What goal(s) is the user trying to accomplish?] +## Active Project Instructions + +[List any specific rules, guidelines, or behavioral instructions from AGENTS.md, CLAUDE.md, the system prompt, or user directives that were actively being followed during this conversation. Include items such as: git safety rules, coding conventions, reasoning requirements, file handling constraints, tool usage restrictions, or any other project-specific instructions that the next agent must continue to follow. If no special instructions were in effect, write "None".] + ## Instructions - [What important instructions did the user give you that are relevant] @@ -199,13 +206,17 @@ When constructing the summary, try to stick to this template: ---` const promptText = compacting.prompt ?? [defaultPrompt, ...compacting.context].join("\n\n") + // Include project instructions (AGENTS.md, CLAUDE.md) in the compaction system + // prompt so the summarizer knows which rules and guidelines are important to + // preserve in the summary. Without this, instruction context is silently lost. + const instructions = await InstructionPrompt.system() const result = await processor.process({ user: userMessage, agent, abort: input.abort, sessionID: input.sessionID, tools: {}, - system: [], + system: instructions, messages: [ ...MessageV2.toModelMessages(messages, model, { stripMedia: true }), { @@ -233,6 +244,39 @@ When constructing the summary, try to stick to this template: } if (result === "continue" && input.auto) { + // After compaction, inject project instructions as a synthetic message so they + // appear in the conversation context (not just the system prompt). This ensures + // the post-compaction agent has instructions reinforced in both places, similar + // to how Claude Code re-injects CLAUDE.md after compression. + if (instructions.length > 0) { + const instructionMsg = await Session.updateMessage({ + id: Identifier.ascending("message"), + role: "user", + sessionID: input.sessionID, + time: { created: Date.now() }, + agent: userMessage.agent, + model: userMessage.model, + }) + await Session.updatePart({ + id: Identifier.ascending("part"), + messageID: instructionMsg.id, + sessionID: input.sessionID, + type: "text", + synthetic: true, + text: [ + "", + "The conversation was just compacted. The following project instructions must continue to be followed:", + "", + ...instructions, + "", + ].join("\n"), + time: { + start: Date.now(), + end: Date.now(), + }, + }) + } + if (replay) { const original = replay.info as MessageV2.User const replayMsg = await Session.updateMessage({