blob: 261ab42dd19c1bfda93513bfc8b82b911199c413 [file]
import { createHash } from 'node:crypto';
import type { ModelMessage } from './model-protocol.js';
import type { RuntimeEvent } from '@maka/core/runtime-event';
import { estimateTokens } from './context-budget-helpers.js';
import { serializeToolResultForArchive } from './tool-result-archive.js';
import {
type ActiveArchivedToolResultPlaceholder,
isActiveArchivedToolResultPlaceholder,
} from './active-tool-result-prune.js';
const DEFAULT_CHARS_PER_TOKEN = 4;
/**
* Exact current-turn user message that active compaction must never rewrite.
* The index is captured before the first provider step, after prior replay has
* been materialized, so it is intentionally not assumed to be message zero.
*/
export interface ActiveCompactionHeadAnchor {
messageIndex: number;
messageSignature: string;
bodySha256: string;
estimatedTokens: number;
}
export interface ActiveCompactionSafeSpanPolicy {
enabled: boolean;
mode?: 'off' | string;
minStepNumber?: number;
highWaterRatio?: number;
maxActiveEstimatedTokens?: number;
minSafePrefixEstimatedTokens?: number;
/**
* Number of most-recent completed provider episodes that must remain
* verbatim after the compacted middle span. Capacity fallback leaves this
* unset; attention compaction uses one episode to preserve execution
* momentum in addition to any open protocol tail.
*/
preserveRecentCompletedEpisodes?: number;
archiveRequired?: boolean;
}
export interface ActiveCompactionSourceIndexInput {
sessionId: string;
turnId: string;
runId?: string;
invocationId?: string;
messages: readonly ModelMessage[];
runtimeEvents?: readonly RuntimeEvent[];
stepNumber?: number;
charsPerToken?: number;
}
export type ActiveCompactionProviderRole = 'system' | 'user' | 'assistant' | 'tool';
export type ActiveCompactionContentKind =
| 'text'
| 'thinking'
| 'function_call'
| 'function_response'
| 'tool_result'
| 'active_archive_placeholder'
| 'unknown';
export interface ActiveCompactionArchiveRef {
kind: 'toolResult' | 'compactSource';
sessionId?: string;
turnId?: string;
runtimeEventId?: string;
toolCallId?: string;
toolName?: string;
artifactId: string;
bodySha256: string;
originalEstimatedTokens?: number;
originalBytes?: number;
}
export interface ActiveCompactionSourceEntry {
sourceId: string;
messageIndex: number;
partIndex?: number;
role: ActiveCompactionProviderRole;
runtimeEventId?: string;
turnId: string;
runId?: string;
invocationId?: string;
toolCallId?: string;
toolName?: string;
contentKind: ActiveCompactionContentKind;
bodySha256: string;
estimatedTokens: number;
originalEstimatedTokens?: number;
originalBytes?: number;
archiveRef?: ActiveCompactionArchiveRef;
}
export interface ActiveCompactionSourceIndex {
sessionId: string;
turnId: string;
runId?: string;
invocationId?: string;
stepNumber?: number;
providerMessageCount: number;
entries: ActiveCompactionSourceEntry[];
toolLedger: ActiveCompactionToolLedger;
estimatedTokens: number;
}
export interface ActiveCompactionToolEpisode {
toolCallId: string;
callSourceIds: string[];
resultSourceIds: string[];
valid: boolean;
}
export interface ActiveCompactionToolLedger {
episodes: ActiveCompactionToolEpisode[];
missingIdentitySourceIds: string[];
}
export interface ActiveCompactionCoverage {
turnIds: string[];
runtimeEventIds: string[];
providerMessageSourceIds: string[];
toolCallIds: string[];
contentKinds: string[];
bodySha256: string[];
}
export type ActiveCompactionSafeSpanSelection =
| {
decision: 'selected';
startMessageIndex: number;
endMessageIndex: number;
entries: ActiveCompactionSourceEntry[];
coverage: ActiveCompactionCoverage;
estimatedTokens: number;
}
| {
decision: 'unchanged' | 'failedOpen';
reason:
| ActiveCompactionFailOpenReason
| 'disabled'
| 'below_min_step'
| 'below_high_water'
| 'below_min_safe_prefix'
| 'no_candidate'
| 'head_anchor_mismatch'
| 'head_anchor_exceeds_capacity'
| 'unexpected_user_after_head_anchor'
| 'no_safe_completed_span';
skippedReasonCounts: Readonly<Record<string, number>>;
};
export interface ActiveCompactionSourceRef {
kind: 'provider_message' | 'runtime_event' | 'active_archive_placeholder';
sourceId: string;
messageIndex: number;
partIndex?: number;
sessionId: string;
turnId: string;
runtimeEventId?: string;
toolCallId?: string;
toolName?: string;
contentKind: ActiveCompactionContentKind;
bodySha256: string;
archiveRef?: ActiveCompactionArchiveRef;
}
export type ActiveCompactionFailOpenReason =
| 'session_mismatch'
| 'turn_mismatch'
| 'source_missing'
| 'coverage_miss'
| 'source_hash_mismatch'
| 'tool_pair_split'
| 'archive_missing'
| 'archive_mismatch'
| 'head_anchor_exceeds_capacity'
| 'provider_message_only_when_runtime_required';
export function activeCompactionMessageSignature(message: ModelMessage): string {
return sha256(stableStringify(message));
}
export function buildActiveCompactionHeadAnchor(
messages: readonly ModelMessage[],
messageIndex: number,
charsPerToken = DEFAULT_CHARS_PER_TOKEN,
): ActiveCompactionHeadAnchor {
const message = messages[messageIndex];
if (!message || (message as { role?: unknown }).role !== 'user') {
throw new Error(
`active compaction head anchor must reference a user message at index ${messageIndex}`,
);
}
const body = stableStringify(message);
return {
messageIndex,
messageSignature: activeCompactionMessageSignature(message),
bodySha256: sha256(body),
estimatedTokens: estimateTokens(body.length, charsPerToken),
};
}
export interface ActiveCompactionValidationResult {
valid: boolean;
reasons: ActiveCompactionFailOpenReason[];
reasonCounts: Readonly<Record<ActiveCompactionFailOpenReason, number>>;
}
export function buildActiveCompactionSourceIndex(
input: ActiveCompactionSourceIndexInput,
): ActiveCompactionSourceIndex {
const charsPerToken = input.charsPerToken ?? DEFAULT_CHARS_PER_TOKEN;
const runtimeIndex = buildRuntimeEventIndex(input.runtimeEvents ?? []);
const entries: ActiveCompactionSourceEntry[] = [];
input.messages.forEach((message, messageIndex) => {
const role = normalizeProviderRole(message.role);
const content = (message as { content?: unknown }).content;
if (typeof content === 'string') {
entries.push(
entryFromProviderPart({
sourceId: providerSourceId(messageIndex),
messageIndex,
role,
turnId: input.turnId,
runId: input.runId,
invocationId: input.invocationId,
contentKind: 'text',
body: content,
charsPerToken,
runtimeIndex,
}),
);
return;
}
if (!Array.isArray(content)) {
entries.push(
entryFromProviderPart({
sourceId: providerSourceId(messageIndex),
messageIndex,
role,
turnId: input.turnId,
runId: input.runId,
invocationId: input.invocationId,
contentKind: 'unknown',
body: content,
charsPerToken,
runtimeIndex,
}),
);
return;
}
content.forEach((part, partIndex) => {
entries.push(
entryFromProviderPart({
sourceId: providerSourceId(messageIndex, partIndex),
messageIndex,
partIndex,
role,
turnId: input.turnId,
runId: input.runId,
invocationId: input.invocationId,
...providerPartBody(part),
charsPerToken,
runtimeIndex,
}),
);
});
});
const toolLedger = buildActiveCompactionToolLedger(entries);
return {
sessionId: input.sessionId,
turnId: input.turnId,
...(input.runId ? { runId: input.runId } : {}),
...(input.invocationId ? { invocationId: input.invocationId } : {}),
...(input.stepNumber !== undefined ? { stepNumber: input.stepNumber } : {}),
providerMessageCount: input.messages.length,
entries,
toolLedger,
estimatedTokens: estimateActiveCompactionTokens(entries),
};
}
export function activeCompactionCoverageFromEntries(
entries: readonly ActiveCompactionSourceEntry[],
): ActiveCompactionCoverage {
return {
turnIds: uniqueSorted(entries.map((entry) => entry.turnId)),
runtimeEventIds: uniqueSorted(entries.map((entry) => entry.runtimeEventId).filter(nonEmpty)),
providerMessageSourceIds: uniqueSorted(entries.map((entry) => entry.sourceId)),
toolCallIds: uniqueSorted(entries.map((entry) => entry.toolCallId).filter(nonEmpty)),
contentKinds: uniqueSorted(entries.map((entry) => entry.contentKind)),
bodySha256: uniqueSorted(entries.map((entry) => entry.bodySha256)),
};
}
export function estimateActiveCompactionTokens(
entries: readonly ActiveCompactionSourceEntry[],
): number {
return entries.reduce((total, entry) => total + entry.estimatedTokens, 0);
}
/**
* Select the completed active-turn span after the exact current-user anchor.
* This deliberately makes no semantic relevance judgment. It only groups
* provider protocol episodes and stops before the first open/incomplete one.
*/
export function selectActiveCompactionSafeSpan(input: {
index: ActiveCompactionSourceIndex;
messages: readonly ModelMessage[];
policy: ActiveCompactionSafeSpanPolicy | undefined;
headAnchor: ActiveCompactionHeadAnchor;
/** A prior semantic projection immediately after the anchor is context, not raw source. */
afterMessageIndex?: number;
}): ActiveCompactionSafeSpanSelection {
const { index, messages, policy, headAnchor } = input;
if (policy?.enabled !== true || policy.mode === 'off')
return safeSpanSkipped('unchanged', 'disabled');
const minStepNumber = Math.max(0, Math.floor(policy.minStepNumber ?? 1));
if ((index.stepNumber ?? 0) < minStepNumber)
return safeSpanSkipped('unchanged', 'below_min_step');
const anchorMessage = messages[headAnchor.messageIndex];
if (
!anchorMessage ||
(anchorMessage as { role?: unknown }).role !== 'user' ||
activeCompactionMessageSignature(anchorMessage) !== headAnchor.messageSignature
) {
return safeSpanSkipped('failedOpen', 'head_anchor_mismatch');
}
const highWaterRatio = finiteRatio(policy.highWaterRatio, 0.8);
const maxActiveEstimatedTokens = finitePositive(policy.maxActiveEstimatedTokens);
if (
maxActiveEstimatedTokens !== undefined &&
index.estimatedTokens <= Math.floor(maxActiveEstimatedTokens * highWaterRatio)
) {
return safeSpanSkipped('unchanged', 'below_high_water');
}
const firstCandidateMessageIndex = Math.max(
headAnchor.messageIndex + 1,
(input.afterMessageIndex ?? headAnchor.messageIndex) + 1,
);
if (toolLedgerHasInvalidSourceAtOrAfter(index, firstCandidateMessageIndex)) {
return safeSpanSkipped('failedOpen', 'tool_pair_split');
}
let cursor = firstCandidateMessageIndex;
const completedEpisodes: Array<{ startMessageIndex: number; endMessageIndex: number }> = [];
while (cursor < index.providerMessageCount) {
const episodeStart = cursor;
const messageEntries = entriesAtMessageIndex(index.entries, cursor);
const role = providerMessageRole(messages[cursor], messageEntries);
if (role === 'user' || role === 'system') {
return safeSpanSkipped('failedOpen', 'unexpected_user_after_head_anchor');
}
if (role === 'tool') {
break;
}
if (role !== 'assistant') {
break;
}
// One provider episode may materialize reasoning/text and tool calls as
// multiple consecutive assistant messages. Group all of them before
// deciding whether any part is completed and eligible.
let assistantEnd = cursor;
const assistantEntries = [...messageEntries];
while (assistantEnd + 1 < index.providerMessageCount) {
const nextEntries = entriesAtMessageIndex(index.entries, assistantEnd + 1);
if (providerMessageRole(messages[assistantEnd + 1], nextEntries) !== 'assistant') break;
assistantEnd += 1;
assistantEntries.push(...nextEntries);
}
const toolCallIds = uniqueSorted(
assistantEntries
.filter((entry) => entry.contentKind === 'function_call')
.map((entry) => entry.toolCallId)
.filter(nonEmpty),
);
if (toolCallIds.length === 0) {
completedEpisodes.push({ startMessageIndex: episodeStart, endMessageIndex: assistantEnd });
cursor = assistantEnd + 1;
continue;
}
const resultIds = new Set<string>();
let tailCursor = assistantEnd + 1;
while (tailCursor < index.providerMessageCount) {
const resultEntries = entriesAtMessageIndex(index.entries, tailCursor);
if (providerMessageRole(messages[tailCursor], resultEntries) !== 'tool') break;
for (const entry of resultEntries) {
if (
entry.toolCallId &&
(entry.contentKind === 'function_response' ||
entry.contentKind === 'tool_result' ||
entry.contentKind === 'active_archive_placeholder')
)
resultIds.add(entry.toolCallId);
}
tailCursor += 1;
}
if (!toolCallIds.every((id) => resultIds.has(id))) break;
completedEpisodes.push({ startMessageIndex: episodeStart, endMessageIndex: tailCursor - 1 });
cursor = tailCursor;
}
const preserveRecentCompletedEpisodes = Math.max(
0,
Math.floor(policy.preserveRecentCompletedEpisodes ?? 0),
);
const compactableEpisodeCount = completedEpisodes.length - preserveRecentCompletedEpisodes;
if (compactableEpisodeCount <= 0) {
return safeSpanSkipped('unchanged', 'no_safe_completed_span');
}
const completedEnd = completedEpisodes[compactableEpisodeCount - 1]!.endMessageIndex;
return safeSpanSelected(index, firstCandidateMessageIndex, completedEnd, policy);
}
function safeSpanSelected(
index: ActiveCompactionSourceIndex,
startMessageIndex: number,
endMessageIndex: number,
policy: ActiveCompactionSafeSpanPolicy,
): ActiveCompactionSafeSpanSelection {
const entries = index.entries.filter(
(entry) => entry.messageIndex >= startMessageIndex && entry.messageIndex <= endMessageIndex,
);
if (entries.length === 0) return safeSpanSkipped('unchanged', 'no_candidate');
if (entries.some((entry) => !nonEmpty(entry.sourceId) || !nonEmpty(entry.bodySha256))) {
return safeSpanSkipped('failedOpen', 'source_missing');
}
if (
policy.archiveRequired === true &&
entries.some((entry) => !entry.runtimeEventId && !entry.archiveRef)
) {
return safeSpanSkipped('failedOpen', 'provider_message_only_when_runtime_required');
}
if (toolPairIntegrityViolation(entries, index.toolLedger))
return safeSpanSkipped('failedOpen', 'tool_pair_split');
const estimatedTokens = estimateActiveCompactionTokens(entries);
const minSafePrefixEstimatedTokens = Math.max(
0,
Math.floor(policy.minSafePrefixEstimatedTokens ?? 0),
);
if (estimatedTokens < minSafePrefixEstimatedTokens) {
return safeSpanSkipped('unchanged', 'below_min_safe_prefix');
}
return {
decision: 'selected',
startMessageIndex,
endMessageIndex,
entries,
coverage: activeCompactionCoverageFromEntries(entries),
estimatedTokens,
};
}
function entriesAtMessageIndex(
entries: readonly ActiveCompactionSourceEntry[],
messageIndex: number,
): ActiveCompactionSourceEntry[] {
return entries.filter((entry) => entry.messageIndex === messageIndex);
}
function providerMessageRole(
message: ModelMessage | undefined,
entries: readonly ActiveCompactionSourceEntry[],
): ActiveCompactionProviderRole | undefined {
const role = (message as { role?: unknown } | undefined)?.role;
if (role === 'system' || role === 'user' || role === 'assistant' || role === 'tool') return role;
return entries[0]?.role;
}
function safeSpanSkipped(
decision: 'unchanged' | 'failedOpen',
reason: Extract<
ActiveCompactionSafeSpanSelection,
{ decision: 'unchanged' | 'failedOpen' }
>['reason'],
): Extract<ActiveCompactionSafeSpanSelection, { decision: 'unchanged' | 'failedOpen' }> {
return { decision, reason, skippedReasonCounts: { [reason]: 1 } };
}
/**
* Validate the durable source coverage used by an active compaction projection.
* This kernel intentionally knows nothing about the projection's rendered schema.
*/
export function validateActiveCompactionCoverageForSourceIndex(
coverage: ActiveCompactionCoverage,
index: ActiveCompactionSourceIndex,
options: {
archiveRefs?: readonly ActiveCompactionArchiveRef[];
sessionId?: string;
turnId?: string;
archiveRequired?: boolean;
requireRuntimeEventCoverage?: boolean;
} = {},
): ActiveCompactionValidationResult {
const reasons: ActiveCompactionFailOpenReason[] = [];
const add = (reason: ActiveCompactionFailOpenReason) => {
if (!reasons.includes(reason)) reasons.push(reason);
};
if (options.sessionId && options.sessionId !== index.sessionId) add('session_mismatch');
if (options.turnId && options.turnId !== index.turnId) add('turn_mismatch');
const entriesBySource = new Map(index.entries.map((entry) => [entry.sourceId, entry]));
const selectedEntries: ActiveCompactionSourceEntry[] = [];
for (const sourceId of coverage.providerMessageSourceIds) {
const entry = entriesBySource.get(sourceId);
if (!entry) {
add('source_missing');
continue;
}
selectedEntries.push(entry);
if (!coverage.turnIds.includes(entry.turnId)) add('coverage_miss');
if (entry.runtimeEventId && !coverage.runtimeEventIds.includes(entry.runtimeEventId)) {
add('coverage_miss');
}
if (entry.toolCallId && !coverage.toolCallIds.includes(entry.toolCallId)) add('coverage_miss');
if (!coverage.contentKinds.includes(entry.contentKind)) add('coverage_miss');
if (!coverage.bodySha256.includes(entry.bodySha256)) add('source_hash_mismatch');
if (options.requireRuntimeEventCoverage === true && !entry.runtimeEventId) {
add('provider_message_only_when_runtime_required');
}
if (
options.archiveRequired === true &&
entry.contentKind === 'active_archive_placeholder' &&
!entry.archiveRef
) {
add('archive_missing');
}
}
for (const hash of coverage.bodySha256) {
if (!selectedEntries.some((entry) => entry.bodySha256 === hash)) add('source_hash_mismatch');
}
if (toolPairIntegrityViolation(selectedEntries, index.toolLedger)) add('tool_pair_split');
for (const ref of options.archiveRefs ?? []) {
if (!selectedEntries.some((entry) => archiveRefsEqual(entry.archiveRef, ref))) {
add('archive_mismatch');
}
}
return {
valid: reasons.length === 0,
reasons,
reasonCounts: countReasons(reasons),
};
}
function entryFromProviderPart(input: {
sourceId: string;
messageIndex: number;
partIndex?: number;
role: ActiveCompactionProviderRole;
turnId: string;
runId?: string;
invocationId?: string;
contentKind: ActiveCompactionContentKind;
body: unknown;
toolCallId?: string;
toolName?: string;
placeholder?: ActiveArchivedToolResultPlaceholder;
charsPerToken: number;
runtimeIndex: RuntimeEventIndex;
}): ActiveCompactionSourceEntry {
const bodyText = typeof input.body === 'string' ? input.body : stableStringify(input.body);
const bodySha256 = input.placeholder?.bodySha256 ?? sha256(bodyText);
const runtimeEvent = matchRuntimeEvent(input.runtimeIndex, {
bodySha256,
toolCallId: input.toolCallId,
toolName: input.toolName,
role: input.role,
contentKind: input.contentKind,
});
const contentKind = input.placeholder
? 'active_archive_placeholder'
: runtimeEvent?.content?.kind === 'function_response'
? 'function_response'
: runtimeEvent?.content?.kind === 'function_call'
? 'function_call'
: runtimeEvent?.content?.kind === 'thinking'
? 'thinking'
: input.contentKind;
const archiveRef = input.placeholder
? {
kind: 'toolResult' as const,
turnId: input.placeholder.turnId,
...(runtimeEvent?.sessionId ? { sessionId: runtimeEvent.sessionId } : {}),
...(runtimeEvent?.id ? { runtimeEventId: runtimeEvent.id } : {}),
toolCallId: input.placeholder.toolCallId,
toolName: input.placeholder.toolName,
artifactId: input.placeholder.artifactId,
bodySha256: input.placeholder.bodySha256,
originalEstimatedTokens: input.placeholder.originalEstimatedTokens,
originalBytes: input.placeholder.originalBytes,
}
: undefined;
return {
sourceId: input.sourceId,
messageIndex: input.messageIndex,
...(input.partIndex !== undefined ? { partIndex: input.partIndex } : {}),
role: input.role,
...(runtimeEvent?.id ? { runtimeEventId: runtimeEvent.id } : {}),
turnId: runtimeEvent?.turnId ?? input.placeholder?.turnId ?? input.turnId,
...((runtimeEvent?.runId ?? input.runId) ? { runId: runtimeEvent?.runId ?? input.runId } : {}),
...((runtimeEvent?.invocationId ?? input.invocationId)
? { invocationId: runtimeEvent?.invocationId ?? input.invocationId }
: {}),
...((input.toolCallId ?? runtimeToolCallId(runtimeEvent))
? { toolCallId: input.toolCallId ?? runtimeToolCallId(runtimeEvent) }
: {}),
...((input.toolName ?? runtimeToolName(runtimeEvent))
? { toolName: input.toolName ?? runtimeToolName(runtimeEvent) }
: {}),
contentKind,
bodySha256,
estimatedTokens: estimateTokens(bodyText.length, input.charsPerToken),
...(input.placeholder
? { originalEstimatedTokens: input.placeholder.originalEstimatedTokens }
: {}),
...(input.placeholder ? { originalBytes: input.placeholder.originalBytes } : {}),
...(archiveRef ? { archiveRef } : {}),
};
}
function providerPartBody(part: unknown): {
contentKind: ActiveCompactionContentKind;
body: unknown;
toolCallId?: string;
toolName?: string;
placeholder?: ActiveArchivedToolResultPlaceholder;
} {
if (!part || typeof part !== 'object') return { contentKind: 'unknown', body: part };
const candidate = part as Record<string, unknown>;
if (candidate.type === 'text') return { contentKind: 'text', body: candidate.text ?? '' };
if (candidate.type === 'reasoning' || candidate.type === 'thinking') {
return { contentKind: 'thinking', body: candidate.text ?? candidate.reasoning ?? '' };
}
if (candidate.type === 'tool-call') {
return {
contentKind: 'function_call',
body: candidate.input ?? candidate.args ?? candidate,
...(typeof candidate.toolCallId === 'string' ? { toolCallId: candidate.toolCallId } : {}),
...(typeof candidate.toolName === 'string' ? { toolName: candidate.toolName } : {}),
};
}
if (candidate.type === 'tool-result') {
const payload = toolResultPayload(candidate);
const placeholder = activePlaceholderFromPayload(payload);
return {
contentKind: placeholder ? 'active_archive_placeholder' : 'tool_result',
body: payload,
...(typeof candidate.toolCallId === 'string' ? { toolCallId: candidate.toolCallId } : {}),
...(typeof candidate.toolName === 'string' ? { toolName: candidate.toolName } : {}),
...(placeholder ? { placeholder } : {}),
};
}
return { contentKind: 'unknown', body: candidate };
}
function toolResultPayload(part: Record<string, unknown>): unknown {
if ('result' in part) return part.result;
const output = part.output;
if (output && typeof output === 'object' && 'value' in output) {
return (output as { value?: unknown }).value;
}
return output ?? part;
}
function activePlaceholderFromPayload(
payload: unknown,
): ActiveArchivedToolResultPlaceholder | undefined {
if (isActiveArchivedToolResultPlaceholder(payload)) return payload;
if (typeof payload === 'string') {
try {
const parsed = JSON.parse(payload) as unknown;
return isActiveArchivedToolResultPlaceholder(parsed) ? parsed : undefined;
} catch {
return undefined;
}
}
return undefined;
}
interface RuntimeEventIndex {
byToolCallId: Map<string, RuntimeEvent[]>;
byBodySha256: Map<string, RuntimeEvent[]>;
consumedEventIds: Set<string>;
}
function buildRuntimeEventIndex(events: readonly RuntimeEvent[]): RuntimeEventIndex {
const byToolCallId = new Map<string, RuntimeEvent[]>();
const byBodySha256 = new Map<string, RuntimeEvent[]>();
for (const event of events) {
const toolCallId = runtimeToolCallId(event);
if (toolCallId) pushMap(byToolCallId, toolCallId, event);
pushMap(byBodySha256, runtimeEventBodySha256(event), event);
}
return { byToolCallId, byBodySha256, consumedEventIds: new Set() };
}
function matchRuntimeEvent(
index: RuntimeEventIndex,
input: {
bodySha256: string;
toolCallId?: string;
toolName?: string;
role: ActiveCompactionProviderRole;
contentKind: ActiveCompactionContentKind;
},
): RuntimeEvent | undefined {
const preferredKind =
input.contentKind === 'function_call'
? 'function_call'
: input.contentKind === 'tool_result' || input.contentKind === 'active_archive_placeholder'
? 'function_response'
: input.contentKind;
const expectedRole = runtimeRoleForProviderRole(input.role);
const candidates = (
input.toolCallId
? (index.byToolCallId.get(input.toolCallId) ?? [])
: (index.byBodySha256.get(input.bodySha256) ?? [])
).filter(
(event) =>
!index.consumedEventIds.has(event.id) &&
event.role === expectedRole &&
event.content?.kind === preferredKind &&
(!input.toolName || !runtimeToolName(event) || runtimeToolName(event) === input.toolName),
);
const uniqueCandidates = [...new Map(candidates.map((event) => [event.id, event])).values()];
if (uniqueCandidates.length !== 1) return undefined;
const matched = uniqueCandidates[0]!;
index.consumedEventIds.add(matched.id);
return matched;
}
function runtimeRoleForProviderRole(role: ActiveCompactionProviderRole): RuntimeEvent['role'] {
return role === 'assistant' ? 'model' : role;
}
function runtimeEventBodySha256(event: RuntimeEvent): string {
const content = event.content;
if (!content) return sha256('');
switch (content.kind) {
case 'text':
case 'thinking':
return sha256(content.text);
case 'function_call':
return sha256(stableStringify(content.args));
case 'function_response':
return sha256(serializeToolResultForArchive(content.result));
case 'error':
return sha256(stableStringify(content));
}
}
function runtimeToolCallId(event: RuntimeEvent | undefined): string | undefined {
if (!event) return undefined;
if (event.content?.kind === 'function_call' || event.content?.kind === 'function_response')
return event.content.id;
return event.refs?.toolCallId;
}
function runtimeToolName(event: RuntimeEvent | undefined): string | undefined {
if (!event) return undefined;
if (event.content?.kind === 'function_call' || event.content?.kind === 'function_response')
return event.content.name;
return undefined;
}
function buildActiveCompactionToolLedger(
entries: readonly ActiveCompactionSourceEntry[],
): ActiveCompactionToolLedger {
const missingIdentitySourceIds: string[] = [];
const byToolCallId = new Map<
string,
{ calls: ActiveCompactionSourceEntry[]; results: ActiveCompactionSourceEntry[] }
>();
for (const entry of entries) {
const isCall = entry.contentKind === 'function_call';
const isResult = isToolResultKind(entry.contentKind);
if (!isCall && !isResult) continue;
if (!entry.toolCallId) {
missingIdentitySourceIds.push(entry.sourceId);
continue;
}
const group = byToolCallId.get(entry.toolCallId) ?? { calls: [], results: [] };
if (isCall) group.calls.push(entry);
else group.results.push(entry);
byToolCallId.set(entry.toolCallId, group);
}
return {
episodes: [...byToolCallId.entries()]
.sort(([left], [right]) => left.localeCompare(right))
.map(([toolCallId, group]) => {
const toolNames = uniqueSorted(
[...group.calls, ...group.results].map((entry) => entry.toolName).filter(nonEmpty),
);
return {
toolCallId,
callSourceIds: group.calls.map((entry) => entry.sourceId),
resultSourceIds: group.results.map((entry) => entry.sourceId),
valid: group.calls.length === 1 && group.results.length <= 1 && toolNames.length <= 1,
};
}),
missingIdentitySourceIds: uniqueSorted(missingIdentitySourceIds),
};
}
function toolLedgerHasInvalidSourceAtOrAfter(
index: ActiveCompactionSourceIndex,
firstCandidateMessageIndex: number,
): boolean {
const sourceMessageIndex = new Map(
index.entries.map((entry) => [entry.sourceId, entry.messageIndex]),
);
const atOrAfter = (sourceId: string) =>
(sourceMessageIndex.get(sourceId) ?? -1) >= firstCandidateMessageIndex;
if (index.toolLedger.missingIdentitySourceIds.some(atOrAfter)) return true;
return index.toolLedger.episodes.some(
(episode) =>
!episode.valid && [...episode.callSourceIds, ...episode.resultSourceIds].some(atOrAfter),
);
}
function toolPairIntegrityViolation(
selectedEntries: readonly ActiveCompactionSourceEntry[],
ledger: ActiveCompactionToolLedger,
): boolean {
const selectedSourceIds = new Set(selectedEntries.map((entry) => entry.sourceId));
if (ledger.missingIdentitySourceIds.some((sourceId) => selectedSourceIds.has(sourceId))) {
return true;
}
for (const episode of ledger.episodes) {
const episodeSourceIds = [...episode.callSourceIds, ...episode.resultSourceIds];
const selectedCount = episodeSourceIds.filter((sourceId) =>
selectedSourceIds.has(sourceId),
).length;
if (selectedCount === 0) continue;
if (!episode.valid || episode.resultSourceIds.length !== 1) return true;
if (selectedCount !== episodeSourceIds.length) return true;
}
return false;
}
function isToolResultKind(kind: ActiveCompactionContentKind): boolean {
return (
kind === 'function_response' || kind === 'tool_result' || kind === 'active_archive_placeholder'
);
}
function archiveRefsEqual(
left: ActiveCompactionArchiveRef | undefined,
right: ActiveCompactionArchiveRef,
): boolean {
return (
Boolean(left) &&
left?.kind === right.kind &&
left.artifactId === right.artifactId &&
left.bodySha256 === right.bodySha256 &&
left.toolCallId === right.toolCallId &&
left.toolName === right.toolName
);
}
function countReasons(
reasons: readonly ActiveCompactionFailOpenReason[],
): Readonly<Record<ActiveCompactionFailOpenReason, number>> {
const counts: Partial<Record<ActiveCompactionFailOpenReason, number>> = {};
for (const reason of reasons) counts[reason] = (counts[reason] ?? 0) + 1;
return counts as Readonly<Record<ActiveCompactionFailOpenReason, number>>;
}
function providerSourceId(messageIndex: number, partIndex?: number): string {
return partIndex === undefined
? `provider:${messageIndex}`
: `provider:${messageIndex}:${partIndex}`;
}
function normalizeProviderRole(role: string): ActiveCompactionProviderRole {
if (role === 'system' || role === 'user' || role === 'assistant' || role === 'tool') return role;
return 'user';
}
function stableStringify(value: unknown): string {
if (value === undefined) return '';
if (value === null || typeof value !== 'object') return JSON.stringify(value) ?? '';
if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`;
const object = value as Record<string, unknown>;
return `{${Object.keys(object)
.sort()
.map((key) => `${JSON.stringify(key)}:${stableStringify(object[key])}`)
.join(',')}}`;
}
function sha256(text: string): string {
return createHash('sha256').update(text).digest('hex');
}
function nonEmpty(value: unknown): value is string {
return typeof value === 'string' && value.length > 0;
}
function finitePositive(value: number | undefined): number | undefined {
return typeof value === 'number' && Number.isFinite(value) && value > 0
? Math.floor(value)
: undefined;
}
function finiteRatio(value: number | undefined, fallback: number): number {
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) return fallback;
return Math.min(1, value);
}
function uniqueSorted(values: readonly string[]): string[] {
return [...new Set(values)].sort();
}
function pushMap<K, V>(map: Map<K, V[]>, key: K, value: V): void {
const existing = map.get(key);
if (existing) existing.push(value);
else map.set(key, [value]);
}