// Find the latest compaction entry in the current branch
let latestCompactionIdx = -1; for (let i = branch.length - 1; i >= 0; i--) { if (branch[i].type === "compaction") {
latestCompactionIdx = i; break;
}
}
if (latestCompactionIdx < 0) { return { truncated: false, entriesRemoved: 0, reason: "no compaction entry found" };
}
// Nothing to truncate if compaction is already at root if (latestCompactionIdx === 0) { return { truncated: false, entriesRemoved: 0, reason: "compaction already at root" };
}
// The compaction's firstKeptEntryId marks the start of the "unsummarized // tail" — entries from firstKeptEntryId through the compaction that // buildSessionContext() expects to find when reconstructing the session. // Only entries *before* firstKeptEntryId were actually summarized. const compactionEntry = branch[latestCompactionIdx] as CompactionEntry; const { firstKeptEntryId } = compactionEntry;
// Collect IDs of entries in the current branch that were actually summarized // (everything before firstKeptEntryId). Entries from firstKeptEntryId through // the compaction are the unsummarized tail and must be preserved. const summarizedBranchIds = new Set<string>(); for (let i = 0; i < latestCompactionIdx; i++) { if (firstKeptEntryId && branch[i].id === firstKeptEntryId) { break; // Everything from here to the compaction is the unsummarized tail
}
summarizedBranchIds.add(branch[i].id);
}
// Operate on the full transcript so sibling branches and tree metadata // are not silently dropped. const allEntries = sm.getEntries();
// Only remove message-type entries that the compaction actually summarized. // Non-message session state (custom, model_change, thinking_level_change, // session_info, custom_message) is preserved even if it sits in the // summarized portion of the branch. // // label and branch_summary entries that reference removed message IDs are // also dropped to avoid dangling metadata (consistent with the approach in // tool-result-truncation.ts). const removedIds = new Set<string>(); for (const entry of allEntries) { if (summarizedBranchIds.has(entry.id) && entry.type === "message") {
removedIds.add(entry.id);
}
}
// Labels bookmark targetId while parentId just records the leaf when the // label was changed, so targetId determines whether the label is still valid. // Branch summaries still hang off the summarized branch via parentId. for (const entry of allEntries) { if (entry.type === "label" && removedIds.has(entry.targetId)) {
removedIds.add(entry.id); continue;
} if (
entry.type === "branch_summary" &&
entry.parentId !== null &&
removedIds.has(entry.parentId)
) {
removedIds.add(entry.id);
}
}
if (removedIds.size === 0) { return { truncated: false, entriesRemoved: 0, reason: "no entries to remove" };
}
// Build an id→entry map for walking parent chains during re-parenting. const entryById = new Map<string, SessionEntry>(); for (const entry of allEntries) {
entryById.set(entry.id, entry);
}
// Keep every entry that was not removed, re-parenting where necessary so // the tree stays connected. const keptEntries: SessionEntry[] = []; for (const entry of allEntries) { if (removedIds.has(entry.id)) { continue;
}
// Walk up the parent chain to find the nearest kept ancestor.
let newParentId = entry.parentId; while (newParentId !== null && removedIds.has(newParentId)) { const parent = entryById.get(newParentId);
newParentId = parent?.parentId ?? null;
}
// Get file size before truncation
let bytesBefore = 0; try { const stat = await fs.stat(sessionFile);
bytesBefore = stat.size;
} catch { // If stat fails, continue anyway
}
// Archive original file if requested if (params.archivePath) { try { const archiveDir = path.dirname(params.archivePath);
await fs.mkdir(archiveDir, { recursive: true });
await fs.copyFile(sessionFile, params.archivePath);
log.info(`[session-truncation] Archived pre-truncation file to ${params.archivePath}`);
} catch (err) { const reason = formatErrorMessage(err);
log.warn(`[session-truncation] Failed to archive: ${reason}`);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.