@caupulican/pi-agent-core 0.81.2 → 0.81.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.
Files changed (44) hide show
  1. package/dist/compaction/compaction.d.ts +11 -2
  2. package/dist/compaction/compaction.d.ts.map +1 -1
  3. package/dist/compaction/compaction.js +258 -106
  4. package/dist/compaction/compaction.js.map +1 -1
  5. package/dist/compaction/extraction.d.ts +15 -0
  6. package/dist/compaction/extraction.d.ts.map +1 -0
  7. package/dist/compaction/extraction.js +342 -0
  8. package/dist/compaction/extraction.js.map +1 -0
  9. package/dist/compaction/index.d.ts +4 -0
  10. package/dist/compaction/index.d.ts.map +1 -1
  11. package/dist/compaction/index.js +4 -0
  12. package/dist/compaction/index.js.map +1 -1
  13. package/dist/compaction/loop.d.ts +52 -0
  14. package/dist/compaction/loop.d.ts.map +1 -0
  15. package/dist/compaction/loop.js +154 -0
  16. package/dist/compaction/loop.js.map +1 -0
  17. package/dist/compaction/token-budget.d.ts +12 -0
  18. package/dist/compaction/token-budget.d.ts.map +1 -0
  19. package/dist/compaction/token-budget.js +52 -0
  20. package/dist/compaction/token-budget.js.map +1 -0
  21. package/dist/compaction/utils.d.ts +1 -1
  22. package/dist/compaction/utils.d.ts.map +1 -1
  23. package/dist/compaction/utils.js +45 -2
  24. package/dist/compaction/utils.js.map +1 -1
  25. package/dist/compaction/verification.d.ts +20 -0
  26. package/dist/compaction/verification.d.ts.map +1 -0
  27. package/dist/compaction/verification.js +169 -0
  28. package/dist/compaction/verification.js.map +1 -0
  29. package/dist/reliability/classifier.d.ts +2 -0
  30. package/dist/reliability/classifier.d.ts.map +1 -1
  31. package/dist/reliability/classifier.js +18 -1
  32. package/dist/reliability/classifier.js.map +1 -1
  33. package/dist/reliability/index.d.ts +1 -0
  34. package/dist/reliability/index.d.ts.map +1 -1
  35. package/dist/reliability/index.js +1 -0
  36. package/dist/reliability/index.js.map +1 -1
  37. package/dist/reliability/provider-signatures.d.ts +10 -0
  38. package/dist/reliability/provider-signatures.d.ts.map +1 -0
  39. package/dist/reliability/provider-signatures.js +11 -0
  40. package/dist/reliability/provider-signatures.js.map +1 -0
  41. package/dist/reliability/retry-controller.d.ts.map +1 -1
  42. package/dist/reliability/retry-controller.js +1 -0
  43. package/dist/reliability/retry-controller.js.map +1 -1
  44. package/package.json +2 -2
