@mono-agent/agent-runtime 0.11.4 → 0.12.0

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.
@@ -32,8 +32,9 @@ export function usageFromMessages(messages = []) {
32
32
  }
33
33
 
34
34
  /**
35
- * Classify a pi error message into a runtime failure kind. Context-limit /
36
- * max-turns terminations map to usage_limit; credential/config auth failures
35
+ * Classify a pi error message into a runtime failure kind. Context-window
36
+ * overflows map to context_limit so the router can try the configured fallback;
37
+ * max-turns terminations remain usage_limit. Credential/config auth failures
37
38
  * map to provider_auth; everything else to provider_unavailable. Null message → null.
38
39
  * @param {string|null} message
39
40
  * @param {Record<string, unknown>} diagnostics
@@ -42,9 +43,8 @@ export function usageFromMessages(messages = []) {
42
43
  */
43
44
  export function failureKindForPiError(message, diagnostics, { maxTurnsHit = false } = {}) {
44
45
  if (!message) return null;
45
- if (maxTurnsHit || isContextLimitError(message) || isLikelyContextTermination(message, diagnostics)) {
46
- return "usage_limit";
47
- }
46
+ if (maxTurnsHit) return "usage_limit";
47
+ if (isContextLimitError(message) || isLikelyContextTermination(message, diagnostics)) return "context_limit";
48
48
  if (isProviderAuthFailureText(message)) return "provider_auth";
49
49
  return "provider_unavailable";
50
50
  }
@@ -14,17 +14,6 @@ import {
14
14
  streamContentKey,
15
15
  } from "../pi-events.js";
16
16
 
