diff options
| author | Adam Malczewski <[email protected]> | 2026-06-22 01:09:26 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-22 01:09:26 +0900 |
| commit | 5af664777bd64cddd168679d6369cd188212201a (patch) | |
| tree | 8e72022183b8d72eccdb568de5776d74cd91f1aa /packages/session-orchestrator/src | |
| parent | 28154825fb47248be21a0d64fc36492fb01c9a42 (diff) | |
| download | dispatch-5af664777bd64cddd168679d6369cd188212201a.tar.gz dispatch-5af664777bd64cddd168679d6369cd188212201a.zip | |
feat: non-destructive compaction — fork history to archive before replacing
Compaction now preserves the full pre-compaction history:
1. Forks the conversation to a new archive ID (complete copy: chunks,
metadata, cwd, reasoning-effort). Archive gets status=closed,
title='Archive: <original>', compactedFrom=<originalId>.
2. Replaces the original conversation's history with [system: summary]
+ recent N messages (same as before).
3. Sets compactedFrom=<archiveId> on the original conversation's metadata.
The original history is never destroyed. The archive is accessible via
GET /conversations/:id using the archive ID.
Wire/contract changes:
- ConversationMeta: add compactedFrom?: string
- CompactionResult: add archiveId: string
- ConversationCompactedMessage: add archiveId
- CompactResponse: add archiveId
Conversation store:
- forkHistory(sourceId, targetId): copies all chunks + metadata to a
new conversation ID
- setCompactedFrom(conversationId, archiveId): marks the conversation
Diffstat (limited to 'packages/session-orchestrator/src')
| -rw-r--r-- | packages/session-orchestrator/src/orchestrator.test.ts | 8 | ||||
| -rw-r--r-- | packages/session-orchestrator/src/orchestrator.ts | 9 | ||||
| -rw-r--r-- | packages/session-orchestrator/src/queue.test.ts | 2 |
3 files changed, 19 insertions, 0 deletions
diff --git a/packages/session-orchestrator/src/orchestrator.test.ts b/packages/session-orchestrator/src/orchestrator.test.ts index e657fe2..d5585c2 100644 --- a/packages/session-orchestrator/src/orchestrator.test.ts +++ b/packages/session-orchestrator/src/orchestrator.test.ts @@ -95,6 +95,8 @@ function createInMemoryStore(): ConversationStore & { return null; }, async setCompactThreshold() {}, + async forkHistory() {}, + async setCompactedFrom() {}, }; } @@ -550,6 +552,8 @@ describe("turn-sealed event", () => { return null; }, async setCompactThreshold() {}, + async forkHistory() {}, + async setCompactedFrom() {}, }; const { orchestrator } = createSessionOrchestrator({ @@ -621,6 +625,8 @@ describe("turn-sealed event", () => { return null; }, async setCompactThreshold() {}, + async forkHistory() {}, + async setCompactedFrom() {}, }; const { orchestrator } = createSessionOrchestrator({ @@ -981,6 +987,8 @@ describe("turn metrics persistence", () => { return null; }, async setCompactThreshold() {}, + async forkHistory() {}, + async setCompactedFrom() {}, }; const { orchestrator } = createSessionOrchestrator({ diff --git a/packages/session-orchestrator/src/orchestrator.ts b/packages/session-orchestrator/src/orchestrator.ts index c3b94bf..21c068c 100644 --- a/packages/session-orchestrator/src/orchestrator.ts +++ b/packages/session-orchestrator/src/orchestrator.ts @@ -132,6 +132,7 @@ export const conversationStatusChanged: EventHookDescriptor<ConversationStatusCh /** Payload for the conversationCompacted bus event. */ export interface ConversationCompactedPayload { readonly conversationId: string; + readonly archiveId: string; readonly messagesSummarized: number; readonly messagesKept: number; } @@ -801,6 +802,11 @@ export function createCompactionService( return { error: "model produced empty summary" }; } + // Non-destructive: fork the full pre-compaction history to an + // archive conversation before replacing it. + const archiveId = crypto.randomUUID(); + await deps.conversationStore.forkHistory(conversationId, archiveId); + // Replace history: [system: summary] + recent messages const summaryMessage: ChatMessage = { role: "system", @@ -813,15 +819,18 @@ export function createCompactionService( }; await deps.conversationStore.replaceHistory(conversationId, [summaryMessage, ...toKeep]); + await deps.conversationStore.setCompactedFrom(conversationId, archiveId); const result: CompactionResult = { summary, + archiveId, messagesSummarized: toSummarize.length, messagesKept: toKeep.length, }; deps.emit(conversationCompacted, { conversationId, + archiveId, messagesSummarized: toSummarize.length, messagesKept: toKeep.length, }); diff --git a/packages/session-orchestrator/src/queue.test.ts b/packages/session-orchestrator/src/queue.test.ts index ae24ddf..346f9c4 100644 --- a/packages/session-orchestrator/src/queue.test.ts +++ b/packages/session-orchestrator/src/queue.test.ts @@ -91,6 +91,8 @@ function createInMemoryStore(): ConversationStore & { return null; }, async setCompactThreshold() {}, + async forkHistory() {}, + async setCompactedFrom() {}, }; } |