@@ -0,0 +1,342 @@
1
+ const PROHIBITION_PATTERN = /\b(do not|don't|never|stop (?:doing|using|changing)|no more)\b/i;
2
+ const REVERSAL_PATTERN = /\b(stop|undo|revert|roll back|never mind|scrap that|forget (?:it|that))\b/i;
3
+ const FILE_KIND_PRIORITY = {
4
+ read: 1,
5
+ created: 2,
6
+ modified: 3,
7
+ };
8
+ const TOOL_KIND_BY_NAME = {
9
+ read: "read",
10
+ grep: "read",
11
+ find: "read",
12
+ write: "modified",
13
+ edit: "modified",
14
+ bash: null,
15
+ };
16
+ const TOOL_VERB_BY_NAME = {
17
+ write: "WRITE",
18
+ edit: "EDIT",
19
+ bash: "RUN",
20
+ read: "READ",
21
+ grep: "READ",
22
+ find: "READ",
23
+ };
24
+ function messageToText(message) {
25
+ const rawContent = message.content;
26
+ if (typeof rawContent === "string") {
27
+ return rawContent;
28
+ }
29
+ if (!Array.isArray(rawContent)) {
30
+ return "";
31
+ }
32
+ const parts = [];
33
+ for (const block of rawContent) {
34
+ if (!block || typeof block !== "object") {
35
+ continue;
36
+ }
37
+ if (block.type === "text" && typeof block.text === "string") {
38
+ parts.push(block.text);
39
+ }
40
+ }
41
+ return parts.join(" ").trim();
42
+ }
43
+ function assistantToolCallTarget(name, rawArgs) {
44
+ if (!rawArgs || typeof rawArgs !== "object") {
45
+ return undefined;
46
+ }
47
+ const args = rawArgs;
48
+ if ((name === "read" || name === "grep" || name === "find" || name === "write" || name === "edit") &&
49
+ typeof args.path === "string" &&
50
+ args.path.length > 0) {
51
+ return args.path;
52
+ }
53
+ if (name === "bash" && typeof args.command === "string" && args.command.length > 0) {
54
+ return args.command;
55
+ }
56
+ return undefined;
57
+ }
58
+ function clampText(text, maxLen) {
59
+ return text.length > maxLen ? text.slice(0, maxLen) : text;
60
+ }
61
+ function firstLine(text) {
62
+ for (const line of text.split("\n")) {
63
+ const trimmed = line.trim();
64
+ if (trimmed) {
65
+ return trimmed;
66
+ }
67
+ }
68
+ return "";
69
+ }
70
+ function firstOutcome(message) {
71
+ if (message.role !== "toolResult") {
72
+ return "";
73
+ }
74
+ return firstLine(messageToText(message));
75
+ }
76
+ function appearsCreated(message) {
77
+ if (message.role !== "toolResult") {
78
+ return false;
79
+ }
80
+ const text = messageToText(message).toLowerCase();
81
+ if (/(\bnew file\b|\bcreated\b|\bcreated file\b)/.test(text)) {
82
+ return true;
83
+ }
84
+ const details = message.details;
85
+ if (!details || typeof details !== "object") {
86
+ return false;
87
+ }
88
+ if (typeof details.created === "boolean") {
89
+ return details.created;
90
+ }
91
+ if (typeof details.isCreated === "boolean") {
92
+ return details.isCreated;
93
+ }
94
+ if (typeof details.isNewFile === "boolean") {
95
+ return details.isNewFile;
96
+ }
97
+ return false;
98
+ }
99
+ function splitSentenceLines(text) {
100
+ return text
101
+ .split(/[.!?\n]+/)
102
+ .map((line) => line.trim())
103
+ .filter(Boolean);
104
+ }
105
+ function getMessageFromEntry(entry) {
106
+ switch (entry.type) {
107
+ case "message":
108
+ return entry.message;
109
+ case "custom_message":
110
+ return {
111
+ role: "custom",
112
+ customType: entry.customType,
113
+ content: entry.content,
114
+ details: entry.details,
115
+ display: entry.display,
116
+ timestamp: new Date(entry.timestamp).getTime(),
117
+ };
118
+ case "branch_summary":
119
+ return {
120
+ role: "branchSummary",
121
+ summary: entry.summary,
122
+ fromId: entry.fromId,
123
+ timestamp: new Date(entry.timestamp).getTime(),
124
+ };
125
+ case "compaction":
126
+ return undefined;
127
+ default:
128
+ return undefined;
129
+ }
130
+ }
131
+ function isToolCallBlock(block) {
132
+ if (!block || typeof block !== "object") {
133
+ return false;
134
+ }
135
+ if (block.type !== "toolCall") {
136
+ return false;
137
+ }
138
+ if (typeof block.name !== "string") {
139
+ return false;
140
+ }
141
+ if (!block.arguments) {
142
+ return false;
143
+ }
144
+ return true;
145
+ }
146
+ function toolCallVerb(name) {
147
+ return TOOL_VERB_BY_NAME[name] ?? name.toUpperCase();
148
+ }
149
+ export function extractCompactionFacts(entries, start, end) {
150
+ const rangeStart = Math.max(0, start);
151
+ const rangeEnd = Math.min(entries.length, Math.max(rangeStart, end));
152
+ if (rangeStart >= rangeEnd) {
153
+ return { files: [], actions: [], prohibitions: [], cancelledText: "", activeTaskSource: "" };
154
+ }
155
+ const filesByPath = new Map();
156
+ const filesNotes = new Map();
157
+ const seenProhibitions = new Set();
158
+ const actionFacts = [];
159
+ const pendingById = new Map();
160
+ const pendingByOrder = [];
161
+ const actions = [];
162
+ const prohibitions = [];
163
+ let activeTaskSource = "";
164
+ const cancelledParts = [];
165
+ const sinceLastUser = [];
166
+ for (let i = rangeStart; i < rangeEnd; i++) {
167
+ const message = getMessageFromEntry(entries[i]);
168
+ if (!message) {
169
+ continue;
170
+ }
171
+ if (message.role === "user") {
172
+ const userText = messageToText(message);
173
+ if (REVERSAL_PATTERN.test(userText)) {
174
+ cancelledParts.push(...sinceLastUser);
175
+ }
176
+ sinceLastUser.length = 0;
177
+ if (userText) {
178
+ activeTaskSource = userText;
179
+ for (const sentence of splitSentenceLines(userText)) {
180
+ if (!PROHIBITION_PATTERN.test(sentence)) {
181
+ continue;
182
+ }
183
+ const normalized = clampText(sentence, 160);
184
+ const dedupeKey = normalized.toLowerCase();
185
+ if (!seenProhibitions.has(dedupeKey)) {
186
+ seenProhibitions.add(dedupeKey);
187
+ prohibitions.push(normalized);
188
+ }
189
+ }
190
+ }
191
+ continue;
192
+ }
193
+ if (message.role === "assistant" || message.role === "toolResult") {
194
+ const messageText = messageToText(message);
195
+ if (messageText) {
196
+ sinceLastUser.push(messageText);
197
+ }
198
+ }
199
+ if (message.role === "assistant" && Array.isArray(message.content)) {
200
+ for (const block of message.content) {
201
+ if (!isToolCallBlock(block)) {
202
+ continue;
203
+ }
204
+ const name = block.name;
205
+ const verb = toolCallVerb(name);
206
+ const path = assistantToolCallTarget(name, block.arguments);
207
+ const baseKind = TOOL_KIND_BY_NAME[name] ?? null;
208
+ const fact = {
209
+ id: block.id,
210
+ name,
211
+ path,
212
+ baseKind,
213
+ finalKind: baseKind,
214
+ verb,
215
+ outcome: "",
216
+ resolved: false,
217
+ };
218
+ actionFacts.push(fact);
219
+ const index = actionFacts.length - 1;
220
+ pendingByOrder.push(index);
221
+ if (fact.id) {
222
+ const bucket = pendingById.get(fact.id) ?? [];
223
+ bucket.push(index);
224
+ pendingById.set(fact.id, bucket);
225
+ }
226
+ }
227
+ }
228
+ if (message.role === "toolResult") {
229
+ const toolCallId = message.toolCallId;
230
+ const toolName = message.toolName;
231
+ let matchedIndex;
232
+ if (toolCallId) {
233
+ const bucket = pendingById.get(toolCallId);
234
+ if (bucket && bucket.length > 0) {
235
+ matchedIndex = bucket.shift();
236
+ if (bucket.length === 0) {
237
+ pendingById.delete(toolCallId);
238
+ }
239
+ }
240
+ }
241
+ if (matchedIndex === undefined && toolName) {
242
+ for (const index of pendingByOrder) {
243
+ const candidate = actionFacts[index];
244
+ if (!candidate.resolved && candidate.name === toolName) {
245
+ matchedIndex = index;
246
+ break;
247
+ }
248
+ }
249
+ }
250
+ if (matchedIndex !== undefined) {
251
+ const fact = actionFacts[matchedIndex];
252
+ const outcome = firstOutcome(message);
253
+ fact.outcome = outcome;
254
+ fact.resolved = true;
255
+ if (fact.baseKind === "modified" && appearsCreated(message)) {
256
+ fact.finalKind = "created";
257
+ }
258
+ const finalOutcome = clampText(fact.outcome, 80);
259
+ fact.outcome = finalOutcome;
260
+ if (fact.path && fact.finalKind) {
261
+ const existing = filesByPath.get(fact.path);
262
+ const nextKind = fact.finalKind;
263
+ const note = fact.verb;
264
+ if (!existing || FILE_KIND_PRIORITY[nextKind] > FILE_KIND_PRIORITY[existing.kind]) {
265
+ filesByPath.set(fact.path, { path: fact.path, kind: nextKind, note });
266
+ }
267
+ else if (existing.kind === nextKind) {
268
+ existing.note = `${note}: ${finalOutcome}`;
269
+ }
270
+ }
271
+ const pendingPos = pendingByOrder.indexOf(matchedIndex);
272
+ if (pendingPos >= 0) {
273
+ pendingByOrder.splice(pendingPos, 1);
274
+ }
275
+ if (toolCallId) {
276
+ const bucket = pendingById.get(toolCallId);
277
+ if (bucket) {
278
+ const pos = bucket.indexOf(matchedIndex);
279
+ if (pos >= 0) {
280
+ bucket.splice(pos, 1);
281
+ }
282
+ if (bucket.length === 0) {
283
+ pendingById.delete(toolCallId);
284
+ }
285
+ }
286
+ }
287
+ }
288
+ }
289
+ }
290
+ for (const action of actionFacts) {
291
+ if (action.finalKind && action.path && !filesByPath.has(action.path)) {
292
+ const note = action.outcome ? `${action.verb}: ${action.outcome}` : action.verb;
293
+ filesByPath.set(action.path, { path: action.path, kind: action.finalKind, note });
294
+ }
295
+ actions.push(`${action.verb} ${action.path ?? "(unknown)"} — ${action.outcome}`);
296
+ }
297
+ for (const file of filesByPath.values()) {
298
+ if (!file.note && filesNotes.has(file.path)) {
299
+ continue;
300
+ }
301
+ if (file.note && filesNotes.get(file.path) === file.note) {
302
+ continue;
303
+ }
304
+ if (file.note) {
305
+ filesNotes.set(file.path, file.note);
306
+ }
307
+ }
308
+ for (const file of filesByPath.values()) {
309
+ const suffix = filesNotes.get(file.path);
310
+ if (suffix) {
311
+ file.note = suffix;
312
+ }
313
+ }
314
+ return {
315
+ files: Array.from(filesByPath.values()).sort((a, b) => {
316
+ if (a.path === b.path) {
317
+ return FILE_KIND_PRIORITY[a.kind] - FILE_KIND_PRIORITY[b.kind];
318
+ }
319
+ return a.path.localeCompare(b.path);
320
+ }),
321
+ actions,
322
+ prohibitions,
323
+ cancelledText: cancelledParts.join("\n"),
324
+ activeTaskSource,
325
+ };
326
+ }
327
+ export function renderFactsBlock(facts) {
328
+ const lines = ["files:"];
329
+ for (const file of facts.files) {
330
+ lines.push(`${file.kind}: ${file.path} — ${file.note}`);
331
+ }
332
+ lines.push("actions:");
333
+ for (const action of facts.actions) {
334
+ lines.push(action);
335
+ }
336
+ lines.push("prohibitions:");
337
+ for (const prohibition of facts.prohibitions) {
338
+ lines.push(prohibition);
339
+ }
340
+ return lines.join("\n");
341
+ }
342
+ //# sourceMappingURL=extraction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extraction.js","sourceRoot":"","sources":["../../src/compaction/extraction.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SessionEntry } from \"../session/session-manager.ts\";\nimport type { AgentMessage } from \"../types.ts\";\n\nexport interface CompactionFacts {\n\tfiles: Array<{ path: string; kind: \"modified\" | \"created\" | \"read\"; note: string }>;\n\tactions: string[];\n\tprohibitions: string[];\n\tcancelledText: string;\n\tactiveTaskSource: string;\n}\n\ninterface ToolCallFact {\n\tid: string | undefined;\n\tname: string;\n\tpath: string | undefined;\n\tbaseKind: \"modified\" | \"created\" | \"read\" | null;\n\tfinalKind: \"modified\" | \"created\" | \"read\" | null;\n\tverb: string;\n\toutcome: string;\n\tresolved: boolean;\n}\n\nconst PROHIBITION_PATTERN = /\\b(do not|don't|never|stop (?:doing|using|changing)|no more)\\b/i;\nconst REVERSAL_PATTERN = /\\b(stop|undo|revert|roll back|never mind|scrap that|forget (?:it|that))\\b/i;\n\nconst FILE_KIND_PRIORITY: Record<NonNullable<ToolCallFact[\"finalKind\"]>, number> = {\n\tread: 1,\n\tcreated: 2,\n\tmodified: 3,\n};\n\nconst TOOL_KIND_BY_NAME: Record<string, \"modified\" | \"created\" | \"read\" | null> = {\n\tread: \"read\",\n\tgrep: \"read\",\n\tfind: \"read\",\n\twrite: \"modified\",\n\tedit: \"modified\",\n\tbash: null,\n};\n\nconst TOOL_VERB_BY_NAME: Record<string, string> = {\n\twrite: \"WRITE\",\n\tedit: \"EDIT\",\n\tbash: \"RUN\",\n\tread: \"READ\",\n\tgrep: \"READ\",\n\tfind: \"READ\",\n};\n\nfunction messageToText(message: AgentMessage): string {\n\tconst rawContent = (message as { content?: unknown }).content;\n\tif (typeof rawContent === \"string\") {\n\t\treturn rawContent;\n\t}\n\tif (!Array.isArray(rawContent)) {\n\t\treturn \"\";\n\t}\n\n\tconst parts: string[] = [];\n\tfor (const block of rawContent) {\n\t\tif (!block || typeof block !== \"object\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif ((block as { type?: unknown }).type === \"text\" && typeof (block as { text?: unknown }).text === \"string\") {\n\t\t\tparts.push((block as { text: string }).text);\n\t\t}\n\t}\n\treturn parts.join(\" \").trim();\n}\n\nfunction assistantToolCallTarget(name: string, rawArgs: unknown): string | undefined {\n\tif (!rawArgs || typeof rawArgs !== \"object\") {\n\t\treturn undefined;\n\t}\n\tconst args = rawArgs as Record<string, unknown>;\n\n\tif (\n\t\t(name === \"read\" || name === \"grep\" || name === \"find\" || name === \"write\" || name === \"edit\") &&\n\t\ttypeof args.path === \"string\" &&\n\t\targs.path.length > 0\n\t) {\n\t\treturn args.path;\n\t}\n\n\tif (name === \"bash\" && typeof args.command === \"string\" && args.command.length > 0) {\n\t\treturn args.command;\n\t}\n\n\treturn undefined;\n}\n\nfunction clampText(text: string, maxLen: number): string {\n\treturn text.length > maxLen ? text.slice(0, maxLen) : text;\n}\n\nfunction firstLine(text: string): string {\n\tfor (const line of text.split(\"\\n\")) {\n\t\tconst trimmed = line.trim();\n\t\tif (trimmed) {\n\t\t\treturn trimmed;\n\t\t}\n\t}\n\treturn \"\";\n}\n\nfunction firstOutcome(message: AgentMessage): string {\n\tif (message.role !== \"toolResult\") {\n\t\treturn \"\";\n\t}\n\treturn firstLine(messageToText(message));\n}\n\nfunction appearsCreated(message: AgentMessage): boolean {\n\tif (message.role !== \"toolResult\") {\n\t\treturn false;\n\t}\n\n\tconst text = messageToText(message).toLowerCase();\n\tif (/(\\bnew file\\b|\\bcreated\\b|\\bcreated file\\b)/.test(text)) {\n\t\treturn true;\n\t}\n\n\tconst details = (message as { details?: unknown }).details;\n\tif (!details || typeof details !== \"object\") {\n\t\treturn false;\n\t}\n\n\tif (typeof (details as { created?: unknown }).created === \"boolean\") {\n\t\treturn (details as { created: boolean }).created;\n\t}\n\tif (typeof (details as { isCreated?: unknown }).isCreated === \"boolean\") {\n\t\treturn (details as { isCreated: boolean }).isCreated;\n\t}\n\tif (typeof (details as { isNewFile?: unknown }).isNewFile === \"boolean\") {\n\t\treturn (details as { isNewFile: boolean }).isNewFile;\n\t}\n\n\treturn false;\n}\n\nfunction splitSentenceLines(text: string): string[] {\n\treturn text\n\t\t.split(/[.!?\\n]+/)\n\t\t.map((line) => line.trim())\n\t\t.filter(Boolean);\n}\n\nfunction getMessageFromEntry(entry: SessionEntry): AgentMessage | undefined {\n\tswitch (entry.type) {\n\t\tcase \"message\":\n\t\t\treturn entry.message;\n\t\tcase \"custom_message\":\n\t\t\treturn {\n\t\t\t\trole: \"custom\",\n\t\t\t\tcustomType: entry.customType,\n\t\t\t\tcontent: entry.content,\n\t\t\t\tdetails: entry.details,\n\t\t\t\tdisplay: entry.display,\n\t\t\t\ttimestamp: new Date(entry.timestamp).getTime(),\n\t\t\t};\n\t\tcase \"branch_summary\":\n\t\t\treturn {\n\t\t\t\trole: \"branchSummary\",\n\t\t\t\tsummary: entry.summary,\n\t\t\t\tfromId: entry.fromId,\n\t\t\t\ttimestamp: new Date(entry.timestamp).getTime(),\n\t\t\t};\n\t\tcase \"compaction\":\n\t\t\treturn undefined;\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nfunction isToolCallBlock(block: unknown): block is { type: \"toolCall\"; id?: string; name: string; arguments: unknown } {\n\tif (!block || typeof block !== \"object\") {\n\t\treturn false;\n\t}\n\tif ((block as { type?: unknown }).type !== \"toolCall\") {\n\t\treturn false;\n\t}\n\tif (typeof (block as { name?: unknown }).name !== \"string\") {\n\t\treturn false;\n\t}\n\tif (!(block as { arguments?: unknown }).arguments) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nfunction toolCallVerb(name: string): string {\n\treturn TOOL_VERB_BY_NAME[name] ?? name.toUpperCase();\n}\n\nexport function extractCompactionFacts(entries: SessionEntry[], start: number, end: number): CompactionFacts {\n\tconst rangeStart = Math.max(0, start);\n\tconst rangeEnd = Math.min(entries.length, Math.max(rangeStart, end));\n\n\tif (rangeStart >= rangeEnd) {\n\t\treturn { files: [], actions: [], prohibitions: [], cancelledText: \"\", activeTaskSource: \"\" };\n\t}\n\n\tconst filesByPath = new Map<string, { path: string; kind: \"modified\" | \"created\" | \"read\"; note: string }>();\n\tconst filesNotes = new Map<string, string>();\n\tconst seenProhibitions = new Set<string>();\n\tconst actionFacts: ToolCallFact[] = [];\n\tconst pendingById = new Map<string, number[]>();\n\tconst pendingByOrder: number[] = [];\n\n\tconst actions: string[] = [];\n\tconst prohibitions: string[] = [];\n\tlet activeTaskSource = \"\";\n\tconst cancelledParts: string[] = [];\n\tconst sinceLastUser: string[] = [];\n\n\tfor (let i = rangeStart; i < rangeEnd; i++) {\n\t\tconst message = getMessageFromEntry(entries[i]);\n\t\tif (!message) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (message.role === \"user\") {\n\t\t\tconst userText = messageToText(message);\n\t\t\tif (REVERSAL_PATTERN.test(userText)) {\n\t\t\t\tcancelledParts.push(...sinceLastUser);\n\t\t\t}\n\t\t\tsinceLastUser.length = 0;\n\n\t\t\tif (userText) {\n\t\t\t\tactiveTaskSource = userText;\n\t\t\t\tfor (const sentence of splitSentenceLines(userText)) {\n\t\t\t\t\tif (!PROHIBITION_PATTERN.test(sentence)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst normalized = clampText(sentence, 160);\n\t\t\t\t\tconst dedupeKey = normalized.toLowerCase();\n\t\t\t\t\tif (!seenProhibitions.has(dedupeKey)) {\n\t\t\t\t\t\tseenProhibitions.add(dedupeKey);\n\t\t\t\t\t\tprohibitions.push(normalized);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (message.role === \"assistant\" || message.role === \"toolResult\") {\n\t\t\tconst messageText = messageToText(message);\n\t\t\tif (messageText) {\n\t\t\t\tsinceLastUser.push(messageText);\n\t\t\t}\n\t\t}\n\n\t\tif (message.role === \"assistant\" && Array.isArray(message.content)) {\n\t\t\tfor (const block of message.content) {\n\t\t\t\tif (!isToolCallBlock(block)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst name = block.name;\n\t\t\t\tconst verb = toolCallVerb(name);\n\t\t\t\tconst path = assistantToolCallTarget(name, block.arguments);\n\t\t\t\tconst baseKind = TOOL_KIND_BY_NAME[name] ?? null;\n\t\t\t\tconst fact: ToolCallFact = {\n\t\t\t\t\tid: block.id,\n\t\t\t\t\tname,\n\t\t\t\t\tpath,\n\t\t\t\t\tbaseKind,\n\t\t\t\t\tfinalKind: baseKind,\n\t\t\t\t\tverb,\n\t\t\t\t\toutcome: \"\",\n\t\t\t\t\tresolved: false,\n\t\t\t\t};\n\t\t\t\tactionFacts.push(fact);\n\t\t\t\tconst index = actionFacts.length - 1;\n\t\t\t\tpendingByOrder.push(index);\n\t\t\t\tif (fact.id) {\n\t\t\t\t\tconst bucket = pendingById.get(fact.id) ?? [];\n\t\t\t\t\tbucket.push(index);\n\t\t\t\t\tpendingById.set(fact.id, bucket);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (message.role === \"toolResult\") {\n\t\t\tconst toolCallId = (message as { toolCallId?: string }).toolCallId;\n\t\t\tconst toolName = (message as { toolName?: string }).toolName;\n\t\t\tlet matchedIndex: number | undefined;\n\n\t\t\tif (toolCallId) {\n\t\t\t\tconst bucket = pendingById.get(toolCallId);\n\t\t\t\tif (bucket && bucket.length > 0) {\n\t\t\t\t\tmatchedIndex = bucket.shift();\n\t\t\t\t\tif (bucket.length === 0) {\n\t\t\t\t\t\tpendingById.delete(toolCallId);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (matchedIndex === undefined && toolName) {\n\t\t\t\tfor (const index of pendingByOrder) {\n\t\t\t\t\tconst candidate = actionFacts[index];\n\t\t\t\t\tif (!candidate.resolved && candidate.name === toolName) {\n\t\t\t\t\t\tmatchedIndex = index;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (matchedIndex !== undefined) {\n\t\t\t\tconst fact = actionFacts[matchedIndex];\n\t\t\t\tconst outcome = firstOutcome(message);\n\t\t\t\tfact.outcome = outcome;\n\t\t\t\tfact.resolved = true;\n\t\t\t\tif (fact.baseKind === \"modified\" && appearsCreated(message)) {\n\t\t\t\t\tfact.finalKind = \"created\";\n\t\t\t\t}\n\n\t\t\t\tconst finalOutcome = clampText(fact.outcome, 80);\n\t\t\t\tfact.outcome = finalOutcome;\n\t\t\t\tif (fact.path && fact.finalKind) {\n\t\t\t\t\tconst existing = filesByPath.get(fact.path);\n\t\t\t\t\tconst nextKind = fact.finalKind;\n\t\t\t\t\tconst note = fact.verb;\n\t\t\t\t\tif (!existing || FILE_KIND_PRIORITY[nextKind] > FILE_KIND_PRIORITY[existing.kind]) {\n\t\t\t\t\t\tfilesByPath.set(fact.path, { path: fact.path, kind: nextKind, note });\n\t\t\t\t\t} else if (existing.kind === nextKind) {\n\t\t\t\t\t\texisting.note = `${note}: ${finalOutcome}`;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst pendingPos = pendingByOrder.indexOf(matchedIndex);\n\t\t\t\tif (pendingPos >= 0) {\n\t\t\t\t\tpendingByOrder.splice(pendingPos, 1);\n\t\t\t\t}\n\t\t\t\tif (toolCallId) {\n\t\t\t\t\tconst bucket = pendingById.get(toolCallId);\n\t\t\t\t\tif (bucket) {\n\t\t\t\t\t\tconst pos = bucket.indexOf(matchedIndex);\n\t\t\t\t\t\tif (pos >= 0) {\n\t\t\t\t\t\t\tbucket.splice(pos, 1);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (bucket.length === 0) {\n\t\t\t\t\t\t\tpendingById.delete(toolCallId);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const action of actionFacts) {\n\t\tif (action.finalKind && action.path && !filesByPath.has(action.path)) {\n\t\t\tconst note = action.outcome ? `${action.verb}: ${action.outcome}` : action.verb;\n\t\t\tfilesByPath.set(action.path, { path: action.path, kind: action.finalKind, note });\n\t\t}\n\n\t\tactions.push(`${action.verb} ${action.path ?? \"(unknown)\"} — ${action.outcome}`);\n\t}\n\n\tfor (const file of filesByPath.values()) {\n\t\tif (!file.note && filesNotes.has(file.path)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (file.note && filesNotes.get(file.path) === file.note) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (file.note) {\n\t\t\tfilesNotes.set(file.path, file.note);\n\t\t}\n\t}\n\n\tfor (const file of filesByPath.values()) {\n\t\tconst suffix = filesNotes.get(file.path);\n\t\tif (suffix) {\n\t\t\tfile.note = suffix;\n\t\t}\n\t}\n\n\treturn {\n\t\tfiles: Array.from(filesByPath.values()).sort((a, b) => {\n\t\t\tif (a.path === b.path) {\n\t\t\t\treturn FILE_KIND_PRIORITY[a.kind] - FILE_KIND_PRIORITY[b.kind];\n\t\t\t}\n\t\t\treturn a.path.localeCompare(b.path);\n\t\t}),\n\t\tactions,\n\t\tprohibitions,\n\t\tcancelledText: cancelledParts.join(\"\\n\"),\n\t\tactiveTaskSource,\n\t};\n}\n\nexport function renderFactsBlock(facts: CompactionFacts): string {\n\tconst lines: string[] = [\"files:\"];\n\tfor (const file of facts.files) {\n\t\tlines.push(`${file.kind}: ${file.path} — ${file.note}`);\n\t}\n\tlines.push(\"actions:\");\n\tfor (const action of facts.actions) {\n\t\tlines.push(action);\n\t}\n\tlines.push(\"prohibitions:\");\n\tfor (const prohibition of facts.prohibitions) {\n\t\tlines.push(prohibition);\n\t}\n\treturn lines.join(\"\\n\");\n}\n"]}
@@ -3,5 +3,9 @@
3
3
  */
4
4
  export * from "./branch-summarization.ts";
5
5
  export * from "./compaction.ts";
6
+ export * from "./extraction.ts";
7
+ export * from "./loop.ts";
8
+ export * from "./token-budget.ts";
6
9
  export * from "./utils.ts";
10
+ export * from "./verification.ts";
7
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compaction/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC","sourcesContent":["/**\n * Compaction and summarization utilities.\n */\n\nexport * from \"./branch-summarization.ts\";\nexport * from \"./compaction.ts\";\nexport * from \"./utils.ts\";\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compaction/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC","sourcesContent":["/**\n * Compaction and summarization utilities.\n */\n\nexport * from \"./branch-summarization.ts\";\nexport * from \"./compaction.ts\";\nexport * from \"./extraction.ts\";\nexport * from \"./loop.ts\";\nexport * from \"./token-budget.ts\";\nexport * from \"./utils.ts\";\nexport * from \"./verification.ts\";\n"]}
@@ -3,5 +3,9 @@
3
3
  */