17
- /**
18
- * The tool-start progress line surfaced as a thinking block, or null for a
19
- * blank/invalid tool name.
20
- * @param {unknown} toolName
21
- * @returns {string|null}
22
- */
23
- export function toolStartProgressText(toolName) {
24
- if (typeof toolName !== "string" || toolName.trim().length === 0) return null;
25
- return `Running ${toolName}...`;
26
- }
27
-
28
17
  function toolResultFileChange(result) {
29
18
  const fileChange = result?.details?.file_change;
30
19
  return fileChange && typeof fileChange === "object" && !Array.isArray(fileChange)
@@ -84,10 +73,6 @@ export function createStreamSubscriber(runState, { onEvent, options, toolLimits,
84
73
  if (event.toolName) runState.lastToolName = event.toolName;
85
74
  if (event.toolCallId) runState.toolStartTimes.set(event.toolCallId, Date.now());
86
75
  const input = eventToolArgs(event.toolName, event.args, { cwd: options.cwd, toolLimits });
87
- const progressText = toolStartProgressText(event.toolName);
88
- if (progressText) {
89
- onEvent({ type: "assistant", message: { content: [{ type: "thinking", text: progressText }] } });
90
- }
91
76
  onEvent({
92
77
  type: "assistant",
93
78
  message: { content: [{ type: "tool_use", id: event.toolCallId, name: event.toolName, input }] },
@@ -14,6 +14,7 @@ import {
14
14
  getPiBuiltinTools,
15
15
  initPiMcpTools,
16
16
  } from "../../../agent/tools/pi-bridge.js";
17
+ import { createNodeReplController } from "../../../agent/tools/node-repl.js";
17
18
  import { readToolRuntime } from "../../../agent/tools/shared/runtime-context.js";
18
19
  import { formatLiveInputGuidance } from "../../live-input-prompt.js";
19
20
  import { appendStructuredOutputInstruction } from "./structured-output.js";
@@ -24,10 +25,11 @@ import { createStreamSubscriber } from "./stream-subscriber.js";
24
25
  * tools, the MCP tool bridge, and the StructuredOutput tool (whose callback
25
26
  * writes runState.structuredResult). Surfaces MCP init/list failures to both the
26
27
  * event stream and runtimeWarnings. Returns the assembled tools plus the MCP
27
- * clients (closed by the caller's finally) and the structured tool.
28
+ * clients (closed by the caller's finally), run-owned tool cleanup, and the
29
+ * structured tool.
28
30
  * @param {any} runState
29
31
  * @param {any} params
30
- * @returns {Promise<{tools: any[], structuredTool: any, mcpClients: any[]}>}
32
+ * @returns {Promise<{tools: any[], structuredTool: any, mcpClients: any[], closeRunTools: () => Promise<void>}>}
31
33
  */
32
34
  export async function buildTurnTools(runState, {
33
35
  options,
@@ -62,6 +64,15 @@ export async function buildTurnTools(runState, {
62
64
  ? { ...(options.toolContext ?? readToolRuntime()), sandbox: options.sandbox }
63
65
  : options.toolContext;
64
66
  const sandboxEngine = options.sandboxEngine ?? runCtx?.sandboxEngine;
67
+ const nodeReplController = capabilities.tool_use === false
68
+ ? null
69
+ : createNodeReplController({
70
+ cwd: options.cwd,
71
+ maxOutputChars: toolLimits.bashOutputLimitChars || toolLimits.toolTextLimitChars,
72
+ sandboxPolicy: options.sandboxPolicy,
73
+ sandboxEngine,
74
+ ctx: runCtx,
75
+ });
65
76
 
66
77
  // REUSED custom pieces: built-in tool sandboxing + allowlist/bloat filter +
67
78
  // approval gates. These are identical to the legacy bridge.
@@ -97,6 +108,7 @@ export async function buildTurnTools(runState, {
97
108
  sandboxEngine,
98
109
  approvalManager,
99
110
  approvalModel: runtime.model?.id || runtime.model?.name || resolved.model,
111
+ nodeReplController,
100
112
  ctx: runCtx,
101
113
  }));
102
114
 
@@ -134,7 +146,12 @@ export async function buildTurnTools(runState, {
134
146
  ...mcpInit.tools,
135
147
  ...(structuredTool ? [structuredTool] : []),
136
148
  ];
137
- return { tools, structuredTool, mcpClients: mcpInit.clients };
149
+ return {
150
+ tools,
151
+ structuredTool,
152
+ mcpClients: mcpInit.clients,
153
+ closeRunTools: async () => { await nodeReplController?.close(); },
154
+ };
138
155
  }
139
156
 
140
157
  /**
@@ -223,6 +223,7 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
223
223
  const events = [];
224
224
  const runtimeWarnings = [];
225
225
  let mcpClients = [];
226
+ let closeRunTools = async () => {};
226
227
  let harness = null;
227
228
  // The ONE explicit runState the extracted modules (stream subscriber, session
228
229
  // lifecycle, compaction driver, turn runner, result builder) read/write.
@@ -399,7 +400,12 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
399
400
  // Build the turn's tools (builtins + MCP bridge + StructuredOutput). The
400
401
  // StructuredOutput callback writes runState.structuredResult; the MCP clients
401
402
  // are closed in the finally.
402
- const { tools, structuredTool, mcpClients: builtMcpClients } = await buildTurnTools(runState, {
403
+ const {
404
+ tools,
405
+ structuredTool,
406
+ mcpClients: builtMcpClients,
407
+ closeRunTools: builtCloseRunTools,
408
+ } = await buildTurnTools(runState, {
403
409
  options,
404
410
  capabilities,
405
411
  toolLimits,
@@ -410,6 +416,7 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
410
416
  runtimeWarnings,
411
417
  });
412
418
  mcpClients = builtMcpClients;
419
+ closeRunTools = builtCloseRunTools;
413
420
 
414
421
  // Provider retry/backoff is delegated to pi-ai via streamOptions, replacing
415
422
  // the legacy hand-rolled stream-retry loop.
@@ -654,7 +661,7 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
654
661
  toolCompactionApplied: toolCompactionAppliedFromWarnings(runtimeWarnings),
655
662
  // Tristate: true = a compaction fired this run (proactive or reactive),
656
663
  // false = the path is enabled but did not need to fire, null = disabled via
657
- // agent_compaction_enabled. See docs/reference/feature-registry.md runtime.context-compaction.
664
+ // runtime.compaction.enabled. See docs/reference/feature-registry.md runtime.context-compaction.
658
665
  contextCompactionApplied: runState.compaction.policy?.enabled ? runState.compaction.applied : null,
659
666
  });
660
667
  emitCapabilitiesResolved(onEvent, { sdk: resolved.sdk, model: reference, capabilitiesUsed });
@@ -750,7 +757,11 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
750
757
  } finally {
751
758
  if (runState.sessionEntry) runState.sessionEntry.busy = false;
752
759
  runState.removeAbortHandler?.();
753
- await closePiMcpClients(mcpClients);
760
+ try {
761
+ await closeRunTools();
762
+ } finally {
763
+ await closePiMcpClients(mcpClients);
764
+ }
754
765
  }
755
766
  }
756
767
 
package/src/ai/types.js CHANGED
@@ -98,14 +98,15 @@
98
98
  * @typedef {Object} RuntimeCompactionPolicy
99
99
  * Typed per-run context-compaction policy (the supported replacement for the
100
100
  * `agent_compaction_*` keys of the deprecated `settings` bag). Every field is
101
- * optional; an omitted field falls back to the kernel default.
101
+ * optional; omitted scalar budgets resolve adaptively against the effective
102
+ * model context window.
102
103
  * @property {boolean} [enabled] Whether auto-compaction runs at all.
103
104
  * @property {number} [triggerRatio] Fraction of the context window that arms the proactive trigger.
104
105
  * @property {number} [keepRecentTokens] Recent-token budget preserved across a compaction.
105
- * @property {number} [summaryMaxTokens] Max tokens of the generated summary.
106
- * @property {number} [minSavingsTokens] Minimum token savings required to keep a compaction.
106
+ * @property {number} [summaryMaxTokens] Combined output-token budget for generated compaction summaries.
107
+ * @property {number} [minSavingsTokens] Minimum token savings required for proactive compaction; reactive recovery accepts any positive reduction.
107
108
  * @property {boolean} [fixedOverheadEnabled] Whether the system-prompt + tool-schema overhead correction is folded into the trigger.
108
- * @property {number} [contextWindowOverride] Forces the compaction window instead of the live-model-recognized one (applied at resolveLiveCompactionPolicy; has no legacy settings equivalent).
109
+ * @property {number} [contextWindowOverride] Persistent correction for provider context-window metadata; learned overflow evidence may lower it process-locally (applied at resolveLiveCompactionPolicy; has no legacy settings equivalent).
109
110
  */
110
111
 
111
112
  /**
@@ -0,0 +1,19 @@
1
+ /**
2
+ * One lazy Node REPL process owned by a single Pi run.
3
+ * @param {{cwd?: string, maxOutputChars?: number, sandboxPolicy?: any, sandboxEngine?: any, ctx?: any}} [options]
4
+ */
5
+ export function createNodeReplController({ cwd, maxOutputChars, sandboxPolicy, sandboxEngine, ctx, }?: {
6
+ cwd?: string;
7
+ maxOutputChars?: number;
8
+ sandboxPolicy?: any;
9
+ sandboxEngine?: any;
10
+ ctx?: any;
11
+ }): {
12
+ /** @param {{code: string}} params @param {{signal?: AbortSignal}} [execution] */
13
+ execute({ code }: {
14
+ code: string;
15
+ }, { signal }?: {
16
+ signal?: AbortSignal;
17
+ }): Promise<any>;
18
+ close(): Promise<void>;
19
+ };
@@ -35,9 +35,9 @@ export function createStructuredOutputTool(outputSchema: any, onStructuredOutput
35
35
  };
36
36
  /**
37
37
  * @param {any} allowedTools
38
- * @param {{disallowedTools?: any[], skillNames?: any[], skills?: any[], skillsRoot?: any, dataDir?: any, cwd?: any, onEvent?: (event: any) => void, toolLimits?: any, persistArtifact?: any, onTruncate?: any, toolPayloadMaxBytes?: number, imageInlineMaxBytes?: any, toolPolicy?: any, sandboxPolicy?: any, sandboxEngine?: any, approvalManager?: any, approvalModel?: any, ctx?: any}} [options]
38
+ * @param {{disallowedTools?: any[], skillNames?: any[], skills?: any[], skillsRoot?: any, dataDir?: any, cwd?: any, onEvent?: (event: any) => void, toolLimits?: any, persistArtifact?: any, onTruncate?: any, toolPayloadMaxBytes?: number, imageInlineMaxBytes?: any, toolPolicy?: any, sandboxPolicy?: any, sandboxEngine?: any, approvalManager?: any, approvalModel?: any, nodeReplController?: any, ctx?: any}} [options]
39
39
  */
40
- export function getPiBuiltinTools(allowedTools: any, { disallowedTools, skillNames, skills, skillsRoot, dataDir, cwd, onEvent, toolLimits, persistArtifact, onTruncate, toolPayloadMaxBytes, imageInlineMaxBytes, toolPolicy, sandboxPolicy, sandboxEngine, approvalManager, approvalModel, ctx, }?: {
40
+ export function getPiBuiltinTools(allowedTools: any, { disallowedTools, skillNames, skills, skillsRoot, dataDir, cwd, onEvent, toolLimits, persistArtifact, onTruncate, toolPayloadMaxBytes, imageInlineMaxBytes, toolPolicy, sandboxPolicy, sandboxEngine, approvalManager, approvalModel, nodeReplController, ctx, }?: {
41
41
  disallowedTools?: any[];
42
42
  skillNames?: any[];
43
43
  skills?: any[];
@@ -55,6 +55,7 @@ export function getPiBuiltinTools(allowedTools: any, { disallowedTools, skillNam
55
55
  sandboxEngine?: any;
56
56
  approvalManager?: any;
57
57
  approvalModel?: any;
58
+ nodeReplController?: any;
58
59
  ctx?: any;
59
60
  }): any[];
60
61
  export function resolveMcpStdioCwd(cfg?: {}, cwd?: any): any;
@@ -3,6 +3,15 @@
3
3
  * @returns {boolean}
4
4
  */
5
5
  export function isProviderAuthFailureText(text?: string): boolean;
6
+ /**
7
+ * Identify request-input/context-window overflows without conflating provider
8
+ * throttling or output-token ceilings. Context overflows are route-local: a
9
+ * fallback model may have a larger usable window, while rate/quota/max-turn
10
+ * failures retain the terminal `usage_limit` classification.
11
+ * @param {string} text
12
+ * @returns {boolean}
13
+ */
14
+ export function isContextLimitFailureText(text?: string): boolean;
6
15
  /**
7
16
  * @param {Object} [options]
8
17
  * @param {string} [options.errorText]
@@ -64,7 +73,7 @@ export function createStderrTail({ limit }?: {
64
73
  * @property {string|null} requestId
65
74
  */
66
75
  /**
67
- * @typedef {"spawn" | "timeout" | "stall" | "usage_limit" | "invalid_result"
76
+ * @typedef {"spawn" | "timeout" | "stall" | "context_limit" | "usage_limit" | "invalid_result"
68
77
  * | "invalid_delegation" | "tool_failure" | "provider_unavailable"
69
78
  * | "provider_unavailable_exhausted" | "provider_auth"
70
79
  * | "skipped_capability_mismatch" | "cancelled" | "cancelled_user"
@@ -114,4 +123,4 @@ export type RetryableProviderFailureInfo = {
114
123
  * Hosts (e.g. worklab's coordinator) validate against `FAILURE_KINDS` and may
115
124
  * define additional kinds — accepting them at the type level is deliberate.
116
125
  */
117
- export type FailureKind = "spawn" | "timeout" | "stall" | "usage_limit" | "invalid_result" | "invalid_delegation" | "tool_failure" | "provider_unavailable" | "provider_unavailable_exhausted" | "provider_auth" | "skipped_capability_mismatch" | "cancelled" | "cancelled_user" | "cancelled_shutdown" | "cancelled_signal" | "abandoned" | "session_not_found" | "session_busy" | (string & {});
126
+ export type FailureKind = "spawn" | "timeout" | "stall" | "context_limit" | "usage_limit" | "invalid_result" | "invalid_delegation" | "tool_failure" | "provider_unavailable" | "provider_unavailable_exhausted" | "provider_auth" | "skipped_capability_mismatch" | "cancelled" | "cancelled_user" | "cancelled_shutdown" | "cancelled_signal" | "abandoned" | "session_not_found" | "session_busy" | (string & {});
@@ -4,7 +4,7 @@
4
4
  * can contain provider credentials or echoed request secrets.
5
5
  */
6
6
  export function safeOpenCodeErrorMessage(error: any, fallback?: string): any;
7
- export function mapErrorFailureKind(error: any): "usage_limit" | "provider_unavailable" | "provider_auth" | "cancelled";
7
+ export function mapErrorFailureKind(error: any): "context_limit" | "usage_limit" | "provider_unavailable" | "provider_auth" | "cancelled";
8
8
  export function mapSpawnFailureKind(err: any): "spawn" | "provider_unavailable";
9
9
  export namespace opencodeAppRuntimeBridge {
10
10
  export let id: string;
@@ -1,17 +1,31 @@
1
- export function estimateCurrentContextTokens(session: any, fixedOverheadTokens?: number): Promise<{
1
+ export function estimateCurrentContextTokens(session: any, fixedOverheadTokens?: number, usageIncrementTokens?: number): Promise<{
2
2
  tokens: number;
3
3
  source: string;
4
4
  }>;
5
- export function tryCompact(harness: any, { trigger, onEvent, runtimeWarnings, onCompactionRecorded, runId, model }: {
5
+ /**
6
+ * Pi derives its summary output limit from reserveTokens. A normal compaction
7
+ * uses floor(0.8 * reserve); a split-turn compaction may generate both that
8
+ * history summary and floor(0.5 * reserve) for the turn prefix. Return the
9
+ * largest reserve whose derived generation budget does not exceed the public
10
+ * summaryMaxTokens setting.
11
+ * @param {number} summaryMaxTokens
12
+ * @param {boolean} isSplitTurn
13
+ */
14
+ export function piSummaryReserveTokens(summaryMaxTokens: number, isSplitTurn: boolean): number;
15
+ export function tryCompact(harness: any, { trigger, onEvent, runtimeWarnings, onCompactionRecorded, runId, model, session, policy, }: {
6
16
  trigger: any;
7
17
  onEvent: any;
8
18
  runtimeWarnings: any;
9
19
  onCompactionRecorded: any;
10
20
  runId: any;
11
21
  model: any;
22
+ session: any;
23
+ policy: any;
12
24
  }): Promise<{
13
25
  applied: boolean;
14
26
  tokensBefore: number;
27
+ tokensAfter: any;
28
+ reduced: boolean;
15
29
  nothingToCompact: boolean;
16
30
  }>;
17
31
  /**
@@ -49,10 +63,10 @@ export function piCompactionSettings(policy: {
49
63
  * Resolve the compaction policy against the LIVE model's context window
50
64
  * (auto-recognized from the model actually serving the request, lowered by any
51
65
  * ceiling learned from a prior overflow). A positive `contextWindowOverride`
52
- * (from the typed `compaction` policy object) replaces the auto-recognized
53
- * window it is not a legacy `settings` key, so it is applied here directly
54
- * rather than through the settings shim. Drives the proactive trigger +
55
- * reactive recovery.
66
+ * (from the typed `compaction` policy object) replaces provider metadata, but
67
+ * process-local overflow evidence can still lower it. It is not a legacy
68
+ * `settings` key, so it is applied here directly rather than through the
69
+ * settings shim. Drives the proactive trigger + reactive recovery.
56
70
  * @param {{harness: any, runtime: any, resolved: any, settings: any, contextWindowOverride?: number}} params
57
71
  */
58
72
  export function resolveLiveCompactionPolicy({ harness, runtime, resolved, settings, contextWindowOverride }: {
@@ -11,8 +11,9 @@ export function usageFromMessages(messages?: Array<any>): {
11
11
  cost: number;
12
12
  };
13
13
  /**
14
- * Classify a pi error message into a runtime failure kind. Context-limit /
15
- * max-turns terminations map to usage_limit; credential/config auth failures
14
+ * Classify a pi error message into a runtime failure kind. Context-window
15
+ * overflows map to context_limit so the router can try the configured fallback;
16
+ * max-turns terminations remain usage_limit. Credential/config auth failures
16
17
  * map to provider_auth; everything else to provider_unavailable. Null message → null.
17
18
  * @param {string|null} message
18
19
  * @param {Record<string, unknown>} diagnostics
@@ -1,10 +1,3 @@
1
- /**
2
- * The tool-start progress line surfaced as a thinking block, or null for a
3
- * blank/invalid tool name.
4
- * @param {unknown} toolName
5
- * @returns {string|null}
6
- */
7
- export function toolStartProgressText(toolName: unknown): string | null;
8
1
  /**
9
2
  * The slice of run state the stream subscriber reads and mutates. A structural
10
3
  * subset of the orchestrator's runState.
@@ -3,15 +3,17 @@
3
3
  * tools, the MCP tool bridge, and the StructuredOutput tool (whose callback
4
4
  * writes runState.structuredResult). Surfaces MCP init/list failures to both the
5
5
  * event stream and runtimeWarnings. Returns the assembled tools plus the MCP
6
- * clients (closed by the caller's finally) and the structured tool.
6
+ * clients (closed by the caller's finally), run-owned tool cleanup, and the
7
+ * structured tool.
7
8
  * @param {any} runState
8
9
  * @param {any} params
9
- * @returns {Promise<{tools: any[], structuredTool: any, mcpClients: any[]}>}
10
+ * @returns {Promise<{tools: any[], structuredTool: any, mcpClients: any[], closeRunTools: () => Promise<void>}>}
10
11
  */
11
12
  export function buildTurnTools(runState: any, { options, capabilities, toolLimits, approvalManager, runtime, resolved, onEvent, runtimeWarnings, }: any): Promise<{
12
13
  tools: any[];
13
14
  structuredTool: any;
14
15
  mcpClients: any[];
16
+ closeRunTools: () => Promise<void>;
15
17
  }>;
16
18
  /**
17
19
  * Map an effort level to the harness thinkingLevel, respecting model reasoning
@@ -71,14 +71,15 @@
71
71
  * @typedef {Object} RuntimeCompactionPolicy
72
72
  * Typed per-run context-compaction policy (the supported replacement for the
73
73
  * `agent_compaction_*` keys of the deprecated `settings` bag). Every field is
74
- * optional; an omitted field falls back to the kernel default.
74
+ * optional; omitted scalar budgets resolve adaptively against the effective
75
+ * model context window.
75
76
  * @property {boolean} [enabled] Whether auto-compaction runs at all.
76
77
  * @property {number} [triggerRatio] Fraction of the context window that arms the proactive trigger.
77
78
  * @property {number} [keepRecentTokens] Recent-token budget preserved across a compaction.
78
- * @property {number} [summaryMaxTokens] Max tokens of the generated summary.
79
- * @property {number} [minSavingsTokens] Minimum token savings required to keep a compaction.
79
+ * @property {number} [summaryMaxTokens] Combined output-token budget for generated compaction summaries.
80
+ * @property {number} [minSavingsTokens] Minimum token savings required for proactive compaction; reactive recovery accepts any positive reduction.
80
81
  * @property {boolean} [fixedOverheadEnabled] Whether the system-prompt + tool-schema overhead correction is folded into the trigger.
81
- * @property {number} [contextWindowOverride] Forces the compaction window instead of the live-model-recognized one (applied at resolveLiveCompactionPolicy; has no legacy settings equivalent).
82
+ * @property {number} [contextWindowOverride] Persistent correction for provider context-window metadata; learned overflow evidence may lower it process-locally (applied at resolveLiveCompactionPolicy; has no legacy settings equivalent).
82
83
  */
83
84
  /**
84
85
  * @typedef {Object} RuntimePromptOverrides
@@ -406,7 +407,8 @@ export type RuntimeToolLimits = {
406
407
  /**
407
408
  * Typed per-run context-compaction policy (the supported replacement for the
408
409
  * `agent_compaction_*` keys of the deprecated `settings` bag). Every field is
409
- * optional; an omitted field falls back to the kernel default.
410
+ * optional; omitted scalar budgets resolve adaptively against the effective
411
+ * model context window.
410
412
  */
411
413
  export type RuntimeCompactionPolicy = {
412
414
  /**
@@ -422,11 +424,11 @@ export type RuntimeCompactionPolicy = {
422
424
  */
423
425
  keepRecentTokens?: number;
424
426
  /**
425
- * Max tokens of the generated summary.
427
+ * Combined output-token budget for generated compaction summaries.
426
428
  */
427
429
  summaryMaxTokens?: number;
428
430
  /**
429
- * Minimum token savings required to keep a compaction.
431
+ * Minimum token savings required for proactive compaction; reactive recovery accepts any positive reduction.
430
432
  */
431
433
  minSavingsTokens?: number;
432
434
  /**
@@ -434,7 +436,7 @@ export type RuntimeCompactionPolicy = {
434
436
  */
435
437
  fixedOverheadEnabled?: boolean;
436
438
  /**
437
- * Forces the compaction window instead of the live-model-recognized one (applied at resolveLiveCompactionPolicy; has no legacy settings equivalent).
439
+ * Persistent correction for provider context-window metadata; learned overflow evidence may lower it process-locally (applied at resolveLiveCompactionPolicy; has no legacy settings equivalent).
438
440
  */
439
441
  contextWindowOverride?: number;
440
442
  };