{"version":3,"file":"context-governor.d.ts","sourceRoot":"","sources":["../../../src/core/context-runtime/context-governor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Context Governor (Long-Horizon Context Virtualization, 2.5.0; production\n * hardening 2.6.0).\n *\n * First-class preflight enforcement of the safe input budget. Before a provider\n * is invoked, the candidate assembly (system prompt + tools + dynamic prompt +\n * messages) is conservatively counted and reduced until it fits — or a\n * structured unrecoverable condition is returned with region diagnostics.\n *\n * Reduction is deterministic and LLM-free (it runs BEFORE the model call, so it\n * cannot depend on another model call):\n *   1. Re-apply the durable virtualization ledger (already-archived tool\n *      results are restored to their virtualized representation, never the raw\n *      payload).\n *   2. Virtualize oversized/older tool results into the cold evidence archive\n *      (retaining a synopsis + durable evidence reference).\n *   3. Roll the context over: retire the historical transcript and replace the\n *      prefix with a bounded mission-context-checkpoint preamble + recent tail.\n *   4. Trim the recent tail deterministically (bounded iterations, pinned state\n *      never evicted).\n *\n * The governor never invents mission state: the checkpoint is an operational\n * projection, and completion authority stays with the Reliability Kernel.\n */\n\nimport type { AgentMessage } from \"@apholdings/jensen-agent-core\";\nimport type { ToolResultMessage } from \"@apholdings/jensen-ai\";\nimport {\n\ttype ContextCapability,\n\tcomputeSafeInputBudget,\n\tcontextPressureRatio,\n\texceedsSafeInputBudget,\n} from \"./context-capability.js\";\nimport {\n\ttype ContextAssembly,\n\testimateAssemblyInputTokens,\n\testimateContextRegionCosts,\n\testimateMessageTokensFor,\n\ttype TokenAccountingMode,\n} from \"./context-token.js\";\nimport type { EvidenceArchive } from \"./evidence-archive.js\";\nimport { buildEvidenceRecord } from \"./evidence-archive.js\";\nimport {\n\tcheckpointToRehydrationPreamble,\n\ttype EvidenceReference,\n\ttype MissionContextCheckpoint,\n} from \"./mission-checkpoint.js\";\nimport { MAX_CALIBRATED_MULTIPLIER, resolveAccountingMode } from \"./token-accounting.js\";\n\nexport type GovernorAction = \"pass\" | \"virtualized\" | \"rollover\" | \"compacted\" | \"unrecoverable\";\n\nexport interface ContextGovernorDiagnostics {\n\taction: GovernorAction;\n\tcapability: ContextCapability;\n\titerations: number;\n\tinputTokensBefore: number;\n\tinputTokensAfter: number;\n\tpressureRatioBefore: number;\n\tpressureRatioAfter: number;\n\ttoolResultsArchived: number;\n\t/** Tool results restored to a virtualized form from the durable ledger. */\n\ttoolResultsRevirtualized: number;\n\tmessagesEvicted: number;\n\ttokensEvicted: number;\n\trolloverOccurred: boolean;\n\tcheckpointRevision?: number;\n\tevidenceIdsArchived: string[];\n\treducedRegions: string[];\n\tunrecoverableReason?: string;\n\t/** Fixed prefix (system + tools + dynamic prompt) token cost. */\n\tfixedPrefixTokens: number;\n\t/** Current token accounting mode. */\n\ttokenAccountingMode: TokenAccountingMode;\n\trecoveryIteration: number;\n}\n\nexport interface ContextGovernorResult {\n\tassembly: ContextAssembly;\n\tdiagnostics: ContextGovernorDiagnostics;\n}\n\nexport interface ContextGovernorOptions {\n\tcapability: ContextCapability;\n\tarchive: EvidenceArchive;\n\t/** Supplies the current durable mission checkpoint for rollover rehydration. */\n\tcheckpointProvider?: () => MissionContextCheckpoint | undefined;\n\t/** When present, resolves the capability per call (model may change at runtime). */\n\tcapabilityProvider?: () => ContextCapability;\n\tmaxIterations?: number;\n\t/** Recent-token tail kept hot across a rollover. */\n\tkeepRecentTokens?: number;\n\t/** Tool results at/above this cost are candidates for virtualization. */\n\ttoolResultVirtualizeThreshold?: number;\n\t/** Minimum number of messages always retained (never evicted below this). */\n\tminRetainedMessages?: number;\n\tnow?: () => number;\n\t/** Durable virtualization ledger source (e.g., the current session). */\n\tvirtualizationProvider?: () => ToolVirtualizationRecord[];\n\t/** Durable virtualization ledger sink (persists the full snapshot). */\n\tvirtualizationSink?: (records: ToolVirtualizationRecord[]) => void;\n\t/** Initial virtualization records to seed the ledger (session resume). */\n\tinitialVirtualizations?: ToolVirtualizationRecord[];\n\t/** Initial evidence references to seed the archive map (session resume). */\n\tinitialEvidenceRefs?: EvidenceReference[];\n}\n\nconst DEFAULT_MAX_ITERATIONS = 8;\n// The recent hot tail kept across a rollover must be a fraction of the safe\n// input budget, never the whole conversation. A fixed 20000-token tail made\n// rollover/trim no-ops on small windows because it retained every message.\nconst DEFAULT_KEEP_RECENT_RATIO = 0.3;\nconst MIN_KEEP_RECENT_TOKENS = 1024;\nconst DEFAULT_TOOL_RESULT_VIRTUALIZE_THRESHOLD = 768;\nconst DEFAULT_MIN_RETAINED_MESSAGES = 4;\nconst TOOL_RESULT_SYNOPSIS_CHARS = 400;\n\nconst CHECKPOINT_CUSTOM_TYPE = \"mission_context_checkpoint\";\n\n/** Bounded adaptive safety reserve ceiling (fraction of configured window). */\nconst MAX_ADAPTIVE_SAFETY_RATIO = 0.25;\n/** Per-overflow adaptive safety step. */\nconst ADAPTIVE_SAFETY_STEP = 512;\n\n/** Durable virtualization provenance: which tool result maps to which cold evidence. */\nexport interface ToolVirtualizationRecord {\n\ttoolCallId: string;\n\tevidenceId: string;\n\tcontentHash: string;\n\tsource: string;\n\tsynopsis: string;\n\tcontentBytes: number;\n\tvirtualizedAtMs: number;\n}\n\n/** Structured context captured when a provider rejects a request for size. */\nexport interface ProviderOverflowContext {\n\tconfiguredContextWindow: number;\n\testimatedInputTokens: number;\n\treservedOutputTokens: number;\n\tsafetyReserveTokens: number;\n\taccountingMode: TokenAccountingMode;\n\tproviderError?: string;\n\t/** Provider-reported input token count when available. */\n\tobservedInputTokens?: number;\n\t/** Provider-reported maximum context when available. */\n\tobservedContextWindow?: number;\n}\n\n/** Opt-in telemetry surface (metadata only; never prompt content). */\nexport interface ContextGovernorTelemetry {\n\ttokenAccountingMode: TokenAccountingMode;\n\testimatedInputTokens: number;\n\tproviderObservedInputTokens?: number;\n\testimationError?: number;\n\testimationErrorRatio?: number;\n\tconfiguredContextWindow: number;\n\tbaseSafeInputBudget: number;\n\teffectiveSafeInputBudget: number;\n\treservedOutputTokens: number;\n\tsafetyReserveTokens: number;\n\toverflowCount: number;\n\tproviderOverflowCount: number;\n\tforcedReductionCount: number;\n\trolloverCount: number;\n\tarchivedEvidenceCount: number;\n\treusedEvidenceCount: number;\n\tduplicateArchiveAvoidedCount: number;\n\tfixedPrefixTokens: number;\n\tmessageTokens: number;\n\ttoolSchemaTokens: number;\n\trecoveryIteration: number;\n\tadaptiveSafetyReserveTokens: number;\n\tcalibratedMultiplier: number;\n\tcalibrationSamples: number;\n}\n\n/** Estimate the token cost of a single tool result's text content. */\nfunction toolResultTextTokens(message: ToolResultMessage): number {\n\tlet chars = 0;\n\tfor (const block of message.content) {\n\t\tif (block.type === \"text\" && block.text) chars += block.text.length;\n\t\telse if (block.type === \"image\") chars += 4800;\n\t}\n\treturn Math.ceil(chars / 4);\n}\n\nfunction toolResultText(message: ToolResultMessage): string {\n\treturn message.content\n\t\t.filter((block): block is { type: \"text\"; text: string } => block.type === \"text\")\n\t\t.map((block) => block.text)\n\t\t.join(\"\\n\");\n}\n\nfunction synopsisFor(text: string): string {\n\tconst normalized = text.replace(/\\s+/g, \" \").trim();\n\tif (normalized.length <= TOOL_RESULT_SYNOPSIS_CHARS) return normalized;\n\treturn `${normalized.slice(0, TOOL_RESULT_SYNOPSIS_CHARS)}…`;\n}\n\nfunction makeCheckpointMessage(checkpoint: MissionContextCheckpoint): AgentMessage {\n\treturn {\n\t\trole: \"custom\",\n\t\tcustomType: CHECKPOINT_CUSTOM_TYPE,\n\t\tcontent: checkpointToRehydrationPreamble(checkpoint),\n\t\tdisplay: false,\n\t\tdetails: { missionId: checkpoint.missionId, revision: checkpoint.revision, pinned: true },\n\t\ttimestamp: checkpoint.updatedAtMs,\n\t} as AgentMessage;\n}\n\n/** Return the originating archive id when a tool result came from retrieve_evidence. */\nfunction getEvidenceSourceId(message: ToolResultMessage): string | undefined {\n\tconst details = (message as { details?: unknown }).details;\n\tif (typeof details === \"object\" && details !== null) {\n\t\tconst id = (details as Record<string, unknown>).__evidenceSourceId;\n\t\treturn typeof id === \"string\" && id.length > 0 ? id : undefined;\n\t}\n\treturn undefined;\n}\n\n/** True when a message is already in the virtualized representation. */\nfunction isAlreadyVirtualized(message: ToolResultMessage): boolean {\n\tconst details = (message as { details?: unknown }).details;\n\tif (typeof details === \"object\" && details !== null) {\n\t\tconst id = (details as Record<string, unknown>).__virtualizedEvidenceId;\n\t\treturn typeof id === \"string\" && id.length > 0;\n\t}\n\treturn false;\n}\n\nfunction isPinnedMessage(message: AgentMessage): boolean {\n\tif ((message as { customType?: string }).customType === CHECKPOINT_CUSTOM_TYPE) return true;\n\tconst details = (message as { details?: unknown }).details;\n\tif (typeof details === \"object\" && details !== null) {\n\t\treturn (details as Record<string, unknown>).pinned === true;\n\t}\n\treturn false;\n}\n\nfunction virtualizedText(source: string, synopsis: string, evidenceId: string, collapsed: boolean): string {\n\tif (collapsed) {\n\t\treturn `[evidence retrieval collapsed] ${source} — synopsis: ${synopsis}\\n<evidence ref=\"${evidenceId}\"/>`;\n\t}\n\treturn `[tool result virtualized] ${source}. Synopsis: ${synopsis}\\n<evidence ref=\"${evidenceId}\"/>`;\n}\n\nfunction applyVirtualizedForm(\n\tresult: ToolResultMessage,\n\tevidenceId: string,\n\tsynopsis: string,\n\tcontentBytes: number,\n\tcollapsed: boolean,\n): ToolResultMessage {\n\treturn {\n\t\t...result,\n\t\tcontent: [{ type: \"text\", text: virtualizedText(result.toolName, synopsis, evidenceId, collapsed) }],\n\t\tdetails: {\n\t\t\t...(typeof result.details === \"object\" && result.details !== null ? result.details : {}),\n\t\t\t__virtualizedEvidenceId: evidenceId,\n\t\t\t__virtualizedContentBytes: contentBytes,\n\t\t},\n\t} as ToolResultMessage;\n}\n\n/** Find a safe front cut index: the suffix must not start with a toolResult. */\nfunction findSafeCutIndex(messages: AgentMessage[], maxSuffixTokens: number): number {\n\tlet accumulated = 0;\n\tfor (let i = messages.length - 1; i >= 0; i--) {\n\t\tconst message = messages[i]!;\n\t\tconst cost = estimateMessageTokensFor([message]);\n\t\taccumulated += cost;\n\n\t\tif (accumulated > maxSuffixTokens) {\n\t\t\t// The next candidate start is i+1, but only if it isn't a toolResult.\n\t\t\tlet start = i + 1;\n\t\t\twhile (start < messages.length && messages[start]!.role === \"toolResult\") {\n\t\t\t\tstart += 1;\n\t\t\t}\n\t\t\treturn start;\n\t\t}\n\n\t\tif (i === 0) return 0;\n\t}\n\treturn messages.length;\n}\n\nfunction buildRecentTail(messages: AgentMessage[], keepRecentTokens: number): AgentMessage[] {\n\tif (messages.length === 0) return [];\n\tconst cut = findSafeCutIndex(messages, keepRecentTokens);\n\treturn messages.slice(cut);\n}\n\nexport class ContextGovernor {\n\tprivate readonly _options: Required<\n\t\tPick<\n\t\t\tContextGovernorOptions,\n\t\t\t| \"capability\"\n\t\t\t| \"archive\"\n\t\t\t| \"maxIterations\"\n\t\t\t| \"keepRecentTokens\"\n\t\t\t| \"toolResultVirtualizeThreshold\"\n\t\t\t| \"minRetainedMessages\"\n\t\t>\n\t> & {\n\t\tcheckpointProvider?: ContextGovernorOptions[\"checkpointProvider\"];\n\t\tcapabilityProvider?: ContextGovernorOptions[\"capabilityProvider\"];\n\t\tvirtualizationProvider?: ContextGovernorOptions[\"virtualizationProvider\"];\n\t\tvirtualizationSink?: ContextGovernorOptions[\"virtualizationSink\"];\n\t\tnow: () => number;\n\t};\n\n\t/** Durable virtualization ledger: toolCallId -> cold evidence record. */\n\tprivate readonly _virtualizations = new Map<string, ToolVirtualizationRecord>();\n\n\t/** Cumulative evidence refs (id -> short summary) archived during this governor's lifetime. */\n\tprivate readonly _archivedEvidenceRefs = new Map<string, string>();\n\n\t/** Optional sink notified when cumulative evidence refs change (durable persistence). */\n\tprivate _evidenceRefsSink?: (refs: EvidenceReference[]) => void;\n\n\t/** Total provider overflow disagreements recorded. */\n\tprivate _overflowCount = 0;\n\n\t/** Provider overflow disagreements specifically (subset of _overflowCount). */\n\tprivate _providerOverflowCount = 0;\n\n\t/** Number of forced reductions (adaptive safety applied). */\n\tprivate _forcedReductionCount = 0;\n\n\t/** Number of rollovers performed across this governor's lifetime. */\n\tprivate _rolloverCount = 0;\n\n\t/** Number of times an existing cold evidence id was reused (no duplicate write). */\n\tprivate _reusedEvidenceCount = 0;\n\n\t/** Number of duplicate archive writes avoided. */\n\tprivate _duplicateArchiveAvoidedCount = 0;\n\n\t/** Cumulative adaptive safety reserve tokens (bounded). */\n\tprivate _adaptiveSafetyReserveTokens = 0;\n\n\t/** Calibrated conservative multiplier from provider usage (>= 1). */\n\tprivate _calibratedMultiplier: number | undefined;\n\n\t/** Number of provider usage observations used for calibration. */\n\tprivate _calibrationSamples = 0;\n\n\t/** Last observed provider usage (estimated vs observed). */\n\tprivate _lastUsageObservation: { estimated: number; observed: number } | undefined;\n\n\t/** Most recent governance diagnostics (telemetry surface). */\n\tprivate _lastDiagnostics: ContextGovernorDiagnostics | undefined;\n\n\t/** Most recent structured overflow context. */\n\tprivate _lastOverflowContext: ProviderOverflowContext | undefined;\n\n\t/** Most recent region cost breakdown (telemetry). */\n\tprivate _lastRegionCosts: { fixedPrefixTokens: number; messageTokens: number; toolSchemaTokens: number } | undefined;\n\n\t/** Most recent governance diagnostics (telemetry surface). */\n\tget lastDiagnostics(): ContextGovernorDiagnostics | undefined {\n\t\treturn this._lastDiagnostics;\n\t}\n\n\t/** Number of provider overflow disagreements recorded. */\n\tget overflowCount(): number {\n\t\treturn this._overflowCount;\n\t}\n\n\t/** Number of provider-reported context overflow events. */\n\tget providerOverflowCount(): number {\n\t\treturn this._providerOverflowCount;\n\t}\n\n\t/** Most recent structured provider overflow context, if any. */\n\tget lastOverflowContext(): ProviderOverflowContext | undefined {\n\t\treturn this._lastOverflowContext;\n\t}\n\n\tgetTokenAccountingMode(): TokenAccountingMode {\n\t\treturn resolveAccountingMode(this._calibratedMultiplier);\n\t}\n\n\t/**\n\t * Cumulative evidence references archived during this governor's lifetime.\n\t * Used to persist references into the durable mission checkpoint so they\n\t * survive a rollover without embedding the raw artifact.\n\t */\n\tgetArchivedEvidenceRefs(): { evidenceId: string; summary: string }[] {\n\t\treturn [...this._archivedEvidenceRefs].map(([evidenceId, summary]) => ({ evidenceId, summary }));\n\t}\n\n\t/** Current durable virtualization ledger snapshot. */\n\tgetVirtualizations(): ToolVirtualizationRecord[] {\n\t\treturn [...this._virtualizations.values()].sort((a, b) => a.virtualizedAtMs - b.virtualizedAtMs);\n\t}\n\n\tconstructor(options: ContextGovernorOptions) {\n\t\tthis._options = {\n\t\t\tcapability: options.capability,\n\t\t\tarchive: options.archive,\n\t\t\tcheckpointProvider: options.checkpointProvider,\n\t\t\tcapabilityProvider: options.capabilityProvider,\n\t\t\tvirtualizationProvider: options.virtualizationProvider,\n\t\t\tvirtualizationSink: options.virtualizationSink,\n\t\t\tmaxIterations: options.maxIterations ?? DEFAULT_MAX_ITERATIONS,\n\t\t\tkeepRecentTokens:\n\t\t\t\toptions.keepRecentTokens ??\n\t\t\t\tMath.max(\n\t\t\t\t\tMIN_KEEP_RECENT_TOKENS,\n\t\t\t\t\tMath.floor(options.capability.safeInputBudget * DEFAULT_KEEP_RECENT_RATIO),\n\t\t\t\t),\n\t\t\ttoolResultVirtualizeThreshold:\n\t\t\t\toptions.toolResultVirtualizeThreshold ?? DEFAULT_TOOL_RESULT_VIRTUALIZE_THRESHOLD,\n\t\t\tminRetainedMessages: options.minRetainedMessages ?? DEFAULT_MIN_RETAINED_MESSAGES,\n\t\t\tnow: options.now ?? Date.now,\n\t\t};\n\n\t\tfor (const record of options.initialVirtualizations ?? []) {\n\t\t\tthis._virtualizations.set(record.toolCallId, record);\n\t\t}\n\t\tfor (const ref of options.initialEvidenceRefs ?? []) {\n\t\t\tif (!this._archivedEvidenceRefs.has(ref.evidenceId)) {\n\t\t\t\tthis._archivedEvidenceRefs.set(ref.evidenceId, ref.summary);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Update the checkpoint provider (set after the session builds it). */\n\tsetCheckpointProvider(provider: ContextGovernorOptions[\"checkpointProvider\"]): void {\n\t\tthis._options.checkpointProvider = provider;\n\t}\n\n\t/**\n\t * Attach a durable sink for archived evidence references. Invoked whenever\n\t * new cold evidence is archived so references can survive process death and\n\t * be restored on explicit child resume.\n\t */\n\tsetEvidenceRefsSink(sink: (refs: EvidenceReference[]) => void): void {\n\t\tthis._evidenceRefsSink = sink;\n\t}\n\n\tprivate _emitEvidenceRefs(): void {\n\t\tif (!this._evidenceRefsSink) return;\n\t\tthis._evidenceRefsSink(this.getArchivedEvidenceRefs());\n\t}\n\n\t/** Update the capability provider (for runtime model switches). */\n\tsetCapabilityProvider(provider: ContextGovernorOptions[\"capabilityProvider\"]): void {\n\t\tthis._options.capabilityProvider = provider;\n\t}\n\n\t/**\n\t * Refresh the durable virtualization ledger from the persistence source\n\t * (e.g., after a session resume where a new session snapshot was loaded).\n\t */\n\tprivate _refreshVirtualizations(): void {\n\t\tconst records = this._options.virtualizationProvider?.() ?? [];\n\t\tfor (const record of records) {\n\t\t\tconst existing = this._virtualizations.get(record.toolCallId);\n\t\t\tif (existing && existing.virtualizedAtMs >= record.virtualizedAtMs) continue;\n\t\t\tthis._virtualizations.set(record.toolCallId, record);\n\t\t\tif (!this._archivedEvidenceRefs.has(record.evidenceId)) {\n\t\t\t\tthis._archivedEvidenceRefs.set(record.evidenceId, record.synopsis.slice(0, 120));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate _persistVirtualizations(): void {\n\t\tthis._options.virtualizationSink?.(this.getVirtualizations());\n\t}\n\n\t/**\n\t * Record a provider context-overflow disagreement. Subsequent `govern` calls\n\t * apply a progressively stricter budget (bounded) so a tokenizer mismatch\n\t * forces stronger deterministic reduction instead of looping.\n\t */\n\trecordOverflow(context?: ProviderOverflowContext): void {\n\t\tthis._overflowCount += 1;\n\t\tthis._providerOverflowCount += 1;\n\t\tthis._forcedReductionCount += 1;\n\n\t\tif (context) {\n\t\t\tthis._lastOverflowContext = context;\n\t\t\tif (context.observedInputTokens && context.estimatedInputTokens > 0) {\n\t\t\t\tconst ratio = context.observedInputTokens / context.estimatedInputTokens;\n\t\t\t\tthis._applyCalibrationRatio(ratio);\n\t\t\t}\n\t\t}\n\n\t\t// Adaptive safety: reserve more headroom after authoritative disagreement.\n\t\tconst step = Math.max(\n\t\t\t256,\n\t\t\tMath.min(ADAPTIVE_SAFETY_STEP, Math.floor(this._options.capability.safetyReserveTokens * 0.5)),\n\t\t);\n\t\tthis._adaptiveSafetyReserveTokens = Math.min(\n\t\t\tMath.floor(this._options.capability.configuredContextWindow * MAX_ADAPTIVE_SAFETY_RATIO),\n\t\t\tthis._adaptiveSafetyReserveTokens + step,\n\t\t);\n\t}\n\n\t/**\n\t * Feed an authoritative provider usage observation into deterministic\n\t * calibration. Only ever biases the estimator MORE conservative (never less).\n\t */\n\tobserveProviderUsage(estimatedInputTokens: number, observedInputTokens: number): void {\n\t\tif (estimatedInputTokens <= 0 || observedInputTokens <= 0) return;\n\t\tthis._calibrationSamples += 1;\n\t\tthis._lastUsageObservation = { estimated: estimatedInputTokens, observed: observedInputTokens };\n\t\tconst ratio = observedInputTokens / estimatedInputTokens;\n\t\tthis._applyCalibrationRatio(ratio);\n\t}\n\n\tprivate _applyCalibrationRatio(ratio: number): void {\n\t\tif (ratio <= 1) return;\n\t\tthis._calibratedMultiplier = Math.min(\n\t\t\tMAX_CALIBRATED_MULTIPLIER,\n\t\t\tMath.max(this._calibratedMultiplier ?? 1, ratio),\n\t\t);\n\t}\n\n\tprivate _deriveAdaptiveCapability(base: ContextCapability): ContextCapability {\n\t\tif (this._adaptiveSafetyReserveTokens <= 0) return base;\n\t\tconst safetyReserveTokens = Math.min(\n\t\t\tbase.safetyReserveTokens + this._adaptiveSafetyReserveTokens,\n\t\t\tMath.floor(base.configuredContextWindow * 0.4),\n\t\t);\n\t\tif (safetyReserveTokens <= base.safetyReserveTokens) return base;\n\t\tconst safeInputBudget = computeSafeInputBudget(\n\t\t\tbase.configuredContextWindow,\n\t\t\tbase.reservedOutputTokens,\n\t\t\tsafetyReserveTokens,\n\t\t\t256,\n\t\t);\n\t\tconst softPressureRatio = base.safeInputBudget > 0 ? base.softPressureThreshold / base.safeInputBudget : 0.8;\n\t\treturn {\n\t\t\t...base,\n\t\t\tsafetyReserveTokens,\n\t\t\tsafeInputBudget,\n\t\t\tsoftPressureThreshold: Math.floor(safeInputBudget * softPressureRatio),\n\t\t};\n\t}\n\n\tprivate _accounting(): { calibratedMultiplier?: number } {\n\t\treturn { calibratedMultiplier: this._calibratedMultiplier };\n\t}\n\n\t/**\n\t * Preflight-reduce an assembly to satisfy the safe input budget.\n\t *\n\t * @returns the reduced assembly plus diagnostics. When `action` is\n\t * \"unrecoverable\", the caller must NOT send the request.\n\t */\n\tasync govern(assembly: ContextAssembly): Promise<ContextGovernorResult> {\n\t\tthis._refreshVirtualizations();\n\t\tconst baseCapability = this._options.capabilityProvider?.() ?? this._options.capability;\n\t\tconst capability = this._deriveAdaptiveCapability(baseCapability);\n\t\tconst accounting = this._accounting();\n\t\tconst accountingMode = this.getTokenAccountingMode();\n\n\t\tconst inputTokensBefore = estimateAssemblyInputTokens(assembly, accounting);\n\t\tconst pressureRatioBefore = contextPressureRatio(capability, inputTokensBefore);\n\t\tconst regionCosts = estimateContextRegionCosts(assembly, accounting);\n\t\tthis._lastRegionCosts = {\n\t\t\tfixedPrefixTokens: regionCosts.fixedPrefixTokens,\n\t\t\tmessageTokens: regionCosts.messageTokens,\n\t\t\ttoolSchemaTokens: regionCosts.toolSchemaTokens,\n\t\t};\n\n\t\t// Phase 0: restore any tool result that the durable ledger has already\n\t\t// virtualized. This is what keeps raw payloads from reappearing on the\n\t\t// next request / process restart / session resume.\n\t\tconst revirtualized = this._applyPersistedVirtualizations(assembly);\n\t\tlet current = revirtualized.assembly;\n\t\tlet inputTokens = estimateAssemblyInputTokens(current, accounting);\n\t\tconst toolResultsRevirtualized = revirtualized.count;\n\n\t\tconst evidenceIdsArchived: string[] = [];\n\t\tconst reducedRegions: string[] = [];\n\t\tif (toolResultsRevirtualized > 0) reducedRegions.push(\"virtualization-restore\");\n\n\t\tconst baseDiagnostics: ContextGovernorDiagnostics = {\n\t\t\taction: \"pass\",\n\t\t\tcapability,\n\t\t\titerations: 0,\n\t\t\tinputTokensBefore,\n\t\t\tinputTokensAfter: inputTokens,\n\t\t\tpressureRatioBefore,\n\t\t\tpressureRatioAfter: contextPressureRatio(capability, inputTokens),\n\t\t\ttoolResultsArchived: 0,\n\t\t\ttoolResultsRevirtualized,\n\t\t\tmessagesEvicted: 0,\n\t\t\ttokensEvicted: 0,\n\t\t\trolloverOccurred: false,\n\t\t\tevidenceIdsArchived: [],\n\t\t\treducedRegions: [],\n\t\t\tfixedPrefixTokens: regionCosts.fixedPrefixTokens,\n\t\t\ttokenAccountingMode: accountingMode,\n\t\t\trecoveryIteration: this._overflowCount,\n\t\t};\n\n\t\t// Phase 1: proactively virtualize oversized tool results regardless of\n\t\t// overall pressure. A single huge raw result must never live forever in\n\t\t// the hot working set even when the window still has headroom.\n\t\tconst proactive = await this._virtualizeToolResults(current, false);\n\t\tconst proactiveChanged = proactive.changed;\n\t\tif (proactiveChanged) {\n\t\t\tcurrent = proactive.assembly;\n\t\t\tfor (const id of proactive.archivedIds) evidenceIdsArchived.push(id);\n\t\t\treducedRegions.push(\"tool-result-virtualization\");\n\t\t\tinputTokens = estimateAssemblyInputTokens(current, accounting);\n\t\t}\n\n\t\t// Below soft pressure and nothing was virtualized: no further action.\n\t\tif (inputTokens <= capability.softPressureThreshold && !proactiveChanged && toolResultsRevirtualized === 0) {\n\t\t\tthis._lastDiagnostics = baseDiagnostics;\n\t\t\treturn { assembly, diagnostics: baseDiagnostics };\n\t\t}\n\n\t\t// Below soft pressure but virtualization ran (archive or identity reuse): report it.\n\t\tif (inputTokens <= capability.softPressureThreshold) {\n\t\t\tconst diagnostics: ContextGovernorDiagnostics = {\n\t\t\t\t...baseDiagnostics,\n\t\t\t\taction: \"virtualized\",\n\t\t\t\tinputTokensAfter: inputTokens,\n\t\t\t\tpressureRatioAfter: contextPressureRatio(capability, inputTokens),\n\t\t\t\ttoolResultsArchived: evidenceIdsArchived.length,\n\t\t\t\ttokensEvicted: Math.max(0, inputTokensBefore - inputTokens),\n\t\t\t\tevidenceIdsArchived,\n\t\t\t\treducedRegions: [...new Set(reducedRegions)],\n\t\t\t};\n\t\t\tthis._lastDiagnostics = diagnostics;\n\t\t\treturn { assembly: current, diagnostics };\n\t\t}\n\n\t\tlet rolloverOccurred = false;\n\t\tlet rolloverDone = false;\n\t\tlet checkpointRevision: number | undefined;\n\t\tlet iterations = 0;\n\n\t\tfor (let iteration = 1; iteration <= this._options.maxIterations; iteration++) {\n\t\t\titerations = iteration;\n\t\t\tif (!exceedsSafeInputBudget(capability, inputTokens)) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Step 1: virtualize oversized/older tool results.\n\t\t\tconst virtualized = await this._virtualizeToolResults(current, true);\n\t\t\tif (virtualized.changed) {\n\t\t\t\tcurrent = virtualized.assembly;\n\t\t\t\tfor (const id of virtualized.archivedIds) evidenceIdsArchived.push(id);\n\t\t\t\treducedRegions.push(\"tool-result-virtualization\");\n\t\t\t\tinputTokens = estimateAssemblyInputTokens(current, accounting);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Step 2: rollover via mission checkpoint (once).\n\t\t\tif (!rolloverDone) {\n\t\t\t\tlet checkpoint: MissionContextCheckpoint | undefined;\n\t\t\t\ttry {\n\t\t\t\t\tcheckpoint = this._options.checkpointProvider?.();\n\t\t\t\t} catch {\n\t\t\t\t\t// A corrupt/unreadable checkpoint must never fabricate state; fall\n\t\t\t\t\t// back to deterministic trimming only.\n\t\t\t\t\tcheckpoint = undefined;\n\t\t\t\t}\n\t\t\t\tif (checkpoint) {\n\t\t\t\t\tconst rehydrated = this._rollover(current, checkpoint);\n\t\t\t\t\tif (rehydrated) {\n\t\t\t\t\t\tcurrent = rehydrated.assembly;\n\t\t\t\t\t\tinputTokens = estimateAssemblyInputTokens(current, accounting);\n\t\t\t\t\t\trolloverOccurred = true;\n\t\t\t\t\t\trolloverDone = true;\n\t\t\t\t\t\tthis._rolloverCount += 1;\n\t\t\t\t\t\tcheckpointRevision = checkpoint.revision;\n\t\t\t\t\t\treducedRegions.push(\"checkpoint-rollover\");\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trolloverDone = true;\n\t\t\t}\n\n\t\t\t// Step 3: deterministic tail trim (evict oldest non-pinned messages).\n\t\t\tconst trimmed = this._trimTail(current);\n\t\t\tif (trimmed.changed) {\n\t\t\t\tcurrent = trimmed.assembly;\n\t\t\t\tinputTokens = estimateAssemblyInputTokens(current, accounting);\n\t\t\t\treducedRegions.push(\"tail-trim\");\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// No progress possible: structured unrecoverable.\n\t\t\tbreak;\n\t\t}\n\n\t\tconst inputTokensAfter = estimateAssemblyInputTokens(current, accounting);\n\t\tconst stillOver = exceedsSafeInputBudget(capability, inputTokensAfter);\n\t\tconst tokensEvicted = Math.max(0, inputTokensBefore - inputTokensAfter);\n\n\t\tlet action: GovernorAction;\n\t\tif (stillOver) {\n\t\t\taction = \"unrecoverable\";\n\t\t} else if (rolloverOccurred) {\n\t\t\taction = \"rollover\";\n\t\t} else if (evidenceIdsArchived.length > 0 || toolResultsRevirtualized > 0 || proactiveChanged) {\n\t\t\taction = \"virtualized\";\n\t\t} else if (this._countEvicted(assembly, current) > 0) {\n\t\t\taction = \"compacted\";\n\t\t} else {\n\t\t\taction = \"pass\";\n\t\t}\n\n\t\tconst diagnostics: ContextGovernorDiagnostics = {\n\t\t\t...baseDiagnostics,\n\t\t\taction,\n\t\t\titerations,\n\t\t\tinputTokensAfter,\n\t\t\tpressureRatioAfter: contextPressureRatio(capability, inputTokensAfter),\n\t\t\ttoolResultsArchived: evidenceIdsArchived.length,\n\t\t\tmessagesEvicted: this._countEvicted(assembly, current),\n\t\t\ttokensEvicted,\n\t\t\trolloverOccurred,\n\t\t\tcheckpointRevision,\n\t\t\tevidenceIdsArchived,\n\t\t\treducedRegions: [...new Set(reducedRegions)],\n\t\t\tunrecoverableReason: stillOver ? this._unrecoverableReason(current, accounting) : undefined,\n\t\t};\n\n\t\tthis._lastDiagnostics = diagnostics;\n\t\treturn { assembly: current, diagnostics };\n\t}\n\n\tprivate _countEvicted(before: ContextAssembly, after: ContextAssembly): number {\n\t\treturn Math.max(0, before.messages.length - after.messages.length);\n\t}\n\n\t/**\n\t * Restore the virtualized representation for any tool result present in the\n\t * durable ledger. Returns a new assembly (message objects are NOT mutated so\n\t * the caller's durable state is preserved) and the count of restored results.\n\t */\n\tprivate _applyPersistedVirtualizations(assembly: ContextAssembly): {\n\t\tassembly: ContextAssembly;\n\t\tcount: number;\n\t} {\n\t\tif (this._virtualizations.size === 0) return { assembly, count: 0 };\n\t\tlet count = 0;\n\t\tconst messages = assembly.messages.map((message) => {\n\t\t\tif (message.role !== \"toolResult\") return message;\n\t\t\tconst result = message as ToolResultMessage;\n\t\t\tif (isAlreadyVirtualized(result)) return message;\n\t\t\tconst record = this._virtualizations.get(result.toolCallId);\n\t\t\tif (!record) return message;\n\t\t\tcount += 1;\n\t\t\treturn applyVirtualizedForm(result, record.evidenceId, record.synopsis, record.contentBytes, false);\n\t\t});\n\t\treturn { assembly: { ...assembly, messages }, count };\n\t}\n\n\tprivate _unrecoverableReason(assembly: ContextAssembly, accounting: { calibratedMultiplier?: number }): string {\n\t\tconst regions = estimateContextRegionCosts(assembly, accounting);\n\t\tconst safe = this._deriveAdaptiveCapability(\n\t\t\tthis._options.capabilityProvider?.() ?? this._options.capability,\n\t\t).safeInputBudget;\n\t\tconst parts = [\n\t\t\t`systemPrompt≈${regions.systemPromptTokens}`,\n\t\t\t`toolSchemas≈${regions.toolSchemaTokens}`,\n\t\t\t`dynamicPrompt≈${regions.dynamicPromptTokens}`,\n\t\t\t`messages≈${regions.messageTokens}`,\n\t\t\t`fixedPrefix≈${regions.fixedPrefixTokens}`,\n\t\t\t`safeInputBudget=${safe}`,\n\t\t];\n\t\treturn (\n\t\t\t`Cannot fit working set within safe input budget (${parts.join(\", \")}). ` +\n\t\t\t\"Reduce tools, shrink the system prompt, or configure a larger context window.\"\n\t\t);\n\t}\n\n\t/**\n\t * Archive tool results, replacing raw text with a synopsis + a durable\n\t * evidence reference. The full content remains recoverable.\n\t *\n\t * @param includeOlder When false, only individually oversized results are\n\t * virtualized (proactive LARGE rule). When true, older results outside the\n\t * recent hot tail are also virtualized (pressure reduction).\n\t */\n\tprivate async _virtualizeToolResults(\n\t\tassembly: ContextAssembly,\n\t\tincludeOlder: boolean,\n\t): Promise<{\n\t\tassembly: ContextAssembly;\n\t\tchanged: boolean;\n\t\tarchivedIds: string[];\n\t}> {\n\t\tconst archivedIds: string[] = [];\n\t\tlet changed = false;\n\t\tconst messages = assembly.messages.map((message) => message);\n\t\tconst recentTail = buildRecentTail(messages, this._options.keepRecentTokens);\n\t\tconst recentTailStart = messages.length - recentTail.length;\n\n\t\tfor (let i = 0; i < messages.length; i++) {\n\t\t\tconst message = messages[i];\n\t\t\tif (!message || message.role !== \"toolResult\") continue;\n\n\t\t\tconst result = message as ToolResultMessage;\n\t\t\tif (isAlreadyVirtualized(result)) continue;\n\n\t\t\tconst cost = toolResultTextTokens(result);\n\t\t\tconst inRecentTail = i >= recentTailStart;\n\t\t\t// Large results always; older results only when reducing pressure.\n\t\t\tif (cost < this._options.toolResultVirtualizeThreshold && !(includeOlder && !inRecentTail)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst text = toolResultText(result);\n\t\t\tif (text.length === 0) continue;\n\n\t\t\t// Identity reuse: content fetched via retrieve_evidence already has an\n\t\t\t// authoritative cold copy. Collapse it back to the ORIGINAL evidence ref\n\t\t\t// instead of archiving a duplicate (avoids archive -> retrieve -> archive\n\t\t\t// duplicate chains).\n\t\t\tconst originEvidenceId = getEvidenceSourceId(result);\n\t\t\tconst synopsis = synopsisFor(text);\n\n\t\t\tif (originEvidenceId) {\n\t\t\t\tconst contentHash = buildEvidenceRecord({\n\t\t\t\t\tkind: \"tool-result\",\n\t\t\t\t\tsource: result.toolName,\n\t\t\t\t\tcontent: text,\n\t\t\t\t\tnow: this._options.now(),\n\t\t\t\t}).contentHash;\n\t\t\t\tthis._recordVirtualization(result.toolCallId, {\n\t\t\t\t\tevidenceId: originEvidenceId,\n\t\t\t\t\tcontentHash,\n\t\t\t\t\tsource: result.toolName,\n\t\t\t\t\tsynopsis,\n\t\t\t\t\tcontentBytes: Buffer.byteLength(text, \"utf8\"),\n\t\t\t\t});\n\t\t\t\tmessages[i] = applyVirtualizedForm(\n\t\t\t\t\tresult,\n\t\t\t\t\toriginEvidenceId,\n\t\t\t\t\tsynopsis,\n\t\t\t\t\tBuffer.byteLength(text, \"utf8\"),\n\t\t\t\t\ttrue,\n\t\t\t\t);\n\t\t\t\tchanged = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst record = buildEvidenceRecord({\n\t\t\t\tkind: \"tool-result\",\n\t\t\t\tsource: result.toolName,\n\t\t\t\tcontent: text,\n\t\t\t\tnow: this._options.now(),\n\t\t\t});\n\n\t\t\t// Content-addressed identity reuse: never write the same artifact twice.\n\t\t\tconst alreadyStored = await this._options.archive.has(record.evidenceId);\n\t\t\tif (alreadyStored) {\n\t\t\t\tthis._reusedEvidenceCount += 1;\n\t\t\t\tthis._duplicateArchiveAvoidedCount += 1;\n\t\t\t} else {\n\t\t\t\tawait this._options.archive.store({\n\t\t\t\t\tkind: record.kind,\n\t\t\t\t\tsource: record.source,\n\t\t\t\t\tcontent: record.content,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tarchivedIds.push(record.evidenceId);\n\t\t\tthis._recordVirtualization(result.toolCallId, {\n\t\t\t\tevidenceId: record.evidenceId,\n\t\t\t\tcontentHash: record.contentHash,\n\t\t\t\tsource: record.source,\n\t\t\t\tsynopsis,\n\t\t\t\tcontentBytes: record.contentBytes,\n\t\t\t});\n\n\t\t\tif (!this._archivedEvidenceRefs.has(record.evidenceId)) {\n\t\t\t\tthis._archivedEvidenceRefs.set(record.evidenceId, synopsis.slice(0, 120));\n\t\t\t\tthis._emitEvidenceRefs();\n\t\t\t}\n\n\t\t\tmessages[i] = applyVirtualizedForm(result, record.evidenceId, synopsis, record.contentBytes, false);\n\t\t\tchanged = true;\n\t\t}\n\n\t\treturn { assembly: { ...assembly, messages }, changed, archivedIds };\n\t}\n\n\tprivate _recordVirtualization(\n\t\ttoolCallId: string,\n\t\trecord: Omit<ToolVirtualizationRecord, \"toolCallId\" | \"virtualizedAtMs\">,\n\t): void {\n\t\tconst existing = this._virtualizations.get(toolCallId);\n\t\tconst full: ToolVirtualizationRecord = {\n\t\t\ttoolCallId,\n\t\t\t...record,\n\t\t\tvirtualizedAtMs: existing?.virtualizedAtMs ?? this._options.now(),\n\t\t};\n\t\tthis._virtualizations.set(toolCallId, full);\n\t\tthis._persistVirtualizations();\n\t}\n\n\t/** Roll the context over: preamble + recent tail. Returns undefined if no change. */\n\tprivate _rollover(\n\t\tassembly: ContextAssembly,\n\t\tcheckpoint: MissionContextCheckpoint,\n\t): { assembly: ContextAssembly } | undefined {\n\t\tconst preamble = makeCheckpointMessage(checkpoint);\n\t\tconst tail = buildRecentTail(assembly.messages, this._options.keepRecentTokens);\n\t\tconst nextMessages = [preamble, ...tail];\n\t\tif (nextMessages.length >= assembly.messages.length) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn { assembly: { ...assembly, messages: nextMessages } };\n\t}\n\n\t/** Drop the oldest non-pinned messages while retaining at least the recent tail + minimum. */\n\tprivate _trimTail(assembly: ContextAssembly): { assembly: ContextAssembly; changed: boolean } {\n\t\tconst pinned = assembly.messages.filter(isPinnedMessage);\n\t\tconst normal = assembly.messages.filter((m) => !isPinnedMessage(m));\n\t\tif (normal.length <= this._options.minRetainedMessages) {\n\t\t\treturn { assembly, changed: false };\n\t\t}\n\t\tconst tail = buildRecentTail(normal, this._options.keepRecentTokens);\n\t\tconst keepCount = Math.max(this._options.minRetainedMessages, tail.length);\n\t\tif (keepCount >= normal.length) {\n\t\t\treturn { assembly, changed: false };\n\t\t}\n\t\tlet kept = normal.slice(normal.length - keepCount);\n\t\t// Ensure the cut does not orphan a leading tool result.\n\t\tlet start = 0;\n\t\twhile (start < kept.length && kept[start]!.role === \"toolResult\") start += 1;\n\t\tkept = kept.slice(start);\n\t\treturn { assembly: { ...assembly, messages: [...pinned, ...kept] }, changed: true };\n\t}\n\n\t/** Opt-in telemetry snapshot (metadata only; never prompt content). */\n\tgetTelemetry(): ContextGovernorTelemetry {\n\t\tconst capability = this._options.capabilityProvider?.() ?? this._options.capability;\n\t\tconst effective = this._deriveAdaptiveCapability(capability);\n\t\tconst last = this._lastDiagnostics;\n\t\tconst regions = this._lastRegionCosts;\n\t\tconst estimationError =\n\t\t\tthis._lastUsageObservation && this._lastUsageObservation.observed - this._lastUsageObservation.estimated;\n\t\tconst estimationErrorRatio =\n\t\t\tthis._lastUsageObservation && this._lastUsageObservation.estimated > 0\n\t\t\t\t? this._lastUsageObservation.observed / this._lastUsageObservation.estimated\n\t\t\t\t: undefined;\n\n\t\treturn {\n\t\t\ttokenAccountingMode: this.getTokenAccountingMode(),\n\t\t\testimatedInputTokens: last?.inputTokensAfter ?? last?.inputTokensBefore ?? 0,\n\t\t\tproviderObservedInputTokens: this._lastUsageObservation?.observed,\n\t\t\testimationError,\n\t\t\testimationErrorRatio,\n\t\t\tconfiguredContextWindow: effective.configuredContextWindow,\n\t\t\tbaseSafeInputBudget: capability.safeInputBudget,\n\t\t\teffectiveSafeInputBudget: effective.safeInputBudget,\n\t\t\treservedOutputTokens: effective.reservedOutputTokens,\n\t\t\tsafetyReserveTokens: effective.safetyReserveTokens,\n\t\t\toverflowCount: this._overflowCount,\n\t\t\tproviderOverflowCount: this._providerOverflowCount,\n\t\t\tforcedReductionCount: this._forcedReductionCount,\n\t\t\trolloverCount: this._rolloverCount,\n\t\t\tarchivedEvidenceCount: this._archivedEvidenceRefs.size,\n\t\t\treusedEvidenceCount: this._reusedEvidenceCount,\n\t\t\tduplicateArchiveAvoidedCount: this._duplicateArchiveAvoidedCount,\n\t\t\tfixedPrefixTokens: regions?.fixedPrefixTokens ?? last?.fixedPrefixTokens ?? 0,\n\t\t\tmessageTokens: regions?.messageTokens ?? (last ? last.inputTokensAfter - last.fixedPrefixTokens : 0),\n\t\t\ttoolSchemaTokens: regions?.toolSchemaTokens ?? 0,\n\t\t\trecoveryIteration: this._overflowCount,\n\t\t\tadaptiveSafetyReserveTokens: this._adaptiveSafetyReserveTokens,\n\t\t\tcalibratedMultiplier: this._calibratedMultiplier ?? 1,\n\t\t\tcalibrationSamples: this._calibrationSamples,\n\t\t};\n\t}\n}\n\n/**\n * Rehydrate an archived evidence artifact back into hot context text. Used when\n * a model needs an old tool output again; the governor does not auto-page-in\n * (avoiding loops).\n */\nexport async function rehydrateEvidence(\n\tarchive: EvidenceArchive,\n\tevidenceId: string,\n\tmaxChars = 8000,\n): Promise<string | undefined> {\n\tconst record = await archive.load(evidenceId);\n\tif (!record) return undefined;\n\tif (record.content.length <= maxChars) return record.content;\n\treturn `${record.content.slice(0, maxChars)}\\n\\n[... truncated from durable evidence ${record.evidenceId} (${record.contentBytes} bytes total)]`;\n}\n"]}