@code-yeongyu/senpi 2026.8.30-2 → 2026.8.30-3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -1
- package/dist/core/agent-session.d.ts +2 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +77 -10
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts +1 -0
- package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/context-pipeline.js +6 -2
- package/dist/core/extensions/builtin/compaction/context-pipeline.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +3 -2
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/idle-retry.d.ts +1 -1
- package/dist/core/extensions/builtin/compaction/idle-retry.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/idle-retry.js +1 -1
- package/dist/core/extensions/builtin/compaction/idle-retry.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.js +34 -10
- package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/model-usability-budget.d.ts +30 -0
- package/dist/core/extensions/builtin/compaction/model-usability-budget.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/model-usability-budget.js +74 -0
- package/dist/core/extensions/builtin/compaction/model-usability-budget.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/orchestration.d.ts +3 -13
- package/dist/core/extensions/builtin/compaction/orchestration.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/orchestration.js +50 -83
- package/dist/core/extensions/builtin/compaction/orchestration.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/policy.d.ts +8 -1
- package/dist/core/extensions/builtin/compaction/policy.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/policy.js +12 -3
- package/dist/core/extensions/builtin/compaction/policy.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/token-budget-reminder.d.ts +8 -2
- package/dist/core/extensions/builtin/compaction/token-budget-reminder.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/token-budget-reminder.js +12 -5
- package/dist/core/extensions/builtin/compaction/token-budget-reminder.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/tool-admission.d.ts +12 -21
- package/dist/core/extensions/builtin/compaction/tool-admission.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/tool-admission.js +27 -63
- package/dist/core/extensions/builtin/compaction/tool-admission.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/runtime-session.d.ts +11 -0
- package/dist/core/extensions/builtin/terminal/runtime-session.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/runtime-session.js +18 -2
- package/dist/core/extensions/builtin/terminal/runtime-session.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +5 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/index.d.ts +1 -1
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js +1 -1
- package/dist/modes/index.js.map +1 -1
- package/dist/modes/interactive/interactive-host-runtime.d.ts +13 -2
- package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-host-runtime.js +317 -72
- package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
- package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
- package/dist/modes/rpc/connection-handler.js +17 -4
- package/dist/modes/rpc/connection-handler.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +10 -0
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +32 -10
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/docs/rpc.md +4 -0
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +12 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-pty/dist/screen-operations.d.ts +42 -0
- package/node_modules/@earendil-works/pi-pty/dist/screen-operations.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-pty/dist/screen-operations.js +34 -0
- package/node_modules/@earendil-works/pi-pty/dist/screen-operations.js.map +1 -0
- package/node_modules/@earendil-works/pi-pty/dist/screen-text.d.ts +8 -0
- package/node_modules/@earendil-works/pi-pty/dist/screen-text.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-pty/dist/screen-text.js +51 -0
- package/node_modules/@earendil-works/pi-pty/dist/screen-text.js.map +1 -0
- package/node_modules/@earendil-works/pi-pty/dist/screen.d.ts +45 -0
- package/node_modules/@earendil-works/pi-pty/dist/screen.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-pty/dist/screen.js +178 -55
- package/node_modules/@earendil-works/pi-pty/dist/screen.js.map +1 -1
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { join } from "node:path";
|
|
1
|
+
import { copyContextProvenance } from "@earendil-works/pi-ai";
|
|
3
2
|
import { estimateTokens } from "../../../compaction/index.js";
|
|
4
3
|
import { shouldWarmAtIdle } from "./idle.js";
|
|
5
4
|
import * as policy from "./policy.js";
|
|
6
5
|
import { isWarmResultStale, isWithinGraceBand, resolveSpeculationLeadTokens } from "./speculation-lead.js";
|
|
7
|
-
import { admitToolResult,
|
|
8
|
-
export function estimateTextTokens(text) {
|
|
9
|
-
return estimateTokens({ role: "user", content: text, timestamp: 0 });
|
|
10
|
-
}
|
|
6
|
+
import { admitToolResult, admitToolResultWithinBudget, resolveToolResultAdmissionCapTokens } from "./tool-admission.js";
|
|
11
7
|
export function resolveCompactionGeometry(input) {
|
|
12
8
|
const thresholdTokens = input.contextWindow * policy.computeEffectiveThreshold(input.contextWindow, input.lastYield);
|
|
13
9
|
return {
|
|
14
|
-
reserveTokens: policy.resolveEffectiveReserveTokens(input.contextWindow, input.settings
|
|
10
|
+
reserveTokens: policy.resolveEffectiveReserveTokens(input.contextWindow, input.settings),
|
|
15
11
|
thresholdTokens,
|
|
16
12
|
leadTokens: resolveSpeculationLeadTokens(thresholdTokens, input.settings.speculativeLeadTokens),
|
|
17
13
|
};
|
|
@@ -21,6 +17,25 @@ export function shouldDeferGraceBand(input) {
|
|
|
21
17
|
input.graceBandEnabled !== false &&
|
|
22
18
|
isWithinGraceBand(input.tokens, input.thresholdTokens, input.leadTokens, input.contextWindow, input.reserveTokens));
|
|
23
19
|
}
|
|
20
|
+
export function injectTokenBudgetReminder(messages, reminder) {
|
|
21
|
+
if (!reminder)
|
|
22
|
+
return messages;
|
|
23
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
24
|
+
const message = messages[index];
|
|
25
|
+
if (message?.role !== "user")
|
|
26
|
+
continue;
|
|
27
|
+
const content = typeof message.content === "string" ? [{ type: "text", text: message.content }] : message.content;
|
|
28
|
+
const firstPart = content[0];
|
|
29
|
+
if (firstPart?.type === "text" && firstPart.text === reminder)
|
|
30
|
+
return messages;
|
|
31
|
+
const reminded = copyContextProvenance(message, {
|
|
32
|
+
...message,
|
|
33
|
+
content: [{ type: "text", text: reminder }, ...content],
|
|
34
|
+
});
|
|
35
|
+
return messages.map((candidate, candidateIndex) => (candidateIndex === index ? reminded : candidate));
|
|
36
|
+
}
|
|
37
|
+
return messages;
|
|
38
|
+
}
|
|
24
39
|
export function resolveBeforeAgentStartMessage(input) {
|
|
25
40
|
if (!input.reminder || input.reminderEnabled === false)
|
|
26
41
|
return input.message;
|
|
@@ -41,96 +56,48 @@ export function resolveIdleWarmAction(decision, job) {
|
|
|
41
56
|
const currentTokens = decision.usage?.tokens ?? 0;
|
|
42
57
|
return isWarmResultStale(job.armedAtTokens, currentTokens, decision.settings.keepRecentTokens) ? "replace" : "none";
|
|
43
58
|
}
|
|
44
|
-
export function admitContextToolResult(text, contextWindow
|
|
45
|
-
const result = admitToolResult({ text, contextWindow
|
|
46
|
-
return { text: result.text, admitted: result.
|
|
47
|
-
}
|
|
48
|
-
export function buildToolResultOmissionLine(omitted) {
|
|
49
|
-
return `[tool-result admission: ${omitted.length} later text part(s) omitted (~${omitted.reduce((sum, item) => sum + item.tokens, 0)} tokens); full outputs at: ${omitted.map((item) => item.path).join("; ")} - read with the read tool]`;
|
|
50
|
-
}
|
|
51
|
-
export function estimateToolResultOmissionTokens(omitted) {
|
|
52
|
-
return estimateTextTokens(buildToolResultOmissionLine(omitted));
|
|
59
|
+
export function admitContextToolResult(text, contextWindow) {
|
|
60
|
+
const result = admitToolResult({ text, contextWindow });
|
|
61
|
+
return { text: result.text, admitted: result.projected };
|
|
53
62
|
}
|
|
54
|
-
export function
|
|
55
|
-
return Math.max(capTokens, estimateTextTokens(`${"\n".repeat(Math.max(1, textPartCount))}${omission}`));
|
|
56
|
-
}
|
|
57
|
-
export function admitContextToolResults(messages, contextWindow, enabled, capOverride, spillDir = join(tmpdir(), "senpi-tool-spill")) {
|
|
63
|
+
export function admitContextToolResults(messages, contextWindow, enabled) {
|
|
58
64
|
if (!enabled)
|
|
59
65
|
return messages;
|
|
60
66
|
return messages.map((message) => {
|
|
61
67
|
if (message.role !== "toolResult")
|
|
62
68
|
return message;
|
|
63
69
|
if (typeof message.content === "string") {
|
|
64
|
-
const admitted = admitContextToolResult(message.content, contextWindow
|
|
70
|
+
const admitted = admitContextToolResult(message.content, contextWindow);
|
|
65
71
|
return admitted.admitted ? { ...message, content: [{ type: "text", text: admitted.text }] } : message;
|
|
66
72
|
}
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
.join("\n");
|
|
72
|
-
if (estimateTextTokens(joinedText) <= resultCap)
|
|
73
|
+
const textTokens = message.content.map((part) => part.type === "text" ? estimateTokens({ role: "user", content: part.text, timestamp: 0 }) : 0);
|
|
74
|
+
const totalTextTokens = textTokens.reduce((total, tokens) => total + tokens, 0);
|
|
75
|
+
const capTokens = resolveToolResultAdmissionCapTokens(contextWindow);
|
|
76
|
+
if (totalTextTokens <= capTokens)
|
|
73
77
|
return message;
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
let
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
const preservedTokens = textTokens.reduce((total, tokens) => total + (tokens <= capTokens ? tokens : 0), 0);
|
|
79
|
+
const preserveUnderCap = preservedTokens <= capTokens;
|
|
80
|
+
let remainingBudget = preserveUnderCap ? capTokens - preservedTokens : capTokens;
|
|
81
|
+
let remainingOversizedTokens = preserveUnderCap
|
|
82
|
+
? textTokens.reduce((total, tokens) => total + (tokens > capTokens ? tokens : 0), 0)
|
|
83
|
+
: totalTextTokens;
|
|
84
|
+
let projected = false;
|
|
85
|
+
const content = message.content.map((part, index) => {
|
|
86
|
+
if (part.type !== "text")
|
|
87
|
+
return part;
|
|
88
|
+
const partTokens = textTokens[index] ?? 0;
|
|
89
|
+
if (preserveUnderCap && partTokens <= capTokens)
|
|
81
90
|
return part;
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (
|
|
87
|
-
remainingTokens -= partTokens;
|
|
91
|
+
const budget = Math.floor((remainingBudget * partTokens) / Math.max(1, remainingOversizedTokens));
|
|
92
|
+
const admitted = admitToolResultWithinBudget(part.text, budget);
|
|
93
|
+
remainingOversizedTokens -= partTokens;
|
|
94
|
+
remainingBudget -= estimateTokens({ role: "user", content: admitted.text, timestamp: 0 });
|
|
95
|
+
if (!admitted.projected)
|
|
88
96
|
return part;
|
|
89
|
-
|
|
90
|
-
const admitted = admitContextToolResult(part.text, contextWindow, spillDir, Math.max(0, remainingTokens - omissionReserve));
|
|
91
|
-
changed = true;
|
|
92
|
-
remainingTokens = Math.max(0, remainingTokens - estimateTextTokens(admitted.text));
|
|
93
|
-
if (admitted.spillPath)
|
|
94
|
-
omitted.push({ tokens: partTokens, path: admitted.spillPath });
|
|
97
|
+
projected = true;
|
|
95
98
|
return { ...part, text: admitted.text };
|
|
96
99
|
});
|
|
97
|
-
|
|
98
|
-
if (omitted.length > 0) {
|
|
99
|
-
const omission = buildToolResultOmissionLine(omitted);
|
|
100
|
-
omissionCost = resolveMultipartRetainedBound(resultCap, omission, textParts.length);
|
|
101
|
-
const lastText = content.findLastIndex((part) => part.type === "text" && part.text);
|
|
102
|
-
const target = lastText >= 0 ? lastText : content.findIndex((part) => part.type === "text");
|
|
103
|
-
const targetPart = target >= 0 ? content[target] : undefined;
|
|
104
|
-
if (target >= 0 && targetPart?.type === "text") {
|
|
105
|
-
content[target] = { ...targetPart, text: `${targetPart.text ?? ""}\n${omission}` };
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
// Contract: total retained text tokens per tool result <= cap, and every omitted byte is reachable via a spill path named within the result.
|
|
109
|
-
const aggregateTokens = () => estimateTextTokens(content
|
|
110
|
-
.filter((part) => part.type === "text")
|
|
111
|
-
.map((part) => part.text ?? "")
|
|
112
|
-
.join("\n"));
|
|
113
|
-
const retainedLimit = omissionCost || resultCap;
|
|
114
|
-
while (aggregateTokens() > retainedLimit) {
|
|
115
|
-
const target = content.findLastIndex((part) => {
|
|
116
|
-
if (part.type !== "text" || !part.text)
|
|
117
|
-
return false;
|
|
118
|
-
const omissionIndex = part.text.indexOf("[tool-result admission:");
|
|
119
|
-
return omissionIndex < 0 || part.text.slice(0, omissionIndex).trim().length > 0;
|
|
120
|
-
});
|
|
121
|
-
if (target < 0)
|
|
122
|
-
break;
|
|
123
|
-
const part = content[target];
|
|
124
|
-
if (part.type !== "text")
|
|
125
|
-
break;
|
|
126
|
-
const text = part.text ?? "";
|
|
127
|
-
const omissionIndex = text.indexOf("[tool-result admission:");
|
|
128
|
-
const prefix = omissionIndex >= 0 ? text.slice(0, omissionIndex) : text;
|
|
129
|
-
const suffix = omissionIndex >= 0 ? text.slice(omissionIndex) : "";
|
|
130
|
-
const nextText = `${prefix.slice(0, Math.floor(prefix.length / 2))}${suffix}`;
|
|
131
|
-
content[target] = { ...part, text: nextText === text ? "" : nextText };
|
|
132
|
-
}
|
|
133
|
-
return changed ? { ...message, content } : message;
|
|
100
|
+
return projected ? { ...message, content } : message;
|
|
134
101
|
});
|
|
135
102
|
}
|
|
136
103
|
//# sourceMappingURL=orchestration.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestration.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/orchestration.ts"],"names":[],"mappings":"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","sourcesContent":["import { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport { type CompactionPreparation, estimateTokens } from \"../../../compaction/index.ts\";\nimport type { BeforeAgentStartEventResult } from \"../../types.ts\";\nimport { type IdleCompactionDecision, shouldWarmAtIdle } from \"./idle.ts\";\nimport * as policy from \"./policy.ts\";\nimport { isWarmResultStale, isWithinGraceBand, resolveSpeculationLeadTokens } from \"./speculation-lead.ts\";\nimport {\n\tadmitToolResult,\n\testimateAdmissionMarkerTokens,\n\tresolveToolResultAdmissionCapTokens,\n} from \"./tool-admission.ts\";\n\nexport interface CompactionGeometry {\n\treserveTokens: number;\n\tthresholdTokens: number;\n\tleadTokens: number;\n}\n\nexport function estimateTextTokens(text: string): number {\n\treturn estimateTokens({ role: \"user\", content: text, timestamp: 0 });\n}\n\nexport function resolveCompactionGeometry(input: {\n\tcontextWindow: number;\n\tsettings: CompactionPreparation[\"settings\"];\n\tlastYield?: { savedTokens: number; tokensBefore: number };\n}): CompactionGeometry {\n\tconst thresholdTokens = input.contextWindow * policy.computeEffectiveThreshold(input.contextWindow, input.lastYield);\n\treturn {\n\t\treserveTokens: policy.resolveEffectiveReserveTokens(\n\t\t\tinput.contextWindow,\n\t\t\tinput.settings.reserveTokens,\n\t\t\tinput.settings.reserveScalingEnabled !== false,\n\t\t),\n\t\tthresholdTokens,\n\t\tleadTokens: resolveSpeculationLeadTokens(thresholdTokens, input.settings.speculativeLeadTokens),\n\t};\n}\n\nexport function shouldDeferGraceBand(input: {\n\ttokens: number;\n\tthresholdTokens: number;\n\tleadTokens: number;\n\tcontextWindow: number;\n\treserveTokens: number;\n\tcompactionInFlight: boolean;\n\tgraceBandEnabled?: boolean;\n}): boolean {\n\treturn (\n\t\tinput.compactionInFlight &&\n\t\tinput.graceBandEnabled !== false &&\n\t\tisWithinGraceBand(input.tokens, input.thresholdTokens, input.leadTokens, input.contextWindow, input.reserveTokens)\n\t);\n}\n\nexport function resolveBeforeAgentStartMessage(input: {\n\tmessage?: BeforeAgentStartEventResult[\"message\"];\n\treminder?: string;\n\treminderEnabled?: boolean;\n}): BeforeAgentStartEventResult[\"message\"] | undefined {\n\tif (!input.reminder || input.reminderEnabled === false) return input.message;\n\tif (!input.message) return undefined;\n\treturn { ...input.message, content: `${input.message.content}\\n\\n${input.reminder}` };\n}\n\nexport function resolveReminderSystemPrompt(input: {\n\tsystemPrompt: string;\n\treminder?: string;\n\treminderEnabled?: boolean;\n}): string | undefined {\n\tif (!input.reminder || input.reminderEnabled === false) return undefined;\n\treturn `${input.systemPrompt}\\n\\n${input.reminder}`;\n}\n\nexport function resolveIdleWarmAction(\n\tdecision: IdleCompactionDecision,\n\tjob: { armedAtTokens: number } | undefined,\n): \"none\" | \"start\" | \"replace\" {\n\tif (!shouldWarmAtIdle(decision)) return \"none\";\n\tif (!job) return \"start\";\n\tconst currentTokens = decision.usage?.tokens ?? 0;\n\treturn isWarmResultStale(job.armedAtTokens, currentTokens, decision.settings.keepRecentTokens) ? \"replace\" : \"none\";\n}\n\nexport function admitContextToolResult(\n\ttext: string,\n\tcontextWindow: number,\n\tspillDir: string,\n\tcapTokens?: number,\n): { text: string; admitted: boolean; spillPath?: string } {\n\tconst result = admitToolResult({ text, contextWindow, spillDir, capTokens });\n\treturn { text: result.text, admitted: result.spilled, spillPath: result.spillPath };\n}\n\nexport function buildToolResultOmissionLine(omitted: ReadonlyArray<{ tokens: number; path: string }>): string {\n\treturn `[tool-result admission: ${omitted.length} later text part(s) omitted (~${omitted.reduce((sum, item) => sum + item.tokens, 0)} tokens); full outputs at: ${omitted.map((item) => item.path).join(\"; \")} - read with the read tool]`;\n}\n\nexport function estimateToolResultOmissionTokens(omitted: ReadonlyArray<{ tokens: number; path: string }>): number {\n\treturn estimateTextTokens(buildToolResultOmissionLine(omitted));\n}\n\nexport function resolveMultipartRetainedBound(capTokens: number, omission: string, textPartCount: number): number {\n\treturn Math.max(capTokens, estimateTextTokens(`${\"\\n\".repeat(Math.max(1, textPartCount))}${omission}`));\n}\n\nexport function admitContextToolResults(\n\tmessages: AgentMessage[],\n\tcontextWindow: number,\n\tenabled: boolean,\n\tcapOverride?: number,\n\tspillDir = join(tmpdir(), \"senpi-tool-spill\"),\n): AgentMessage[] {\n\tif (!enabled) return messages;\n\treturn messages.map((message) => {\n\t\tif (message.role !== \"toolResult\") return message;\n\t\tif (typeof message.content === \"string\") {\n\t\t\tconst admitted = admitContextToolResult(message.content, contextWindow, spillDir);\n\t\t\treturn admitted.admitted ? { ...message, content: [{ type: \"text\" as const, text: admitted.text }] } : message;\n\t\t}\n\t\tconst resultCap = capOverride ?? resolveToolResultAdmissionCapTokens(contextWindow);\n\t\tconst joinedText = message.content\n\t\t\t.filter((part) => part.type === \"text\")\n\t\t\t.map((part) => part.text ?? \"\")\n\t\t\t.join(\"\\n\");\n\t\tif (estimateTextTokens(joinedText) <= resultCap) return message;\n\t\tlet changed = false;\n\t\tconst textParts = message.content.filter((part) => part.type === \"text\");\n\t\tconst hasSharedOmission = textParts.some((part) => part.text) && textParts.length >= 2;\n\t\tlet remainingTokens = resultCap;\n\t\tconst omitted: Array<{ tokens: number; path: string }> = [];\n\t\tconst content = message.content.map((part) => {\n\t\t\tif (part.type !== \"text\" || !part.text) return part;\n\t\t\tconst partTokens = estimateTextTokens(part.text);\n\t\t\tconst omissionReserve = hasSharedOmission\n\t\t\t\t? 256 + estimateAdmissionMarkerTokens(partTokens, join(spillDir, \"tool-result-0000000000-000000.txt\"))\n\t\t\t\t: 0;\n\t\t\tif (partTokens <= remainingTokens - omissionReserve) {\n\t\t\t\tremainingTokens -= partTokens;\n\t\t\t\treturn part;\n\t\t\t}\n\t\t\tconst admitted = admitContextToolResult(\n\t\t\t\tpart.text,\n\t\t\t\tcontextWindow,\n\t\t\t\tspillDir,\n\t\t\t\tMath.max(0, remainingTokens - omissionReserve),\n\t\t\t);\n\t\t\tchanged = true;\n\t\t\tremainingTokens = Math.max(0, remainingTokens - estimateTextTokens(admitted.text));\n\t\t\tif (admitted.spillPath) omitted.push({ tokens: partTokens, path: admitted.spillPath });\n\t\t\treturn { ...part, text: admitted.text };\n\t\t});\n\t\tlet omissionCost = 0;\n\t\tif (omitted.length > 0) {\n\t\t\tconst omission = buildToolResultOmissionLine(omitted);\n\t\t\tomissionCost = resolveMultipartRetainedBound(resultCap, omission, textParts.length);\n\t\t\tconst lastText = content.findLastIndex((part) => part.type === \"text\" && part.text);\n\t\t\tconst target = lastText >= 0 ? lastText : content.findIndex((part) => part.type === \"text\");\n\t\t\tconst targetPart = target >= 0 ? content[target] : undefined;\n\t\t\tif (target >= 0 && targetPart?.type === \"text\") {\n\t\t\t\tcontent[target] = { ...targetPart, text: `${targetPart.text ?? \"\"}\\n${omission}` };\n\t\t\t}\n\t\t}\n\t\t// Contract: total retained text tokens per tool result <= cap, and every omitted byte is reachable via a spill path named within the result.\n\t\tconst aggregateTokens = () =>\n\t\t\testimateTextTokens(\n\t\t\t\tcontent\n\t\t\t\t\t.filter((part) => part.type === \"text\")\n\t\t\t\t\t.map((part) => part.text ?? \"\")\n\t\t\t\t\t.join(\"\\n\"),\n\t\t\t);\n\t\tconst retainedLimit = omissionCost || resultCap;\n\t\twhile (aggregateTokens() > retainedLimit) {\n\t\t\tconst target = content.findLastIndex((part) => {\n\t\t\t\tif (part.type !== \"text\" || !part.text) return false;\n\t\t\t\tconst omissionIndex = part.text.indexOf(\"[tool-result admission:\");\n\t\t\t\treturn omissionIndex < 0 || part.text.slice(0, omissionIndex).trim().length > 0;\n\t\t\t});\n\t\t\tif (target < 0) break;\n\t\t\tconst part = content[target];\n\t\t\tif (part.type !== \"text\") break;\n\t\t\tconst text = part.text ?? \"\";\n\t\t\tconst omissionIndex = text.indexOf(\"[tool-result admission:\");\n\t\t\tconst prefix = omissionIndex >= 0 ? text.slice(0, omissionIndex) : text;\n\t\t\tconst suffix = omissionIndex >= 0 ? text.slice(omissionIndex) : \"\";\n\t\t\tconst nextText = `${prefix.slice(0, Math.floor(prefix.length / 2))}${suffix}`;\n\t\t\tcontent[target] = { ...part, text: nextText === text ? \"\" : nextText };\n\t\t}\n\t\treturn changed ? { ...message, content } : message;\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"orchestration.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/orchestration.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport { copyContextProvenance } from \"@earendil-works/pi-ai\";\nimport { type CompactionPreparation, estimateTokens } from \"../../../compaction/index.ts\";\nimport type { BeforeAgentStartEventResult } from \"../../types.ts\";\nimport { type IdleCompactionDecision, shouldWarmAtIdle } from \"./idle.ts\";\nimport * as policy from \"./policy.ts\";\nimport { isWarmResultStale, isWithinGraceBand, resolveSpeculationLeadTokens } from \"./speculation-lead.ts\";\nimport { admitToolResult, admitToolResultWithinBudget, resolveToolResultAdmissionCapTokens } from \"./tool-admission.ts\";\n\nexport interface CompactionGeometry {\n\treserveTokens: number;\n\tthresholdTokens: number;\n\tleadTokens: number;\n}\n\nexport function resolveCompactionGeometry(input: {\n\tcontextWindow: number;\n\tsettings: CompactionPreparation[\"settings\"];\n\tlastYield?: { savedTokens: number; tokensBefore: number };\n}): CompactionGeometry {\n\tconst thresholdTokens = input.contextWindow * policy.computeEffectiveThreshold(input.contextWindow, input.lastYield);\n\treturn {\n\t\treserveTokens: policy.resolveEffectiveReserveTokens(input.contextWindow, input.settings),\n\t\tthresholdTokens,\n\t\tleadTokens: resolveSpeculationLeadTokens(thresholdTokens, input.settings.speculativeLeadTokens),\n\t};\n}\n\nexport function shouldDeferGraceBand(input: {\n\ttokens: number;\n\tthresholdTokens: number;\n\tleadTokens: number;\n\tcontextWindow: number;\n\treserveTokens: number;\n\tcompactionInFlight: boolean;\n\tgraceBandEnabled?: boolean;\n}): boolean {\n\treturn (\n\t\tinput.compactionInFlight &&\n\t\tinput.graceBandEnabled !== false &&\n\t\tisWithinGraceBand(input.tokens, input.thresholdTokens, input.leadTokens, input.contextWindow, input.reserveTokens)\n\t);\n}\n\nexport function injectTokenBudgetReminder(messages: AgentMessage[], reminder?: string): AgentMessage[] {\n\tif (!reminder) return messages;\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tconst message = messages[index];\n\t\tif (message?.role !== \"user\") continue;\n\t\tconst content =\n\t\t\ttypeof message.content === \"string\" ? [{ type: \"text\" as const, text: message.content }] : message.content;\n\t\tconst firstPart = content[0];\n\t\tif (firstPart?.type === \"text\" && firstPart.text === reminder) return messages;\n\t\tconst reminded = copyContextProvenance(message, {\n\t\t\t...message,\n\t\t\tcontent: [{ type: \"text\" as const, text: reminder }, ...content],\n\t\t});\n\t\treturn messages.map((candidate, candidateIndex) => (candidateIndex === index ? reminded : candidate));\n\t}\n\treturn messages;\n}\n\nexport function resolveBeforeAgentStartMessage(input: {\n\tmessage?: BeforeAgentStartEventResult[\"message\"];\n\treminder?: string;\n\treminderEnabled?: boolean;\n}): BeforeAgentStartEventResult[\"message\"] | undefined {\n\tif (!input.reminder || input.reminderEnabled === false) return input.message;\n\tif (!input.message) return undefined;\n\treturn { ...input.message, content: `${input.message.content}\\n\\n${input.reminder}` };\n}\n\nexport function resolveReminderSystemPrompt(input: {\n\tsystemPrompt: string;\n\treminder?: string;\n\treminderEnabled?: boolean;\n}): string | undefined {\n\tif (!input.reminder || input.reminderEnabled === false) return undefined;\n\treturn `${input.systemPrompt}\\n\\n${input.reminder}`;\n}\n\nexport function resolveIdleWarmAction(\n\tdecision: IdleCompactionDecision,\n\tjob: { armedAtTokens: number } | undefined,\n): \"none\" | \"start\" | \"replace\" {\n\tif (!shouldWarmAtIdle(decision)) return \"none\";\n\tif (!job) return \"start\";\n\tconst currentTokens = decision.usage?.tokens ?? 0;\n\treturn isWarmResultStale(job.armedAtTokens, currentTokens, decision.settings.keepRecentTokens) ? \"replace\" : \"none\";\n}\n\nexport function admitContextToolResult(text: string, contextWindow: number): { text: string; admitted: boolean } {\n\tconst result = admitToolResult({ text, contextWindow });\n\treturn { text: result.text, admitted: result.projected };\n}\n\nexport function admitContextToolResults(\n\tmessages: AgentMessage[],\n\tcontextWindow: number,\n\tenabled: boolean,\n): AgentMessage[] {\n\tif (!enabled) return messages;\n\treturn messages.map((message) => {\n\t\tif (message.role !== \"toolResult\") return message;\n\t\tif (typeof message.content === \"string\") {\n\t\t\tconst admitted = admitContextToolResult(message.content, contextWindow);\n\t\t\treturn admitted.admitted ? { ...message, content: [{ type: \"text\", text: admitted.text }] } : message;\n\t\t}\n\n\t\tconst textTokens = message.content.map((part) =>\n\t\t\tpart.type === \"text\" ? estimateTokens({ role: \"user\", content: part.text, timestamp: 0 }) : 0,\n\t\t);\n\t\tconst totalTextTokens = textTokens.reduce((total, tokens) => total + tokens, 0);\n\t\tconst capTokens = resolveToolResultAdmissionCapTokens(contextWindow);\n\t\tif (totalTextTokens <= capTokens) return message;\n\n\t\tconst preservedTokens = textTokens.reduce((total, tokens) => total + (tokens <= capTokens ? tokens : 0), 0);\n\t\tconst preserveUnderCap = preservedTokens <= capTokens;\n\t\tlet remainingBudget = preserveUnderCap ? capTokens - preservedTokens : capTokens;\n\t\tlet remainingOversizedTokens = preserveUnderCap\n\t\t\t? textTokens.reduce((total, tokens) => total + (tokens > capTokens ? tokens : 0), 0)\n\t\t\t: totalTextTokens;\n\t\tlet projected = false;\n\t\tconst content = message.content.map((part, index) => {\n\t\t\tif (part.type !== \"text\") return part;\n\t\t\tconst partTokens = textTokens[index] ?? 0;\n\t\t\tif (preserveUnderCap && partTokens <= capTokens) return part;\n\t\t\tconst budget = Math.floor((remainingBudget * partTokens) / Math.max(1, remainingOversizedTokens));\n\t\t\tconst admitted = admitToolResultWithinBudget(part.text, budget);\n\t\t\tremainingOversizedTokens -= partTokens;\n\t\t\tremainingBudget -= estimateTokens({ role: \"user\", content: admitted.text, timestamp: 0 });\n\t\t\tif (!admitted.projected) return part;\n\t\t\tprojected = true;\n\t\t\treturn { ...part, text: admitted.text };\n\t\t});\n\t\treturn projected ? { ...message, content } : message;\n\t});\n}\n"]}
|
|
@@ -7,11 +7,18 @@ export interface CompactionYield {
|
|
|
7
7
|
}
|
|
8
8
|
export declare function baseThresholdRatioForWindow(contextWindow: number): number;
|
|
9
9
|
export declare function resolveReserveTokens(contextWindow: number, configuredReserve: number): number;
|
|
10
|
+
/**
|
|
11
|
+
* The single effective reserve for one window. Every consumer that reasons about
|
|
12
|
+
* the usable prompt budget — the hard-limit valve, restoration, and deterministic
|
|
13
|
+
* fallback acceptance — must resolve it here so acceptance can never admit a
|
|
14
|
+
* context the hard valve would immediately compact again. Scaling is idempotent,
|
|
15
|
+
* so re-resolving an already-scaled reserve never double-scales it.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveEffectiveReserveTokens(contextWindow: number, settings: Pick<CompactionSettings, "reserveTokens" | "reserveScalingEnabled">): number;
|
|
10
18
|
export declare function computeAdaptiveThresholdRatio(contextWindow: number, priorCompactionSavedTokens?: number): number;
|
|
11
19
|
export declare function computeEffectiveThreshold(contextWindow: number, lastYield?: CompactionYield): number;
|
|
12
20
|
export declare function computeEffectiveKeepRecentTokens(setting: number, contextWindow: number, thresholdRatio: number, margin?: number): number;
|
|
13
21
|
export declare function shouldStartSpeculativeCompaction(usage: ContextUsage, contextWindow: number, settings: CompactionSettings, lastYield?: CompactionYield, leadTokens?: number): boolean;
|
|
14
|
-
export declare function resolveEffectiveReserveTokens(contextWindow: number, configuredReserve: number, reserveScalingEnabled?: boolean): number;
|
|
15
22
|
export declare function isAtHardLimit(usage: ContextUsage, contextWindow: number, reserveTokens: number, additionalTokens?: number): boolean;
|
|
16
23
|
export declare function shouldTriggerCompaction(usage: ContextUsage, contextWindow: number, settings: CompactionSettings, lastYield?: CompactionYield): boolean;
|
|
17
24
|
//# sourceMappingURL=policy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAanD,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,MAAM,WAAW,eAAe;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACrB;AAyCD,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAoBzE;AAED,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,MAAM,CAK7F;AAED,wBAAgB,6BAA6B,CAAC,aAAa,EAAE,MAAM,EAAE,0BAA0B,CAAC,EAAE,MAAM,GAAG,MAAM,CAQhH;AAED,wBAAgB,yBAAyB,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,eAAe,GAAG,MAAM,CAMpG;AAED,wBAAgB,gCAAgC,CAC/C,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,MAAM,SAAO,GACX,MAAM,CAUR;AAED,wBAAgB,gCAAgC,CAC/C,KAAK,EAAE,YAAY,EACnB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,kBAAkB,EAC5B,SAAS,CAAC,EAAE,eAAe,EAC3B,UAAU,CAAC,EAAE,MAAM,GACjB,OAAO,CAYT;AAED,wBAAgB,
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAanD,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,MAAM,WAAW,eAAe;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACrB;AAyCD,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAoBzE;AAED,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,MAAM,CAK7F;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC5C,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,IAAI,CAAC,kBAAkB,EAAE,eAAe,GAAG,uBAAuB,CAAC,GAC3E,MAAM,CAIR;AAED,wBAAgB,6BAA6B,CAAC,aAAa,EAAE,MAAM,EAAE,0BAA0B,CAAC,EAAE,MAAM,GAAG,MAAM,CAQhH;AAED,wBAAgB,yBAAyB,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,eAAe,GAAG,MAAM,CAMpG;AAED,wBAAgB,gCAAgC,CAC/C,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,MAAM,SAAO,GACX,MAAM,CAUR;AAED,wBAAgB,gCAAgC,CAC/C,KAAK,EAAE,YAAY,EACnB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,kBAAkB,EAC5B,SAAS,CAAC,EAAE,eAAe,EAC3B,UAAU,CAAC,EAAE,MAAM,GACjB,OAAO,CAYT;AAED,wBAAgB,aAAa,CAC5B,KAAK,EAAE,YAAY,EACnB,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,EACrB,gBAAgB,SAAI,GAClB,OAAO,CAET;AAED,wBAAgB,uBAAuB,CACtC,KAAK,EAAE,YAAY,EACnB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,kBAAkB,EAC5B,SAAS,CAAC,EAAE,eAAe,GACzB,OAAO,CAMT"}
|
|
@@ -62,6 +62,18 @@ export function baseThresholdRatioForWindow(contextWindow) {
|
|
|
62
62
|
export function resolveReserveTokens(contextWindow, configuredReserve) {
|
|
63
63
|
return Math.max(configuredReserve, Math.min(Math.floor(RESERVE_WINDOW_FRACTION * contextWindow), MAX_SCALED_RESERVE_TOKENS));
|
|
64
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* The single effective reserve for one window. Every consumer that reasons about
|
|
67
|
+
* the usable prompt budget — the hard-limit valve, restoration, and deterministic
|
|
68
|
+
* fallback acceptance — must resolve it here so acceptance can never admit a
|
|
69
|
+
* context the hard valve would immediately compact again. Scaling is idempotent,
|
|
70
|
+
* so re-resolving an already-scaled reserve never double-scales it.
|
|
71
|
+
*/
|
|
72
|
+
export function resolveEffectiveReserveTokens(contextWindow, settings) {
|
|
73
|
+
return settings.reserveScalingEnabled === false
|
|
74
|
+
? settings.reserveTokens
|
|
75
|
+
: resolveReserveTokens(contextWindow, settings.reserveTokens);
|
|
76
|
+
}
|
|
65
77
|
export function computeAdaptiveThresholdRatio(contextWindow, priorCompactionSavedTokens) {
|
|
66
78
|
const ratio = baseThresholdRatioForWindow(contextWindow);
|
|
67
79
|
if (priorCompactionSavedTokens === undefined) {
|
|
@@ -94,9 +106,6 @@ export function shouldStartSpeculativeCompaction(usage, contextWindow, settings,
|
|
|
94
106
|
const fraction = settings.speculativeFraction ?? SPECULATIVE_FRACTION;
|
|
95
107
|
return usage.tokens >= contextWindow * computeEffectiveThreshold(contextWindow, lastYield) * fraction;
|
|
96
108
|
}
|
|
97
|
-
export function resolveEffectiveReserveTokens(contextWindow, configuredReserve, reserveScalingEnabled = true) {
|
|
98
|
-
return reserveScalingEnabled ? resolveReserveTokens(contextWindow, configuredReserve) : configuredReserve;
|
|
99
|
-
}
|
|
100
109
|
export function isAtHardLimit(usage, contextWindow, reserveTokens, additionalTokens = 0) {
|
|
101
110
|
return usage.tokens !== null && usage.tokens + additionalTokens + reserveTokens >= contextWindow;
|
|
102
111
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/policy.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CompactionSettings } from \"../../../compaction/index.ts\";\nimport type { ContextUsage } from \"../../types.ts\";\n\nconst MIN_ADAPTIVE_THRESHOLD_RATIO = 0.4;\nconst MAX_ADAPTIVE_THRESHOLD_RATIO = 0.85;\nconst HIGH_YIELD_SAVING_RATIO = 0.5;\nconst LOW_YIELD_SAVING_RATIO = 0.1;\nconst YIELD_ADJUSTMENT_RATIO = 0.05;\nconst MIN_EFFECTIVE_KEEP_RECENT_TOKENS = 1024;\nconst RESERVE_WINDOW_FRACTION = 0.04;\nconst MAX_SCALED_RESERVE_TOKENS = 49_152;\nconst KEEP_RECENT_WINDOW_FRACTION = 0.05;\nconst MAX_SCALED_KEEP_RECENT_TOKENS = 60_000;\n\nexport const SPECULATIVE_FRACTION = 0.75;\n\nexport interface CompactionYield {\n\tsavedTokens: number;\n\ttokensBefore: number;\n}\n\nfunction clampThresholdRatio(ratio: number): number {\n\treturn Math.min(MAX_ADAPTIVE_THRESHOLD_RATIO, Math.max(MIN_ADAPTIVE_THRESHOLD_RATIO, ratio));\n}\n\nfunction adjustThresholdRatio(ratio: number, savedTokens: number, contextWindow: number): number {\n\tif (contextWindow <= 0) {\n\t\treturn ratio;\n\t}\n\n\tconst savedRatio = savedTokens / contextWindow;\n\tif (savedRatio > HIGH_YIELD_SAVING_RATIO) {\n\t\treturn clampThresholdRatio(ratio - YIELD_ADJUSTMENT_RATIO);\n\t}\n\tif (savedRatio < LOW_YIELD_SAVING_RATIO) {\n\t\treturn Math.min(baseThresholdRatioForWindow(contextWindow), clampThresholdRatio(ratio + YIELD_ADJUSTMENT_RATIO));\n\t}\n\treturn ratio;\n}\n\nfunction adjustEffectiveThresholdRatio(\n\tratio: number,\n\tsavedTokens: number,\n\ttokensBefore: number,\n\tcontextWindow: number,\n): number {\n\tif (tokensBefore <= 0) {\n\t\treturn ratio;\n\t}\n\n\tconst savedRatio = savedTokens / tokensBefore;\n\tif (savedRatio > HIGH_YIELD_SAVING_RATIO) {\n\t\treturn ratio - YIELD_ADJUSTMENT_RATIO;\n\t}\n\tif (savedRatio < LOW_YIELD_SAVING_RATIO) {\n\t\treturn Math.min(baseThresholdRatioForWindow(contextWindow), ratio + YIELD_ADJUSTMENT_RATIO);\n\t}\n\treturn ratio;\n}\n\nexport function baseThresholdRatioForWindow(contextWindow: number): number {\n\tif (!(contextWindow > 0)) {\n\t\treturn 0.5;\n\t}\n\tif (contextWindow <= 16_000) {\n\t\treturn 0.45;\n\t}\n\tif (contextWindow <= 32_000) {\n\t\treturn 0.5;\n\t}\n\tif (contextWindow <= 64_000) {\n\t\treturn 0.55;\n\t}\n\tif (contextWindow <= 128_000) {\n\t\treturn 0.6;\n\t}\n\tif (contextWindow <= 512_000) {\n\t\treturn 0.7;\n\t}\n\treturn 0.8;\n}\n\nexport function resolveReserveTokens(contextWindow: number, configuredReserve: number): number {\n\treturn Math.max(\n\t\tconfiguredReserve,\n\t\tMath.min(Math.floor(RESERVE_WINDOW_FRACTION * contextWindow), MAX_SCALED_RESERVE_TOKENS),\n\t);\n}\n\nexport function computeAdaptiveThresholdRatio(contextWindow: number, priorCompactionSavedTokens?: number): number {\n\tconst ratio = baseThresholdRatioForWindow(contextWindow);\n\n\tif (priorCompactionSavedTokens === undefined) {\n\t\treturn ratio;\n\t}\n\n\treturn adjustThresholdRatio(ratio, priorCompactionSavedTokens, contextWindow);\n}\n\nexport function computeEffectiveThreshold(contextWindow: number, lastYield?: CompactionYield): number {\n\tlet ratio = computeAdaptiveThresholdRatio(contextWindow);\n\tif (lastYield) {\n\t\tratio = adjustEffectiveThresholdRatio(ratio, lastYield.savedTokens, lastYield.tokensBefore, contextWindow);\n\t}\n\treturn clampThresholdRatio(ratio);\n}\n\nexport function computeEffectiveKeepRecentTokens(\n\tsetting: number,\n\tcontextWindow: number,\n\tthresholdRatio: number,\n\tmargin = 0.05,\n): number {\n\tconst scaled =\n\t\tcontextWindow > 409_600 && setting >= 10_000\n\t\t\t? Math.max(\n\t\t\t\t\tsetting,\n\t\t\t\t\tMath.min(Math.floor(KEEP_RECENT_WINDOW_FRACTION * contextWindow), MAX_SCALED_KEEP_RECENT_TOKENS),\n\t\t\t\t)\n\t\t\t: setting;\n\tconst capped = Math.floor(contextWindow * (1 - thresholdRatio - margin));\n\treturn Math.min(scaled, Math.max(MIN_EFFECTIVE_KEEP_RECENT_TOKENS, capped));\n}\n\nexport function shouldStartSpeculativeCompaction(\n\tusage: ContextUsage,\n\tcontextWindow: number,\n\tsettings: CompactionSettings,\n\tlastYield?: CompactionYield,\n\tleadTokens?: number,\n): boolean {\n\tif (settings.speculativeEnabled === false || usage.tokens === null || contextWindow <= 0) {\n\t\treturn false;\n\t}\n\n\tif (typeof leadTokens === \"number\" && Number.isFinite(leadTokens) && leadTokens > 0) {\n\t\tconst leadTrigger = Math.max(0, contextWindow * computeEffectiveThreshold(contextWindow, lastYield) - leadTokens);\n\t\treturn usage.tokens >= leadTrigger;\n\t}\n\n\tconst fraction = settings.speculativeFraction ?? SPECULATIVE_FRACTION;\n\treturn usage.tokens >= contextWindow * computeEffectiveThreshold(contextWindow, lastYield) * fraction;\n}\n\nexport function resolveEffectiveReserveTokens(\n\tcontextWindow: number,\n\tconfiguredReserve: number,\n\treserveScalingEnabled = true,\n): number {\n\treturn reserveScalingEnabled ? resolveReserveTokens(contextWindow, configuredReserve) : configuredReserve;\n}\n\nexport function isAtHardLimit(\n\tusage: ContextUsage,\n\tcontextWindow: number,\n\treserveTokens: number,\n\tadditionalTokens = 0,\n): boolean {\n\treturn usage.tokens !== null && usage.tokens + additionalTokens + reserveTokens >= contextWindow;\n}\n\nexport function shouldTriggerCompaction(\n\tusage: ContextUsage,\n\tcontextWindow: number,\n\tsettings: CompactionSettings,\n\tlastYield?: CompactionYield,\n): boolean {\n\tif (!settings.enabled || usage.tokens === null || contextWindow <= 0) {\n\t\treturn false;\n\t}\n\n\treturn usage.tokens >= contextWindow * computeEffectiveThreshold(contextWindow, lastYield);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/policy.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CompactionSettings } from \"../../../compaction/index.ts\";\nimport type { ContextUsage } from \"../../types.ts\";\n\nconst MIN_ADAPTIVE_THRESHOLD_RATIO = 0.4;\nconst MAX_ADAPTIVE_THRESHOLD_RATIO = 0.85;\nconst HIGH_YIELD_SAVING_RATIO = 0.5;\nconst LOW_YIELD_SAVING_RATIO = 0.1;\nconst YIELD_ADJUSTMENT_RATIO = 0.05;\nconst MIN_EFFECTIVE_KEEP_RECENT_TOKENS = 1024;\nconst RESERVE_WINDOW_FRACTION = 0.04;\nconst MAX_SCALED_RESERVE_TOKENS = 49_152;\nconst KEEP_RECENT_WINDOW_FRACTION = 0.05;\nconst MAX_SCALED_KEEP_RECENT_TOKENS = 60_000;\n\nexport const SPECULATIVE_FRACTION = 0.75;\n\nexport interface CompactionYield {\n\tsavedTokens: number;\n\ttokensBefore: number;\n}\n\nfunction clampThresholdRatio(ratio: number): number {\n\treturn Math.min(MAX_ADAPTIVE_THRESHOLD_RATIO, Math.max(MIN_ADAPTIVE_THRESHOLD_RATIO, ratio));\n}\n\nfunction adjustThresholdRatio(ratio: number, savedTokens: number, contextWindow: number): number {\n\tif (contextWindow <= 0) {\n\t\treturn ratio;\n\t}\n\n\tconst savedRatio = savedTokens / contextWindow;\n\tif (savedRatio > HIGH_YIELD_SAVING_RATIO) {\n\t\treturn clampThresholdRatio(ratio - YIELD_ADJUSTMENT_RATIO);\n\t}\n\tif (savedRatio < LOW_YIELD_SAVING_RATIO) {\n\t\treturn Math.min(baseThresholdRatioForWindow(contextWindow), clampThresholdRatio(ratio + YIELD_ADJUSTMENT_RATIO));\n\t}\n\treturn ratio;\n}\n\nfunction adjustEffectiveThresholdRatio(\n\tratio: number,\n\tsavedTokens: number,\n\ttokensBefore: number,\n\tcontextWindow: number,\n): number {\n\tif (tokensBefore <= 0) {\n\t\treturn ratio;\n\t}\n\n\tconst savedRatio = savedTokens / tokensBefore;\n\tif (savedRatio > HIGH_YIELD_SAVING_RATIO) {\n\t\treturn ratio - YIELD_ADJUSTMENT_RATIO;\n\t}\n\tif (savedRatio < LOW_YIELD_SAVING_RATIO) {\n\t\treturn Math.min(baseThresholdRatioForWindow(contextWindow), ratio + YIELD_ADJUSTMENT_RATIO);\n\t}\n\treturn ratio;\n}\n\nexport function baseThresholdRatioForWindow(contextWindow: number): number {\n\tif (!(contextWindow > 0)) {\n\t\treturn 0.5;\n\t}\n\tif (contextWindow <= 16_000) {\n\t\treturn 0.45;\n\t}\n\tif (contextWindow <= 32_000) {\n\t\treturn 0.5;\n\t}\n\tif (contextWindow <= 64_000) {\n\t\treturn 0.55;\n\t}\n\tif (contextWindow <= 128_000) {\n\t\treturn 0.6;\n\t}\n\tif (contextWindow <= 512_000) {\n\t\treturn 0.7;\n\t}\n\treturn 0.8;\n}\n\nexport function resolveReserveTokens(contextWindow: number, configuredReserve: number): number {\n\treturn Math.max(\n\t\tconfiguredReserve,\n\t\tMath.min(Math.floor(RESERVE_WINDOW_FRACTION * contextWindow), MAX_SCALED_RESERVE_TOKENS),\n\t);\n}\n\n/**\n * The single effective reserve for one window. Every consumer that reasons about\n * the usable prompt budget — the hard-limit valve, restoration, and deterministic\n * fallback acceptance — must resolve it here so acceptance can never admit a\n * context the hard valve would immediately compact again. Scaling is idempotent,\n * so re-resolving an already-scaled reserve never double-scales it.\n */\nexport function resolveEffectiveReserveTokens(\n\tcontextWindow: number,\n\tsettings: Pick<CompactionSettings, \"reserveTokens\" | \"reserveScalingEnabled\">,\n): number {\n\treturn settings.reserveScalingEnabled === false\n\t\t? settings.reserveTokens\n\t\t: resolveReserveTokens(contextWindow, settings.reserveTokens);\n}\n\nexport function computeAdaptiveThresholdRatio(contextWindow: number, priorCompactionSavedTokens?: number): number {\n\tconst ratio = baseThresholdRatioForWindow(contextWindow);\n\n\tif (priorCompactionSavedTokens === undefined) {\n\t\treturn ratio;\n\t}\n\n\treturn adjustThresholdRatio(ratio, priorCompactionSavedTokens, contextWindow);\n}\n\nexport function computeEffectiveThreshold(contextWindow: number, lastYield?: CompactionYield): number {\n\tlet ratio = computeAdaptiveThresholdRatio(contextWindow);\n\tif (lastYield) {\n\t\tratio = adjustEffectiveThresholdRatio(ratio, lastYield.savedTokens, lastYield.tokensBefore, contextWindow);\n\t}\n\treturn clampThresholdRatio(ratio);\n}\n\nexport function computeEffectiveKeepRecentTokens(\n\tsetting: number,\n\tcontextWindow: number,\n\tthresholdRatio: number,\n\tmargin = 0.05,\n): number {\n\tconst scaled =\n\t\tcontextWindow > 409_600 && setting >= 10_000\n\t\t\t? Math.max(\n\t\t\t\t\tsetting,\n\t\t\t\t\tMath.min(Math.floor(KEEP_RECENT_WINDOW_FRACTION * contextWindow), MAX_SCALED_KEEP_RECENT_TOKENS),\n\t\t\t\t)\n\t\t\t: setting;\n\tconst capped = Math.floor(contextWindow * (1 - thresholdRatio - margin));\n\treturn Math.min(scaled, Math.max(MIN_EFFECTIVE_KEEP_RECENT_TOKENS, capped));\n}\n\nexport function shouldStartSpeculativeCompaction(\n\tusage: ContextUsage,\n\tcontextWindow: number,\n\tsettings: CompactionSettings,\n\tlastYield?: CompactionYield,\n\tleadTokens?: number,\n): boolean {\n\tif (settings.speculativeEnabled === false || usage.tokens === null || contextWindow <= 0) {\n\t\treturn false;\n\t}\n\n\tif (typeof leadTokens === \"number\" && Number.isFinite(leadTokens) && leadTokens > 0) {\n\t\tconst leadTrigger = Math.max(0, contextWindow * computeEffectiveThreshold(contextWindow, lastYield) - leadTokens);\n\t\treturn usage.tokens >= leadTrigger;\n\t}\n\n\tconst fraction = settings.speculativeFraction ?? SPECULATIVE_FRACTION;\n\treturn usage.tokens >= contextWindow * computeEffectiveThreshold(contextWindow, lastYield) * fraction;\n}\n\nexport function isAtHardLimit(\n\tusage: ContextUsage,\n\tcontextWindow: number,\n\treserveTokens: number,\n\tadditionalTokens = 0,\n): boolean {\n\treturn usage.tokens !== null && usage.tokens + additionalTokens + reserveTokens >= contextWindow;\n}\n\nexport function shouldTriggerCompaction(\n\tusage: ContextUsage,\n\tcontextWindow: number,\n\tsettings: CompactionSettings,\n\tlastYield?: CompactionYield,\n): boolean {\n\tif (!settings.enabled || usage.tokens === null || contextWindow <= 0) {\n\t\treturn false;\n\t}\n\n\treturn usage.tokens >= contextWindow * computeEffectiveThreshold(contextWindow, lastYield);\n}\n"]}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
export interface TokenBudgetReminderLease {
|
|
2
|
+
compactionEpoch: number;
|
|
3
|
+
message: string;
|
|
4
|
+
}
|
|
1
5
|
export interface TokenBudgetReminderState {
|
|
2
|
-
|
|
6
|
+
lastFiredEpoch: number | null;
|
|
7
|
+
lease?: TokenBudgetReminderLease;
|
|
3
8
|
}
|
|
4
9
|
export interface TokenBudgetReminderInput {
|
|
5
10
|
contextTokens: number;
|
|
6
11
|
contextWindow: number;
|
|
7
12
|
thresholdTokens: number;
|
|
8
13
|
leadTokens: number;
|
|
9
|
-
|
|
14
|
+
compactionEpoch: number;
|
|
10
15
|
state: TokenBudgetReminderState;
|
|
11
16
|
}
|
|
12
17
|
export interface TokenBudgetReminderResult {
|
|
@@ -14,5 +19,6 @@ export interface TokenBudgetReminderResult {
|
|
|
14
19
|
nextState: TokenBudgetReminderState;
|
|
15
20
|
}
|
|
16
21
|
export declare function createInitialReminderState(): TokenBudgetReminderState;
|
|
22
|
+
export declare function clearTokenBudgetReminderLease(state: TokenBudgetReminderState): TokenBudgetReminderState;
|
|
17
23
|
export declare function computeTokenBudgetReminder(input: TokenBudgetReminderInput): TokenBudgetReminderResult;
|
|
18
24
|
//# sourceMappingURL=token-budget-reminder.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-budget-reminder.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/token-budget-reminder.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,wBAAwB;IACxC,
|
|
1
|
+
{"version":3,"file":"token-budget-reminder.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/token-budget-reminder.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,wBAAwB;IACxC,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,wBAAwB;IACxC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,KAAK,CAAC,EAAE,wBAAwB,CAAC;CACjC;AAED,MAAM,WAAW,wBAAwB;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,wBAAwB,CAAC;CAChC;AAED,MAAM,WAAW,yBAAyB;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,wBAAwB,CAAC;CACpC;AAED,wBAAgB,0BAA0B,IAAI,wBAAwB,CAErE;AAMD,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,wBAAwB,GAAG,wBAAwB,CAEvG;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,wBAAwB,GAAG,yBAAyB,CAcrG"}
|
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
export function createInitialReminderState() {
|
|
2
|
-
return {
|
|
2
|
+
return { lastFiredEpoch: null };
|
|
3
3
|
}
|
|
4
4
|
function formatReminderMessage(remaining) {
|
|
5
5
|
return `[context budget] Approximately ${remaining} tokens remain before automatic compaction. Wrap up verbose exploration, prefer concise summaries over full dumps, and front-load conclusions.`;
|
|
6
6
|
}
|
|
7
|
+
export function clearTokenBudgetReminderLease(state) {
|
|
8
|
+
return state.lease ? { lastFiredEpoch: state.lastFiredEpoch } : state;
|
|
9
|
+
}
|
|
7
10
|
export function computeTokenBudgetReminder(input) {
|
|
8
11
|
const remaining = input.thresholdTokens - input.contextTokens;
|
|
9
12
|
const inZone = remaining > 0 && remaining <= 2 * input.leadTokens;
|
|
10
|
-
if (!inZone || input.state.
|
|
11
|
-
return { nextState: input.state };
|
|
13
|
+
if (!inZone || input.state.lastFiredEpoch === input.compactionEpoch) {
|
|
14
|
+
return { nextState: clearTokenBudgetReminderLease(input.state) };
|
|
12
15
|
}
|
|
16
|
+
const message = formatReminderMessage(remaining);
|
|
13
17
|
return {
|
|
14
|
-
message
|
|
15
|
-
nextState: {
|
|
18
|
+
message,
|
|
19
|
+
nextState: {
|
|
20
|
+
lastFiredEpoch: input.compactionEpoch,
|
|
21
|
+
lease: { compactionEpoch: input.compactionEpoch, message },
|
|
22
|
+
},
|
|
16
23
|
};
|
|
17
24
|
}
|
|
18
25
|
//# sourceMappingURL=token-budget-reminder.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-budget-reminder.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/token-budget-reminder.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"token-budget-reminder.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/token-budget-reminder.ts"],"names":[],"mappings":"AAwBA,MAAM,UAAU,0BAA0B;IACzC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;AACjC,CAAC;AAED,SAAS,qBAAqB,CAAC,SAAiB;IAC/C,OAAO,kCAAkC,SAAS,gJAAgJ,CAAC;AACpM,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,KAA+B;IAC5E,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,KAA+B;IACzE,MAAM,SAAS,GAAG,KAAK,CAAC,eAAe,GAAG,KAAK,CAAC,aAAa,CAAC;IAC9D,MAAM,MAAM,GAAG,SAAS,GAAG,CAAC,IAAI,SAAS,IAAI,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC;IAClE,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,cAAc,KAAK,KAAK,CAAC,eAAe,EAAE,CAAC;QACrE,OAAO,EAAE,SAAS,EAAE,6BAA6B,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;IAClE,CAAC;IACD,MAAM,OAAO,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;IACjD,OAAO;QACN,OAAO;QACP,SAAS,EAAE;YACV,cAAc,EAAE,KAAK,CAAC,eAAe;YACrC,KAAK,EAAE,EAAE,eAAe,EAAE,KAAK,CAAC,eAAe,EAAE,OAAO,EAAE;SAC1D;KACD,CAAC;AACH,CAAC","sourcesContent":["export interface TokenBudgetReminderLease {\n\tcompactionEpoch: number;\n\tmessage: string;\n}\n\nexport interface TokenBudgetReminderState {\n\tlastFiredEpoch: number | null;\n\tlease?: TokenBudgetReminderLease;\n}\n\nexport interface TokenBudgetReminderInput {\n\tcontextTokens: number;\n\tcontextWindow: number;\n\tthresholdTokens: number;\n\tleadTokens: number;\n\tcompactionEpoch: number;\n\tstate: TokenBudgetReminderState;\n}\n\nexport interface TokenBudgetReminderResult {\n\tmessage?: string;\n\tnextState: TokenBudgetReminderState;\n}\n\nexport function createInitialReminderState(): TokenBudgetReminderState {\n\treturn { lastFiredEpoch: null };\n}\n\nfunction formatReminderMessage(remaining: number): string {\n\treturn `[context budget] Approximately ${remaining} tokens remain before automatic compaction. Wrap up verbose exploration, prefer concise summaries over full dumps, and front-load conclusions.`;\n}\n\nexport function clearTokenBudgetReminderLease(state: TokenBudgetReminderState): TokenBudgetReminderState {\n\treturn state.lease ? { lastFiredEpoch: state.lastFiredEpoch } : state;\n}\n\nexport function computeTokenBudgetReminder(input: TokenBudgetReminderInput): TokenBudgetReminderResult {\n\tconst remaining = input.thresholdTokens - input.contextTokens;\n\tconst inZone = remaining > 0 && remaining <= 2 * input.leadTokens;\n\tif (!inZone || input.state.lastFiredEpoch === input.compactionEpoch) {\n\t\treturn { nextState: clearTokenBudgetReminderLease(input.state) };\n\t}\n\tconst message = formatReminderMessage(remaining);\n\treturn {\n\t\tmessage,\n\t\tnextState: {\n\t\t\tlastFiredEpoch: input.compactionEpoch,\n\t\t\tlease: { compactionEpoch: input.compactionEpoch, message },\n\t\t},\n\t};\n}\n"]}
|
|
@@ -2,35 +2,26 @@
|
|
|
2
2
|
* Always-on admission cap for tool results.
|
|
3
3
|
*
|
|
4
4
|
* Oversized tool output is the dominant driver of single-step context bursts, so it is
|
|
5
|
-
*
|
|
6
|
-
* (see `tool-truncation.ts`).
|
|
7
|
-
*
|
|
5
|
+
* projected to a deterministic head/tail excerpt at the entrance instead of waiting for
|
|
6
|
+
* an emergency/compaction-budget pass (see `tool-truncation.ts`). Admission is derived
|
|
7
|
+
* from the supplied text on every request; model-visible marker text is never state.
|
|
8
8
|
*/
|
|
9
|
-
export declare const TOOL_ADMISSION_MARKER_PREFIX = "[tool result
|
|
10
|
-
/** True when `text` already carries an admission marker line, at any position. */
|
|
11
|
-
export declare function containsToolAdmissionMarker(text: string): boolean;
|
|
9
|
+
export declare const TOOL_ADMISSION_MARKER_PREFIX = "[tool result projected:";
|
|
12
10
|
export interface AdmitToolResultInput {
|
|
13
|
-
text: string;
|
|
14
|
-
contextWindow: number;
|
|
15
|
-
spillDir: string;
|
|
16
|
-
capTokens?: number;
|
|
11
|
+
readonly text: string;
|
|
12
|
+
readonly contextWindow: number;
|
|
17
13
|
}
|
|
18
14
|
export interface AdmitToolResultOutput {
|
|
19
|
-
text: string;
|
|
20
|
-
|
|
21
|
-
spillPath?: string;
|
|
15
|
+
readonly text: string;
|
|
16
|
+
readonly projected: boolean;
|
|
22
17
|
}
|
|
23
|
-
/** Token budget a single tool result may occupy before it is
|
|
18
|
+
/** Token budget a single tool result may occupy before it is projected. */
|
|
24
19
|
export declare function resolveToolResultAdmissionCapTokens(contextWindow: number): number;
|
|
25
|
-
export declare function buildAdmissionMarker(keptTokens: number, totalTokens: number, spillPath: string): string;
|
|
26
|
-
export declare function estimateAdmissionMarkerTokens(totalTokens: number, spillPath: string): number;
|
|
27
20
|
/**
|
|
28
21
|
* Cap a tool result at admission time. Under-cap text passes through untouched; over-cap
|
|
29
|
-
* text
|
|
30
|
-
*
|
|
31
|
-
* Already-admitted text is returned unchanged: re-spilling an excerpt would persist the
|
|
32
|
-
* excerpt (not the original output) and break the read-back pointer chain. This matters
|
|
33
|
-
* when a model switch shrinks the context window below the cap the excerpt was built for.
|
|
22
|
+
* text becomes a deterministic, diskless head/tail projection. Re-evaluating projected
|
|
23
|
+
* text under a smaller model window is safe because admission never trusts marker text.
|
|
34
24
|
*/
|
|
25
|
+
export declare function admitToolResultWithinBudget(text: string, budgetTokens: number): AdmitToolResultOutput;
|
|
35
26
|
export declare function admitToolResult(input: AdmitToolResultInput): AdmitToolResultOutput;
|
|
36
27
|
//# sourceMappingURL=tool-admission.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-admission.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/tool-admission.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"tool-admission.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/tool-admission.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAUH,eAAO,MAAM,4BAA4B,4BAA4B,CAAC;AAEtE,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC5B;AAED,2EAA2E;AAC3E,wBAAgB,mCAAmC,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAKjF;AAeD;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,qBAAqB,CAgBrG;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,qBAAqB,CAElF"}
|
|
@@ -2,91 +2,55 @@
|
|
|
2
2
|
* Always-on admission cap for tool results.
|
|
3
3
|
*
|
|
4
4
|
* Oversized tool output is the dominant driver of single-step context bursts, so it is
|
|
5
|
-
*
|
|
6
|
-
* (see `tool-truncation.ts`).
|
|
7
|
-
*
|
|
5
|
+
* projected to a deterministic head/tail excerpt at the entrance instead of waiting for
|
|
6
|
+
* an emergency/compaction-budget pass (see `tool-truncation.ts`). Admission is derived
|
|
7
|
+
* from the supplied text on every request; model-visible marker text is never state.
|
|
8
8
|
*/
|
|
9
|
-
import { createHash } from "node:crypto";
|
|
10
|
-
import { mkdirSync, writeFileSync } from "node:fs";
|
|
11
|
-
import { join } from "node:path";
|
|
12
9
|
import { estimateTokens } from "../../../compaction/index.js";
|
|
13
10
|
const MAX_ADMISSION_CAP_TOKENS = 50_000;
|
|
14
11
|
const MIN_ADMISSION_CAP_TOKENS = 8192;
|
|
15
12
|
const ADMISSION_CAP_FRACTION = 0.05;
|
|
16
13
|
const HEAD_BUDGET_FRACTION = 0.6;
|
|
17
14
|
const TAIL_BUDGET_FRACTION = 0.2;
|
|
18
|
-
export const TOOL_ADMISSION_MARKER_PREFIX = "[tool result
|
|
19
|
-
/**
|
|
20
|
-
* A whole line of the exact shape emitted by `buildMarker`. Anchored per line (`m` flag)
|
|
21
|
-
* so the marker is found wherever it sits - including the middle of a head/tail excerpt -
|
|
22
|
-
* while ordinary output that merely mentions the prefix mid-line is not matched.
|
|
23
|
-
*/
|
|
24
|
-
const TOOL_ADMISSION_MARKER_LINE = /^\[tool result truncated: kept \d+ of ~\d+ tokens; full output at .+ - read it with the read tool if needed\]$/m;
|
|
25
|
-
/** True when `text` already carries an admission marker line, at any position. */
|
|
26
|
-
export function containsToolAdmissionMarker(text) {
|
|
27
|
-
return TOOL_ADMISSION_MARKER_LINE.test(text);
|
|
28
|
-
}
|
|
29
|
-
/** Token budget a single tool result may occupy before it is spilled to disk. */
|
|
15
|
+
export const TOOL_ADMISSION_MARKER_PREFIX = "[tool result projected:";
|
|
16
|
+
/** Token budget a single tool result may occupy before it is projected. */
|
|
30
17
|
export function resolveToolResultAdmissionCapTokens(contextWindow) {
|
|
31
18
|
return Math.min(MAX_ADMISSION_CAP_TOKENS, Math.max(MIN_ADMISSION_CAP_TOKENS, Math.floor(ADMISSION_CAP_FRACTION * contextWindow)));
|
|
32
19
|
}
|
|
33
20
|
function estimateTextTokens(text) {
|
|
34
21
|
return estimateTokens({ role: "user", content: text, timestamp: 0 });
|
|
35
22
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
export function estimateAdmissionMarkerTokens(totalTokens, spillPath) {
|
|
40
|
-
return estimateTextTokens(buildAdmissionMarker(0, totalTokens, spillPath));
|
|
41
|
-
}
|
|
42
|
-
function spillToFile(spillDir, text) {
|
|
43
|
-
mkdirSync(spillDir, { recursive: true });
|
|
44
|
-
const digest = createHash("sha256").update(text).digest("hex").slice(0, 16);
|
|
45
|
-
const spillPath = join(spillDir, `tool-result-${Number.parseInt(digest.slice(0, 10), 16)}-${digest.slice(10, 16)}.txt`);
|
|
46
|
-
writeFileSync(spillPath, text, "utf-8");
|
|
47
|
-
return spillPath;
|
|
48
|
-
}
|
|
49
|
-
function buildExcerpt(text, budgetChars, totalTokens, spillPath) {
|
|
50
|
-
const headChars = Math.max(1, Math.floor(budgetChars * HEAD_BUDGET_FRACTION));
|
|
51
|
-
const tailChars = Math.max(1, Math.floor(budgetChars * TAIL_BUDGET_FRACTION));
|
|
23
|
+
function buildExcerpt(text, budgetChars, totalTokens) {
|
|
24
|
+
const headChars = Math.floor(budgetChars * HEAD_BUDGET_FRACTION);
|
|
25
|
+
const tailChars = Math.floor(budgetChars * TAIL_BUDGET_FRACTION);
|
|
52
26
|
const head = text.slice(0, headChars);
|
|
53
27
|
const tail = text.slice(Math.max(headChars, text.length - tailChars));
|
|
54
28
|
const keptTokens = estimateTextTokens(head) + estimateTextTokens(tail);
|
|
55
|
-
return `${head}\n${
|
|
29
|
+
return `${head}\n${TOOL_ADMISSION_MARKER_PREFIX} kept ${keptTokens} of ~${totalTokens} tokens]\n${tail}`;
|
|
56
30
|
}
|
|
57
31
|
/**
|
|
58
32
|
* Cap a tool result at admission time. Under-cap text passes through untouched; over-cap
|
|
59
|
-
* text
|
|
60
|
-
*
|
|
61
|
-
* Already-admitted text is returned unchanged: re-spilling an excerpt would persist the
|
|
62
|
-
* excerpt (not the original output) and break the read-back pointer chain. This matters
|
|
63
|
-
* when a model switch shrinks the context window below the cap the excerpt was built for.
|
|
33
|
+
* text becomes a deterministic, diskless head/tail projection. Re-evaluating projected
|
|
34
|
+
* text under a smaller model window is safe because admission never trusts marker text.
|
|
64
35
|
*/
|
|
65
|
-
export function
|
|
66
|
-
const { text, contextWindow, spillDir } = input;
|
|
67
|
-
const capTokens = input.capTokens ?? resolveToolResultAdmissionCapTokens(contextWindow);
|
|
36
|
+
export function admitToolResultWithinBudget(text, budgetTokens) {
|
|
68
37
|
const totalTokens = estimateTextTokens(text);
|
|
69
|
-
if (totalTokens <=
|
|
70
|
-
return { text,
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
spillPath = spillToFile(spillDir, text);
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
// Admission is a safety boundary: if persistence fails, still cap model-visible text.
|
|
77
|
-
}
|
|
78
|
-
const pointer = spillPath ?? "(spill unavailable)";
|
|
38
|
+
if (totalTokens <= budgetTokens)
|
|
39
|
+
return { text, projected: false };
|
|
40
|
+
if (budgetTokens <= 0)
|
|
41
|
+
return { text: "", projected: true };
|
|
79
42
|
const charsPerToken = text.length / Math.max(1, totalTokens);
|
|
80
|
-
let budgetChars = Math.floor(
|
|
81
|
-
let excerpt = buildExcerpt(text, budgetChars, totalTokens
|
|
82
|
-
while (estimateTextTokens(excerpt) >
|
|
83
|
-
budgetChars = Math.floor(budgetChars
|
|
84
|
-
excerpt =
|
|
43
|
+
let budgetChars = Math.floor(budgetTokens * charsPerToken);
|
|
44
|
+
let excerpt = buildExcerpt(text, budgetChars, totalTokens);
|
|
45
|
+
while (estimateTextTokens(excerpt) > budgetTokens && budgetChars > 0) {
|
|
46
|
+
budgetChars = Math.floor(budgetChars * 0.8);
|
|
47
|
+
excerpt = buildExcerpt(text, budgetChars, totalTokens);
|
|
85
48
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
49
|
+
return estimateTextTokens(excerpt) <= budgetTokens
|
|
50
|
+
? { text: excerpt, projected: true }
|
|
51
|
+
: { text: "", projected: true };
|
|
52
|
+
}
|
|
53
|
+
export function admitToolResult(input) {
|
|
54
|
+
return admitToolResultWithinBudget(input.text, resolveToolResultAdmissionCapTokens(input.contextWindow));
|
|
91
55
|
}
|
|
92
56
|
//# sourceMappingURL=tool-admission.js.map
|