@ponythewhite/base-context 1.0.11 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/base-context-runtime/pyproject.toml +1 -1
- package/dist/base-context-runtime/uv.lock +1 -1
- package/dist/build-info.json +1 -1
- package/dist/bundle/amazon-bedrock.js +3 -3
- package/dist/bundle/{anthropic-5PSCZEER.js → anthropic-BJVPUMPB.js} +2 -2
- package/dist/bundle/{azure-openai-responses-AYTLJR33.js → azure-openai-responses-ERRMAXK6.js} +4 -5
- package/dist/bundle/{bundled-modules-PEFT72QN.js → bundled-modules-URKVA3UB.js} +5 -5
- package/dist/bundle/{chunk-D3KOF57W.js → chunk-25VYRAC6.js} +14 -1
- package/dist/bundle/{chunk-2IPDSTDR.js → chunk-AEMRBMI2.js} +9 -9
- package/dist/bundle/{chunk-Q3MBSBEB.js → chunk-FOGKLC7V.js} +1 -1
- package/dist/bundle/{chunk-KT2BNEPE.js → chunk-MUFZODP5.js} +1 -1
- package/dist/bundle/{chunk-QKIFBA7N.js → chunk-Q6UAHLII.js} +2 -2
- package/dist/bundle/{chunk-TOOVREMP.js → chunk-TKOBEJ5E.js} +515 -107
- package/dist/bundle/{chunk-JZHGOOFM.js → chunk-TXVF6Q5H.js} +118 -22
- package/dist/bundle/{cli-main-BRXGYTGK.js → cli-main-ZTJM5EEH.js} +4 -4
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/{google-HTMCUJ7X.js → google-4GA4YMUX.js} +2 -2
- package/dist/bundle/{google-vertex-ZQO65AXF.js → google-vertex-7AMALD7D.js} +2 -2
- package/dist/bundle/{main-POUXGEL6.js → main-53I74INO.js} +5 -5
- package/dist/bundle/{mistral-SB7LU56G.js → mistral-74BXMNRM.js} +1 -1
- package/dist/bundle/{openai-codex-responses-5CEW4VN3.js → openai-codex-responses-COXNC4IH.js} +2 -2
- package/dist/bundle/{openai-completions-BAZJQSS2.js → openai-completions-BCSA5Q6S.js} +2 -2
- package/dist/bundle/{openai-responses-VHOE427P.js → openai-responses-ONCIZSJS.js} +3 -3
- package/dist/core/agent-session.d.ts +6 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +362 -103
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +9 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +29 -3
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/context-tree.d.ts +13 -10
- package/dist/core/context-tree.d.ts.map +1 -1
- package/dist/core/context-tree.js +49 -3
- package/dist/core/context-tree.js.map +1 -1
- package/dist/core/inference-coordinator.d.ts +1 -1
- package/dist/core/inference-coordinator.d.ts.map +1 -1
- package/dist/core/inference-coordinator.js +18 -5
- package/dist/core/inference-coordinator.js.map +1 -1
- package/dist/core/messages.d.ts +2 -0
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +2 -0
- package/dist/core/messages.js.map +1 -1
- package/dist/core/refinement/refinement.d.ts +2 -0
- package/dist/core/refinement/refinement.d.ts.map +1 -1
- package/dist/core/refinement/refinement.js +10 -1
- package/dist/core/refinement/refinement.js.map +1 -1
- package/dist/core/request-usage.d.ts +34 -0
- package/dist/core/request-usage.d.ts.map +1 -0
- package/dist/core/request-usage.js +111 -0
- package/dist/core/request-usage.js.map +1 -0
- package/dist/core/settings-manager.d.ts +3 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +7 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts +2 -0
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +4 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/modes/agent-connection/daemon-agent-connection.d.ts.map +1 -1
- package/dist/modes/agent-connection/daemon-agent-connection.js +8 -1
- package/dist/modes/agent-connection/daemon-agent-connection.js.map +1 -1
- package/dist/modes/daemon/daemon-protocol.d.ts +13 -3
- package/dist/modes/daemon/daemon-protocol.d.ts.map +1 -1
- package/dist/modes/daemon/daemon-protocol.js +10 -2
- package/dist/modes/daemon/daemon-protocol.js.map +1 -1
- package/dist/modes/daemon/daemon-session-summarizer.d.ts +3 -0
- package/dist/modes/daemon/daemon-session-summarizer.d.ts.map +1 -1
- package/dist/modes/daemon/daemon-session-summarizer.js +71 -32
- package/dist/modes/daemon/daemon-session-summarizer.js.map +1 -1
- package/dist/modes/interactive/components/context-tree-format.d.ts.map +1 -1
- package/dist/modes/interactive/components/context-tree-format.js +49 -0
- package/dist/modes/interactive/components/context-tree-format.js.map +1 -1
- package/docs/compaction.md +57 -4
- package/docs/context-management.md +23 -1
- package/docs/rlm.md +1 -1
- package/docs/settings.md +21 -0
- package/docs/usage.md +24 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compaction.js","sourceRoot":"","sources":["../../../src/core/compaction/compaction.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Context compaction for long sessions.\n *\n * Pure functions for compaction logic. The session manager handles I/O,\n * and after compaction the session is reloaded.\n */\n\nimport type { AgentMessage, ThinkingLevel } from \"@ponythewhite/base-context-agent\";\nimport type { AssistantMessage, Model, Usage } from \"@ponythewhite/base-context-ai\";\nimport {\n\tcompleteInference,\n\tInferenceCoordinator,\n\ttype NativeCompactionOutputBinding,\n} from \"../inference-coordinator.js\";\nimport {\n\tCOMPACTION_SUMMARY_PREFIX,\n\tCOMPACTION_SUMMARY_SUFFIX,\n\tconvertToLlm,\n\tcreateBranchSummaryMessage,\n\tcreateCompactionSummaryMessage,\n\tcreateCustomMessage,\n} from \"../messages.js\";\nimport type { NativeCompactionRequestOutputAssociation } from \"../request-events.js\";\nimport { MODEL_REQUEST_ID_HEADER } from \"../semantic-edges.js\";\nimport { buildSessionContext, type CompactionEntry, type SessionEntry } from \"../session-manager.js\";\nimport { addAssistantUsage, emptyUsage } from \"../usage.js\";\nimport {\n\tcomputeFileLists,\n\tcreateFileOps,\n\textractFileOpsFromMessage,\n\ttype FileOperations,\n\tformatFileOperations,\n\tSUMMARIZATION_SYSTEM_PROMPT,\n\tserializeConversation,\n} from \"./utils.js\";\n/** Details stored in CompactionEntry.details for file tracking */\nexport interface CompactionDetails {\n\treadFiles: string[];\n\tmodifiedFiles: string[];\n}\n\nexport interface SummarySlice {\n\tsummary: string;\n\tusage?: Usage;\n}\n\n// Identity follows the actual completion -> text slice -> built-in composition, never copied fields.\nconst nativeSummarySlices = new WeakMap<SummarySlice, { summary: string; binding: NativeCompactionOutputBinding }>();\nconst nativeCompactionResults = new WeakMap<object, { summary: string; bindings: NativeCompactionOutputBinding[] }>();\n\nfunction captureSummarySlice(\n\tslice: SummarySlice,\n\trequests: InferenceCoordinator | undefined,\n\tcompletion: Promise<AssistantMessage>,\n\tpart: NativeCompactionRequestOutputAssociation[\"part\"],\n): SummarySlice {\n\tconst binding =\n\t\trequests instanceof InferenceCoordinator\n\t\t\t? InferenceCoordinator.prototype.takeCompactionOutput.call(requests, completion)\n\t\t\t: undefined;\n\tif (binding?.output.part === part) nativeSummarySlices.set(slice, { summary: slice.summary, binding });\n\treturn slice;\n}\n\n/** Internal append input: only this exact built-in result and original sink can contribute links. */\nexport function takeCompactionRequestOutputs(\n\tresult: object | undefined,\n\tsink: object,\n\tsummary: string,\n): readonly NativeCompactionRequestOutputAssociation[] | undefined {\n\tconst captured = result ? nativeCompactionResults.get(result) : undefined;\n\tif (result) nativeCompactionResults.delete(result);\n\t// Equality only checks an already privately bound projection for mutation; it never discovers a link.\n\tif (!captured || captured.summary !== summary) return undefined;\n\tconst outputs = captured.bindings.filter((binding) => binding.sink === sink).map((binding) => binding.output);\n\treturn outputs.length ? outputs : undefined;\n}\n\n/**\n * Extract file operations from messages and previous compaction entries.\n */\n/** Preserve file operations recorded by prior compactions and current tool calls. */\nfunction extractFileOperations(\n\tmessages: AgentMessage[],\n\tentries: SessionEntry[],\n\tprevCompactionIndex: number,\n): FileOperations {\n\tconst fileOps = createFileOps();\n\tif (prevCompactionIndex >= 0) {\n\t\tconst prevCompaction = entries[prevCompactionIndex] as CompactionEntry;\n\t\tif (!prevCompaction.fromHook && prevCompaction.details) {\n\t\t\t// fromHook field kept for session file compatibility\n\t\t\tconst details = prevCompaction.details as CompactionDetails;\n\t\t\tif (Array.isArray(details.readFiles)) {\n\t\t\t\tfor (const f of details.readFiles) fileOps.read.add(f);\n\t\t\t}\n\t\t\tif (Array.isArray(details.modifiedFiles)) {\n\t\t\t\tfor (const f of details.modifiedFiles) fileOps.edited.add(f);\n\t\t\t}\n\t\t}\n\t}\n\tfor (const msg of messages) {\n\t\textractFileOpsFromMessage(msg, fileOps);\n\t}\n\n\treturn fileOps;\n}\n/**\n * Extract AgentMessage from an entry if it produces one.\n * Returns undefined for entries that don't contribute to LLM context.\n */\nfunction getMessageFromEntry(entry: SessionEntry): AgentMessage | undefined {\n\tif (entry.type === \"message\") {\n\t\treturn entry.message;\n\t}\n\tif (entry.type === \"custom_message\") {\n\t\treturn createCustomMessage(entry.customType, entry.content, entry.display, entry.details, entry.timestamp);\n\t}\n\tif (entry.type === \"branch_summary\") {\n\t\treturn createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\t}\n\tif (entry.type === \"compaction\") {\n\t\treturn createCompactionSummaryMessage(\n\t\t\tentry.summary,\n\t\t\tentry.tokensBefore,\n\t\t\tentry.timestamp,\n\t\t\tentry.customInstructions,\n\t\t);\n\t}\n\treturn undefined;\n}\n\nfunction getMessageFromEntryForCompaction(entry: SessionEntry): AgentMessage | undefined {\n\tif (entry.type === \"compaction\") {\n\t\treturn undefined;\n\t}\n\treturn getMessageFromEntry(entry);\n}\n\n/** Result from compact() - SessionManager adds uuid/parentUuid when saving */\nexport interface CompactionResult<T = unknown> {\n\tsummary: string;\n\tfirstKeptEntryId: string;\n\t/** Prior-context estimate; null when the original context is not measurable. */\n\ttokensBefore: number | null;\n\t/** Extension-specific data (e.g., ArtifactIndex, version markers for structured compaction) */\n\tdetails?: T;\n\t/** What the summarization call(s) billed; persisted on the compaction entry. */\n\tusage?: Usage;\n}\nexport const COMPACT_SKILL_NAME = \"compact\";\n\nexport interface CompactionSettings {\n\tenabled: boolean;\n\treserveTokens: number;\n\tkeepRecentTokens: number;\n}\n\nexport const DEFAULT_COMPACTION_SETTINGS: CompactionSettings = {\n\tenabled: true,\n\treserveTokens: 16384,\n\tkeepRecentTokens: 20000,\n};\n/**\n * Calculate total context tokens from usage.\n * Uses the native totalTokens field when available, falls back to computing from components.\n *\n * Includes output: the assistant's response becomes part of the prompt on the next\n * request, so it counts toward the context the next turn will send.\n */\nexport function calculateContextTokens(usage: Usage): number {\n\treturn usage.totalTokens || usage.input + usage.output + usage.cacheRead + usage.cacheWrite;\n}\n\n/**\n * Get usage from an assistant message if available.\n * Skips aborted and error messages as they don't have valid usage data.\n */\nfunction getAssistantUsage(msg: AgentMessage): Usage | undefined {\n\tif (msg.role === \"assistant\" && \"usage\" in msg) {\n\t\tconst assistantMsg = msg as AssistantMessage;\n\t\tif (assistantMsg.stopReason !== \"aborted\" && assistantMsg.stopReason !== \"error\" && assistantMsg.usage) {\n\t\t\treturn assistantMsg.usage;\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/**\n * Find the last non-aborted assistant message usage from session entries.\n */\nexport function getLastAssistantUsage(entries: SessionEntry[]): Usage | undefined {\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tif (entry.type === \"message\") {\n\t\t\tconst usage = getAssistantUsage(entry.message);\n\t\t\tif (usage) return usage;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nexport interface ContextUsageEstimate {\n\ttokens: number;\n\tusageTokens: number;\n\ttrailingTokens: number;\n\tlastUsageIndex: number | null;\n}\n\nfunction getLastAssistantUsageInfo(messages: readonly AgentMessage[]): { usage: Usage; index: number } | undefined {\n\tfor (let i = messages.length - 1; i >= 0; i--) {\n\t\tconst usage = getAssistantUsage(messages[i]);\n\t\tif (usage) return { usage, index: i };\n\t}\n\treturn undefined;\n}\n\n/**\n * Estimate context tokens from messages, using the last assistant usage when available.\n * If there are messages after the last usage, estimate their tokens with estimateTokens.\n */\nexport function estimateContextTokens(messages: AgentMessage[]): ContextUsageEstimate {\n\tconst usageInfo = getLastAssistantUsageInfo(messages);\n\n\tif (!usageInfo) {\n\t\tlet estimated = 0;\n\t\tfor (const message of messages) {\n\t\t\testimated += estimateTokens(message);\n\t\t}\n\t\treturn {\n\t\t\ttokens: estimated,\n\t\t\tusageTokens: 0,\n\t\t\ttrailingTokens: estimated,\n\t\t\tlastUsageIndex: null,\n\t\t};\n\t}\n\n\tconst usageTokens = calculateContextTokens(usageInfo.usage);\n\tlet trailingTokens = 0;\n\tfor (let i = usageInfo.index + 1; i < messages.length; i++) {\n\t\ttrailingTokens += estimateTokens(messages[i]);\n\t}\n\n\treturn {\n\t\ttokens: usageTokens + trailingTokens,\n\t\tusageTokens,\n\t\ttrailingTokens,\n\t\tlastUsageIndex: usageInfo.index,\n\t};\n}\n\n/**\n * Check if compaction should trigger based on context usage.\n */\nexport function shouldCompact(contextTokens: number, contextWindow: number, settings: CompactionSettings): boolean {\n\tif (!settings.enabled) return false;\n\tif (contextWindow <= 0) return false;\n\treturn contextTokens > contextWindow - settings.reserveTokens;\n}\n/** Raise suffix estimates when observed usage exceeds chars/4 for the same prefix. */\nfunction compactionTokenScale(messages: readonly AgentMessage[]): number {\n\tconst usageInfo = getLastAssistantUsageInfo(messages);\n\tif (!usageInfo) return 1;\n\tlet estimatedTokens = 0;\n\tfor (let index = 0; index <= usageInfo.index; index++) {\n\t\testimatedTokens += estimateTokens(messages[index]);\n\t}\n\treturn estimatedTokens > 0 ? Math.max(1, calculateContextTokens(usageInfo.usage) / estimatedTokens) : 1;\n}\n\n/** Estimate message tokens with chars/4; dense content can exceed this heuristic. */\nexport function estimateTokens(message: AgentMessage): number {\n\tlet chars = 0;\n\n\tswitch (message.role) {\n\t\tcase \"user\": {\n\t\t\tconst content = (message as { content: string | Array<{ type: string; text?: string }> }).content;\n\t\t\tif (typeof content === \"string\") {\n\t\t\t\tchars = content.length;\n\t\t\t} else if (Array.isArray(content)) {\n\t\t\t\tfor (const block of content) {\n\t\t\t\t\tif (block.type === \"text\" && block.text) {\n\t\t\t\t\t\tchars += block.text.length;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn Math.ceil(chars / 4);\n\t\t}\n\t\tcase \"assistant\": {\n\t\t\tconst assistant = message as AssistantMessage;\n\t\t\tfor (const block of assistant.content) {\n\t\t\t\tif (block.type === \"text\") {\n\t\t\t\t\tchars += block.text.length;\n\t\t\t\t} else if (block.type === \"thinking\") {\n\t\t\t\t\tchars += block.thinking.length;\n\t\t\t\t} else if (block.type === \"toolCall\") {\n\t\t\t\t\tchars += block.name.length + JSON.stringify(block.arguments).length;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn Math.ceil(chars / 4);\n\t\t}\n\t\tcase \"custom\":\n\t\tcase \"toolResult\": {\n\t\t\tif (typeof message.content === \"string\") {\n\t\t\t\tchars = message.content.length;\n\t\t\t} else {\n\t\t\t\tfor (const block of message.content) {\n\t\t\t\t\tif (block.type === \"text\" && block.text) {\n\t\t\t\t\t\tchars += block.text.length;\n\t\t\t\t\t}\n\t\t\t\t\tif (block.type === \"image\") {\n\t\t\t\t\t\tchars += 4800; // Estimate images as 4000 chars, or 1200 tokens\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn Math.ceil(chars / 4);\n\t\t}\n\t\tcase \"bashExecution\": {\n\t\t\tchars = message.command.length + message.output.length;\n\t\t\treturn Math.ceil(chars / 4);\n\t\t}\n\t\tcase \"branchSummary\":\n\t\tcase \"compactionSummary\": {\n\t\t\tchars = message.summary.length;\n\t\t\treturn Math.ceil(chars / 4);\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n/**\n * Find valid cut points: indices of user, assistant, custom, or bashExecution messages.\n * Never cut at tool results (they must follow their tool call).\n * When we cut at an assistant message with tool calls, its tool results follow it\n * and will be kept.\n * BashExecutionMessage is treated like a user message (user-initiated context).\n */\nfunction findValidCutPoints(entries: SessionEntry[], startIndex: number, endIndex: number): number[] {\n\tconst cutPoints: number[] = [];\n\tfor (let i = startIndex; i < endIndex; i++) {\n\t\tconst entry = entries[i];\n\t\tswitch (entry.type) {\n\t\t\tcase \"message\": {\n\t\t\t\tconst role = entry.message.role;\n\t\t\t\tswitch (role) {\n\t\t\t\t\tcase \"bashExecution\":\n\t\t\t\t\tcase \"custom\":\n\t\t\t\t\tcase \"branchSummary\":\n\t\t\t\t\tcase \"compactionSummary\":\n\t\t\t\t\tcase \"user\":\n\t\t\t\t\tcase \"assistant\":\n\t\t\t\t\t\tcutPoints.push(i);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"toolResult\":\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"thinking_level_change\":\n\t\t\tcase \"model_change\":\n\t\t\tcase \"compaction\":\n\t\t\tcase \"branch_summary\":\n\t\t\tcase \"custom\":\n\t\t\tcase \"custom_message\":\n\t\t\tcase \"label\":\n\t\t\tcase \"session_info\":\n\t\t\t\tbreak;\n\t\t}\n\t\t// Branch summaries and custom messages are user-role turn boundaries.\n\t\tif (entry.type === \"branch_summary\" || entry.type === \"custom_message\") {\n\t\t\tcutPoints.push(i);\n\t\t}\n\t}\n\treturn cutPoints;\n}\n\n/**\n * Find the user message (or bashExecution) that starts the turn containing the given entry index.\n * Returns -1 if no turn start found before the index.\n * BashExecutionMessage is treated like a user message for turn boundaries.\n */\nexport function findTurnStartIndex(entries: SessionEntry[], entryIndex: number, startIndex: number): number {\n\tfor (let i = entryIndex; i >= startIndex; i--) {\n\t\tconst entry = entries[i];\n\t\tif (entry.type === \"branch_summary\" || entry.type === \"custom_message\") {\n\t\t\treturn i;\n\t\t}\n\t\tif (entry.type === \"message\") {\n\t\t\tconst role = entry.message.role;\n\t\t\tif (role === \"user\" || role === \"bashExecution\") {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t}\n\treturn -1;\n}\n\nexport interface CutPointResult {\n\t/** Index of first entry to keep */\n\tfirstKeptEntryIndex: number;\n\t/** Index of user message that starts the turn being split, or -1 if not splitting */\n\tturnStartIndex: number;\n\t/** Whether this cut splits a turn (cut point is not a user message) */\n\tisSplitTurn: boolean;\n}\n\n/**\n * Find the cut point in session entries that keeps approximately `keepRecentTokens`.\n *\n * Algorithm: Walk backwards from newest, accumulating estimated message sizes.\n * Stop when we've accumulated >= keepRecentTokens. Cut at that point.\n *\n * Can cut at user OR assistant messages (never tool results). When cutting at an\n * assistant message with tool calls, its tool results come after and will be kept.\n *\n * Returns CutPointResult with:\n * - firstKeptEntryIndex: the entry index to start keeping from\n * - turnStartIndex: if cutting mid-turn, the user message that started that turn\n * - isSplitTurn: whether we're cutting in the middle of a turn\n *\n * Only considers entries between `startIndex` and `endIndex` (exclusive).\n */\nexport function findCutPoint(\n\tentries: SessionEntry[],\n\tstartIndex: number,\n\tendIndex: number,\n\tkeepRecentTokens: number,\n\tallowShortSession = false,\n\ttokenScale = 1,\n): CutPointResult {\n\tconst cutPoints = findValidCutPoints(entries, startIndex, endIndex);\n\n\tif (cutPoints.length === 0) {\n\t\treturn { firstKeptEntryIndex: startIndex, turnStartIndex: -1, isSplitTurn: false };\n\t}\n\tlet accumulatedTokens = 0;\n\tlet cutIndex = cutPoints[0]; // Default: keep from first message (not header)\n\n\tfor (let i = endIndex - 1; i >= startIndex; i--) {\n\t\tconst entry = entries[i];\n\t\tif (entry.type !== \"message\") continue;\n\t\tconst messageTokens = estimateTokens(entry.message) * tokenScale;\n\t\taccumulatedTokens += messageTokens;\n\t\tif (accumulatedTokens >= keepRecentTokens) {\n\t\t\tcutIndex = cutPoints.filter((candidate) => candidate <= i).at(-1) ?? cutIndex;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (allowShortSession && cutIndex === cutPoints[0]) {\n\t\t// An explicit compact may summarize a short prefix, but must keep a real suffix.\n\t\tconst latest = cutPoints\n\t\t\t.filter((index) => {\n\t\t\t\tconst entry = entries[index];\n\t\t\t\treturn entry.type !== \"message\" || entry.message.role !== \"compactionSummary\";\n\t\t\t})\n\t\t\t.at(-1);\n\t\tif (latest !== undefined) cutIndex = latest;\n\t}\n\twhile (cutIndex > startIndex) {\n\t\tconst prevEntry = entries[cutIndex - 1];\n\t\tif (prevEntry.type === \"compaction\") {\n\t\t\tbreak;\n\t\t}\n\t\tif (prevEntry.type === \"message\") {\n\t\t\tbreak;\n\t\t}\n\t\tcutIndex--;\n\t}\n\tconst cutEntry = entries[cutIndex];\n\tconst isUserMessage = cutEntry.type === \"message\" && cutEntry.message.role === \"user\";\n\t// A cut in a non-user turn requires a prefix summary.\n\tconst turnStartIndex = isUserMessage ? -1 : findTurnStartIndex(entries, cutIndex, startIndex);\n\n\treturn {\n\t\tfirstKeptEntryIndex: cutIndex,\n\t\tturnStartIndex,\n\t\tisSplitTurn: !isUserMessage && turnStartIndex !== -1,\n\t};\n}\nconst SUMMARIZATION_PROMPT = `The messages above are a conversation to summarize. Create a structured context checkpoint summary that another LLM will use to continue the work.\n\nUse this EXACT format:\n\n## Goal\n[What is the user trying to accomplish? Can be multiple items if the session covers different tasks.]\n\n## Constraints & Preferences\n- [Any constraints, preferences, or requirements mentioned by user]\n- [Or \"(none)\" if none were mentioned]\n\n## Progress\n### Done\n- [x] [Completed tasks/changes]\n\n### In Progress\n- [ ] [Current work]\n\n### Blocked\n- [Issues preventing progress, if any]\n\n## Key Decisions\n- **[Decision]**: [Brief rationale]\n\n## Next Steps\n1. [Ordered list of what should happen next]\n\n## Critical Context\n- [Any data, examples, or references needed to continue]\n- [Or \"(none)\" if not applicable]\n\nKeep each section concise. Preserve exact file paths, function names, and error messages.`;\n\nconst RUNTIME_STATE_SUMMARY_NOTE =\n\t\"Runtime note: this summary does not establish whether a Python kernel is live or whether its variables, imports, helpers, or jobs remain available. Preserve useful names and their last observed state, including uncertainty. Use current runtime reports before relying on them; do not infer either survival or loss from compaction.\";\n\nconst UPDATE_SUMMARIZATION_PROMPT = `The messages above are NEW conversation messages to incorporate into the existing summary provided in <previous-summary> tags.\n\nUpdate the existing structured summary with new information. RULES:\n- PRESERVE all existing information from the previous summary\n- ADD new progress, decisions, and context from the new messages\n- UPDATE the Progress section: move items from \"In Progress\" to \"Done\" when completed\n- UPDATE \"Next Steps\" based on what was accomplished\n- PRESERVE exact file paths, function names, and error messages\n- If something is no longer relevant, you may remove it\n\nUse this EXACT format:\n\n## Goal\n[Preserve existing goals, add new ones if the task expanded]\n\n## Constraints & Preferences\n- [Preserve existing, add new ones discovered]\n\n## Progress\n### Done\n- [x] [Include previously done items AND newly completed items]\n\n### In Progress\n- [ ] [Current work - update based on progress]\n\n### Blocked\n- [Current blockers - remove if resolved]\n\n## Key Decisions\n- **[Decision]**: [Brief rationale] (preserve all previous, add new)\n\n## Next Steps\n1. [Update based on current state]\n\n## Critical Context\n- [Preserve important context, add new if needed]\n\nKeep each section concise. Preserve exact file paths, function names, and error messages.`;\n\n/**\n * Build the instruction portion of the summarization prompt: the initial or\n * update template, optional user instructions, and the runtime-state qualification.\n */\nexport function buildSummarizationPrompt(customInstructions?: string, previousSummary?: string): string {\n\tlet basePrompt = previousSummary ? UPDATE_SUMMARIZATION_PROMPT : SUMMARIZATION_PROMPT;\n\tif (customInstructions) {\n\t\tbasePrompt += `\\n\\n<user-instructions>\\nThe user provided these instructions for this summary. Follow them with high priority while keeping the section format above: emphasize what they ask to focus on, and preserve verbatim anything they ask to remember.\\n${customInstructions}\\n</user-instructions>`;\n\t}\n\treturn `${basePrompt}\\n\\n${RUNTIME_STATE_SUMMARY_NOTE}`;\n}\n\n/**\n * Generate a summary of the conversation using the LLM.\n * If previousSummary is provided, uses the update prompt to merge.\n */\nexport async function generateSummary(\n\tcurrentMessages: AgentMessage[],\n\tmodel: Model<any>,\n\treserveTokens: number,\n\tapiKey: string,\n\theaders?: Record<string, string>,\n\tsignal?: AbortSignal,\n\tcustomInstructions?: string,\n\tpreviousSummary?: string,\n\tthinkingLevel?: ThinkingLevel,\n\trequests?: InferenceCoordinator,\n\toutputTokenLimit?: number,\n): Promise<SummarySlice> {\n\tconst maxTokens = outputTokenLimit ?? Math.floor(0.8 * reserveTokens);\n\n\tconst basePrompt = buildSummarizationPrompt(customInstructions, previousSummary);\n\t// Serialize before the LLM call so it summarizes rather than continues this conversation.\n\tconst llmMessages = convertToLlm(currentMessages);\n\tconst conversationText = serializeConversation(llmMessages);\n\tlet promptText = `<conversation>\\n${conversationText}\\n</conversation>\\n\\n`;\n\tif (previousSummary) {\n\t\tpromptText += `<previous-summary>\\n${previousSummary}\\n</previous-summary>\\n\\n`;\n\t}\n\tpromptText += basePrompt;\n\n\tconst summarizationMessages = [\n\t\t{\n\t\t\trole: \"user\" as const,\n\t\t\tcontent: [{ type: \"text\" as const, text: promptText }],\n\t\t\ttimestamp: Date.now(),\n\t\t},\n\t];\n\n\tconst completionOptions =\n\t\tmodel.reasoning && thinkingLevel && thinkingLevel !== \"off\"\n\t\t\t? { maxTokens, signal, apiKey, headers, reasoning: thinkingLevel }\n\t\t\t: { maxTokens, signal, apiKey, headers };\n\n\tconst completion = completeInference(\n\t\trequests,\n\t\tmodel,\n\t\t{ systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages },\n\t\tcompletionOptions,\n\t\t{\n\t\t\tpurpose: \"summary\",\n\t\t\tpurposeDetail: \"compaction\",\n\t\t\toperationId: headers?.[MODEL_REQUEST_ID_HEADER],\n\t\t\tsemanticEdgeId: headers?.[MODEL_REQUEST_ID_HEADER],\n\t\t},\n\t);\n\n\tconst response = await completion;\n\tif (response.stopReason === \"error\") {\n\t\tthrow new Error(`Summarization failed: ${response.errorMessage || \"Unknown error\"}`);\n\t}\n\n\tconst textContent = response.content\n\t\t.filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n\t\t.map((c) => c.text)\n\t\t.join(\"\\n\");\n\n\treturn captureSummarySlice({ summary: textContent, usage: response.usage }, requests, completion, \"history\");\n}\nexport interface CompactionPreparation {\n\t/** UUID of first entry to keep */\n\tfirstKeptEntryId: string;\n\t/** Messages that will be summarized and discarded */\n\tmessagesToSummarize: AgentMessage[];\n\t/** Messages that will be turned into turn prefix summary (if splitting) */\n\tturnPrefixMessages: AgentMessage[];\n\t/** Whether this is a split turn (cut point in middle of turn) */\n\tisSplitTurn: boolean;\n\ttokensBefore: number;\n\t/** Summary from previous compaction, for iterative update */\n\tpreviousSummary?: string;\n\t/** File operations extracted from messagesToSummarize */\n\tfileOps: FileOperations;\n\t/** Compaction settions from settings.jsonl\t*/\n\tsettings: CompactionSettings;\n}\n\n/** Prepare the actual selected epoch view. Entry IDs are only real cut anchors, never synthetic rows. */\nexport function prepareViewCompaction(\n\tmessages: readonly AgentMessage[],\n\tentryIds: readonly (string | undefined)[],\n\tpathEntries: SessionEntry[],\n\tsettings: CompactionSettings,\n\tmaxCutEntryId?: string,\n\tallowShortSession = false,\n\tbudgetPressure = false,\n): CompactionPreparation | undefined {\n\tif (messages.length !== entryIds.length) throw new Error(\"Compaction views do not match their source anchors\");\n\t// firstKeptEntryId restores a chronological source suffix, not a selected-view suffix.\n\t// Older pinned views cannot become cut anchors across omitted source messages.\n\tconst selectedIds = new Set(entryIds);\n\tconst suffixAnchors = new Set<string>();\n\tfor (let index = pathEntries.length - 1; index >= 0; index--) {\n\t\tconst entry = pathEntries[index];\n\t\tif (!getMessageFromEntryForCompaction(entry)) continue;\n\t\tif (!selectedIds.has(entry.id)) break;\n\t\tsuffixAnchors.add(entry.id);\n\t}\n\tlet cuts = messages.flatMap((message, index) =>\n\t\tentryIds[index] &&\n\t\tsuffixAnchors.has(entryIds[index]!) &&\n\t\tmessage.role !== \"toolResult\" &&\n\t\tmessage.role !== \"compactionSummary\"\n\t\t\t? [index]\n\t\t\t: [],\n\t);\n\tlet relaxRetention = false;\n\tif (budgetPressure) {\n\t\t// Only caller-confirmed public OVER may relax retention; source/tool groups remain whole.\n\t\tconst spans: [number, number][] = [];\n\t\tconst sourceStarts = new Map<string, number>();\n\t\tconst resultEnds = new Map<string, number>();\n\t\t// Public history renders original roles as custom text; use its captured source anchors.\n\t\tconst sourceMessages = new Map(pathEntries.map((entry) => [entry.id, getMessageFromEntry(entry)]));\n\t\tconst anchoredMessages = messages.map((message, index) => sourceMessages.get(entryIds[index] ?? \"\") ?? message);\n\t\tfor (const [index, message] of anchoredMessages.entries()) {\n\t\t\tconst entryId = entryIds[index];\n\t\t\tif (entryId) {\n\t\t\t\tconst start = sourceStarts.get(entryId);\n\t\t\t\tif (start === undefined) sourceStarts.set(entryId, index);\n\t\t\t\telse spans.push([start, index]);\n\t\t\t}\n\t\t\tif (message.role === \"toolResult\") resultEnds.set(message.toolCallId, index);\n\t\t}\n\t\tlet latestCompleteExchange = messages.length - 1;\n\t\tconst completedAssistants: number[] = [];\n\t\tfor (const [index, message] of anchoredMessages.entries()) {\n\t\t\tif (message.role !== \"assistant\") continue;\n\t\t\tconst calls = message.content.filter((block) => block.type === \"toolCall\");\n\t\t\tconst completeExchange = calls.length > 0 && calls.every((call) => (resultEnds.get(call.id) ?? -1) > index);\n\t\t\tif (completeExchange) latestCompleteExchange = index;\n\t\t\tif (completeExchange || (!calls.length && message.stopReason === \"stop\")) completedAssistants.push(index);\n\t\t\tfor (const call of calls) {\n\t\t\t\tconst end = resultEnds.get(call.id);\n\t\t\t\tif (end !== undefined && end > index) spans.push([index, end]);\n\t\t\t}\n\t\t}\n\t\t// A lone current instruction before its first exchange is not removable history.\n\t\trelaxRetention = completedAssistants.some((index) => index < latestCompleteExchange);\n\t\tif (relaxRetention) {\n\t\t\tcuts = cuts.filter(\n\t\t\t\t(candidate) =>\n\t\t\t\t\tcandidate <= latestCompleteExchange &&\n\t\t\t\t\tspans.every(([start, end]) => candidate <= start || candidate > end),\n\t\t\t);\n\t\t}\n\t}\n\tif (!cuts.length) return;\n\tconst tokenScale = compactionTokenScale(messages);\n\tlet cut = cuts[0];\n\tlet tokens = 0;\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tif (!entryIds[index] || messages[index].role === \"compactionSummary\") continue;\n\t\ttokens += estimateTokens(messages[index]) * tokenScale;\n\t\tif (tokens >= settings.keepRecentTokens) {\n\t\t\t// Include the message that crossed the threshold and its preceding legal group boundary.\n\t\t\tcut = cuts.filter((candidate) => candidate <= index).at(-1) ?? cut;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (relaxRetention || (allowShortSession && cut === cuts[0])) cut = cuts.at(-1)!;\n\tif (maxCutEntryId !== undefined) {\n\t\tconst maximum = entryIds.indexOf(maxCutEntryId);\n\t\tconst lastAllowed = cuts.filter((candidate) => candidate <= maximum).at(-1);\n\t\tif (lastAllowed === undefined) throw new Error(\"Compaction recovery boundary is unavailable\");\n\t\tcut = Math.min(cut, lastAllowed);\n\t}\n\tlet turnStart = -1;\n\tif (messages[cut].role !== \"user\") {\n\t\tfor (let index = cut; index >= 0; index--) {\n\t\t\tif (entryIds[index] && [\"user\", \"custom\", \"branchSummary\", \"bashExecution\"].includes(messages[index].role)) {\n\t\t\t\tturnStart = index;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tconst isSplitTurn = turnStart >= 0;\n\tconst historyEnd = isSplitTurn ? turnStart : cut;\n\tlet previousSummary: string | undefined;\n\tlet previousSummaryIndex = -1;\n\tfor (const [index, message] of messages.entries()) {\n\t\tif (message.role !== \"compactionSummary\") continue;\n\t\tpreviousSummary = message.summary;\n\t\tpreviousSummaryIndex = pathEntries.findIndex((entry) => entry.id === entryIds[index]);\n\t}\n\tconst historical = messages.filter(\n\t\t(message, index) => entryIds[index] && index < historyEnd && message.role !== \"compactionSummary\",\n\t);\n\tconst turnPrefixMessages = isSplitTurn\n\t\t? messages.filter(\n\t\t\t\t(message, index) =>\n\t\t\t\t\tentryIds[index] && index >= turnStart && index < cut && message.role !== \"compactionSummary\",\n\t\t\t)\n\t\t: [];\n\tif (!historical.length && !turnPrefixMessages.length && (budgetPressure || allowShortSession || !previousSummary))\n\t\treturn;\n\t// Virtual TaskFrame displays are continuity input, never a canonical suffix boundary.\n\tconst messagesToSummarize = messages.filter(\n\t\t(message, index) => message.role !== \"compactionSummary\" && (!entryIds[index] || index < historyEnd),\n\t);\n\tconst fileOps = extractFileOperations(messagesToSummarize, pathEntries, previousSummaryIndex);\n\tfor (const message of turnPrefixMessages) extractFileOpsFromMessage(message, fileOps);\n\treturn {\n\t\tfirstKeptEntryId: entryIds[cut]!,\n\t\tmessagesToSummarize: structuredClone(messagesToSummarize),\n\t\tturnPrefixMessages: structuredClone(turnPrefixMessages),\n\t\tisSplitTurn,\n\t\ttokensBefore: estimateContextTokens([...messages]).tokens,\n\t\tpreviousSummary,\n\t\tfileOps,\n\t\tsettings,\n\t};\n}\n\nexport function prepareCompaction(\n\tpathEntries: SessionEntry[],\n\tsettings: CompactionSettings,\n\tallowShortSession = false,\n): CompactionPreparation | undefined {\n\tif (pathEntries.length > 0 && pathEntries[pathEntries.length - 1].type === \"compaction\") {\n\t\treturn undefined;\n\t}\n\n\tlet prevCompactionIndex = -1;\n\tfor (let i = pathEntries.length - 1; i >= 0; i--) {\n\t\tif (pathEntries[i].type === \"compaction\") {\n\t\t\tprevCompactionIndex = i;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tlet previousSummary: string | undefined;\n\tlet boundaryStart = 0;\n\tif (prevCompactionIndex >= 0) {\n\t\tconst prevCompaction = pathEntries[prevCompactionIndex] as CompactionEntry;\n\t\tpreviousSummary = prevCompaction.summary;\n\t\tconst firstKeptEntryIndex = pathEntries.findIndex((entry) => entry.id === prevCompaction.firstKeptEntryId);\n\t\tboundaryStart = firstKeptEntryIndex >= 0 ? firstKeptEntryIndex : prevCompactionIndex + 1;\n\t}\n\tconst boundaryEnd = pathEntries.length;\n\n\tconst messages = buildSessionContext(pathEntries).messages;\n\tconst tokensBefore = estimateContextTokens(messages).tokens;\n\n\tconst cutPoint = findCutPoint(\n\t\tpathEntries,\n\t\tboundaryStart,\n\t\tboundaryEnd,\n\t\tsettings.keepRecentTokens,\n\t\tallowShortSession,\n\t\tcompactionTokenScale(messages),\n\t);\n\tconst firstKeptEntry = pathEntries[cutPoint.firstKeptEntryIndex];\n\tif (!firstKeptEntry?.id) {\n\t\treturn undefined; // Session needs migration\n\t}\n\tconst firstKeptEntryId = firstKeptEntry.id;\n\n\tconst historyEnd = cutPoint.isSplitTurn ? cutPoint.turnStartIndex : cutPoint.firstKeptEntryIndex;\n\tconst messagesToSummarize: AgentMessage[] = [];\n\tfor (let i = boundaryStart; i < historyEnd; i++) {\n\t\tconst msg = getMessageFromEntryForCompaction(pathEntries[i]);\n\t\tif (msg) messagesToSummarize.push(msg);\n\t}\n\tconst turnPrefixMessages: AgentMessage[] = [];\n\tif (cutPoint.isSplitTurn) {\n\t\tfor (let i = cutPoint.turnStartIndex; i < cutPoint.firstKeptEntryIndex; i++) {\n\t\t\tconst msg = getMessageFromEntryForCompaction(pathEntries[i]);\n\t\t\tif (msg) turnPrefixMessages.push(msg);\n\t\t}\n\t}\n\n\t// Avoid a compaction that would summarize no history.\n\tif (messagesToSummarize.length === 0 && turnPrefixMessages.length === 0 && (allowShortSession || !previousSummary)) {\n\t\treturn undefined;\n\t}\n\tconst fileOps = extractFileOperations(messagesToSummarize, pathEntries, prevCompactionIndex);\n\t// Split turns retain their suffix, but their prefix file operations still belong in the summary.\n\tif (cutPoint.isSplitTurn) {\n\t\tfor (const msg of turnPrefixMessages) {\n\t\t\textractFileOpsFromMessage(msg, fileOps);\n\t\t}\n\t}\n\n\treturn {\n\t\tfirstKeptEntryId,\n\t\tmessagesToSummarize,\n\t\tturnPrefixMessages,\n\t\tisSplitTurn: cutPoint.isSplitTurn,\n\t\ttokensBefore,\n\t\tpreviousSummary,\n\t\tfileOps,\n\t\tsettings,\n\t};\n}\nconst TURN_PREFIX_SUMMARIZATION_PROMPT = `This is the PREFIX of a turn that was too large to keep. The SUFFIX (recent work) is retained.\n\nSummarize the prefix to provide context for the retained suffix:\n\n## Original Request\n[What did the user ask for in this turn?]\n\n## Early Progress\n- [Key decisions and work done in the prefix]\n\n## Context for Suffix\n- [Information needed to understand the retained recent work]\n\nBe concise. Focus on what's needed to understand the kept suffix.`;\n\n/**\n * Generate summaries for compaction using prepared data.\n * Returns CompactionResult - SessionManager adds uuid/parentUuid when saving.\n *\n * @param preparation - Pre-calculated preparation from prepareCompaction()\n * @param customInstructions - Optional custom focus for the summary\n */\n/** Runs one summary wire call; hosts decorate each call with its own request identity. */\nexport type SummaryCallRunner = <T>(\n\tcall: (callHeaders: Record<string, string> | undefined) => Promise<T>,\n) => Promise<T>;\n\nexport async function compact(\n\tpreparation: CompactionPreparation,\n\tmodel: Model<any>,\n\tapiKey: string,\n\theaders?: Record<string, string>,\n\tcustomInstructions?: string,\n\tsignal?: AbortSignal,\n\tthinkingLevel?: ThinkingLevel,\n\tsummaryCall: SummaryCallRunner = (call) => call(headers),\n\trequests?: InferenceCoordinator,\n\tsummaryCapacity?: (wrapper: string) => number | undefined,\n): Promise<CompactionResult> {\n\tconst {\n\t\tfirstKeptEntryId,\n\t\tmessagesToSummarize,\n\t\tturnPrefixMessages,\n\t\tisSplitTurn,\n\t\ttokensBefore,\n\t\tpreviousSummary,\n\t\tfileOps,\n\t\tsettings,\n\t} = preparation;\n\tlet summary: string;\n\tconst slices: SummarySlice[] = [];\n\tconst { readFiles, modifiedFiles } = computeFileLists(fileOps);\n\tconst fileSummary = formatFileOperations(readFiles, modifiedFiles);\n\tconst split = isSplitTurn && turnPrefixMessages.length > 0;\n\tconst splitSeparator = \"\\n\\n---\\n\\n**Turn Context (split turn):**\\n\\n\";\n\tconst wrapper =\n\t\tCOMPACTION_SUMMARY_PREFIX +\n\t\t(split ? (messagesToSummarize.length ? \"\" : \"No prior history.\") + splitSeparator : \"\") +\n\t\tfileSummary +\n\t\tCOMPACTION_SUMMARY_SUFFIX;\n\tconst capacity = summaryCapacity?.(wrapper);\n\tconst historyNeeded = !split || messagesToSummarize.length > 0;\n\tconst calls = Number(historyNeeded) + Number(split);\n\tif (capacity !== undefined && capacity < calls)\n\t\tthrow new Error(\n\t\t\t\"Context capacity exceeded: required evidence, retained tail and summary wrapper leave no room for a summary.\",\n\t\t);\n\tconst historyLimit =\n\t\tcapacity === undefined\n\t\t\t? undefined\n\t\t\t: Math.min(\n\t\t\t\t\tMath.floor(0.8 * settings.reserveTokens),\n\t\t\t\t\tsplit && historyNeeded ? Math.max(1, Math.floor(capacity * 0.6)) : capacity,\n\t\t\t\t);\n\tconst turnLimit =\n\t\tcapacity === undefined\n\t\t\t? undefined\n\t\t\t: Math.min(Math.floor(0.5 * settings.reserveTokens), capacity - (historyNeeded ? historyLimit! : 0));\n\n\tif (split) {\n\t\t// Split turns make two wire calls with different bodies; each needs its own identity.\n\t\tconst [historyResult, turnPrefixResult] = await Promise.all([\n\t\t\tmessagesToSummarize.length > 0\n\t\t\t\t? summaryCall((callHeaders) =>\n\t\t\t\t\t\tgenerateSummary(\n\t\t\t\t\t\t\tmessagesToSummarize,\n\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\tsettings.reserveTokens,\n\t\t\t\t\t\t\tapiKey,\n\t\t\t\t\t\t\tcallHeaders,\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t\tcustomInstructions,\n\t\t\t\t\t\t\tpreviousSummary,\n\t\t\t\t\t\t\tthinkingLevel,\n\t\t\t\t\t\t\trequests,\n\t\t\t\t\t\t\thistoryLimit,\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t: Promise.resolve<SummarySlice>({ summary: \"No prior history.\" }),\n\t\t\tsummaryCall((callHeaders) =>\n\t\t\t\tgenerateTurnPrefixSummary(\n\t\t\t\t\tturnPrefixMessages,\n\t\t\t\t\tmodel,\n\t\t\t\t\tsettings.reserveTokens,\n\t\t\t\t\tapiKey,\n\t\t\t\t\tcallHeaders,\n\t\t\t\t\tsignal,\n\t\t\t\t\tthinkingLevel,\n\t\t\t\t\trequests,\n\t\t\t\t\tturnLimit,\n\t\t\t\t),\n\t\t\t),\n\t\t]);\n\t\tslices.push(historyResult, turnPrefixResult);\n\t\tsummary = `${historyResult.summary}${splitSeparator}${turnPrefixResult.summary}`;\n\t} else {\n\t\tconst result = await summaryCall((callHeaders) =>\n\t\t\tgenerateSummary(\n\t\t\t\tmessagesToSummarize,\n\t\t\t\tmodel,\n\t\t\t\tsettings.reserveTokens,\n\t\t\t\tapiKey,\n\t\t\t\tcallHeaders,\n\t\t\t\tsignal,\n\t\t\t\tcustomInstructions,\n\t\t\t\tpreviousSummary,\n\t\t\t\tthinkingLevel,\n\t\t\t\trequests,\n\t\t\t\thistoryLimit,\n\t\t\t),\n\t\t);\n\t\tslices.push(result);\n\t\tsummary = result.summary;\n\t}\n\tsummary += fileSummary;\n\n\tif (!firstKeptEntryId) {\n\t\tthrow new Error(\"First kept entry has no UUID - session may need migration\");\n\t}\n\n\tlet usage: Usage | undefined;\n\tfor (const slice of slices) {\n\t\tif (!slice.usage) continue;\n\t\tusage ??= emptyUsage();\n\t\taddAssistantUsage(usage, slice.usage);\n\t}\n\tconst result: CompactionResult = {\n\t\tsummary,\n\t\tfirstKeptEntryId,\n\t\ttokensBefore,\n\t\tdetails: { readFiles, modifiedFiles } as CompactionDetails,\n\t\tusage,\n\t};\n\tconst bindings = slices.flatMap((slice) => {\n\t\tconst captured = nativeSummarySlices.get(slice);\n\t\tnativeSummarySlices.delete(slice);\n\t\treturn captured?.summary === slice.summary ? [captured.binding] : [];\n\t});\n\tif (bindings.length) nativeCompactionResults.set(result, { summary, bindings });\n\treturn result;\n}\n\n/**\n * Generate a summary for a turn prefix (when splitting a turn).\n */\nasync function generateTurnPrefixSummary(\n\tmessages: AgentMessage[],\n\tmodel: Model<any>,\n\treserveTokens: number,\n\tapiKey: string,\n\theaders?: Record<string, string>,\n\tsignal?: AbortSignal,\n\tthinkingLevel?: ThinkingLevel,\n\trequests?: InferenceCoordinator,\n\toutputTokenLimit?: number,\n): Promise<SummarySlice> {\n\tconst maxTokens = outputTokenLimit ?? Math.floor(0.5 * reserveTokens); // Smaller budget for turn prefix\n\tconst llmMessages = convertToLlm(messages);\n\tconst conversationText = serializeConversation(llmMessages);\n\tconst promptText = `<conversation>\\n${conversationText}\\n</conversation>\\n\\n${TURN_PREFIX_SUMMARIZATION_PROMPT}`;\n\tconst summarizationMessages = [\n\t\t{\n\t\t\trole: \"user\" as const,\n\t\t\tcontent: [{ type: \"text\" as const, text: promptText }],\n\t\t\ttimestamp: Date.now(),\n\t\t},\n\t];\n\n\tconst completion = completeInference(\n\t\trequests,\n\t\tmodel,\n\t\t{ systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages },\n\t\tmodel.reasoning && thinkingLevel && thinkingLevel !== \"off\"\n\t\t\t? { maxTokens, signal, apiKey, headers, reasoning: thinkingLevel }\n\t\t\t: { maxTokens, signal, apiKey, headers },\n\t\t{\n\t\t\tpurpose: \"summary\",\n\t\t\tpurposeDetail: \"compaction-turn-prefix\",\n\t\t\toperationId: headers?.[MODEL_REQUEST_ID_HEADER],\n\t\t\tsemanticEdgeId: headers?.[MODEL_REQUEST_ID_HEADER],\n\t\t},\n\t);\n\n\tconst response = await completion;\n\tif (response.stopReason === \"error\") {\n\t\tthrow new Error(`Turn prefix summarization failed: ${response.errorMessage || \"Unknown error\"}`);\n\t}\n\n\tconst slice: SummarySlice = {\n\t\tsummary: response.content\n\t\t\t.filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n\t\t\t.map((c) => c.text)\n\t\t\t.join(\"\\n\"),\n\t\tusage: response.usage,\n\t};\n\treturn captureSummarySlice(slice, requests, completion, \"turn-prefix\");\n}\n"]}
|
|
1
|
+
{"version":3,"file":"compaction.js","sourceRoot":"","sources":["../../../src/core/compaction/compaction.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Context compaction for long sessions.\n *\n * Pure functions for compaction logic. The session manager handles I/O,\n * and after compaction the session is reloaded.\n */\n\nimport type { AgentMessage, ThinkingLevel } from \"@ponythewhite/base-context-agent\";\nimport type { AssistantMessage, Model, Usage } from \"@ponythewhite/base-context-ai\";\nimport {\n\tcompleteInference,\n\tInferenceCoordinator,\n\ttype NativeCompactionOutputBinding,\n} from \"../inference-coordinator.js\";\nimport {\n\tCOMPACTION_SUMMARY_PREFIX,\n\tCOMPACTION_SUMMARY_SUFFIX,\n\tconvertToLlm,\n\tcreateBranchSummaryMessage,\n\tcreateCompactionSummaryMessage,\n\tcreateCustomMessage,\n\tHARNESS_SNAPSHOT_CUSTOM_TYPE,\n} from \"../messages.js\";\nimport type { NativeCompactionRequestOutputAssociation } from \"../request-events.js\";\nimport { MODEL_REQUEST_ID_HEADER } from \"../semantic-edges.js\";\nimport { buildSessionContext, type CompactionEntry, type SessionEntry } from \"../session-manager.js\";\nimport { TASK_FRAME_CUSTOM_TYPE } from \"../task-frame.js\";\nimport { addAssistantUsage, emptyUsage } from \"../usage.js\";\nimport {\n\tcomputeFileLists,\n\tcreateFileOps,\n\textractFileOpsFromMessage,\n\ttype FileOperations,\n\tformatFileOperations,\n\tSUMMARIZATION_SYSTEM_PROMPT,\n\tserializeConversation,\n} from \"./utils.js\";\n/** Details stored in CompactionEntry.details for file tracking */\nexport interface CompactionDetails {\n\treadFiles: string[];\n\tmodifiedFiles: string[];\n}\n\nexport interface SummarySlice {\n\tsummary: string;\n\tusage?: Usage;\n}\n\n// Identity follows the actual completion -> text slice -> built-in composition, never copied fields.\nconst nativeSummarySlices = new WeakMap<SummarySlice, { summary: string; binding: NativeCompactionOutputBinding }>();\nconst nativeCompactionResults = new WeakMap<object, { summary: string; bindings: NativeCompactionOutputBinding[] }>();\n\nfunction captureSummarySlice(\n\tslice: SummarySlice,\n\trequests: InferenceCoordinator | undefined,\n\tcompletion: Promise<AssistantMessage>,\n\tpart: NativeCompactionRequestOutputAssociation[\"part\"],\n): SummarySlice {\n\tconst binding =\n\t\trequests instanceof InferenceCoordinator\n\t\t\t? InferenceCoordinator.prototype.takeCompactionOutput.call(requests, completion)\n\t\t\t: undefined;\n\tif (binding?.output.part === part) nativeSummarySlices.set(slice, { summary: slice.summary, binding });\n\treturn slice;\n}\n\n/** Internal append input: only this exact built-in result and original sink can contribute links. */\nexport function takeCompactionRequestOutputs(\n\tresult: object | undefined,\n\tsink: object,\n\tsummary: string,\n): readonly NativeCompactionRequestOutputAssociation[] | undefined {\n\tconst captured = result ? nativeCompactionResults.get(result) : undefined;\n\tif (result) nativeCompactionResults.delete(result);\n\t// Equality only checks an already privately bound projection for mutation; it never discovers a link.\n\tif (!captured || captured.summary !== summary) return undefined;\n\tconst outputs = captured.bindings.filter((binding) => binding.sink === sink).map((binding) => binding.output);\n\treturn outputs.length ? outputs : undefined;\n}\n\n/**\n * Extract file operations from messages and previous compaction entries.\n */\n/** Preserve file operations recorded by prior compactions and current tool calls. */\nfunction extractFileOperations(\n\tmessages: AgentMessage[],\n\tentries: SessionEntry[],\n\tprevCompactionIndex: number,\n): FileOperations {\n\tconst fileOps = createFileOps();\n\tif (prevCompactionIndex >= 0) {\n\t\tconst prevCompaction = entries[prevCompactionIndex] as CompactionEntry;\n\t\tif (!prevCompaction.fromHook && prevCompaction.details) {\n\t\t\t// fromHook field kept for session file compatibility\n\t\t\tconst details = prevCompaction.details as CompactionDetails;\n\t\t\tif (Array.isArray(details.readFiles)) {\n\t\t\t\tfor (const f of details.readFiles) fileOps.read.add(f);\n\t\t\t}\n\t\t\tif (Array.isArray(details.modifiedFiles)) {\n\t\t\t\tfor (const f of details.modifiedFiles) fileOps.edited.add(f);\n\t\t\t}\n\t\t}\n\t}\n\tfor (const msg of messages) {\n\t\textractFileOpsFromMessage(msg, fileOps);\n\t}\n\n\treturn fileOps;\n}\n/**\n * Extract AgentMessage from an entry if it produces one.\n * Returns undefined for entries that don't contribute to LLM context.\n */\nfunction getMessageFromEntry(entry: SessionEntry): AgentMessage | undefined {\n\tif (entry.type === \"message\") {\n\t\treturn entry.message;\n\t}\n\tif (entry.type === \"custom_message\") {\n\t\treturn createCustomMessage(entry.customType, entry.content, entry.display, entry.details, entry.timestamp);\n\t}\n\tif (entry.type === \"branch_summary\") {\n\t\treturn createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\t}\n\tif (entry.type === \"compaction\") {\n\t\treturn createCompactionSummaryMessage(\n\t\t\tentry.summary,\n\t\t\tentry.tokensBefore,\n\t\t\tentry.timestamp,\n\t\t\tentry.customInstructions,\n\t\t);\n\t}\n\treturn undefined;\n}\n\nfunction getMessageFromEntryForCompaction(entry: SessionEntry): AgentMessage | undefined {\n\tif (entry.type === \"compaction\") {\n\t\treturn undefined;\n\t}\n\treturn getMessageFromEntry(entry);\n}\n\n/** Result from compact() - SessionManager adds uuid/parentUuid when saving */\nexport interface CompactionResult<T = unknown> {\n\tsummary: string;\n\tfirstKeptEntryId: string;\n\t/** Prior-context estimate; null when the original context is not measurable. */\n\ttokensBefore: number | null;\n\t/** Extension-specific data (e.g., ArtifactIndex, version markers for structured compaction) */\n\tdetails?: T;\n\t/** What the summarization call(s) billed; persisted on the compaction entry. */\n\tusage?: Usage;\n}\nexport const COMPACT_SKILL_NAME = \"compact\";\n\nexport interface CompactionSettings {\n\tenabled: boolean;\n\treserveTokens: number;\n\tkeepRecentTokens: number;\n\t/** Soft working-context target; the model limit always takes precedence. */\n\ttargetTokens?: number | \"model-limit\";\n}\n\nexport const DEFAULT_COMPACTION_SETTINGS: CompactionSettings = {\n\tenabled: true,\n\treserveTokens: 16384,\n\tkeepRecentTokens: 20000,\n};\n/**\n * Calculate total context tokens from usage.\n * Uses the native totalTokens field when available, falls back to computing from components.\n *\n * Includes output: the assistant's response becomes part of the prompt on the next\n * request, so it counts toward the context the next turn will send.\n */\nexport function calculateContextTokens(usage: Usage): number {\n\treturn usage.totalTokens || usage.input + usage.output + usage.cacheRead + usage.cacheWrite;\n}\n\n/**\n * Get usage from an assistant message if available.\n * Skips aborted and error messages as they don't have valid usage data.\n */\nfunction getAssistantUsage(msg: AgentMessage): Usage | undefined {\n\tif (msg.role === \"assistant\" && \"usage\" in msg) {\n\t\tconst assistantMsg = msg as AssistantMessage;\n\t\tif (assistantMsg.stopReason !== \"aborted\" && assistantMsg.stopReason !== \"error\" && assistantMsg.usage) {\n\t\t\treturn assistantMsg.usage;\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/**\n * Find the last non-aborted assistant message usage from session entries.\n */\nexport function getLastAssistantUsage(entries: SessionEntry[]): Usage | undefined {\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tif (entry.type === \"message\") {\n\t\t\tconst usage = getAssistantUsage(entry.message);\n\t\t\tif (usage) return usage;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nexport interface ContextUsageEstimate {\n\ttokens: number;\n\tusageTokens: number;\n\ttrailingTokens: number;\n\tlastUsageIndex: number | null;\n}\n\nfunction getLastAssistantUsageInfo(messages: readonly AgentMessage[]): { usage: Usage; index: number } | undefined {\n\tfor (let i = messages.length - 1; i >= 0; i--) {\n\t\tconst usage = getAssistantUsage(messages[i]);\n\t\tif (usage) return { usage, index: i };\n\t}\n\treturn undefined;\n}\n\n/**\n * Estimate context tokens from messages, using the last assistant usage when available.\n * If there are messages after the last usage, estimate their tokens with estimateTokens.\n */\nexport function estimateContextTokens(messages: AgentMessage[]): ContextUsageEstimate {\n\tconst usageInfo = getLastAssistantUsageInfo(messages);\n\n\tif (!usageInfo) {\n\t\tlet estimated = 0;\n\t\tfor (const message of messages) {\n\t\t\testimated += estimateTokens(message);\n\t\t}\n\t\treturn {\n\t\t\ttokens: estimated,\n\t\t\tusageTokens: 0,\n\t\t\ttrailingTokens: estimated,\n\t\t\tlastUsageIndex: null,\n\t\t};\n\t}\n\n\tconst usageTokens = calculateContextTokens(usageInfo.usage);\n\tlet trailingTokens = 0;\n\tfor (let i = usageInfo.index + 1; i < messages.length; i++) {\n\t\ttrailingTokens += estimateTokens(messages[i]);\n\t}\n\n\treturn {\n\t\ttokens: usageTokens + trailingTokens,\n\t\tusageTokens,\n\t\ttrailingTokens,\n\t\tlastUsageIndex: usageInfo.index,\n\t};\n}\n\n/**\n * Check if compaction should trigger based on context usage.\n */\nexport function shouldCompact(\n\tcontextTokens: number,\n\tcontextWindow: number,\n\tsettings: CompactionSettings,\n\tfixedContextTokens = 0,\n): boolean {\n\tif (!settings.enabled) return false;\n\tif (contextWindow <= 0) return false;\n\tconst modelLimit = contextWindow - settings.reserveTokens;\n\t// Fixed instructions cannot shrink. Leave meaningful room for retained history\n\t// and new work above that floor, without relaxing the actual model ceiling.\n\tconst target =\n\t\tsettings.targetTokens === \"model-limit\"\n\t\t\t? modelLimit\n\t\t\t: Math.max(\n\t\t\t\t\tsettings.targetTokens ?? Math.floor(contextWindow * 0.9),\n\t\t\t\t\tfixedContextTokens + 4 * settings.keepRecentTokens,\n\t\t\t\t);\n\treturn contextTokens > Math.min(target, modelLimit);\n}\n\n/** Chars/4 heuristic, not a provider count. Ordinary history never raises this floor. */\nexport function estimateFixedCompactionTokens(\n\tsystemPrompt: string,\n\ttools: readonly { name: string; description: string; parameters: unknown }[],\n\tmessages: readonly AgentMessage[],\n): number {\n\tconst toolSchemas = tools.map(({ name, description, parameters }) => ({ name, description, parameters }));\n\tlet tokens = Math.ceil((systemPrompt.length + (tools.length ? JSON.stringify(toolSchemas).length : 0)) / 4);\n\tlet hasHarnessSnapshot = false;\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tconst message = messages[index];\n\t\tif (message.role !== \"custom\") continue;\n\t\t// A TaskFrame may contain dependent sparse revisions; all current pieces count.\n\t\tif (message.customType === TASK_FRAME_CUSTOM_TYPE) tokens += estimateTokens(message);\n\t\telse if (message.customType === HARNESS_SNAPSHOT_CUSTOM_TYPE && !hasHarnessSnapshot) {\n\t\t\ttokens += estimateTokens(message);\n\t\t\thasHarnessSnapshot = true;\n\t\t}\n\t}\n\treturn tokens;\n}\n/** Raise suffix estimates when observed usage exceeds chars/4 for the same prefix. */\nfunction compactionTokenScale(messages: readonly AgentMessage[]): number {\n\tconst usageInfo = getLastAssistantUsageInfo(messages);\n\tif (!usageInfo) return 1;\n\tlet estimatedTokens = 0;\n\tfor (let index = 0; index <= usageInfo.index; index++) {\n\t\testimatedTokens += estimateTokens(messages[index]);\n\t}\n\treturn estimatedTokens > 0 ? Math.max(1, calculateContextTokens(usageInfo.usage) / estimatedTokens) : 1;\n}\n\n/** Estimate message tokens with chars/4; dense content can exceed this heuristic. */\nexport function estimateTokens(message: AgentMessage): number {\n\tlet chars = 0;\n\n\tswitch (message.role) {\n\t\tcase \"user\": {\n\t\t\tconst content = (message as { content: string | Array<{ type: string; text?: string }> }).content;\n\t\t\tif (typeof content === \"string\") {\n\t\t\t\tchars = content.length;\n\t\t\t} else if (Array.isArray(content)) {\n\t\t\t\tfor (const block of content) {\n\t\t\t\t\tif (block.type === \"text\" && block.text) {\n\t\t\t\t\t\tchars += block.text.length;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn Math.ceil(chars / 4);\n\t\t}\n\t\tcase \"assistant\": {\n\t\t\tconst assistant = message as AssistantMessage;\n\t\t\tfor (const block of assistant.content) {\n\t\t\t\tif (block.type === \"text\") {\n\t\t\t\t\tchars += block.text.length;\n\t\t\t\t} else if (block.type === \"thinking\") {\n\t\t\t\t\tchars += block.thinking.length;\n\t\t\t\t} else if (block.type === \"toolCall\") {\n\t\t\t\t\tchars += block.name.length + JSON.stringify(block.arguments).length;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn Math.ceil(chars / 4);\n\t\t}\n\t\tcase \"custom\":\n\t\tcase \"toolResult\": {\n\t\t\tif (typeof message.content === \"string\") {\n\t\t\t\tchars = message.content.length;\n\t\t\t} else {\n\t\t\t\tfor (const block of message.content) {\n\t\t\t\t\tif (block.type === \"text\" && block.text) {\n\t\t\t\t\t\tchars += block.text.length;\n\t\t\t\t\t}\n\t\t\t\t\tif (block.type === \"image\") {\n\t\t\t\t\t\tchars += 4800; // Estimate images as 4000 chars, or 1200 tokens\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn Math.ceil(chars / 4);\n\t\t}\n\t\tcase \"bashExecution\": {\n\t\t\tchars = message.command.length + message.output.length;\n\t\t\treturn Math.ceil(chars / 4);\n\t\t}\n\t\tcase \"branchSummary\":\n\t\tcase \"compactionSummary\": {\n\t\t\tchars = message.summary.length;\n\t\t\treturn Math.ceil(chars / 4);\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n/**\n * Find valid cut points: indices of user, assistant, custom, or bashExecution messages.\n * Never cut at tool results (they must follow their tool call).\n * When we cut at an assistant message with tool calls, its tool results follow it\n * and will be kept.\n * BashExecutionMessage is treated like a user message (user-initiated context).\n */\nfunction findValidCutPoints(entries: SessionEntry[], startIndex: number, endIndex: number): number[] {\n\tconst cutPoints: number[] = [];\n\tfor (let i = startIndex; i < endIndex; i++) {\n\t\tconst entry = entries[i];\n\t\tswitch (entry.type) {\n\t\t\tcase \"message\": {\n\t\t\t\tconst role = entry.message.role;\n\t\t\t\tswitch (role) {\n\t\t\t\t\tcase \"bashExecution\":\n\t\t\t\t\tcase \"custom\":\n\t\t\t\t\tcase \"branchSummary\":\n\t\t\t\t\tcase \"compactionSummary\":\n\t\t\t\t\tcase \"user\":\n\t\t\t\t\tcase \"assistant\":\n\t\t\t\t\t\tcutPoints.push(i);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"toolResult\":\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"thinking_level_change\":\n\t\t\tcase \"model_change\":\n\t\t\tcase \"compaction\":\n\t\t\tcase \"branch_summary\":\n\t\t\tcase \"custom\":\n\t\t\tcase \"custom_message\":\n\t\t\tcase \"label\":\n\t\t\tcase \"session_info\":\n\t\t\t\tbreak;\n\t\t}\n\t\t// Branch summaries and custom messages are user-role turn boundaries.\n\t\tif (entry.type === \"branch_summary\" || entry.type === \"custom_message\") {\n\t\t\tcutPoints.push(i);\n\t\t}\n\t}\n\treturn cutPoints;\n}\n\n/**\n * Find the user message (or bashExecution) that starts the turn containing the given entry index.\n * Returns -1 if no turn start found before the index.\n * BashExecutionMessage is treated like a user message for turn boundaries.\n */\nexport function findTurnStartIndex(entries: SessionEntry[], entryIndex: number, startIndex: number): number {\n\tfor (let i = entryIndex; i >= startIndex; i--) {\n\t\tconst entry = entries[i];\n\t\tif (entry.type === \"branch_summary\" || entry.type === \"custom_message\") {\n\t\t\treturn i;\n\t\t}\n\t\tif (entry.type === \"message\") {\n\t\t\tconst role = entry.message.role;\n\t\t\tif (role === \"user\" || role === \"bashExecution\") {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t}\n\treturn -1;\n}\n\nexport interface CutPointResult {\n\t/** Index of first entry to keep */\n\tfirstKeptEntryIndex: number;\n\t/** Index of user message that starts the turn being split, or -1 if not splitting */\n\tturnStartIndex: number;\n\t/** Whether this cut splits a turn (cut point is not a user message) */\n\tisSplitTurn: boolean;\n}\n\n/**\n * Find the cut point in session entries that keeps approximately `keepRecentTokens`.\n *\n * Algorithm: Walk backwards from newest, accumulating estimated message sizes.\n * Stop when we've accumulated >= keepRecentTokens. Cut at that point.\n *\n * Can cut at user OR assistant messages (never tool results). When cutting at an\n * assistant message with tool calls, its tool results come after and will be kept.\n *\n * Returns CutPointResult with:\n * - firstKeptEntryIndex: the entry index to start keeping from\n * - turnStartIndex: if cutting mid-turn, the user message that started that turn\n * - isSplitTurn: whether we're cutting in the middle of a turn\n *\n * Only considers entries between `startIndex` and `endIndex` (exclusive).\n */\nexport function findCutPoint(\n\tentries: SessionEntry[],\n\tstartIndex: number,\n\tendIndex: number,\n\tkeepRecentTokens: number,\n\tallowShortSession = false,\n\ttokenScale = 1,\n): CutPointResult {\n\tconst cutPoints = findValidCutPoints(entries, startIndex, endIndex);\n\n\tif (cutPoints.length === 0) {\n\t\treturn { firstKeptEntryIndex: startIndex, turnStartIndex: -1, isSplitTurn: false };\n\t}\n\tlet accumulatedTokens = 0;\n\tlet cutIndex = cutPoints[0]; // Default: keep from first message (not header)\n\n\tfor (let i = endIndex - 1; i >= startIndex; i--) {\n\t\tconst entry = entries[i];\n\t\tif (entry.type !== \"message\") continue;\n\t\tconst messageTokens = estimateTokens(entry.message) * tokenScale;\n\t\taccumulatedTokens += messageTokens;\n\t\tif (accumulatedTokens >= keepRecentTokens) {\n\t\t\tcutIndex = cutPoints.filter((candidate) => candidate <= i).at(-1) ?? cutIndex;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (allowShortSession && cutIndex === cutPoints[0]) {\n\t\t// An explicit compact may summarize a short prefix, but must keep a real suffix.\n\t\tconst latest = cutPoints\n\t\t\t.filter((index) => {\n\t\t\t\tconst entry = entries[index];\n\t\t\t\treturn entry.type !== \"message\" || entry.message.role !== \"compactionSummary\";\n\t\t\t})\n\t\t\t.at(-1);\n\t\tif (latest !== undefined) cutIndex = latest;\n\t}\n\twhile (cutIndex > startIndex) {\n\t\tconst prevEntry = entries[cutIndex - 1];\n\t\tif (prevEntry.type === \"compaction\") {\n\t\t\tbreak;\n\t\t}\n\t\tif (prevEntry.type === \"message\") {\n\t\t\tbreak;\n\t\t}\n\t\tcutIndex--;\n\t}\n\tconst cutEntry = entries[cutIndex];\n\tconst isUserMessage = cutEntry.type === \"message\" && cutEntry.message.role === \"user\";\n\t// A cut in a non-user turn requires a prefix summary.\n\tconst turnStartIndex = isUserMessage ? -1 : findTurnStartIndex(entries, cutIndex, startIndex);\n\n\treturn {\n\t\tfirstKeptEntryIndex: cutIndex,\n\t\tturnStartIndex,\n\t\tisSplitTurn: !isUserMessage && turnStartIndex !== -1,\n\t};\n}\nconst SUMMARIZATION_PROMPT = `The messages above are a conversation to summarize. Create a structured context checkpoint summary that another LLM will use to continue the work.\n\nUse this EXACT format:\n\n## Goal\n[What is the user trying to accomplish? Can be multiple items if the session covers different tasks.]\n\n## Constraints & Preferences\n- [Any constraints, preferences, or requirements mentioned by user]\n- [Or \"(none)\" if none were mentioned]\n\n## Progress\n### Done\n- [x] [Completed tasks/changes]\n\n### In Progress\n- [ ] [Current work]\n\n### Blocked\n- [Issues preventing progress, if any]\n\n## Key Decisions\n- **[Decision]**: [Brief rationale]\n\n## Next Steps\n1. [Ordered list of what should happen next]\n\n## Critical Context\n- [Any data, examples, or references needed to continue]\n- [Or \"(none)\" if not applicable]\n\nKeep each section concise. Preserve exact file paths, function names, and error messages.`;\n\nconst RUNTIME_STATE_SUMMARY_NOTE =\n\t\"Runtime note: this summary does not establish whether a Python kernel is live or whether its variables, imports, helpers, or jobs remain available. Preserve useful names and their last observed state, including uncertainty. Use current runtime reports before relying on them; do not infer either survival or loss from compaction.\";\n\nconst UPDATE_SUMMARIZATION_PROMPT = `The messages above are NEW conversation messages to incorporate into the existing summary provided in <previous-summary> tags.\n\nUpdate the existing structured summary with new information. RULES:\n- PRESERVE all existing information from the previous summary\n- ADD new progress, decisions, and context from the new messages\n- UPDATE the Progress section: move items from \"In Progress\" to \"Done\" when completed\n- UPDATE \"Next Steps\" based on what was accomplished\n- PRESERVE exact file paths, function names, and error messages\n- If something is no longer relevant, you may remove it\n\nUse this EXACT format:\n\n## Goal\n[Preserve existing goals, add new ones if the task expanded]\n\n## Constraints & Preferences\n- [Preserve existing, add new ones discovered]\n\n## Progress\n### Done\n- [x] [Include previously done items AND newly completed items]\n\n### In Progress\n- [ ] [Current work - update based on progress]\n\n### Blocked\n- [Current blockers - remove if resolved]\n\n## Key Decisions\n- **[Decision]**: [Brief rationale] (preserve all previous, add new)\n\n## Next Steps\n1. [Update based on current state]\n\n## Critical Context\n- [Preserve important context, add new if needed]\n\nKeep each section concise. Preserve exact file paths, function names, and error messages.`;\n\n/**\n * Build the instruction portion of the summarization prompt: the initial or\n * update template, optional user instructions, and the runtime-state qualification.\n */\nexport function buildSummarizationPrompt(customInstructions?: string, previousSummary?: string): string {\n\tlet basePrompt = previousSummary ? UPDATE_SUMMARIZATION_PROMPT : SUMMARIZATION_PROMPT;\n\tif (customInstructions) {\n\t\tbasePrompt += `\\n\\n<user-instructions>\\nThe user provided these instructions for this summary. Follow them with high priority while keeping the section format above: emphasize what they ask to focus on, and preserve verbatim anything they ask to remember.\\n${customInstructions}\\n</user-instructions>`;\n\t}\n\treturn `${basePrompt}\\n\\n${RUNTIME_STATE_SUMMARY_NOTE}`;\n}\n\n/**\n * Generate a summary of the conversation using the LLM.\n * If previousSummary is provided, uses the update prompt to merge.\n */\nexport async function generateSummary(\n\tcurrentMessages: AgentMessage[],\n\tmodel: Model<any>,\n\treserveTokens: number,\n\tapiKey: string,\n\theaders?: Record<string, string>,\n\tsignal?: AbortSignal,\n\tcustomInstructions?: string,\n\tpreviousSummary?: string,\n\tthinkingLevel?: ThinkingLevel,\n\trequests?: InferenceCoordinator,\n\toutputTokenLimit?: number,\n): Promise<SummarySlice> {\n\tconst maxTokens = outputTokenLimit ?? Math.floor(0.8 * reserveTokens);\n\n\tconst basePrompt = buildSummarizationPrompt(customInstructions, previousSummary);\n\t// Serialize before the LLM call so it summarizes rather than continues this conversation.\n\tconst llmMessages = convertToLlm(currentMessages);\n\tconst conversationText = serializeConversation(llmMessages);\n\tlet promptText = `<conversation>\\n${conversationText}\\n</conversation>\\n\\n`;\n\tif (previousSummary) {\n\t\tpromptText += `<previous-summary>\\n${previousSummary}\\n</previous-summary>\\n\\n`;\n\t}\n\tpromptText += basePrompt;\n\n\tconst summarizationMessages = [\n\t\t{\n\t\t\trole: \"user\" as const,\n\t\t\tcontent: [{ type: \"text\" as const, text: promptText }],\n\t\t\ttimestamp: Date.now(),\n\t\t},\n\t];\n\n\tconst completionOptions =\n\t\tmodel.reasoning && thinkingLevel && thinkingLevel !== \"off\"\n\t\t\t? { maxTokens, signal, apiKey, headers, reasoning: thinkingLevel }\n\t\t\t: { maxTokens, signal, apiKey, headers };\n\n\tconst completion = completeInference(\n\t\trequests,\n\t\tmodel,\n\t\t{ systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages },\n\t\tcompletionOptions,\n\t\t{\n\t\t\tpurpose: \"summary\",\n\t\t\tpurposeDetail: \"compaction\",\n\t\t\toperationId: headers?.[MODEL_REQUEST_ID_HEADER],\n\t\t\tsemanticEdgeId: headers?.[MODEL_REQUEST_ID_HEADER],\n\t\t},\n\t);\n\n\tconst response = await completion;\n\tif (response.stopReason === \"error\") {\n\t\tthrow new Error(`Summarization failed: ${response.errorMessage || \"Unknown error\"}`);\n\t}\n\n\tconst textContent = response.content\n\t\t.filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n\t\t.map((c) => c.text)\n\t\t.join(\"\\n\");\n\n\treturn captureSummarySlice({ summary: textContent, usage: response.usage }, requests, completion, \"history\");\n}\nexport interface CompactionPreparation {\n\t/** UUID of first entry to keep */\n\tfirstKeptEntryId: string;\n\t/** Messages that will be summarized and discarded */\n\tmessagesToSummarize: AgentMessage[];\n\t/** Messages that will be turned into turn prefix summary (if splitting) */\n\tturnPrefixMessages: AgentMessage[];\n\t/** Whether this is a split turn (cut point in middle of turn) */\n\tisSplitTurn: boolean;\n\ttokensBefore: number;\n\t/** Summary from previous compaction, for iterative update */\n\tpreviousSummary?: string;\n\t/** File operations extracted from messagesToSummarize */\n\tfileOps: FileOperations;\n\t/** Compaction settions from settings.jsonl\t*/\n\tsettings: CompactionSettings;\n}\n\n/** Prepare the actual selected epoch view. Entry IDs are only real cut anchors, never synthetic rows. */\nexport function prepareViewCompaction(\n\tmessages: readonly AgentMessage[],\n\tentryIds: readonly (string | undefined)[],\n\tpathEntries: SessionEntry[],\n\tsettings: CompactionSettings,\n\tmaxCutEntryId?: string,\n\tallowShortSession = false,\n\tbudgetPressure = false,\n): CompactionPreparation | undefined {\n\tif (messages.length !== entryIds.length) throw new Error(\"Compaction views do not match their source anchors\");\n\t// firstKeptEntryId restores a chronological source suffix, not a selected-view suffix.\n\t// Older pinned views cannot become cut anchors across omitted source messages.\n\tconst selectedIds = new Set(entryIds);\n\tconst suffixAnchors = new Set<string>();\n\tfor (let index = pathEntries.length - 1; index >= 0; index--) {\n\t\tconst entry = pathEntries[index];\n\t\tif (!getMessageFromEntryForCompaction(entry)) continue;\n\t\tif (!selectedIds.has(entry.id)) break;\n\t\tsuffixAnchors.add(entry.id);\n\t}\n\tlet cuts = messages.flatMap((message, index) =>\n\t\tentryIds[index] &&\n\t\tsuffixAnchors.has(entryIds[index]!) &&\n\t\tmessage.role !== \"toolResult\" &&\n\t\tmessage.role !== \"compactionSummary\"\n\t\t\t? [index]\n\t\t\t: [],\n\t);\n\tlet relaxRetention = false;\n\tif (budgetPressure) {\n\t\t// Only caller-confirmed public OVER may relax retention; source/tool groups remain whole.\n\t\tconst spans: [number, number][] = [];\n\t\tconst sourceStarts = new Map<string, number>();\n\t\tconst resultEnds = new Map<string, number>();\n\t\t// Public history renders original roles as custom text; use its captured source anchors.\n\t\tconst sourceMessages = new Map(pathEntries.map((entry) => [entry.id, getMessageFromEntry(entry)]));\n\t\tconst anchoredMessages = messages.map((message, index) => sourceMessages.get(entryIds[index] ?? \"\") ?? message);\n\t\tfor (const [index, message] of anchoredMessages.entries()) {\n\t\t\tconst entryId = entryIds[index];\n\t\t\tif (entryId) {\n\t\t\t\tconst start = sourceStarts.get(entryId);\n\t\t\t\tif (start === undefined) sourceStarts.set(entryId, index);\n\t\t\t\telse spans.push([start, index]);\n\t\t\t}\n\t\t\tif (message.role === \"toolResult\") resultEnds.set(message.toolCallId, index);\n\t\t}\n\t\tlet latestCompleteExchange = messages.length - 1;\n\t\tconst completedAssistants: number[] = [];\n\t\tfor (const [index, message] of anchoredMessages.entries()) {\n\t\t\tif (message.role !== \"assistant\") continue;\n\t\t\tconst calls = message.content.filter((block) => block.type === \"toolCall\");\n\t\t\tconst completeExchange = calls.length > 0 && calls.every((call) => (resultEnds.get(call.id) ?? -1) > index);\n\t\t\tif (completeExchange) latestCompleteExchange = index;\n\t\t\tif (completeExchange || (!calls.length && message.stopReason === \"stop\")) completedAssistants.push(index);\n\t\t\tfor (const call of calls) {\n\t\t\t\tconst end = resultEnds.get(call.id);\n\t\t\t\tif (end !== undefined && end > index) spans.push([index, end]);\n\t\t\t}\n\t\t}\n\t\t// A lone current instruction before its first exchange is not removable history.\n\t\trelaxRetention = completedAssistants.some((index) => index < latestCompleteExchange);\n\t\tif (relaxRetention) {\n\t\t\tcuts = cuts.filter(\n\t\t\t\t(candidate) =>\n\t\t\t\t\tcandidate <= latestCompleteExchange &&\n\t\t\t\t\tspans.every(([start, end]) => candidate <= start || candidate > end),\n\t\t\t);\n\t\t}\n\t}\n\tif (!cuts.length) return;\n\tconst tokenScale = compactionTokenScale(messages);\n\tlet cut = cuts[0];\n\tlet tokens = 0;\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tif (!entryIds[index] || messages[index].role === \"compactionSummary\") continue;\n\t\ttokens += estimateTokens(messages[index]) * tokenScale;\n\t\tif (tokens >= settings.keepRecentTokens) {\n\t\t\t// Include the message that crossed the threshold and its preceding legal group boundary.\n\t\t\tcut = cuts.filter((candidate) => candidate <= index).at(-1) ?? cut;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (relaxRetention || (allowShortSession && cut === cuts[0])) cut = cuts.at(-1)!;\n\tif (maxCutEntryId !== undefined) {\n\t\tconst maximum = entryIds.indexOf(maxCutEntryId);\n\t\tconst lastAllowed = cuts.filter((candidate) => candidate <= maximum).at(-1);\n\t\tif (lastAllowed === undefined) throw new Error(\"Compaction recovery boundary is unavailable\");\n\t\tcut = Math.min(cut, lastAllowed);\n\t}\n\tlet turnStart = -1;\n\tif (messages[cut].role !== \"user\") {\n\t\tfor (let index = cut; index >= 0; index--) {\n\t\t\tif (entryIds[index] && [\"user\", \"custom\", \"branchSummary\", \"bashExecution\"].includes(messages[index].role)) {\n\t\t\t\tturnStart = index;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tconst isSplitTurn = turnStart >= 0;\n\tconst historyEnd = isSplitTurn ? turnStart : cut;\n\tlet previousSummary: string | undefined;\n\tlet previousSummaryIndex = -1;\n\tfor (const [index, message] of messages.entries()) {\n\t\tif (message.role !== \"compactionSummary\") continue;\n\t\tpreviousSummary = message.summary;\n\t\tpreviousSummaryIndex = pathEntries.findIndex((entry) => entry.id === entryIds[index]);\n\t}\n\tconst historical = messages.filter(\n\t\t(message, index) => entryIds[index] && index < historyEnd && message.role !== \"compactionSummary\",\n\t);\n\tconst turnPrefixMessages = isSplitTurn\n\t\t? messages.filter(\n\t\t\t\t(message, index) =>\n\t\t\t\t\tentryIds[index] && index >= turnStart && index < cut && message.role !== \"compactionSummary\",\n\t\t\t)\n\t\t: [];\n\tif (!historical.length && !turnPrefixMessages.length && (budgetPressure || allowShortSession || !previousSummary))\n\t\treturn;\n\t// Virtual TaskFrame displays are continuity input, never a canonical suffix boundary.\n\tconst messagesToSummarize = messages.filter(\n\t\t(message, index) => message.role !== \"compactionSummary\" && (!entryIds[index] || index < historyEnd),\n\t);\n\tconst fileOps = extractFileOperations(messagesToSummarize, pathEntries, previousSummaryIndex);\n\tfor (const message of turnPrefixMessages) extractFileOpsFromMessage(message, fileOps);\n\treturn {\n\t\tfirstKeptEntryId: entryIds[cut]!,\n\t\tmessagesToSummarize: structuredClone(messagesToSummarize),\n\t\tturnPrefixMessages: structuredClone(turnPrefixMessages),\n\t\tisSplitTurn,\n\t\ttokensBefore: estimateContextTokens([...messages]).tokens,\n\t\tpreviousSummary,\n\t\tfileOps,\n\t\tsettings,\n\t};\n}\n\nexport function prepareCompaction(\n\tpathEntries: SessionEntry[],\n\tsettings: CompactionSettings,\n\tallowShortSession = false,\n): CompactionPreparation | undefined {\n\tif (pathEntries.length > 0 && pathEntries[pathEntries.length - 1].type === \"compaction\") {\n\t\treturn undefined;\n\t}\n\n\tlet prevCompactionIndex = -1;\n\tfor (let i = pathEntries.length - 1; i >= 0; i--) {\n\t\tif (pathEntries[i].type === \"compaction\") {\n\t\t\tprevCompactionIndex = i;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tlet previousSummary: string | undefined;\n\tlet boundaryStart = 0;\n\tif (prevCompactionIndex >= 0) {\n\t\tconst prevCompaction = pathEntries[prevCompactionIndex] as CompactionEntry;\n\t\tpreviousSummary = prevCompaction.summary;\n\t\tconst firstKeptEntryIndex = pathEntries.findIndex((entry) => entry.id === prevCompaction.firstKeptEntryId);\n\t\tboundaryStart = firstKeptEntryIndex >= 0 ? firstKeptEntryIndex : prevCompactionIndex + 1;\n\t}\n\tconst boundaryEnd = pathEntries.length;\n\n\tconst messages = buildSessionContext(pathEntries).messages;\n\tconst tokensBefore = estimateContextTokens(messages).tokens;\n\n\tconst cutPoint = findCutPoint(\n\t\tpathEntries,\n\t\tboundaryStart,\n\t\tboundaryEnd,\n\t\tsettings.keepRecentTokens,\n\t\tallowShortSession,\n\t\tcompactionTokenScale(messages),\n\t);\n\tconst firstKeptEntry = pathEntries[cutPoint.firstKeptEntryIndex];\n\tif (!firstKeptEntry?.id) {\n\t\treturn undefined; // Session needs migration\n\t}\n\tconst firstKeptEntryId = firstKeptEntry.id;\n\n\tconst historyEnd = cutPoint.isSplitTurn ? cutPoint.turnStartIndex : cutPoint.firstKeptEntryIndex;\n\tconst messagesToSummarize: AgentMessage[] = [];\n\tfor (let i = boundaryStart; i < historyEnd; i++) {\n\t\tconst msg = getMessageFromEntryForCompaction(pathEntries[i]);\n\t\tif (msg) messagesToSummarize.push(msg);\n\t}\n\tconst turnPrefixMessages: AgentMessage[] = [];\n\tif (cutPoint.isSplitTurn) {\n\t\tfor (let i = cutPoint.turnStartIndex; i < cutPoint.firstKeptEntryIndex; i++) {\n\t\t\tconst msg = getMessageFromEntryForCompaction(pathEntries[i]);\n\t\t\tif (msg) turnPrefixMessages.push(msg);\n\t\t}\n\t}\n\n\t// Avoid a compaction that would summarize no history.\n\tif (messagesToSummarize.length === 0 && turnPrefixMessages.length === 0 && (allowShortSession || !previousSummary)) {\n\t\treturn undefined;\n\t}\n\tconst fileOps = extractFileOperations(messagesToSummarize, pathEntries, prevCompactionIndex);\n\t// Split turns retain their suffix, but their prefix file operations still belong in the summary.\n\tif (cutPoint.isSplitTurn) {\n\t\tfor (const msg of turnPrefixMessages) {\n\t\t\textractFileOpsFromMessage(msg, fileOps);\n\t\t}\n\t}\n\n\treturn {\n\t\tfirstKeptEntryId,\n\t\tmessagesToSummarize,\n\t\tturnPrefixMessages,\n\t\tisSplitTurn: cutPoint.isSplitTurn,\n\t\ttokensBefore,\n\t\tpreviousSummary,\n\t\tfileOps,\n\t\tsettings,\n\t};\n}\nconst TURN_PREFIX_SUMMARIZATION_PROMPT = `This is the PREFIX of a turn that was too large to keep. The SUFFIX (recent work) is retained.\n\nSummarize the prefix to provide context for the retained suffix:\n\n## Original Request\n[What did the user ask for in this turn?]\n\n## Early Progress\n- [Key decisions and work done in the prefix]\n\n## Context for Suffix\n- [Information needed to understand the retained recent work]\n\nBe concise. Focus on what's needed to understand the kept suffix.`;\n\n/**\n * Generate summaries for compaction using prepared data.\n * Returns CompactionResult - SessionManager adds uuid/parentUuid when saving.\n *\n * @param preparation - Pre-calculated preparation from prepareCompaction()\n * @param customInstructions - Optional custom focus for the summary\n */\n/** Runs one summary wire call; hosts decorate each call with its own request identity. */\nexport type SummaryCallRunner = <T>(\n\tcall: (callHeaders: Record<string, string> | undefined) => Promise<T>,\n) => Promise<T>;\n\nexport async function compact(\n\tpreparation: CompactionPreparation,\n\tmodel: Model<any>,\n\tapiKey: string,\n\theaders?: Record<string, string>,\n\tcustomInstructions?: string,\n\tsignal?: AbortSignal,\n\tthinkingLevel?: ThinkingLevel,\n\tsummaryCall: SummaryCallRunner = (call) => call(headers),\n\trequests?: InferenceCoordinator,\n\tsummaryCapacity?: (wrapper: string) => number | undefined,\n): Promise<CompactionResult> {\n\tconst {\n\t\tfirstKeptEntryId,\n\t\tmessagesToSummarize,\n\t\tturnPrefixMessages,\n\t\tisSplitTurn,\n\t\ttokensBefore,\n\t\tpreviousSummary,\n\t\tfileOps,\n\t\tsettings,\n\t} = preparation;\n\tlet summary: string;\n\tconst slices: SummarySlice[] = [];\n\tconst { readFiles, modifiedFiles } = computeFileLists(fileOps);\n\tconst fileSummary = formatFileOperations(readFiles, modifiedFiles);\n\tconst split = isSplitTurn && turnPrefixMessages.length > 0;\n\tconst splitSeparator = \"\\n\\n---\\n\\n**Turn Context (split turn):**\\n\\n\";\n\tconst wrapper =\n\t\tCOMPACTION_SUMMARY_PREFIX +\n\t\t(split ? (messagesToSummarize.length ? \"\" : \"No prior history.\") + splitSeparator : \"\") +\n\t\tfileSummary +\n\t\tCOMPACTION_SUMMARY_SUFFIX;\n\tconst capacity = summaryCapacity?.(wrapper);\n\tconst historyNeeded = !split || messagesToSummarize.length > 0;\n\tconst calls = Number(historyNeeded) + Number(split);\n\tif (capacity !== undefined && capacity < calls)\n\t\tthrow new Error(\n\t\t\t\"Context capacity exceeded: required evidence, retained tail and summary wrapper leave no room for a summary.\",\n\t\t);\n\tconst historyLimit =\n\t\tcapacity === undefined\n\t\t\t? undefined\n\t\t\t: Math.min(\n\t\t\t\t\tMath.floor(0.8 * settings.reserveTokens),\n\t\t\t\t\tsplit && historyNeeded ? Math.max(1, Math.floor(capacity * 0.6)) : capacity,\n\t\t\t\t);\n\tconst turnLimit =\n\t\tcapacity === undefined\n\t\t\t? undefined\n\t\t\t: Math.min(Math.floor(0.5 * settings.reserveTokens), capacity - (historyNeeded ? historyLimit! : 0));\n\n\tif (split) {\n\t\t// Split turns make two wire calls with different bodies; each needs its own identity.\n\t\tconst [historyResult, turnPrefixResult] = await Promise.all([\n\t\t\tmessagesToSummarize.length > 0\n\t\t\t\t? summaryCall((callHeaders) =>\n\t\t\t\t\t\tgenerateSummary(\n\t\t\t\t\t\t\tmessagesToSummarize,\n\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\tsettings.reserveTokens,\n\t\t\t\t\t\t\tapiKey,\n\t\t\t\t\t\t\tcallHeaders,\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t\tcustomInstructions,\n\t\t\t\t\t\t\tpreviousSummary,\n\t\t\t\t\t\t\tthinkingLevel,\n\t\t\t\t\t\t\trequests,\n\t\t\t\t\t\t\thistoryLimit,\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t: Promise.resolve<SummarySlice>({ summary: \"No prior history.\" }),\n\t\t\tsummaryCall((callHeaders) =>\n\t\t\t\tgenerateTurnPrefixSummary(\n\t\t\t\t\tturnPrefixMessages,\n\t\t\t\t\tmodel,\n\t\t\t\t\tsettings.reserveTokens,\n\t\t\t\t\tapiKey,\n\t\t\t\t\tcallHeaders,\n\t\t\t\t\tsignal,\n\t\t\t\t\tthinkingLevel,\n\t\t\t\t\trequests,\n\t\t\t\t\tturnLimit,\n\t\t\t\t),\n\t\t\t),\n\t\t]);\n\t\tslices.push(historyResult, turnPrefixResult);\n\t\tsummary = `${historyResult.summary}${splitSeparator}${turnPrefixResult.summary}`;\n\t} else {\n\t\tconst result = await summaryCall((callHeaders) =>\n\t\t\tgenerateSummary(\n\t\t\t\tmessagesToSummarize,\n\t\t\t\tmodel,\n\t\t\t\tsettings.reserveTokens,\n\t\t\t\tapiKey,\n\t\t\t\tcallHeaders,\n\t\t\t\tsignal,\n\t\t\t\tcustomInstructions,\n\t\t\t\tpreviousSummary,\n\t\t\t\tthinkingLevel,\n\t\t\t\trequests,\n\t\t\t\thistoryLimit,\n\t\t\t),\n\t\t);\n\t\tslices.push(result);\n\t\tsummary = result.summary;\n\t}\n\tsummary += fileSummary;\n\n\tif (!firstKeptEntryId) {\n\t\tthrow new Error(\"First kept entry has no UUID - session may need migration\");\n\t}\n\n\tlet usage: Usage | undefined;\n\tfor (const slice of slices) {\n\t\tif (!slice.usage) continue;\n\t\tusage ??= emptyUsage();\n\t\taddAssistantUsage(usage, slice.usage);\n\t}\n\tconst result: CompactionResult = {\n\t\tsummary,\n\t\tfirstKeptEntryId,\n\t\ttokensBefore,\n\t\tdetails: { readFiles, modifiedFiles } as CompactionDetails,\n\t\tusage,\n\t};\n\tconst bindings = slices.flatMap((slice) => {\n\t\tconst captured = nativeSummarySlices.get(slice);\n\t\tnativeSummarySlices.delete(slice);\n\t\treturn captured?.summary === slice.summary ? [captured.binding] : [];\n\t});\n\tif (bindings.length) nativeCompactionResults.set(result, { summary, bindings });\n\treturn result;\n}\n\n/**\n * Generate a summary for a turn prefix (when splitting a turn).\n */\nasync function generateTurnPrefixSummary(\n\tmessages: AgentMessage[],\n\tmodel: Model<any>,\n\treserveTokens: number,\n\tapiKey: string,\n\theaders?: Record<string, string>,\n\tsignal?: AbortSignal,\n\tthinkingLevel?: ThinkingLevel,\n\trequests?: InferenceCoordinator,\n\toutputTokenLimit?: number,\n): Promise<SummarySlice> {\n\tconst maxTokens = outputTokenLimit ?? Math.floor(0.5 * reserveTokens); // Smaller budget for turn prefix\n\tconst llmMessages = convertToLlm(messages);\n\tconst conversationText = serializeConversation(llmMessages);\n\tconst promptText = `<conversation>\\n${conversationText}\\n</conversation>\\n\\n${TURN_PREFIX_SUMMARIZATION_PROMPT}`;\n\tconst summarizationMessages = [\n\t\t{\n\t\t\trole: \"user\" as const,\n\t\t\tcontent: [{ type: \"text\" as const, text: promptText }],\n\t\t\ttimestamp: Date.now(),\n\t\t},\n\t];\n\n\tconst completion = completeInference(\n\t\trequests,\n\t\tmodel,\n\t\t{ systemPrompt: SUMMARIZATION_SYSTEM_PROMPT, messages: summarizationMessages },\n\t\tmodel.reasoning && thinkingLevel && thinkingLevel !== \"off\"\n\t\t\t? { maxTokens, signal, apiKey, headers, reasoning: thinkingLevel }\n\t\t\t: { maxTokens, signal, apiKey, headers },\n\t\t{\n\t\t\tpurpose: \"summary\",\n\t\t\tpurposeDetail: \"compaction-turn-prefix\",\n\t\t\toperationId: headers?.[MODEL_REQUEST_ID_HEADER],\n\t\t\tsemanticEdgeId: headers?.[MODEL_REQUEST_ID_HEADER],\n\t\t},\n\t);\n\n\tconst response = await completion;\n\tif (response.stopReason === \"error\") {\n\t\tthrow new Error(`Turn prefix summarization failed: ${response.errorMessage || \"Unknown error\"}`);\n\t}\n\n\tconst slice: SummarySlice = {\n\t\tsummary: response.content\n\t\t\t.filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n\t\t\t.map((c) => c.text)\n\t\t\t.join(\"\\n\"),\n\t\tusage: response.usage,\n\t};\n\treturn captureSummarySlice(slice, requests, completion, \"turn-prefix\");\n}\n"]}
|
|
@@ -2,6 +2,7 @@ import type { Usage } from "@ponythewhite/base-context-ai";
|
|
|
2
2
|
import type { RlmChildAgentStatus } from "./agent-session.js";
|
|
3
3
|
import type { ContextUsage } from "./extensions/index.js";
|
|
4
4
|
import type { SourceSnapshotRef } from "./request-events.js";
|
|
5
|
+
import { type OwnRequestUsage } from "./request-usage.js";
|
|
5
6
|
import type { SessionHistoryReadLimits } from "./session-history-index.js";
|
|
6
7
|
import { type SessionEntry, type SessionManager } from "./session-manager.js";
|
|
7
8
|
/** Resolves a model's context window so disk-only nodes can report utilization. */
|
|
@@ -21,6 +22,8 @@ export interface ContextTreeNode {
|
|
|
21
22
|
};
|
|
22
23
|
ownUsage: Usage;
|
|
23
24
|
totalUsage: Usage;
|
|
25
|
+
/** Existing generation receipts for this session's captured source, never child aggregates. */
|
|
26
|
+
ownRequestUsage?: OwnRequestUsage;
|
|
24
27
|
contextUsage?: ContextUsage;
|
|
25
28
|
children: ContextTreeNode[];
|
|
26
29
|
}
|
|
@@ -61,26 +64,26 @@ export declare function computeOwnAndTotalUsage(branch: SessionEntry[], allEntri
|
|
|
61
64
|
ownUsage: Usage;
|
|
62
65
|
totalUsage: Usage;
|
|
63
66
|
};
|
|
67
|
+
interface ContextTreeUsage {
|
|
68
|
+
ownUsage: Usage;
|
|
69
|
+
totalUsage: Usage;
|
|
70
|
+
ownRequestUsage?: OwnRequestUsage;
|
|
71
|
+
hasPostCompactionUsage?: boolean;
|
|
72
|
+
}
|
|
64
73
|
/** Complete bounded snapshot of an explicitly resident Manager, never an index-error fallback.
|
|
65
74
|
* This synchronous reduction runs in the pre-await live-tree capture walk. It never
|
|
66
75
|
* retains a decoded resident snapshot while native/disk work waits for a slot.
|
|
67
76
|
*/
|
|
68
|
-
export declare function readResidentContextTreeUsage(manager: SessionManager, limits?: SessionHistoryReadLimits, availabilityMaxSourceBytes?: number):
|
|
69
|
-
ownUsage: Usage;
|
|
70
|
-
totalUsage: Usage;
|
|
71
|
-
hasPostCompactionUsage?: boolean;
|
|
72
|
-
};
|
|
77
|
+
export declare function readResidentContextTreeUsage(manager: SessionManager, limits?: SessionHistoryReadLimits, availabilityMaxSourceBytes?: number): ContextTreeUsage;
|
|
73
78
|
/** Detached, complete source usage and its exact captured parent branch. */
|
|
74
|
-
export declare function readContextTreeUsage(manager: SessionManager, limits?: SessionHistoryReadLimits, request?: ContextTreeRequest, availabilityMaxSourceBytes?: number): Promise<{
|
|
79
|
+
export declare function readContextTreeUsage(manager: SessionManager, limits?: SessionHistoryReadLimits, request?: ContextTreeRequest, availabilityMaxSourceBytes?: number): Promise<(ContextTreeUsage & {
|
|
75
80
|
source: SourceSnapshotRef;
|
|
76
|
-
|
|
77
|
-
totalUsage: Usage;
|
|
78
|
-
hasPostCompactionUsage?: boolean;
|
|
79
|
-
} | undefined>;
|
|
81
|
+
}) | undefined>;
|
|
80
82
|
/** Complete or refuse, sharing the request's slot and counters with descendants.
|
|
81
83
|
* Internal identity is an already-admitted registered run, including its empty placeholder.
|
|
82
84
|
*/
|
|
83
85
|
export declare function loadContextTreeChildFromDisk(childSessionDir: string, resolveContextWindow: ContextWindowResolver, limits?: Partial<ContextTreeRequestLimits> | ContextTreeRequest, identity?: Pick<ContextTreeNode, "id" | "label" | "status">): Promise<ContextTreeNode | undefined>;
|
|
84
86
|
/** Persisted children, in existing directory order, excluding already represented live IDs. */
|
|
85
87
|
export declare function loadContextTreeChildrenFromDisk(rlmSessionDir: string | undefined, resolveContextWindow: ContextWindowResolver, skipIds?: ReadonlySet<string>, limits?: Partial<ContextTreeRequestLimits> | ContextTreeRequest): Promise<ContextTreeNode[]>;
|
|
88
|
+
export {};
|
|
86
89
|
//# sourceMappingURL=context-tree.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-tree.d.ts","sourceRoot":"","sources":["../../src/core/context-tree.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, opendirSync, statSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport type { AssistantMessage, Usage } from \"@ponythewhite/base-context-ai\";\nimport type { RlmChildAgentStatus } from \"./agent-session.js\";\nimport { stringifyBoundedJson } from \"./bounded-json.js\";\nimport { calculateContextTokens, estimateContextTokens } from \"./compaction/index.js\";\nimport { exportHistoryLimits } from \"./export-html/history.js\";\nimport type { ContextUsage } from \"./extensions/index.js\";\nimport type { IndexedSourceEvent, ParentPathCursor } from \"./history-index.js\";\nimport type { SourceSnapshotRef } from \"./request-events.js\";\nimport type { SessionHistoryReadLimits } from \"./session-history-index.js\";\nimport { readCapturedSessionJournal } from \"./session-journal-reader.js\";\nimport {\n\tapplyChildUsageAttributions,\n\tbuildSessionContext,\n\ttype FileEntry,\n\ttype SessionEntry,\n\ttype SessionManager,\n} from \"./session-manager.js\";\nimport { addAssistantUsage, cloneUsage, emptyUsage, subtractAssistantUsage } from \"./usage.js\";\n\n/** Resolves a model's context window so disk-only nodes can report utilization. */\nexport type ContextWindowResolver = (provider: string, modelId: string) => number | undefined;\n\n/**\n * One agent in the context overview: the main session or an RLM (sub-)agent.\n * `ownUsage` excludes descendants; `totalUsage` includes completed descendants, matching /usage.\n */\nexport interface ContextTreeNode {\n\t/** \"root\" for the session itself; sub-xxxx for an RLM child. */\n\tid: string;\n\tlabel: string;\n\tstatus: \"active\" | RlmChildAgentStatus;\n\tmodel?: { provider: string; id: string };\n\townUsage: Usage;\n\ttotalUsage: Usage;\n\tcontextUsage?: ContextUsage;\n\tchildren: ContextTreeNode[];\n}\n\n/** Shared by one overview request, not by concurrent requests or the process. */\nexport interface ContextTreeRequestLimits extends SessionHistoryReadLimits {\n\tmaxNodes: number;\n\tmaxMetadataBytes: number;\n\tmaxDirectoryEntries: number;\n}\n\nclass ContextTreeLimitError extends Error {}\n\n/** Small request-local counters and one history-projection reduction tail. */\nexport class ContextTreeRequest {\n\treadonly limits: Readonly<ContextTreeRequestLimits>;\n\tprivate nodes = 0;\n\tprivate metadataBytes = 0;\n\tprivate directoryEntries = 0;\n\tprivate readTail: Promise<void> = Promise.resolve();\n\n\tconstructor(limits: Partial<ContextTreeRequestLimits> = {}) {\n\t\tthis.limits = Object.freeze({\n\t\t\t...exportHistoryLimits(limits),\n\t\t\tmaxNodes: limits.maxNodes ?? 256,\n\t\t\tmaxMetadataBytes: limits.maxMetadataBytes ?? 4 * 1024 * 1024,\n\t\t\tmaxDirectoryEntries: limits.maxDirectoryEntries ?? 16_384,\n\t\t});\n\t\tif (!Object.values(this.limits).every((value) => Number.isSafeInteger(value) && value > 0))\n\t\t\tthrow new Error(\"Invalid context tree request limits\");\n\t}\n\n\tadmitNode(): void {\n\t\tif (this.nodes >= this.limits.maxNodes) throw new ContextTreeLimitError(\"Context tree node budget exceeded\");\n\t\tthis.nodes++;\n\t}\n\n\tretainMetadata(value: unknown): number {\n\t\tlet json: string;\n\t\ttry {\n\t\t\tjson = stringifyBoundedJson(value, this.limits.maxMetadataBytes - this.metadataBytes);\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error && error.message === \"JSON byte limit exceeded\")\n\t\t\t\tthrow new ContextTreeLimitError(\"Context tree metadata byte budget exceeded\", { cause: error });\n\t\t\tthrow error;\n\t\t}\n\t\tconst bytes = Buffer.byteLength(json);\n\t\tthis.metadataBytes += bytes;\n\t\treturn bytes;\n\t}\n\n\treleaseMetadata(bytes: number): void {\n\t\tthis.metadataBytes -= bytes;\n\t}\n\n\tdirectoryEntry(): void {\n\t\tif (this.directoryEntries >= this.limits.maxDirectoryEntries)\n\t\t\tthrow new ContextTreeLimitError(\"Context tree directory entry budget exceeded\");\n\t\tthis.directoryEntries++;\n\t}\n\n\tread<T>(read: () => Promise<T>): Promise<T> {\n\t\tconst result = this.readTail.then(read);\n\t\tthis.readTail = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn result;\n\t}\n}\n\nfunction treeRequest(limits: Partial<ContextTreeRequestLimits> | ContextTreeRequest): ContextTreeRequest {\n\treturn limits instanceof ContextTreeRequest ? limits : new ContextTreeRequest(limits);\n}\n\nfunction isAssistantEntry(entry: SessionEntry): entry is SessionEntry & {\n\ttype: \"message\";\n\tmessage: AssistantMessage;\n} {\n\treturn entry.type === \"message\" && entry.message.role === \"assistant\";\n}\n\nfunction readUserMessageText(content: unknown): string {\n\tif (typeof content === \"string\") {\n\t\treturn content;\n\t}\n\tif (!Array.isArray(content)) {\n\t\treturn \"\";\n\t}\n\treturn content\n\t\t.filter(\n\t\t\t(block): block is { type: \"text\"; text: string } =>\n\t\t\t\ttypeof block === \"object\" &&\n\t\t\t\tblock !== null &&\n\t\t\t\t(block as { type?: unknown }).type === \"text\" &&\n\t\t\t\ttypeof (block as { text?: unknown }).text === \"string\",\n\t\t)\n\t\t.map((block) => block.text)\n\t\t.join(\"\\n\");\n}\n\nfunction compactLabel(text: string, maxLength = 80): string {\n\tconst compact = text.replace(/\\s+/g, \" \").trim();\n\tif (compact.length <= maxLength) {\n\t\treturn compact;\n\t}\n\treturn `${compact.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;\n}\n\n/**\n * Usage totals for one agent: `totalUsage` sums the branch's assistant usage\n * (attributed aggregates, so descendants are included), `ownUsage` removes the\n * attributions targeting those assistants. Attribution entries are matched by\n * target across ALL entries, not just the branch: attributions rewrite the\n * target assistant's usage no matter which branch they were appended on, so a\n * fork that keeps the assistant but drops the attribution entry must still\n * subtract it.\n *\n * Totals are deliberately cumulative across compactions: compaction shrinks\n * the model-facing context, not what the session has spent, so assistants\n * dropped from the resolved context still count here.\n */\nexport function computeOwnAndTotalUsage(\n\tbranch: SessionEntry[],\n\tallEntries: SessionEntry[],\n): { ownUsage: Usage; totalUsage: Usage } {\n\tconst totalUsage = emptyUsage();\n\tconst branchAssistantIds = new Set<string>();\n\tfor (const entry of branch) {\n\t\tif (isAssistantEntry(entry)) {\n\t\t\tbranchAssistantIds.add(entry.id);\n\t\t\taddAssistantUsage(totalUsage, entry.message.usage);\n\t\t} else if ((entry.type === \"compaction\" || entry.type === \"branch_summary\") && entry.usage) {\n\t\t\taddAssistantUsage(totalUsage, entry.usage);\n\t\t}\n\t}\n\tconst ownUsage = cloneUsage(totalUsage);\n\tfor (const entry of allEntries) {\n\t\tif (entry.type === \"child_usage_attributed\" && branchAssistantIds.has(entry.targetId)) {\n\t\t\tsubtractAssistantUsage(ownUsage, entry.childUsage);\n\t\t}\n\t}\n\treturn { ownUsage, totalUsage };\n}\n\n/** Complete bounded snapshot of an explicitly resident Manager, never an index-error fallback.\n * This synchronous reduction runs in the pre-await live-tree capture walk. It never\n * retains a decoded resident snapshot while native/disk work waits for a slot.\n */\nexport function readResidentContextTreeUsage(\n\tmanager: SessionManager,\n\tlimits: SessionHistoryReadLimits = { maxEntries: 16_384, maxSourceBytes: 64 * 1024 * 1024 },\n\tavailabilityMaxSourceBytes?: number,\n): { ownUsage: Usage; totalUsage: Usage; hasPostCompactionUsage?: boolean } {\n\tconst snapshot = manager.materializeResidentHistory(limits);\n\tconst branch = branchEntries(snapshot.entries, snapshot.leafId, availabilityMaxSourceBytes !== undefined);\n\tif (availabilityMaxSourceBytes !== undefined) {\n\t\tif (!Number.isSafeInteger(availabilityMaxSourceBytes) || availabilityMaxSourceBytes <= 0)\n\t\t\tthrow new Error(\"Invalid parent-path materialization limits\");\n\t\tif (branch.length > 16_384) throw new Error(\"Parent-path entry budget exceeded\");\n\t\tlet bytes = 0;\n\t\tfor (const entry of branch) {\n\t\t\tbytes += Buffer.byteLength(stringifyBoundedJson(entry, availabilityMaxSourceBytes - bytes));\n\t\t}\n\t}\n\treturn {\n\t\t...computeOwnAndTotalUsage(branch, snapshot.entries),\n\t\t...(availabilityMaxSourceBytes === undefined ? {} : { hasPostCompactionUsage: hasPostCompactionUsage(branch) }),\n\t};\n}\n\nfunction isContextTreeUsageReference(reference: IndexedSourceEvent): boolean {\n\treturn (\n\t\t(reference.kind === \"message\" && reference.authority === \"assistant\") ||\n\t\treference.kind === \"compaction\" ||\n\t\treference.kind === \"branch_summary\" ||\n\t\treference.kind === \"child_usage_attributed\"\n\t);\n}\n\n/** Detached, complete source usage and its exact captured parent branch. */\nexport async function readContextTreeUsage(\n\tmanager: SessionManager,\n\tlimits: SessionHistoryReadLimits = { maxEntries: 16_384, maxSourceBytes: 64 * 1024 * 1024 },\n\trequest?: ContextTreeRequest,\n\tavailabilityMaxSourceBytes?: number,\n): Promise<\n\t{ source: SourceSnapshotRef; ownUsage: Usage; totalUsage: Usage; hasPostCompactionUsage?: boolean } | undefined\n> {\n\tif (!manager.supportsCapturedHistoryReads()) return undefined;\n\tconst sourceLimits = request?.limits ?? limits;\n\tconst capturedLimits = { maxEntries: sourceLimits.maxEntries, maxSourceBytes: sourceLimits.maxSourceBytes };\n\tif (!Object.values(capturedLimits).every((value) => Number.isSafeInteger(value) && value > 0))\n\t\tthrow new Error(\"Invalid history materialization limits\");\n\t// The actual source frontier binds NOW, never after waiting for the reduction slot.\n\treturn manager.readSourceHistory((history) => {\n\t\trequest?.retainMetadata(history.source);\n\t\tconst reduce = async () => {\n\t\t\tconst allEntries: SessionEntry[] = [];\n\t\t\tlet sourceBytes = 0;\n\t\t\tlet after = 0;\n\t\t\tfor (;;) {\n\t\t\t\tconst page = await history.page(after);\n\t\t\t\tif (page.indexedThrough < history.source.sourceSequence)\n\t\t\t\t\tthrow new Error(\"Captured history has incomplete index coverage\");\n\t\t\t\tfor (const reference of page.events) {\n\t\t\t\t\tif (!isContextTreeUsageReference(reference)) continue;\n\t\t\t\t\tif (allEntries.length >= capturedLimits.maxEntries) throw new Error(\"History entry budget exceeded\");\n\t\t\t\t\tif (sourceBytes + reference.locator.length > capturedLimits.maxSourceBytes)\n\t\t\t\t\t\tthrow new Error(\"History source byte budget exceeded\");\n\t\t\t\t\tconst hydrated = await history.hydrateEntry(reference.id, capturedLimits.maxSourceBytes - sourceBytes);\n\t\t\t\t\tif (!hydrated) throw new Error(\"Context-tree usage entry source is unavailable\");\n\t\t\t\t\tallEntries.push(hydrated.entry);\n\t\t\t\t\tsourceBytes += reference.locator.length;\n\t\t\t\t}\n\t\t\t\tif (page.nextAfter === null) break;\n\t\t\t\tafter = page.nextAfter;\n\t\t\t}\n\t\t\tapplyChildUsageAttributions(allEntries);\n\t\t\tconst byId = new Map(allEntries.map((entry) => [entry.id, entry]));\n\t\t\tconst branch: SessionEntry[] = [];\n\t\t\tlet cursor: ParentPathCursor | undefined;\n\t\t\tdo {\n\t\t\t\tconst page = await history.parentPath({ cursor });\n\t\t\t\tfor (const reference of page.events) {\n\t\t\t\t\tif (!isContextTreeUsageReference(reference)) continue;\n\t\t\t\t\tconst entry = byId.get(reference.id);\n\t\t\t\t\tif (!entry) throw new Error(\"Context-tree parent-path entry source is unavailable\");\n\t\t\t\t\tbranch.push(entry);\n\t\t\t\t}\n\t\t\t\tcursor = page.nextCursor ?? undefined;\n\t\t\t} while (cursor);\n\t\t\tlet available: boolean | undefined;\n\t\t\tif (availabilityMaxSourceBytes !== undefined) {\n\t\t\t\tconst bootstrap = await history.branchBootstrap();\n\t\t\t\tavailable = !bootstrap.latestCompaction;\n\t\t\t\tconst reference = bootstrap.contextUsageAssistant;\n\t\t\t\tif (bootstrap.latestCompaction && reference) {\n\t\t\t\t\tconst updates = await history.branchContext.contextUpdates({\n\t\t\t\t\t\tkind: \"assistant-usage\",\n\t\t\t\t\t\ttargetId: reference.id,\n\t\t\t\t\t});\n\t\t\t\t\tconst bytes =\n\t\t\t\t\t\treference.locator.length + updates.refs.reduce((sum, update) => sum + update.locator.length, 0);\n\t\t\t\t\tif (bytes > availabilityMaxSourceBytes) throw new Error(\"Context usage source byte budget exceeded\");\n\t\t\t\t\tconst assistant = byId.get(reference.id);\n\t\t\t\t\tif (!assistant || !isAssistantEntry(assistant))\n\t\t\t\t\t\tthrow new Error(\"Context usage assistant source is unavailable\");\n\t\t\t\t\t// Full-source ordered projection above includes off-branch attributions.\n\t\t\t\t\tavailable = calculateContextTokens(assistant.message.usage) > 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst usage = {\n\t\t\t\t...computeOwnAndTotalUsage(branch, allEntries),\n\t\t\t\t...(available === undefined ? {} : { hasPostCompactionUsage: available }),\n\t\t\t};\n\t\t\trequest?.retainMetadata(usage);\n\t\t\treturn { source: history.source, ...usage };\n\t\t};\n\t\treturn request ? request.read(reduce) : reduce();\n\t});\n}\n\nfunction hasPostCompactionUsage(branch: SessionEntry[]): boolean {\n\tlet boundary = -1;\n\tfor (let index = branch.length - 1; index >= 0; index--) {\n\t\tif (branch[index].type === \"compaction\") {\n\t\t\tboundary = index;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (boundary < 0) return true;\n\tfor (let index = branch.length - 1; index > boundary; index--) {\n\t\tconst entry = branch[index];\n\t\tif (!isAssistantEntry(entry)) continue;\n\t\tif (entry.message.stopReason !== \"aborted\" && entry.message.stopReason !== \"error\")\n\t\t\treturn calculateContextTokens(entry.message.usage) > 0;\n\t}\n\treturn false;\n}\n\n/**\n * Current context utilization from persisted entries, mirroring\n * AgentSession.getContextUsage(): unknown right after a compaction until the\n * next assistant response, otherwise the last assistant usage plus an\n * estimate for trailing messages (tool results, queued user input) that have\n * not hit the model yet.\n */\nfunction computeContextUsageFromEntries(\n\tallEntries: SessionEntry[],\n\tbranch: SessionEntry[],\n\tcontextWindow: number | undefined,\n): ContextUsage | undefined {\n\tif (!contextWindow || contextWindow <= 0) {\n\t\treturn undefined;\n\t}\n\n\tif (!hasPostCompactionUsage(branch)) {\n\t\treturn { tokens: null, contextWindow, percent: null };\n\t}\n\n\tconst estimate = estimateContextTokens(buildSessionContext(allEntries, branch.at(-1)?.id ?? null).messages);\n\tif (estimate.tokens <= 0) {\n\t\treturn undefined;\n\t}\n\treturn { tokens: estimate.tokens, contextWindow, percent: (estimate.tokens / contextWindow) * 100 };\n}\n\n/**\n * Entries on the current branch, root to leaf, mirroring\n * SessionManager.getBranch(): the leaf is the last appended entry and the\n * branch is its parentId chain. Keeps forked/abandoned paths out of usage\n * sums so disk nodes match what a live session would report.\n */\nfunction branchEntries(entries: SessionEntry[], leafId?: string | null, strict = false): SessionEntry[] {\n\tif (entries.length === 0) {\n\t\tif (strict && leafId !== undefined && leafId !== null) throw new Error(\"Parent path lineage is unresolved\");\n\t\treturn [];\n\t}\n\tconst byId = new Map(entries.map((entry) => [entry.id, entry]));\n\tconst branch: SessionEntry[] = [];\n\tconst seen = new Set<string>();\n\tlet current: SessionEntry | undefined =\n\t\tleafId === undefined ? entries[entries.length - 1] : leafId === null ? undefined : byId.get(leafId);\n\tif (strict && leafId !== undefined && leafId !== null && !current)\n\t\tthrow new Error(\"Parent path lineage is unresolved\");\n\twhile (current && !seen.has(current.id)) {\n\t\tseen.add(current.id);\n\t\tbranch.push(current);\n\t\tconst parentId = current.parentId;\n\t\tconst hasParent = strict ? parentId !== null : !!parentId;\n\t\tcurrent = hasParent ? byId.get(parentId!) : undefined;\n\t\tif (strict && hasParent && !current) throw new Error(\"Parent path lineage is unresolved\");\n\t}\n\tif (strict && current) throw new Error(\"Parent path lineage is unresolved\");\n\treturn branch.reverse();\n}\n\n/**\n * Terminal status for a persisted child, inferred from how its last assistant\n * turn ended: errored and aborted runs should not render as successful.\n */\nfunction statusFromBranch(entries: SessionEntry[]): \"done\" | \"error\" | \"cancelled\" {\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tif (!isAssistantEntry(entry)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.message.stopReason === \"error\") {\n\t\t\treturn \"error\";\n\t\t}\n\t\tif (entry.message.stopReason === \"aborted\") {\n\t\t\treturn \"cancelled\";\n\t\t}\n\t\treturn \"done\";\n\t}\n\treturn \"done\";\n}\n\n/** Stream names; opening/reading errors keep the caller's existing policy. */\nfunction visitDirectory(dirPath: string, request: ContextTreeRequest, visit: (name: string) => void): void {\n\tconst dir = opendirSync(dirPath);\n\ttry {\n\t\tfor (let entry = dir.readSync(); entry !== null; entry = dir.readSync()) {\n\t\t\trequest.directoryEntry();\n\t\t\tvisit(entry.name);\n\t\t}\n\t} catch (error) {\n\t\ttry {\n\t\t\tdir.closeSync();\n\t\t} catch (closeError) {\n\t\t\tthrow new AggregateError([error, closeError], \"Context tree directory read and close failed\");\n\t\t}\n\t\tthrow error;\n\t}\n\tdir.closeSync();\n}\n\n// readdirSync used UTF8 lexical order; retain its stable mtime-tie ordering.\nfunction comparePaths(a: string, b: string): number {\n\treturn Buffer.compare(Buffer.from(a), Buffer.from(b));\n}\n\nfunction findSessionFile(dir: string, request: ContextTreeRequest): { path: string; bytes: number } | undefined {\n\tlet newest: { path: string; mtime: number; bytes: number } | undefined;\n\ttry {\n\t\tvisitDirectory(dir, request, (name) => {\n\t\t\tif (!name.endsWith(\".jsonl\")) return;\n\t\t\tconst path = join(dir, name);\n\t\t\tlet mtime: number;\n\t\t\ttry {\n\t\t\t\tmtime = statSync(path).mtime.getTime();\n\t\t\t} catch {\n\t\t\t\treturn; // Skip unreadable files.\n\t\t\t}\n\t\t\tif (!newest || mtime > newest.mtime || (mtime === newest.mtime && comparePaths(path, newest.path) < 0)) {\n\t\t\t\tif (newest) request.releaseMetadata(newest.bytes);\n\t\t\t\tnewest = undefined;\n\t\t\t\tconst bytes = request.retainMetadata(path);\n\t\t\t\tnewest = { path, mtime, bytes };\n\t\t\t}\n\t\t});\n\t\treturn newest;\n\t} catch (error) {\n\t\tif (newest) request.releaseMetadata(newest.bytes);\n\t\tthrow error;\n\t}\n}\n\nfunction listChildSessionDirs(rlmSessionDir: string, request: ContextTreeRequest): { paths: string[]; bytes: number } {\n\tconst paths: string[] = [];\n\tlet bytes = 0;\n\ttry {\n\t\tvisitDirectory(rlmSessionDir, request, (name) => {\n\t\t\tif (!name.startsWith(\"sub-\")) return;\n\t\t\tconst path = join(rlmSessionDir, name);\n\t\t\ttry {\n\t\t\t\tif (!statSync(path).isDirectory()) return;\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbytes += request.retainMetadata(path);\n\t\t\tpaths.push(path);\n\t\t});\n\t} catch (error) {\n\t\trequest.releaseMetadata(bytes);\n\t\tif (error instanceof ContextTreeLimitError || error instanceof AggregateError) throw error;\n\t\treturn { paths: [], bytes: 0 };\n\t}\n\tpaths.sort(comparePaths).sort((a, b) => {\n\t\ttry {\n\t\t\treturn statSync(a).mtime.getTime() - statSync(b).mtime.getTime();\n\t\t} catch {\n\t\t\treturn 0;\n\t\t}\n\t});\n\treturn { paths, bytes };\n}\n\n/** Read a fixed disk prefix without retaining private request or tool-intent bodies. */\nasync function readContextTreeProjectionFromDisk(\n\tsessionFile: string,\n\trequest: ContextTreeRequest,\n): Promise<{ entries: SessionEntry[]; leafId: string | null; hasSourceLeaf: boolean }> {\n\tconst entries: SessionEntry[] = [];\n\tconst hiddenParents = new Map<string, string | null>();\n\tlet leafId: string | null = null;\n\tlet hasHeader = false;\n\tlet sourceBytes = 0;\n\tlet metadataBytes = 0;\n\ttry {\n\t\tawait readCapturedSessionJournal(sessionFile, (record) => {\n\t\t\tconst value = record.entry;\n\t\t\tif (!value || typeof value !== \"object\" || !(\"type\" in value) || typeof value.type !== \"string\")\n\t\t\t\tthrow new Error(\"Invalid session journal entry\");\n\t\t\tconst entry = value as FileEntry;\n\t\t\tif (!hasHeader) {\n\t\t\t\tif (entry.type !== \"session\" || typeof entry.id !== \"string\")\n\t\t\t\t\tthrow new Error(\"Session source has no valid header\");\n\t\t\t\thasHeader = true;\n\t\t\t}\n\t\t\tif (entry.type !== \"session\") leafId = entry.id;\n\t\t\tif (entry.type === \"request\" || entry.type === \"tool_intent\") {\n\t\t\t\tmetadataBytes += request.retainMetadata([entry.id, entry.parentId]);\n\t\t\t\thiddenParents.set(entry.id, entry.parentId);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst bytes = record.source?.locator.length ?? Buffer.byteLength(record.json) + 1;\n\t\t\tif (sourceBytes + bytes > request.limits.maxSourceBytes)\n\t\t\t\tthrow new ContextTreeLimitError(\"Context tree source byte budget exceeded\");\n\t\t\tsourceBytes += bytes;\n\t\t\tif (entry.type === \"session\") return;\n\t\t\tif (entries.length >= request.limits.maxEntries)\n\t\t\t\tthrow new ContextTreeLimitError(\"Context tree entry budget exceeded\");\n\t\t\tentries.push(entry);\n\t\t});\n\t\tconst visibleParent = (id: string | null): string | null => {\n\t\t\tlet remaining = hiddenParents.size;\n\t\t\twhile (id !== null && hiddenParents.has(id)) {\n\t\t\t\tif (remaining-- === 0) throw new Error(\"Parent path lineage is unresolved\");\n\t\t\t\tid = hiddenParents.get(id)!;\n\t\t\t}\n\t\t\treturn id;\n\t\t};\n\t\tfor (const entry of entries) entry.parentId = visibleParent(entry.parentId);\n\t\t// A trailing private record can select an older branch, not the last visible entry.\n\t\tconst hasSourceLeaf = leafId !== null;\n\t\tleafId = visibleParent(leafId);\n\t\tapplyChildUsageAttributions(entries);\n\t\treturn { entries, leafId, hasSourceLeaf };\n\t} finally {\n\t\trequest.releaseMetadata(metadataBytes);\n\t}\n}\n\n/**\n * Build a context node for a completed RLM child from its persisted session\n * dir (sub-xxxx/). Children that already attributed grandchild usage carry the\n * aggregate on their assistant messages (applyChildUsageAttributions), so own\n * usage is recovered by subtracting the attribution entries. Returns undefined\n * when the dir holds no readable session.\n */\nasync function readContextTreeChildNodeFromDisk(\n\tchildSessionDir: string,\n\tsessionFile: string,\n\tresolveContextWindow: ContextWindowResolver,\n\trequest: ContextTreeRequest,\n\tidentity?: Pick<ContextTreeNode, \"id\" | \"label\" | \"status\">,\n): Promise<ContextTreeNode | undefined> {\n\tconst { entries: allEntries, leafId, hasSourceLeaf } = await readContextTreeProjectionFromDisk(sessionFile, request);\n\tconst branch = branchEntries(allEntries, leafId);\n\tif (branch.length === 0 && !hasSourceLeaf) {\n\t\treturn undefined;\n\t}\n\n\tconst { ownUsage, totalUsage } = computeOwnAndTotalUsage(branch, allEntries);\n\n\tlet model: { provider: string; id: string } | undefined;\n\tfor (const entry of branch) {\n\t\tif (entry.type === \"model_change\") {\n\t\t\tmodel = { provider: entry.provider, id: entry.modelId };\n\t\t}\n\t}\n\n\tlet label = \"\";\n\tfor (const entry of branch) {\n\t\tif (entry.type === \"message\" && entry.message.role === \"user\") {\n\t\t\tlabel = compactLabel(readUserMessageText(entry.message.content));\n\t\t\tif (label) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst contextWindow = model ? resolveContextWindow(model.provider, model.id) : undefined;\n\n\tconst metadata = {\n\t\t...(identity ?? {\n\t\t\tid: basename(childSessionDir),\n\t\t\tlabel: label || \"child agent\",\n\t\t\tstatus: statusFromBranch(branch),\n\t\t}),\n\t\tmodel,\n\t\townUsage,\n\t\ttotalUsage,\n\t\tcontextUsage: computeContextUsageFromEntries(allEntries, branch, contextWindow),\n\t};\n\t// A registered run's identity was already admitted by its live parent.\n\trequest.retainMetadata(identity ? { model, ownUsage, totalUsage, contextUsage: metadata.contextUsage } : metadata);\n\treturn { ...metadata, children: [] };\n}\n\n/** Complete or refuse, sharing the request's slot and counters with descendants.\n * Internal identity is an already-admitted registered run, including its empty placeholder.\n */\nexport async function loadContextTreeChildFromDisk(\n\tchildSessionDir: string,\n\tresolveContextWindow: ContextWindowResolver,\n\tlimits: Partial<ContextTreeRequestLimits> | ContextTreeRequest = {},\n\tidentity?: Pick<ContextTreeNode, \"id\" | \"label\" | \"status\">,\n): Promise<ContextTreeNode | undefined> {\n\tconst request = treeRequest(limits);\n\tconst directoryBytes = request.retainMetadata(childSessionDir);\n\tlet sessionFile: { path: string; bytes: number } | undefined;\n\tlet node: ContextTreeNode | undefined;\n\ttry {\n\t\tsessionFile = findSessionFile(childSessionDir, request);\n\t\tif (sessionFile && existsSync(sessionFile.path)) {\n\t\t\t// A header-only history still consumes one bounded source admission.\n\t\t\tif (!identity) request.admitNode();\n\t\t\tnode = await request.read(() =>\n\t\t\t\treadContextTreeChildNodeFromDisk(\n\t\t\t\t\tchildSessionDir,\n\t\t\t\t\tsessionFile!.path,\n\t\t\t\t\tresolveContextWindow,\n\t\t\t\t\trequest,\n\t\t\t\t\tidentity,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t} finally {\n\t\tif (sessionFile) request.releaseMetadata(sessionFile.bytes);\n\t\trequest.releaseMetadata(directoryBytes);\n\t}\n\tif (node) {\n\t\t// The full source has been reduced and the serial slot released before recursion.\n\t\tnode.children = await loadContextTreeChildrenFromDisk(childSessionDir, resolveContextWindow, undefined, request);\n\t} else if (identity) {\n\t\tconst usage = { ownUsage: emptyUsage(), totalUsage: emptyUsage() };\n\t\trequest.retainMetadata(usage);\n\t\tnode = { ...identity, ...usage, children: [] };\n\t}\n\treturn node;\n}\n\n/** Persisted children, in existing directory order, excluding already represented live IDs. */\nexport async function loadContextTreeChildrenFromDisk(\n\trlmSessionDir: string | undefined,\n\tresolveContextWindow: ContextWindowResolver,\n\tskipIds?: ReadonlySet<string>,\n\tlimits: Partial<ContextTreeRequestLimits> | ContextTreeRequest = {},\n): Promise<ContextTreeNode[]> {\n\tconst request = treeRequest(limits);\n\tconst skippedIds = new Set<string>();\n\tlet metadataBytes = 0;\n\ttry {\n\t\t// A caller-provided skip set is bounded before copying, too.\n\t\tif (skipIds && skipIds.size > request.limits.maxDirectoryEntries)\n\t\t\tthrow new ContextTreeLimitError(\"Context tree directory entry budget exceeded\");\n\t\tfor (const id of skipIds ?? []) {\n\t\t\tmetadataBytes += request.retainMetadata(id);\n\t\t\tskippedIds.add(id);\n\t\t}\n\t\tif (!rlmSessionDir || !existsSync(rlmSessionDir)) return [];\n\t\tmetadataBytes += request.retainMetadata(rlmSessionDir);\n\t\tconst directories = listChildSessionDirs(rlmSessionDir, request);\n\t\tmetadataBytes += directories.bytes;\n\t\tconst nodes: ContextTreeNode[] = [];\n\t\tfor (const childDir of directories.paths) {\n\t\t\tif (skippedIds.has(basename(childDir))) continue;\n\t\t\tconst node = await loadContextTreeChildFromDisk(childDir, resolveContextWindow, request);\n\t\t\tif (node) nodes.push(node);\n\t\t}\n\t\treturn nodes;\n\t} finally {\n\t\trequest.releaseMetadata(metadataBytes);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"context-tree.d.ts","sourceRoot":"","sources":["../../src/core/context-tree.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, opendirSync, statSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport type { AssistantMessage, Usage } from \"@ponythewhite/base-context-ai\";\nimport type { RlmChildAgentStatus } from \"./agent-session.js\";\nimport { stringifyBoundedJson } from \"./bounded-json.js\";\nimport { calculateContextTokens, estimateContextTokens } from \"./compaction/index.js\";\nimport { exportHistoryLimits } from \"./export-html/history.js\";\nimport type { ContextUsage } from \"./extensions/index.js\";\nimport type { IndexedSourceEvent, ParentPathCursor } from \"./history-index.js\";\nimport type { SourceSnapshotRef } from \"./request-events.js\";\nimport { type OwnRequestUsage, RequestUsageAccumulator } from \"./request-usage.js\";\nimport type { SessionHistoryReadLimits } from \"./session-history-index.js\";\nimport { readCapturedSessionJournal } from \"./session-journal-reader.js\";\nimport {\n\tapplyChildUsageAttributions,\n\tbuildSessionContext,\n\ttype FileEntry,\n\ttype SessionEntry,\n\ttype SessionManager,\n} from \"./session-manager.js\";\nimport { addAssistantUsage, cloneUsage, emptyUsage, subtractAssistantUsage } from \"./usage.js\";\n\n/** Resolves a model's context window so disk-only nodes can report utilization. */\nexport type ContextWindowResolver = (provider: string, modelId: string) => number | undefined;\n\n/**\n * One agent in the context overview: the main session or an RLM (sub-)agent.\n * `ownUsage` excludes descendants; `totalUsage` includes completed descendants, matching /usage.\n */\nexport interface ContextTreeNode {\n\t/** \"root\" for the session itself; sub-xxxx for an RLM child. */\n\tid: string;\n\tlabel: string;\n\tstatus: \"active\" | RlmChildAgentStatus;\n\tmodel?: { provider: string; id: string };\n\townUsage: Usage;\n\ttotalUsage: Usage;\n\t/** Existing generation receipts for this session's captured source, never child aggregates. */\n\townRequestUsage?: OwnRequestUsage;\n\tcontextUsage?: ContextUsage;\n\tchildren: ContextTreeNode[];\n}\n\n/** Shared by one overview request, not by concurrent requests or the process. */\nexport interface ContextTreeRequestLimits extends SessionHistoryReadLimits {\n\tmaxNodes: number;\n\tmaxMetadataBytes: number;\n\tmaxDirectoryEntries: number;\n}\n\nclass ContextTreeLimitError extends Error {}\n\n/** Small request-local counters and one history-projection reduction tail. */\nexport class ContextTreeRequest {\n\treadonly limits: Readonly<ContextTreeRequestLimits>;\n\tprivate nodes = 0;\n\tprivate metadataBytes = 0;\n\tprivate directoryEntries = 0;\n\tprivate readTail: Promise<void> = Promise.resolve();\n\n\tconstructor(limits: Partial<ContextTreeRequestLimits> = {}) {\n\t\tthis.limits = Object.freeze({\n\t\t\t...exportHistoryLimits(limits),\n\t\t\tmaxNodes: limits.maxNodes ?? 256,\n\t\t\tmaxMetadataBytes: limits.maxMetadataBytes ?? 4 * 1024 * 1024,\n\t\t\tmaxDirectoryEntries: limits.maxDirectoryEntries ?? 16_384,\n\t\t});\n\t\tif (!Object.values(this.limits).every((value) => Number.isSafeInteger(value) && value > 0))\n\t\t\tthrow new Error(\"Invalid context tree request limits\");\n\t}\n\n\tadmitNode(): void {\n\t\tif (this.nodes >= this.limits.maxNodes) throw new ContextTreeLimitError(\"Context tree node budget exceeded\");\n\t\tthis.nodes++;\n\t}\n\n\tretainMetadata(value: unknown): number {\n\t\tlet json: string;\n\t\ttry {\n\t\t\tjson = stringifyBoundedJson(value, this.limits.maxMetadataBytes - this.metadataBytes);\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error && error.message === \"JSON byte limit exceeded\")\n\t\t\t\tthrow new ContextTreeLimitError(\"Context tree metadata byte budget exceeded\", { cause: error });\n\t\t\tthrow error;\n\t\t}\n\t\tconst bytes = Buffer.byteLength(json);\n\t\tthis.metadataBytes += bytes;\n\t\treturn bytes;\n\t}\n\n\treleaseMetadata(bytes: number): void {\n\t\tthis.metadataBytes -= bytes;\n\t}\n\n\tdirectoryEntry(): void {\n\t\tif (this.directoryEntries >= this.limits.maxDirectoryEntries)\n\t\t\tthrow new ContextTreeLimitError(\"Context tree directory entry budget exceeded\");\n\t\tthis.directoryEntries++;\n\t}\n\n\tread<T>(read: () => Promise<T>): Promise<T> {\n\t\tconst result = this.readTail.then(read);\n\t\tthis.readTail = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn result;\n\t}\n}\n\nfunction treeRequest(limits: Partial<ContextTreeRequestLimits> | ContextTreeRequest): ContextTreeRequest {\n\treturn limits instanceof ContextTreeRequest ? limits : new ContextTreeRequest(limits);\n}\n\nfunction isAssistantEntry(entry: SessionEntry): entry is SessionEntry & {\n\ttype: \"message\";\n\tmessage: AssistantMessage;\n} {\n\treturn entry.type === \"message\" && entry.message.role === \"assistant\";\n}\n\nfunction readUserMessageText(content: unknown): string {\n\tif (typeof content === \"string\") {\n\t\treturn content;\n\t}\n\tif (!Array.isArray(content)) {\n\t\treturn \"\";\n\t}\n\treturn content\n\t\t.filter(\n\t\t\t(block): block is { type: \"text\"; text: string } =>\n\t\t\t\ttypeof block === \"object\" &&\n\t\t\t\tblock !== null &&\n\t\t\t\t(block as { type?: unknown }).type === \"text\" &&\n\t\t\t\ttypeof (block as { text?: unknown }).text === \"string\",\n\t\t)\n\t\t.map((block) => block.text)\n\t\t.join(\"\\n\");\n}\n\nfunction compactLabel(text: string, maxLength = 80): string {\n\tconst compact = text.replace(/\\s+/g, \" \").trim();\n\tif (compact.length <= maxLength) {\n\t\treturn compact;\n\t}\n\treturn `${compact.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;\n}\n\n/**\n * Usage totals for one agent: `totalUsage` sums the branch's assistant usage\n * (attributed aggregates, so descendants are included), `ownUsage` removes the\n * attributions targeting those assistants. Attribution entries are matched by\n * target across ALL entries, not just the branch: attributions rewrite the\n * target assistant's usage no matter which branch they were appended on, so a\n * fork that keeps the assistant but drops the attribution entry must still\n * subtract it.\n *\n * Totals are deliberately cumulative across compactions: compaction shrinks\n * the model-facing context, not what the session has spent, so assistants\n * dropped from the resolved context still count here.\n */\nexport function computeOwnAndTotalUsage(\n\tbranch: SessionEntry[],\n\tallEntries: SessionEntry[],\n): { ownUsage: Usage; totalUsage: Usage } {\n\tconst totalUsage = emptyUsage();\n\tconst branchAssistantIds = new Set<string>();\n\tfor (const entry of branch) {\n\t\tif (isAssistantEntry(entry)) {\n\t\t\tbranchAssistantIds.add(entry.id);\n\t\t\taddAssistantUsage(totalUsage, entry.message.usage);\n\t\t} else if ((entry.type === \"compaction\" || entry.type === \"branch_summary\") && entry.usage) {\n\t\t\taddAssistantUsage(totalUsage, entry.usage);\n\t\t}\n\t}\n\tconst ownUsage = cloneUsage(totalUsage);\n\tfor (const entry of allEntries) {\n\t\tif (entry.type === \"child_usage_attributed\" && branchAssistantIds.has(entry.targetId)) {\n\t\t\tsubtractAssistantUsage(ownUsage, entry.childUsage);\n\t\t}\n\t}\n\treturn { ownUsage, totalUsage };\n}\n\ninterface ContextTreeUsage {\n\townUsage: Usage;\n\ttotalUsage: Usage;\n\townRequestUsage?: OwnRequestUsage;\n\thasPostCompactionUsage?: boolean;\n}\n\n/** Complete bounded snapshot of an explicitly resident Manager, never an index-error fallback.\n * This synchronous reduction runs in the pre-await live-tree capture walk. It never\n * retains a decoded resident snapshot while native/disk work waits for a slot.\n */\nexport function readResidentContextTreeUsage(\n\tmanager: SessionManager,\n\tlimits: SessionHistoryReadLimits = { maxEntries: 16_384, maxSourceBytes: 64 * 1024 * 1024 },\n\tavailabilityMaxSourceBytes?: number,\n): ContextTreeUsage {\n\tconst snapshot = manager.materializeResidentHistory(limits);\n\tconst receipts = new RequestUsageAccumulator(manager.getSessionId());\n\tfor (const entry of snapshot.entries) if (entry.type === \"request\") receipts.add(entry.request);\n\tconst ownRequestUsage = receipts.finish();\n\tconst branch = branchEntries(snapshot.entries, snapshot.leafId, availabilityMaxSourceBytes !== undefined);\n\tif (availabilityMaxSourceBytes !== undefined) {\n\t\tif (!Number.isSafeInteger(availabilityMaxSourceBytes) || availabilityMaxSourceBytes <= 0)\n\t\t\tthrow new Error(\"Invalid parent-path materialization limits\");\n\t\tif (branch.length > 16_384) throw new Error(\"Parent-path entry budget exceeded\");\n\t\tlet bytes = 0;\n\t\tfor (const entry of branch) {\n\t\t\tbytes += Buffer.byteLength(stringifyBoundedJson(entry, availabilityMaxSourceBytes - bytes));\n\t\t}\n\t}\n\treturn {\n\t\t...computeOwnAndTotalUsage(branch, snapshot.entries),\n\t\t...(ownRequestUsage ? { ownRequestUsage } : {}),\n\t\t...(availabilityMaxSourceBytes === undefined ? {} : { hasPostCompactionUsage: hasPostCompactionUsage(branch) }),\n\t};\n}\n\nfunction isContextTreeUsageReference(reference: IndexedSourceEvent): boolean {\n\treturn (\n\t\t(reference.kind === \"message\" && reference.authority === \"assistant\") ||\n\t\treference.kind === \"compaction\" ||\n\t\treference.kind === \"branch_summary\" ||\n\t\treference.kind === \"child_usage_attributed\"\n\t);\n}\n\n/** Detached, complete source usage and its exact captured parent branch. */\nexport async function readContextTreeUsage(\n\tmanager: SessionManager,\n\tlimits: SessionHistoryReadLimits = { maxEntries: 16_384, maxSourceBytes: 64 * 1024 * 1024 },\n\trequest?: ContextTreeRequest,\n\tavailabilityMaxSourceBytes?: number,\n): Promise<(ContextTreeUsage & { source: SourceSnapshotRef }) | undefined> {\n\tif (!manager.supportsCapturedHistoryReads()) return undefined;\n\tconst sourceLimits = request?.limits ?? limits;\n\tconst capturedLimits = { maxEntries: sourceLimits.maxEntries, maxSourceBytes: sourceLimits.maxSourceBytes };\n\tif (!Object.values(capturedLimits).every((value) => Number.isSafeInteger(value) && value > 0))\n\t\tthrow new Error(\"Invalid history materialization limits\");\n\t// The actual source frontier binds NOW, never after waiting for the reduction slot.\n\treturn manager.readSourceHistory((history) => {\n\t\trequest?.retainMetadata(history.source);\n\t\tconst reduce = async () => {\n\t\t\tconst allEntries: SessionEntry[] = [];\n\t\t\tlet receipts: RequestUsageAccumulator | undefined = new RequestUsageAccumulator(history.source.sessionId);\n\t\t\tlet requestEntries = 0;\n\t\t\tlet requestBytes = 0;\n\t\t\tlet sourceBytes = 0;\n\t\t\tlet after = 0;\n\t\t\tfor (;;) {\n\t\t\t\tconst page = await history.page(after);\n\t\t\t\tif (page.indexedThrough < history.source.sourceSequence)\n\t\t\t\t\tthrow new Error(\"Captured history has incomplete index coverage\");\n\t\t\t\tfor (const reference of page.events) {\n\t\t\t\t\tif (reference.kind === \"request\" && receipts) {\n\t\t\t\t\t\trequestEntries++;\n\t\t\t\t\t\trequestBytes += reference.locator.length;\n\t\t\t\t\t\tif (requestEntries > capturedLimits.maxEntries || requestBytes > capturedLimits.maxSourceBytes) {\n\t\t\t\t\t\t\treceipts = undefined;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst hydrated = await history.hydrateEntry(reference.id, reference.locator.length);\n\t\t\t\t\t\t\t\tif (hydrated?.entry.type !== \"request\") receipts = undefined;\n\t\t\t\t\t\t\t\telse receipts.add(hydrated.entry.request);\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t// Optional accounting metadata must not prevent the context overview.\n\t\t\t\t\t\t\t\treceipts = undefined;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (!isContextTreeUsageReference(reference)) continue;\n\t\t\t\t\tif (allEntries.length >= capturedLimits.maxEntries) throw new Error(\"History entry budget exceeded\");\n\t\t\t\t\tif (sourceBytes + reference.locator.length > capturedLimits.maxSourceBytes)\n\t\t\t\t\t\tthrow new Error(\"History source byte budget exceeded\");\n\t\t\t\t\tconst hydrated = await history.hydrateEntry(reference.id, capturedLimits.maxSourceBytes - sourceBytes);\n\t\t\t\t\tif (!hydrated) throw new Error(\"Context-tree usage entry source is unavailable\");\n\t\t\t\t\tallEntries.push(hydrated.entry);\n\t\t\t\t\tsourceBytes += reference.locator.length;\n\t\t\t\t}\n\t\t\t\tif (page.nextAfter === null) break;\n\t\t\t\tafter = page.nextAfter;\n\t\t\t}\n\t\t\tapplyChildUsageAttributions(allEntries);\n\t\t\tconst byId = new Map(allEntries.map((entry) => [entry.id, entry]));\n\t\t\tconst branch: SessionEntry[] = [];\n\t\t\tlet cursor: ParentPathCursor | undefined;\n\t\t\tdo {\n\t\t\t\tconst page = await history.parentPath({ cursor });\n\t\t\t\tfor (const reference of page.events) {\n\t\t\t\t\tif (!isContextTreeUsageReference(reference)) continue;\n\t\t\t\t\tconst entry = byId.get(reference.id);\n\t\t\t\t\tif (!entry) throw new Error(\"Context-tree parent-path entry source is unavailable\");\n\t\t\t\t\tbranch.push(entry);\n\t\t\t\t}\n\t\t\t\tcursor = page.nextCursor ?? undefined;\n\t\t\t} while (cursor);\n\t\t\tlet available: boolean | undefined;\n\t\t\tif (availabilityMaxSourceBytes !== undefined) {\n\t\t\t\tconst bootstrap = await history.branchBootstrap();\n\t\t\t\tavailable = !bootstrap.latestCompaction;\n\t\t\t\tconst reference = bootstrap.contextUsageAssistant;\n\t\t\t\tif (bootstrap.latestCompaction && reference) {\n\t\t\t\t\tconst updates = await history.branchContext.contextUpdates({\n\t\t\t\t\t\tkind: \"assistant-usage\",\n\t\t\t\t\t\ttargetId: reference.id,\n\t\t\t\t\t});\n\t\t\t\t\tconst bytes =\n\t\t\t\t\t\treference.locator.length + updates.refs.reduce((sum, update) => sum + update.locator.length, 0);\n\t\t\t\t\tif (bytes > availabilityMaxSourceBytes) throw new Error(\"Context usage source byte budget exceeded\");\n\t\t\t\t\tconst assistant = byId.get(reference.id);\n\t\t\t\t\tif (!assistant || !isAssistantEntry(assistant))\n\t\t\t\t\t\tthrow new Error(\"Context usage assistant source is unavailable\");\n\t\t\t\t\t// Full-source ordered projection above includes off-branch attributions.\n\t\t\t\t\tavailable = calculateContextTokens(assistant.message.usage) > 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst ownRequestUsage = receipts?.finish();\n\t\t\tconst usage = {\n\t\t\t\t...computeOwnAndTotalUsage(branch, allEntries),\n\t\t\t\t...(ownRequestUsage ? { ownRequestUsage } : {}),\n\t\t\t\t...(available === undefined ? {} : { hasPostCompactionUsage: available }),\n\t\t\t};\n\t\t\trequest?.retainMetadata(usage);\n\t\t\treturn { source: history.source, ...usage };\n\t\t};\n\t\treturn request ? request.read(reduce) : reduce();\n\t});\n}\n\nfunction hasPostCompactionUsage(branch: SessionEntry[]): boolean {\n\tlet boundary = -1;\n\tfor (let index = branch.length - 1; index >= 0; index--) {\n\t\tif (branch[index].type === \"compaction\") {\n\t\t\tboundary = index;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (boundary < 0) return true;\n\tfor (let index = branch.length - 1; index > boundary; index--) {\n\t\tconst entry = branch[index];\n\t\tif (!isAssistantEntry(entry)) continue;\n\t\tif (entry.message.stopReason !== \"aborted\" && entry.message.stopReason !== \"error\")\n\t\t\treturn calculateContextTokens(entry.message.usage) > 0;\n\t}\n\treturn false;\n}\n\n/**\n * Current context utilization from persisted entries, mirroring\n * AgentSession.getContextUsage(): unknown right after a compaction until the\n * next assistant response, otherwise the last assistant usage plus an\n * estimate for trailing messages (tool results, queued user input) that have\n * not hit the model yet.\n */\nfunction computeContextUsageFromEntries(\n\tallEntries: SessionEntry[],\n\tbranch: SessionEntry[],\n\tcontextWindow: number | undefined,\n): ContextUsage | undefined {\n\tif (!contextWindow || contextWindow <= 0) {\n\t\treturn undefined;\n\t}\n\n\tif (!hasPostCompactionUsage(branch)) {\n\t\treturn { tokens: null, contextWindow, percent: null };\n\t}\n\n\tconst estimate = estimateContextTokens(buildSessionContext(allEntries, branch.at(-1)?.id ?? null).messages);\n\tif (estimate.tokens <= 0) {\n\t\treturn undefined;\n\t}\n\treturn { tokens: estimate.tokens, contextWindow, percent: (estimate.tokens / contextWindow) * 100 };\n}\n\n/**\n * Entries on the current branch, root to leaf, mirroring\n * SessionManager.getBranch(): the leaf is the last appended entry and the\n * branch is its parentId chain. Keeps forked/abandoned paths out of usage\n * sums so disk nodes match what a live session would report.\n */\nfunction branchEntries(entries: SessionEntry[], leafId?: string | null, strict = false): SessionEntry[] {\n\tif (entries.length === 0) {\n\t\tif (strict && leafId !== undefined && leafId !== null) throw new Error(\"Parent path lineage is unresolved\");\n\t\treturn [];\n\t}\n\tconst byId = new Map(entries.map((entry) => [entry.id, entry]));\n\tconst branch: SessionEntry[] = [];\n\tconst seen = new Set<string>();\n\tlet current: SessionEntry | undefined =\n\t\tleafId === undefined ? entries[entries.length - 1] : leafId === null ? undefined : byId.get(leafId);\n\tif (strict && leafId !== undefined && leafId !== null && !current)\n\t\tthrow new Error(\"Parent path lineage is unresolved\");\n\twhile (current && !seen.has(current.id)) {\n\t\tseen.add(current.id);\n\t\tbranch.push(current);\n\t\tconst parentId = current.parentId;\n\t\tconst hasParent = strict ? parentId !== null : !!parentId;\n\t\tcurrent = hasParent ? byId.get(parentId!) : undefined;\n\t\tif (strict && hasParent && !current) throw new Error(\"Parent path lineage is unresolved\");\n\t}\n\tif (strict && current) throw new Error(\"Parent path lineage is unresolved\");\n\treturn branch.reverse();\n}\n\n/**\n * Terminal status for a persisted child, inferred from how its last assistant\n * turn ended: errored and aborted runs should not render as successful.\n */\nfunction statusFromBranch(entries: SessionEntry[]): \"done\" | \"error\" | \"cancelled\" {\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tif (!isAssistantEntry(entry)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.message.stopReason === \"error\") {\n\t\t\treturn \"error\";\n\t\t}\n\t\tif (entry.message.stopReason === \"aborted\") {\n\t\t\treturn \"cancelled\";\n\t\t}\n\t\treturn \"done\";\n\t}\n\treturn \"done\";\n}\n\n/** Stream names; opening/reading errors keep the caller's existing policy. */\nfunction visitDirectory(dirPath: string, request: ContextTreeRequest, visit: (name: string) => void): void {\n\tconst dir = opendirSync(dirPath);\n\ttry {\n\t\tfor (let entry = dir.readSync(); entry !== null; entry = dir.readSync()) {\n\t\t\trequest.directoryEntry();\n\t\t\tvisit(entry.name);\n\t\t}\n\t} catch (error) {\n\t\ttry {\n\t\t\tdir.closeSync();\n\t\t} catch (closeError) {\n\t\t\tthrow new AggregateError([error, closeError], \"Context tree directory read and close failed\");\n\t\t}\n\t\tthrow error;\n\t}\n\tdir.closeSync();\n}\n\n// readdirSync used UTF8 lexical order; retain its stable mtime-tie ordering.\nfunction comparePaths(a: string, b: string): number {\n\treturn Buffer.compare(Buffer.from(a), Buffer.from(b));\n}\n\nfunction findSessionFile(dir: string, request: ContextTreeRequest): { path: string; bytes: number } | undefined {\n\tlet newest: { path: string; mtime: number; bytes: number } | undefined;\n\ttry {\n\t\tvisitDirectory(dir, request, (name) => {\n\t\t\tif (!name.endsWith(\".jsonl\")) return;\n\t\t\tconst path = join(dir, name);\n\t\t\tlet mtime: number;\n\t\t\ttry {\n\t\t\t\tmtime = statSync(path).mtime.getTime();\n\t\t\t} catch {\n\t\t\t\treturn; // Skip unreadable files.\n\t\t\t}\n\t\t\tif (!newest || mtime > newest.mtime || (mtime === newest.mtime && comparePaths(path, newest.path) < 0)) {\n\t\t\t\tif (newest) request.releaseMetadata(newest.bytes);\n\t\t\t\tnewest = undefined;\n\t\t\t\tconst bytes = request.retainMetadata(path);\n\t\t\t\tnewest = { path, mtime, bytes };\n\t\t\t}\n\t\t});\n\t\treturn newest;\n\t} catch (error) {\n\t\tif (newest) request.releaseMetadata(newest.bytes);\n\t\tthrow error;\n\t}\n}\n\nfunction listChildSessionDirs(rlmSessionDir: string, request: ContextTreeRequest): { paths: string[]; bytes: number } {\n\tconst paths: string[] = [];\n\tlet bytes = 0;\n\ttry {\n\t\tvisitDirectory(rlmSessionDir, request, (name) => {\n\t\t\tif (!name.startsWith(\"sub-\")) return;\n\t\t\tconst path = join(rlmSessionDir, name);\n\t\t\ttry {\n\t\t\t\tif (!statSync(path).isDirectory()) return;\n\t\t\t} catch {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbytes += request.retainMetadata(path);\n\t\t\tpaths.push(path);\n\t\t});\n\t} catch (error) {\n\t\trequest.releaseMetadata(bytes);\n\t\tif (error instanceof ContextTreeLimitError || error instanceof AggregateError) throw error;\n\t\treturn { paths: [], bytes: 0 };\n\t}\n\tpaths.sort(comparePaths).sort((a, b) => {\n\t\ttry {\n\t\t\treturn statSync(a).mtime.getTime() - statSync(b).mtime.getTime();\n\t\t} catch {\n\t\t\treturn 0;\n\t\t}\n\t});\n\treturn { paths, bytes };\n}\n\n/** Read a fixed disk prefix without retaining private request or tool-intent bodies. */\nasync function readContextTreeProjectionFromDisk(\n\tsessionFile: string,\n\trequest: ContextTreeRequest,\n): Promise<{\n\tentries: SessionEntry[];\n\tleafId: string | null;\n\thasSourceLeaf: boolean;\n\townRequestUsage?: OwnRequestUsage;\n}> {\n\tconst entries: SessionEntry[] = [];\n\tconst hiddenParents = new Map<string, string | null>();\n\tlet leafId: string | null = null;\n\tlet hasHeader = false;\n\tlet receipts: RequestUsageAccumulator | undefined;\n\tlet requestEntries = 0;\n\tlet requestBytes = 0;\n\tlet sourceBytes = 0;\n\tlet metadataBytes = 0;\n\ttry {\n\t\tawait readCapturedSessionJournal(sessionFile, (record) => {\n\t\t\tconst value = record.entry;\n\t\t\tif (!value || typeof value !== \"object\" || !(\"type\" in value) || typeof value.type !== \"string\")\n\t\t\t\tthrow new Error(\"Invalid session journal entry\");\n\t\t\tconst entry = value as FileEntry;\n\t\t\tif (!hasHeader) {\n\t\t\t\tif (entry.type !== \"session\" || typeof entry.id !== \"string\")\n\t\t\t\t\tthrow new Error(\"Session source has no valid header\");\n\t\t\t\thasHeader = true;\n\t\t\t\treceipts = new RequestUsageAccumulator(entry.id);\n\t\t\t}\n\t\t\tif (entry.type === \"request\" && receipts) {\n\t\t\t\trequestEntries++;\n\t\t\t\trequestBytes += record.source?.locator.length ?? Buffer.byteLength(record.json) + 1;\n\t\t\t\tif (requestEntries > request.limits.maxEntries || requestBytes > request.limits.maxSourceBytes)\n\t\t\t\t\treceipts = undefined;\n\t\t\t\telse receipts.add(entry.request);\n\t\t\t}\n\t\t\tif (entry.type !== \"session\") leafId = entry.id;\n\t\t\tif (entry.type === \"request\" || entry.type === \"tool_intent\") {\n\t\t\t\tmetadataBytes += request.retainMetadata([entry.id, entry.parentId]);\n\t\t\t\thiddenParents.set(entry.id, entry.parentId);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst bytes = record.source?.locator.length ?? Buffer.byteLength(record.json) + 1;\n\t\t\tif (sourceBytes + bytes > request.limits.maxSourceBytes)\n\t\t\t\tthrow new ContextTreeLimitError(\"Context tree source byte budget exceeded\");\n\t\t\tsourceBytes += bytes;\n\t\t\tif (entry.type === \"session\") return;\n\t\t\tif (entries.length >= request.limits.maxEntries)\n\t\t\t\tthrow new ContextTreeLimitError(\"Context tree entry budget exceeded\");\n\t\t\tentries.push(entry);\n\t\t});\n\t\tconst visibleParent = (id: string | null): string | null => {\n\t\t\tlet remaining = hiddenParents.size;\n\t\t\twhile (id !== null && hiddenParents.has(id)) {\n\t\t\t\tif (remaining-- === 0) throw new Error(\"Parent path lineage is unresolved\");\n\t\t\t\tid = hiddenParents.get(id)!;\n\t\t\t}\n\t\t\treturn id;\n\t\t};\n\t\tfor (const entry of entries) entry.parentId = visibleParent(entry.parentId);\n\t\t// A trailing private record can select an older branch, not the last visible entry.\n\t\tconst hasSourceLeaf = leafId !== null;\n\t\tleafId = visibleParent(leafId);\n\t\tapplyChildUsageAttributions(entries);\n\t\tconst ownRequestUsage = receipts?.finish();\n\t\treturn { entries, leafId, hasSourceLeaf, ...(ownRequestUsage ? { ownRequestUsage } : {}) };\n\t} finally {\n\t\trequest.releaseMetadata(metadataBytes);\n\t}\n}\n\n/**\n * Build a context node for a completed RLM child from its persisted session\n * dir (sub-xxxx/). Children that already attributed grandchild usage carry the\n * aggregate on their assistant messages (applyChildUsageAttributions), so own\n * usage is recovered by subtracting the attribution entries. Returns undefined\n * when the dir holds no readable session.\n */\nasync function readContextTreeChildNodeFromDisk(\n\tchildSessionDir: string,\n\tsessionFile: string,\n\tresolveContextWindow: ContextWindowResolver,\n\trequest: ContextTreeRequest,\n\tidentity?: Pick<ContextTreeNode, \"id\" | \"label\" | \"status\">,\n): Promise<ContextTreeNode | undefined> {\n\tconst {\n\t\tentries: allEntries,\n\t\tleafId,\n\t\thasSourceLeaf,\n\t\townRequestUsage,\n\t} = await readContextTreeProjectionFromDisk(sessionFile, request);\n\tconst branch = branchEntries(allEntries, leafId);\n\tif (branch.length === 0 && !hasSourceLeaf) {\n\t\treturn undefined;\n\t}\n\n\tconst { ownUsage, totalUsage } = computeOwnAndTotalUsage(branch, allEntries);\n\n\tlet model: { provider: string; id: string } | undefined;\n\tfor (const entry of branch) {\n\t\tif (entry.type === \"model_change\") {\n\t\t\tmodel = { provider: entry.provider, id: entry.modelId };\n\t\t}\n\t}\n\n\tlet label = \"\";\n\tfor (const entry of branch) {\n\t\tif (entry.type === \"message\" && entry.message.role === \"user\") {\n\t\t\tlabel = compactLabel(readUserMessageText(entry.message.content));\n\t\t\tif (label) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst contextWindow = model ? resolveContextWindow(model.provider, model.id) : undefined;\n\n\tconst metadata = {\n\t\t...(identity ?? {\n\t\t\tid: basename(childSessionDir),\n\t\t\tlabel: label || \"child agent\",\n\t\t\tstatus: statusFromBranch(branch),\n\t\t}),\n\t\tmodel,\n\t\townUsage,\n\t\ttotalUsage,\n\t\t...(ownRequestUsage ? { ownRequestUsage } : {}),\n\t\tcontextUsage: computeContextUsageFromEntries(allEntries, branch, contextWindow),\n\t};\n\t// A registered run's identity was already admitted by its live parent.\n\trequest.retainMetadata(\n\t\tidentity ? { model, ownUsage, totalUsage, ownRequestUsage, contextUsage: metadata.contextUsage } : metadata,\n\t);\n\treturn { ...metadata, children: [] };\n}\n\n/** Complete or refuse, sharing the request's slot and counters with descendants.\n * Internal identity is an already-admitted registered run, including its empty placeholder.\n */\nexport async function loadContextTreeChildFromDisk(\n\tchildSessionDir: string,\n\tresolveContextWindow: ContextWindowResolver,\n\tlimits: Partial<ContextTreeRequestLimits> | ContextTreeRequest = {},\n\tidentity?: Pick<ContextTreeNode, \"id\" | \"label\" | \"status\">,\n): Promise<ContextTreeNode | undefined> {\n\tconst request = treeRequest(limits);\n\tconst directoryBytes = request.retainMetadata(childSessionDir);\n\tlet sessionFile: { path: string; bytes: number } | undefined;\n\tlet node: ContextTreeNode | undefined;\n\ttry {\n\t\tsessionFile = findSessionFile(childSessionDir, request);\n\t\tif (sessionFile && existsSync(sessionFile.path)) {\n\t\t\t// A header-only history still consumes one bounded source admission.\n\t\t\tif (!identity) request.admitNode();\n\t\t\tnode = await request.read(() =>\n\t\t\t\treadContextTreeChildNodeFromDisk(\n\t\t\t\t\tchildSessionDir,\n\t\t\t\t\tsessionFile!.path,\n\t\t\t\t\tresolveContextWindow,\n\t\t\t\t\trequest,\n\t\t\t\t\tidentity,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t} finally {\n\t\tif (sessionFile) request.releaseMetadata(sessionFile.bytes);\n\t\trequest.releaseMetadata(directoryBytes);\n\t}\n\tif (node) {\n\t\t// The full source has been reduced and the serial slot released before recursion.\n\t\tnode.children = await loadContextTreeChildrenFromDisk(childSessionDir, resolveContextWindow, undefined, request);\n\t} else if (identity) {\n\t\tconst usage = { ownUsage: emptyUsage(), totalUsage: emptyUsage() };\n\t\trequest.retainMetadata(usage);\n\t\tnode = { ...identity, ...usage, children: [] };\n\t}\n\treturn node;\n}\n\n/** Persisted children, in existing directory order, excluding already represented live IDs. */\nexport async function loadContextTreeChildrenFromDisk(\n\trlmSessionDir: string | undefined,\n\tresolveContextWindow: ContextWindowResolver,\n\tskipIds?: ReadonlySet<string>,\n\tlimits: Partial<ContextTreeRequestLimits> | ContextTreeRequest = {},\n): Promise<ContextTreeNode[]> {\n\tconst request = treeRequest(limits);\n\tconst skippedIds = new Set<string>();\n\tlet metadataBytes = 0;\n\ttry {\n\t\t// A caller-provided skip set is bounded before copying, too.\n\t\tif (skipIds && skipIds.size > request.limits.maxDirectoryEntries)\n\t\t\tthrow new ContextTreeLimitError(\"Context tree directory entry budget exceeded\");\n\t\tfor (const id of skipIds ?? []) {\n\t\t\tmetadataBytes += request.retainMetadata(id);\n\t\t\tskippedIds.add(id);\n\t\t}\n\t\tif (!rlmSessionDir || !existsSync(rlmSessionDir)) return [];\n\t\tmetadataBytes += request.retainMetadata(rlmSessionDir);\n\t\tconst directories = listChildSessionDirs(rlmSessionDir, request);\n\t\tmetadataBytes += directories.bytes;\n\t\tconst nodes: ContextTreeNode[] = [];\n\t\tfor (const childDir of directories.paths) {\n\t\t\tif (skippedIds.has(basename(childDir))) continue;\n\t\t\tconst node = await loadContextTreeChildFromDisk(childDir, resolveContextWindow, request);\n\t\t\tif (node) nodes.push(node);\n\t\t}\n\t\treturn nodes;\n\t} finally {\n\t\trequest.releaseMetadata(metadataBytes);\n\t}\n}\n"]}
|