Add previousSummary to before_compact hook event

This commit is contained in:
Mario Zechner 2025-12-24 12:47:12 +01:00
parent 97bbd7a642
commit 403faafdbe
5 changed files with 29 additions and 2 deletions

View file

@ -5,6 +5,7 @@
### Added ### Added
- **Compaction hook improvements**: The `before_compact` session event now includes: - **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` - `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) - `resolveApiKey`: Function to resolve API keys for any model (checks settings, OAuth, env vars)
- Removed `apiKey` string in favor of `resolveApiKey` for more flexibility - Removed `apiKey` string in favor of `resolveApiKey` for more flexibility

View file

@ -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. | | `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. | | `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). | | `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). | | `messagesToKeep` | Messages that will be kept verbatim after the summary (from cut point to end). |
| `tokensBefore` | Current context token count (why compaction triggered). | | `tokensBefore` | Current context token count (why compaction triggered). |

View file

@ -21,7 +21,7 @@ export default function (pi: HookAPI) {
pi.on("session", async (event, ctx) => { pi.on("session", async (event, ctx) => {
if (event.reason !== "before_compact") return; 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 // Combine all messages for full summary
const allMessages = [...messagesToSummarize, ...messagesToKeep]; const allMessages = [...messagesToSummarize, ...messagesToKeep];
@ -38,6 +38,9 @@ export default function (pi: HookAPI) {
// Transform app messages to LLM-compatible format // Transform app messages to LLM-compatible format
const transformedMessages = messageTransformer(allMessages); 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 // Build messages that ask for a comprehensive summary
const summaryMessages = [ const summaryMessages = [
...transformedMessages, ...transformedMessages,
@ -46,7 +49,7 @@ export default function (pi: HookAPI) {
content: [ content: [
{ {
type: "text" as const, 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 1. The main goals and objectives discussed
2. Key decisions made and their rationale 2. Key decisions made and their rationale

View file

@ -756,6 +756,15 @@ export class AgentSession {
throw new Error("Already compacted"); 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 compactionEntry: CompactionEntry | undefined;
let fromHook = false; let fromHook = false;
@ -767,6 +776,7 @@ export class AgentSession {
previousSessionFile: null, previousSessionFile: null,
reason: "before_compact", reason: "before_compact",
cutPoint: preparation.cutPoint, cutPoint: preparation.cutPoint,
previousSummary,
messagesToSummarize: [...preparation.messagesToSummarize], messagesToSummarize: [...preparation.messagesToSummarize],
messagesToKeep: [...preparation.messagesToKeep], messagesToKeep: [...preparation.messagesToKeep],
tokensBefore: preparation.tokensBefore, tokensBefore: preparation.tokensBefore,
@ -907,6 +917,15 @@ export class AgentSession {
return; 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 compactionEntry: CompactionEntry | undefined;
let fromHook = false; let fromHook = false;
@ -918,6 +937,7 @@ export class AgentSession {
previousSessionFile: null, previousSessionFile: null,
reason: "before_compact", reason: "before_compact",
cutPoint: preparation.cutPoint, cutPoint: preparation.cutPoint,
previousSummary,
messagesToSummarize: [...preparation.messagesToSummarize], messagesToSummarize: [...preparation.messagesToSummarize],
messagesToKeep: [...preparation.messagesToKeep], messagesToKeep: [...preparation.messagesToKeep],
tokensBefore: preparation.tokensBefore, tokensBefore: preparation.tokensBefore,

View file

@ -130,6 +130,8 @@ export type SessionEvent =
| (SessionEventBase & { | (SessionEventBase & {
reason: "before_compact"; reason: "before_compact";
cutPoint: CutPointResult; 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 */ /** Messages that will be summarized and discarded */
messagesToSummarize: AppMessage[]; messagesToSummarize: AppMessage[];
/** Messages that will be kept after the summary (recent turns) */ /** Messages that will be kept after the summary (recent turns) */