diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 449ee163..4118f082 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added - **Compaction hook improvements**: The `before_compact` session event now includes: + - `previousSummary`: Summary from the last compaction (if any), so hooks can preserve accumulated context - `messagesToKeep`: Messages that will be kept after the summary (recent turns), in addition to `messagesToSummarize` - `resolveApiKey`: Function to resolve API keys for any model (checks settings, OAuth, env vars) - Removed `apiKey` string in favor of `resolveApiKey` for more flexibility diff --git a/packages/coding-agent/docs/hooks.md b/packages/coding-agent/docs/hooks.md index fea8d7f7..a88fd57e 100644 --- a/packages/coding-agent/docs/hooks.md +++ b/packages/coding-agent/docs/hooks.md @@ -194,6 +194,7 @@ The `before_compact` event lets you implement custom compaction strategies. Unde |-------|-------------| | `entries` | All session entries (header, messages, model changes, previous compactions). Use this for custom schemes that need full session history. | | `cutPoint` | Where default compaction would cut. `firstKeptEntryIndex` is the entry index where kept messages start. `isSplitTurn` indicates if cutting mid-turn. | +| `previousSummary` | Summary from the last compaction, if any. Include this in your summary to preserve accumulated context. | | `messagesToSummarize` | Messages that will be summarized and discarded (from after last compaction to cut point). | | `messagesToKeep` | Messages that will be kept verbatim after the summary (from cut point to end). | | `tokensBefore` | Current context token count (why compaction triggered). | diff --git a/packages/coding-agent/examples/hooks/full-compaction.ts b/packages/coding-agent/examples/hooks/full-compaction.ts index a9badf77..3232af33 100644 --- a/packages/coding-agent/examples/hooks/full-compaction.ts +++ b/packages/coding-agent/examples/hooks/full-compaction.ts @@ -21,7 +21,7 @@ export default function (pi: HookAPI) { pi.on("session", async (event, ctx) => { if (event.reason !== "before_compact") return; - const { messagesToSummarize, messagesToKeep, tokensBefore, model, resolveApiKey, entries } = event; + const { messagesToSummarize, messagesToKeep, previousSummary, tokensBefore, model, resolveApiKey, entries } = event; // Combine all messages for full summary const allMessages = [...messagesToSummarize, ...messagesToKeep]; @@ -38,6 +38,9 @@ export default function (pi: HookAPI) { // Transform app messages to LLM-compatible format const transformedMessages = messageTransformer(allMessages); + // Include previous summary context if available + const previousContext = previousSummary ? `\n\nPrevious session summary for context:\n${previousSummary}` : ""; + // Build messages that ask for a comprehensive summary const summaryMessages = [ ...transformedMessages, @@ -46,7 +49,7 @@ export default function (pi: HookAPI) { content: [ { type: "text" as const, - text: `You are a conversation summarizer. Create a comprehensive summary of this entire conversation that captures: + text: `You are a conversation summarizer. Create a comprehensive summary of this entire conversation that captures:${previousContext} 1. The main goals and objectives discussed 2. Key decisions made and their rationale diff --git a/packages/coding-agent/src/core/agent-session.ts b/packages/coding-agent/src/core/agent-session.ts index e605ccf5..80aa88b0 100644 --- a/packages/coding-agent/src/core/agent-session.ts +++ b/packages/coding-agent/src/core/agent-session.ts @@ -756,6 +756,15 @@ export class AgentSession { throw new Error("Already compacted"); } + // Find previous compaction summary if any + let previousSummary: string | undefined; + for (let i = entries.length - 1; i >= 0; i--) { + if (entries[i].type === "compaction") { + previousSummary = (entries[i] as CompactionEntry).summary; + break; + } + } + let compactionEntry: CompactionEntry | undefined; let fromHook = false; @@ -767,6 +776,7 @@ export class AgentSession { previousSessionFile: null, reason: "before_compact", cutPoint: preparation.cutPoint, + previousSummary, messagesToSummarize: [...preparation.messagesToSummarize], messagesToKeep: [...preparation.messagesToKeep], tokensBefore: preparation.tokensBefore, @@ -907,6 +917,15 @@ export class AgentSession { return; } + // Find previous compaction summary if any + let previousSummary: string | undefined; + for (let i = entries.length - 1; i >= 0; i--) { + if (entries[i].type === "compaction") { + previousSummary = (entries[i] as CompactionEntry).summary; + break; + } + } + let compactionEntry: CompactionEntry | undefined; let fromHook = false; @@ -918,6 +937,7 @@ export class AgentSession { previousSessionFile: null, reason: "before_compact", cutPoint: preparation.cutPoint, + previousSummary, messagesToSummarize: [...preparation.messagesToSummarize], messagesToKeep: [...preparation.messagesToKeep], tokensBefore: preparation.tokensBefore, diff --git a/packages/coding-agent/src/core/hooks/types.ts b/packages/coding-agent/src/core/hooks/types.ts index 6b88915e..bf578357 100644 --- a/packages/coding-agent/src/core/hooks/types.ts +++ b/packages/coding-agent/src/core/hooks/types.ts @@ -130,6 +130,8 @@ export type SessionEvent = | (SessionEventBase & { reason: "before_compact"; cutPoint: CutPointResult; + /** Summary from previous compaction, if any. Include this in your summary to preserve context. */ + previousSummary?: string; /** Messages that will be summarized and discarded */ messagesToSummarize: AppMessage[]; /** Messages that will be kept after the summary (recent turns) */