4
4
  export * from "./branch-summarization.js";
5
5
  export * from "./compaction.js";
6
+ export * from "./extraction.js";
7
+ export * from "./loop.js";
8
+ export * from "./token-budget.js";
6
9
  export * from "./utils.js";
10
+ export * from "./verification.js";
7
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compaction/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC","sourcesContent":["/**\n * Compaction and summarization utilities.\n */\n\nexport * from \"./branch-summarization.ts\";\nexport * from \"./compaction.ts\";\nexport * from \"./utils.ts\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compaction/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC","sourcesContent":["/**\n * Compaction and summarization utilities.\n */\n\nexport * from \"./branch-summarization.ts\";\nexport * from \"./compaction.ts\";\nexport * from \"./extraction.ts\";\nexport * from \"./loop.ts\";\nexport * from \"./token-budget.ts\";\nexport * from \"./utils.ts\";\nexport * from \"./verification.ts\";\n"]}
@@ -0,0 +1,52 @@
1
+ import type { Model } from "@caupulican/pi-ai";
2
+ import type { SessionEntry } from "../session/session-manager.ts";
3
+ import type { CompactionResult } from "./compaction.ts";
4
+ export interface CompactionCycleParams {
5
+ modelTier: "cheap" | "session";
6
+ keepRecentTokens: number;
7
+ chunked: boolean;
8
+ deterministicOnly: boolean;
9
+ }
10
+ export interface ModelAndAuth {
11
+ model: Model<any>;
12
+ apiKey?: string;
13
+ headers?: Record<string, string>;
14
+ failure?: string;
15
+ }
16
+ export interface CompactionLoopDeps {
17
+ measureLiveTokens(): number;
18
+ getTriggerThreshold(): number;
19
+ getMargin(): number;
20
+ getBranch(): SessionEntry[];
21
+ resolveModelAndAuth(modelTier: CompactionCycleParams["modelTier"]): Promise<ModelAndAuth>;
22
+ summarizeAndVerify(params: CompactionCycleParams, model: Model<any>, apiKey: string | undefined, headers: Record<string, string> | undefined, branch: SessionEntry[]): Promise<{
23
+ result: CompactionResult;
24
+ }>;
25
+ buildDeterministicCheckpoint(): Promise<{
26
+ result: CompactionResult;
27
+ }> | {
28
+ result: CompactionResult;
29
+ };
30
+ apply(result: CompactionResult): Promise<void> | void;
31
+ onTransition(info: {
32
+ cycle: number;
33
+ from: string;
34
+ cause: string;
35
+ }): void;
36
+ getBaseKeepRecentTokens?(): number;
37
+ signal?: AbortSignal;
38
+ }
39
+ export type CompactionLoopOutcome = {
40
+ kind: "success";
41
+ result: CompactionResult;
42
+ cycles: number;
43
+ } | {
44
+ kind: "skip";
45
+ reason: string;
46
+ } | {
47
+ kind: "failed";
48
+ reason: string;
49
+ cycles: number;
50
+ };
51
+ export declare function runCompactionLoop(deps: CompactionLoopDeps): Promise<CompactionLoopOutcome>;
52
+ //# sourceMappingURL=loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/compaction/loop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD,MAAM,WAAW,qBAAqB;IACrC,SAAS,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC5B,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IAClC,iBAAiB,IAAI,MAAM,CAAC;IAC5B,mBAAmB,IAAI,MAAM,CAAC;IAC9B,SAAS,IAAI,MAAM,CAAC;IACpB,SAAS,IAAI,YAAY,EAAE,CAAC;IAC5B,mBAAmB,CAAC,SAAS,EAAE,qBAAqB,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1F,kBAAkB,CACjB,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAC3C,MAAM,EAAE,YAAY,EAAE,GACpB,OAAO,CAAC;QAAE,MAAM,EAAE,gBAAgB,CAAA;KAAE,CAAC,CAAC;IACzC,4BAA4B,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,gBAAgB,CAAA;KAAE,CAAC,GAAG;QAAE,MAAM,EAAE,gBAAgB,CAAA;KAAE,CAAC;IACrG,KAAK,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACtD,YAAY,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzE,uBAAuB,CAAC,IAAI,MAAM,CAAC;IACnC,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,MAAM,qBAAqB,GAC9B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAMtD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAgGhG","sourcesContent":["import type { Model } from \"@caupulican/pi-ai\";\nimport type { SessionEntry } from \"../session/session-manager.ts\";\nimport type { CompactionResult } from \"./compaction.ts\";\n\nexport interface CompactionCycleParams {\n\tmodelTier: \"cheap\" | \"session\";\n\tkeepRecentTokens: number;\n\tchunked: boolean;\n\tdeterministicOnly: boolean;\n}\n\nexport interface ModelAndAuth {\n\tmodel: Model<any>;\n\tapiKey?: string;\n\theaders?: Record<string, string>;\n\tfailure?: string;\n}\n\nexport interface CompactionLoopDeps {\n\tmeasureLiveTokens(): number;\n\tgetTriggerThreshold(): number;\n\tgetMargin(): number;\n\tgetBranch(): SessionEntry[];\n\tresolveModelAndAuth(modelTier: CompactionCycleParams[\"modelTier\"]): Promise<ModelAndAuth>;\n\tsummarizeAndVerify(\n\t\tparams: CompactionCycleParams,\n\t\tmodel: Model<any>,\n\t\tapiKey: string | undefined,\n\t\theaders: Record<string, string> | undefined,\n\t\tbranch: SessionEntry[],\n\t): Promise<{ result: CompactionResult }>;\n\tbuildDeterministicCheckpoint(): Promise<{ result: CompactionResult }> | { result: CompactionResult };\n\tapply(result: CompactionResult): Promise<void> | void;\n\tonTransition(info: { cycle: number; from: string; cause: string }): void;\n\tgetBaseKeepRecentTokens?(): number;\n\tsignal?: AbortSignal;\n}\n\nexport type CompactionLoopOutcome =\n\t| { kind: \"success\"; result: CompactionResult; cycles: number }\n\t| { kind: \"skip\"; reason: string }\n\t| { kind: \"failed\"; reason: string; cycles: number };\n\nconst MAX_CYCLES = 4;\nconst MAX_LLM_CYCLES = 3;\nconst DEFAULT_KEEP_RECENT = 20_000;\n\nexport async function runCompactionLoop(deps: CompactionLoopDeps): Promise<CompactionLoopOutcome> {\n\tlet lastCause = \"start\";\n\tlet lastParams: CompactionCycleParams | undefined;\n\tlet lastObservedTokens: number | undefined;\n\tlet appliedResult: CompactionResult | undefined;\n\tlet baseKeepRecent = deps.getBaseKeepRecentTokens ? deps.getBaseKeepRecentTokens() : DEFAULT_KEEP_RECENT;\n\tif (!Number.isFinite(baseKeepRecent) || baseKeepRecent <= 0) {\n\t\tbaseKeepRecent = DEFAULT_KEEP_RECENT;\n\t}\n\n\tfor (let cycle = 1; cycle <= MAX_CYCLES; cycle++) {\n\t\tif (deps.signal?.aborted) {\n\t\t\treturn { kind: \"failed\", reason: \"aborted\", cycles: cycle - 1 };\n\t\t}\n\n\t\tconst branch = deps.getBranch();\n\t\tif (branch.length > 0 && branch[branch.length - 1]?.type === \"compaction\") {\n\t\t\tif (appliedResult) {\n\t\t\t\treturn { kind: \"success\", result: appliedResult, cycles: cycle - 1 };\n\t\t\t}\n\t\t\treturn { kind: \"skip\", reason: \"already compacted\" };\n\t\t}\n\n\t\tconst observedTokens = deps.measureLiveTokens();\n\t\tif (observedTokens <= deps.getTriggerThreshold()) {\n\t\t\treturn {\n\t\t\t\tkind: \"skip\",\n\t\t\t\treason:\n\t\t\t\t\tbranch.length > 0 && branch[branch.length - 1]?.type === \"compaction\"\n\t\t\t\t\t\t? \"already compacted\"\n\t\t\t\t\t\t: \"within threshold\",\n\t\t\t};\n\t\t}\n\n\t\tconst selectedParams = selectCycleParams(cycle, lastCause, lastParams, baseKeepRecent);\n\t\tconst params = enforceMonotonicProgress(selectedParams, lastParams, observedTokens, lastObservedTokens);\n\t\tlastObservedTokens = observedTokens;\n\t\tlastParams = params;\n\n\t\tif (params.deterministicOnly || cycle > MAX_LLM_CYCLES) {\n\t\t\tif (deps.signal?.aborted) {\n\t\t\t\treturn { kind: \"failed\", reason: \"aborted\", cycles: cycle - 1 };\n\t\t\t}\n\t\t\tconst { result } = await Promise.resolve(deps.buildDeterministicCheckpoint());\n\t\t\tawait deps.apply(result);\n\t\t\treturn { kind: \"success\", result, cycles: cycle };\n\t\t}\n\n\t\tlet modelInfo: ModelAndAuth;\n\t\ttry {\n\t\t\tmodelInfo = await deps.resolveModelAndAuth(params.modelTier);\n\t\t} catch {\n\t\t\tlastCause = \"auth-failed\";\n\t\t\tdeps.onTransition({ cycle: cycle + 1, from: \"step0\", cause: lastCause });\n\t\t\tcontinue;\n\t\t}\n\t\tif (modelInfo.failure) {\n\t\t\tlastCause = \"auth-failed\";\n\t\t\tdeps.onTransition({ cycle: cycle + 1, from: \"step0\", cause: lastCause });\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet result: CompactionResult;\n\t\ttry {\n\t\t\t({ result } = await deps.summarizeAndVerify(\n\t\t\t\tparams,\n\t\t\t\tmodelInfo.model,\n\t\t\t\tmodelInfo.apiKey,\n\t\t\t\tmodelInfo.headers,\n\t\t\t\tbranch,\n\t\t\t));\n\t\t} catch (error) {\n\t\t\tlastCause = mapFailureCause(error);\n\t\t\tif (lastCause === \"aborted\") {\n\t\t\t\treturn { kind: \"failed\", reason: \"aborted\", cycles: cycle };\n\t\t\t}\n\t\t\tdeps.onTransition({ cycle: cycle + 1, from: \"step3\", cause: lastCause });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (deps.signal?.aborted) {\n\t\t\treturn { kind: \"failed\", reason: \"aborted\", cycles: cycle };\n\t\t}\n\t\tawait deps.apply(result);\n\t\tappliedResult = result;\n\n\t\tconst measuredAfter = deps.measureLiveTokens();\n\t\tif (measuredAfter <= deps.getTriggerThreshold() - deps.getMargin()) {\n\t\t\treturn { kind: \"success\", result, cycles: cycle };\n\t\t}\n\n\t\tlastCause = \"effect-not-restored\";\n\t\tdeps.onTransition({ cycle: cycle + 1, from: \"step5\", cause: lastCause });\n\t}\n\n\treturn { kind: \"failed\", reason: \"exhausted-compaction-cycles\", cycles: MAX_CYCLES };\n}\n\nfunction selectCycleParams(\n\tcycle: number,\n\tcause: string,\n\tlastParams: CompactionCycleParams | undefined,\n\tbaseKeepRecent: number,\n): CompactionCycleParams {\n\tif (cycle >= MAX_CYCLES) {\n\t\treturn {\n\t\t\tmodelTier: lastParams?.modelTier ?? \"session\",\n\t\t\tkeepRecentTokens: Math.max(1, Math.floor((lastParams?.keepRecentTokens ?? baseKeepRecent) / 2)),\n\t\t\tchunked: true,\n\t\t\tdeterministicOnly: true,\n\t\t};\n\t}\n\n\tif (!lastParams) {\n\t\treturn {\n\t\t\tmodelTier: \"cheap\",\n\t\t\tkeepRecentTokens: Math.max(1, baseKeepRecent),\n\t\t\tchunked: false,\n\t\t\tdeterministicOnly: false,\n\t\t};\n\t}\n\n\tconst params: CompactionCycleParams = {\n\t\t...lastParams,\n\t\tdeterministicOnly: false,\n\t};\n\n\tif (cause === \"gate-failed\" || cause === \"auth-failed\") {\n\t\tparams.modelTier = \"session\";\n\t} else if (cause === \"input-overflow\") {\n\t\tparams.chunked = true;\n\t} else if (cause === \"effect-not-restored\") {\n\t\tparams.chunked = true;\n\t\tparams.keepRecentTokens = Math.max(1, Math.floor(lastParams.keepRecentTokens / 2));\n\t}\n\n\treturn params;\n}\n\nfunction enforceMonotonicProgress(\n\tparams: CompactionCycleParams,\n\tlastParams: CompactionCycleParams | undefined,\n\tobservedTokens: number,\n\tlastObservedTokens: number | undefined,\n): CompactionCycleParams {\n\tif (\n\t\t!lastParams ||\n\t\tparams.deterministicOnly ||\n\t\t(lastObservedTokens !== undefined && observedTokens < lastObservedTokens)\n\t) {\n\t\treturn params;\n\t}\n\tif (!sameParams(params, lastParams)) {\n\t\treturn params;\n\t}\n\n\tconst keepRecentTokens = Math.max(1, Math.floor(params.keepRecentTokens / 2));\n\tif (keepRecentTokens !== params.keepRecentTokens) {\n\t\treturn { ...params, chunked: true, keepRecentTokens };\n\t}\n\tif (!params.chunked) {\n\t\treturn { ...params, chunked: true };\n\t}\n\treturn { ...params, modelTier: params.modelTier === \"cheap\" ? \"session\" : \"cheap\" };\n}\n\nfunction sameParams(a: CompactionCycleParams, b: CompactionCycleParams): boolean {\n\treturn (\n\t\ta.modelTier === b.modelTier &&\n\t\ta.keepRecentTokens === b.keepRecentTokens &&\n\t\ta.chunked === b.chunked &&\n\t\ta.deterministicOnly === b.deterministicOnly\n\t);\n}\n\nfunction mapFailureCause(error: unknown): string {\n\tconst message = error instanceof Error ? error.message : typeof error === \"string\" ? error : \"\";\n\tif (message.includes(\"gate-failed\")) return \"gate-failed\";\n\tif (message.includes(\"input-overflow\")) return \"input-overflow\";\n\tif (message.includes(\"auto-compaction-cancelled\") || message.includes(\"aborted\")) return \"aborted\";\n\tif (message.includes(\"auth\") || message.includes(\"api key\") || message.includes(\"not compacted\"))\n\t\treturn \"auth-failed\";\n\treturn \"unknown-failure\";\n}\n"]}
@@ -0,0 +1,154 @@
1
+ const MAX_CYCLES = 4;
2
+ const MAX_LLM_CYCLES = 3;
3
+ const DEFAULT_KEEP_RECENT = 20_000;
4
+ export async function runCompactionLoop(deps) {
5
+ let lastCause = "start";
6
+ let lastParams;
7
+ let lastObservedTokens;
8
+ let appliedResult;
9
+ let baseKeepRecent = deps.getBaseKeepRecentTokens ? deps.getBaseKeepRecentTokens() : DEFAULT_KEEP_RECENT;
10
+ if (!Number.isFinite(baseKeepRecent) || baseKeepRecent <= 0) {
11
+ baseKeepRecent = DEFAULT_KEEP_RECENT;
12
+ }
13
+ for (let cycle = 1; cycle <= MAX_CYCLES; cycle++) {
14
+ if (deps.signal?.aborted) {
15
+ return { kind: "failed", reason: "aborted", cycles: cycle - 1 };
16
+ }
17
+ const branch = deps.getBranch();
18
+ if (branch.length > 0 && branch[branch.length - 1]?.type === "compaction") {
19
+ if (appliedResult) {
20
+ return { kind: "success", result: appliedResult, cycles: cycle - 1 };
21
+ }
22
+ return { kind: "skip", reason: "already compacted" };
23
+ }
24
+ const observedTokens = deps.measureLiveTokens();
25
+ if (observedTokens <= deps.getTriggerThreshold()) {
26
+ return {
27
+ kind: "skip",
28
+ reason: branch.length > 0 && branch[branch.length - 1]?.type === "compaction"
29
+ ? "already compacted"
30
+ : "within threshold",
31
+ };
32
+ }
33
+ const selectedParams = selectCycleParams(cycle, lastCause, lastParams, baseKeepRecent);
34
+ const params = enforceMonotonicProgress(selectedParams, lastParams, observedTokens, lastObservedTokens);
35
+ lastObservedTokens = observedTokens;
36
+ lastParams = params;
37
+ if (params.deterministicOnly || cycle > MAX_LLM_CYCLES) {
38
+ if (deps.signal?.aborted) {
39
+ return { kind: "failed", reason: "aborted", cycles: cycle - 1 };
40
+ }
41
+ const { result } = await Promise.resolve(deps.buildDeterministicCheckpoint());
42
+ await deps.apply(result);
43
+ return { kind: "success", result, cycles: cycle };
44
+ }
45
+ let modelInfo;
46
+ try {
47
+ modelInfo = await deps.resolveModelAndAuth(params.modelTier);
48
+ }
49
+ catch {
50
+ lastCause = "auth-failed";
51
+ deps.onTransition({ cycle: cycle + 1, from: "step0", cause: lastCause });
52
+ continue;
53
+ }
54
+ if (modelInfo.failure) {
55
+ lastCause = "auth-failed";
56
+ deps.onTransition({ cycle: cycle + 1, from: "step0", cause: lastCause });
57
+ continue;
58
+ }
59
+ let result;
60
+ try {
61
+ ({ result } = await deps.summarizeAndVerify(params, modelInfo.model, modelInfo.apiKey, modelInfo.headers, branch));
62
+ }
63
+ catch (error) {
64
+ lastCause = mapFailureCause(error);
65
+ if (lastCause === "aborted") {
66
+ return { kind: "failed", reason: "aborted", cycles: cycle };
67
+ }
68
+ deps.onTransition({ cycle: cycle + 1, from: "step3", cause: lastCause });
69
+ continue;
70
+ }
71
+ if (deps.signal?.aborted) {
72
+ return { kind: "failed", reason: "aborted", cycles: cycle };
73
+ }
74
+ await deps.apply(result);
75
+ appliedResult = result;
76
+ const measuredAfter = deps.measureLiveTokens();
77
+ if (measuredAfter <= deps.getTriggerThreshold() - deps.getMargin()) {
78
+ return { kind: "success", result, cycles: cycle };
79
+ }
80
+ lastCause = "effect-not-restored";
81
+ deps.onTransition({ cycle: cycle + 1, from: "step5", cause: lastCause });
82
+ }
83
+ return { kind: "failed", reason: "exhausted-compaction-cycles", cycles: MAX_CYCLES };
84
+ }
85
+ function selectCycleParams(cycle, cause, lastParams, baseKeepRecent) {
86
+ if (cycle >= MAX_CYCLES) {
87
+ return {
88
+ modelTier: lastParams?.modelTier ?? "session",
89
+ keepRecentTokens: Math.max(1, Math.floor((lastParams?.keepRecentTokens ?? baseKeepRecent) / 2)),
90
+ chunked: true,
91
+ deterministicOnly: true,
92
+ };
93
+ }
94
+ if (!lastParams) {
95
+ return {
96
+ modelTier: "cheap",
97
+ keepRecentTokens: Math.max(1, baseKeepRecent),
98
+ chunked: false,
99
+ deterministicOnly: false,
100
+ };
101
+ }
102
+ const params = {
103
+ ...lastParams,
104
+ deterministicOnly: false,
105
+ };
106
+ if (cause === "gate-failed" || cause === "auth-failed") {
107
+ params.modelTier = "session";
108
+ }
109
+ else if (cause === "input-overflow") {
110
+ params.chunked = true;
111
+ }
112
+ else if (cause === "effect-not-restored") {
113
+ params.chunked = true;
114
+ params.keepRecentTokens = Math.max(1, Math.floor(lastParams.keepRecentTokens / 2));
115
+ }
116
+ return params;
117
+ }
118
+ function enforceMonotonicProgress(params, lastParams, observedTokens, lastObservedTokens) {
119
+ if (!lastParams ||
120
+ params.deterministicOnly ||
121
+ (lastObservedTokens !== undefined && observedTokens < lastObservedTokens)) {
122
+ return params;
123
+ }
124
+ if (!sameParams(params, lastParams)) {
125
+ return params;
126
+ }
127
+ const keepRecentTokens = Math.max(1, Math.floor(params.keepRecentTokens / 2));
128
+ if (keepRecentTokens !== params.keepRecentTokens) {
129
+ return { ...params, chunked: true, keepRecentTokens };
130
+ }
131
+ if (!params.chunked) {
132
+ return { ...params, chunked: true };
133
+ }
134
+ return { ...params, modelTier: params.modelTier === "cheap" ? "session" : "cheap" };
135
+ }
136
+ function sameParams(a, b) {
137
+ return (a.modelTier === b.modelTier &&
138
+ a.keepRecentTokens === b.keepRecentTokens &&
139
+ a.chunked === b.chunked &&
140
+ a.deterministicOnly === b.deterministicOnly);
141
+ }
142
+ function mapFailureCause(error) {
143
+ const message = error instanceof Error ? error.message : typeof error === "string" ? error : "";
144
+ if (message.includes("gate-failed"))
145
+ return "gate-failed";
146
+ if (message.includes("input-overflow"))
147
+ return "input-overflow";
148
+ if (message.includes("auto-compaction-cancelled") || message.includes("aborted"))
149
+ return "aborted";
150
+ if (message.includes("auth") || message.includes("api key") || message.includes("not compacted"))
151
+ return "auth-failed";
152
+ return "unknown-failure";
153
+ }
154
+ //# sourceMappingURL=loop.js.map