feat: sqlite session storage (#6594)

This PR:

- Adds retainedTail to compaction entries in the new agent harness so we don't have to walk up the tree for the 2000 tokens before compaction,
- Changes getPathToRoot to getPathToRootOrCompaction to only load until last compaction, as unnecessary to access all nodes where it is called,
- Adds a SQLite storage backend, in a separate packages/session-backend-sqlite, with a migration system and schemas as per on-site discussions: sessions to match session header messages (except for metadata, which I couldn't understand what it's used for or where it gets written, so I omitted it), session_entries for shared entry types as columns plus payload as a json for what remains, session_sequences to represent the append-only, serialized nature of the jsonl files, branch_entries to attribute nodes to branches (relationship one-to-many), and session_materialized with the session info (see /session in TUI) to act as a "cache" or quick-access for costs, message count, token info, labels, session name, and model-thinking-level config (e.g. for fast resume).
- This is compatible with the new agent harness Session abstraction.
This commit is contained in:
Cristina Poncela Cubeiro
2026-07-21 11:36:31 +02:00
committed by GitHub
parent 54fad505b9
commit 9e7582aa03
40 changed files with 2659 additions and 145 deletions
@@ -97,12 +97,14 @@ function getMessageFromEntryForCompaction(entry: SessionTreeEntry): AgentMessage
export interface CompactionResult<T = unknown> {
/** Summary text that replaces compacted history in future context. */
summary: string;
/** Entry id where retained history starts. */
firstKeptEntryId: string;
/** Entry id where retained history starts. Optional during Pi 2.0 transition. */
firstKeptEntryId?: string;
/** Estimated context tokens before compaction. */
tokensBefore: number;
/** Usage from the LLM call(s) that generated this summary, if available. */
usage?: Usage;
/** Retained recent messages stored directly on the compaction entry. Optional during Pi 2.0 transition. */
retainedTail?: AgentMessage[];
/** Optional implementation-specific details stored with the compaction entry. */
details?: T;
}
@@ -583,6 +585,8 @@ export interface CompactionPreparation {
messagesToSummarize: AgentMessage[];
/** Prefix messages summarized separately when compaction splits a turn. */
turnPrefixMessages: AgentMessage[];
/** Recent messages retained after compaction and stored on the compaction entry. */
retainedTail: AgentMessage[];
/** Whether compaction splits a turn. */
isSplitTurn: boolean;
/** Estimated context tokens before compaction. */
@@ -617,7 +621,9 @@ export function prepareCompaction(
if (prevCompactionIndex >= 0) {
const prevCompaction = pathEntries[prevCompactionIndex] as CompactionEntry;
previousSummary = prevCompaction.summary;
const firstKeptEntryIndex = pathEntries.findIndex((entry) => entry.id === prevCompaction.firstKeptEntryId);
const firstKeptEntryIndex = prevCompaction.firstKeptEntryId
? pathEntries.findIndex((entry) => entry.id === prevCompaction.firstKeptEntryId)
: -1;
boundaryStart = firstKeptEntryIndex >= 0 ? firstKeptEntryIndex : prevCompactionIndex + 1;
}
const boundaryEnd = pathEntries.length;
@@ -644,6 +650,11 @@ export function prepareCompaction(
if (msg) turnPrefixMessages.push(msg);
}
}
const retainedTail: AgentMessage[] = [];
for (let i = cutPoint.firstKeptEntryIndex; i < boundaryEnd; i++) {
const msg = getMessageFromEntryForCompaction(pathEntries[i]);
if (msg) retainedTail.push(msg);
}
const fileOps = extractFileOperations(messagesToSummarize, pathEntries, prevCompactionIndex);
if (cutPoint.isSplitTurn) {
for (const msg of turnPrefixMessages) {
@@ -655,6 +666,7 @@ export function prepareCompaction(
firstKeptEntryId,
messagesToSummarize,
turnPrefixMessages,
retainedTail,
isSplitTurn: cutPoint.isSplitTurn,
tokensBefore,
previousSummary,
@@ -693,6 +705,7 @@ export async function compact(
firstKeptEntryId,
messagesToSummarize,
turnPrefixMessages,
retainedTail,
isSplitTurn,
tokensBefore,
previousSummary,
@@ -762,6 +775,7 @@ export async function compact(
firstKeptEntryId,
tokensBefore,
usage: summaryUsage,
retainedTail,
details: { readFiles, modifiedFiles } as CompactionDetails,
});
}