summaryrefslogtreecommitdiffhomepage
path: root/packages/session-orchestrator/src
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-22 01:09:26 +0900
committerAdam Malczewski <[email protected]>2026-06-22 01:09:26 +0900
commit5af664777bd64cddd168679d6369cd188212201a (patch)
tree8e72022183b8d72eccdb568de5776d74cd91f1aa /packages/session-orchestrator/src
parent28154825fb47248be21a0d64fc36492fb01c9a42 (diff)
downloaddispatch-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.ts8
-rw-r--r--packages/session-orchestrator/src/orchestrator.ts9
-rw-r--r--packages/session-orchestrator/src/queue.test.ts2
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() {},
};
}