@wrongstack/core 0.119.1 → 0.141.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-bridge-PRAbEX-9.d.ts → agent-bridge-r9y6gdn4.d.ts} +1 -1
- package/dist/{agent-subagent-runner-DXWImnNV.d.ts → agent-subagent-runner-1GeQE_L0.d.ts} +7 -7
- package/dist/{brain-NZZqL8sn.d.ts → brain-Cp_3GIS2.d.ts} +14 -3
- package/dist/{compactor-C3stu664.d.ts → compactor-BueGt7LG.d.ts} +9 -1
- package/dist/{config-DZV9VK9n.d.ts → config-BaVThgnT.d.ts} +1 -1
- package/dist/{context-hvXINa6a.d.ts → context-C7G_MtLV.d.ts} +7 -0
- package/dist/coordination/index.d.ts +12 -12
- package/dist/coordination/index.js +31 -17
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +24 -24
- package/dist/defaults/index.js +638 -681
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +14 -14
- package/dist/execution/index.js +326 -317
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/{goal-preamble-COpt55W8.d.ts → goal-preamble-iuIUTQwk.d.ts} +47 -17
- package/dist/{index-Cj3le7TI.d.ts → index-BZdezm3g.d.ts} +8 -8
- package/dist/{index-4VVHuTZF.d.ts → index-CPweVoFM.d.ts} +5 -5
- package/dist/index.d.ts +47 -47
- package/dist/index.js +455 -503
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +64 -19
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +9 -9
- package/dist/kernel/index.js.map +1 -1
- package/dist/{llm-selector-Bmx4PsVM.d.ts → llm-selector-CP72f1lC.d.ts} +2 -2
- package/dist/{mcp-servers-CofNHOqn.d.ts → mcp-servers-Bl5LTvQg.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +53 -20
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-BzlsjKcy.d.ts → models-registry-D90K9UnM.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-CmDmUgq2.d.ts → multi-agent-coordinator-QWEzJDlm.d.ts} +1 -1
- package/dist/{null-fleet-bus-CyH2YqP4.d.ts → null-fleet-bus-BUyfqh23.d.ts} +13 -10
- package/dist/observability/index.d.ts +2 -2
- package/dist/{parallel-eternal-engine-uxVm4y4V.d.ts → parallel-eternal-engine-Dj2SYzha.d.ts} +24 -11
- package/dist/{path-resolver-gfoGxpdd.d.ts → path-resolver-DRjQBkoO.d.ts} +3 -3
- package/dist/{permission-DhLrgA30.d.ts → permission-B7nKnEvQ.d.ts} +1 -1
- package/dist/{permission-policy-DGOvne7i.d.ts → permission-policy-8-6zBmfA.d.ts} +2 -13
- package/dist/{pipeline-CqF7uju2.d.ts → pipeline-BG7UgbDc.d.ts} +2 -2
- package/dist/{plan-templates-DoKhzenv.d.ts → plan-templates-CkKNPU3I.d.ts} +5 -5
- package/dist/{provider-runner-BPlI5sPZ.d.ts → provider-runner-BNpuIyOL.d.ts} +3 -3
- package/dist/{retry-policy-B4Q-FZgE.d.ts → retry-policy-rutAfVeR.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +10 -1
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-B7GnL6o9.d.ts → secret-vault-BTcC_T5v.d.ts} +3 -2
- package/dist/security/index.d.ts +5 -7
- package/dist/security/index.js +4 -35
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-BakeMnWR.d.ts → selector-4vDFZKt3.d.ts} +1 -1
- package/dist/{session-event-bridge-DE5Oekwx.d.ts → session-event-bridge-DWlvglC2.d.ts} +2 -2
- package/dist/{session-reader-DFC81-4p.d.ts → session-reader-BAtCxdaw.d.ts} +1 -1
- package/dist/storage/index.d.ts +13 -13
- package/dist/storage/index.js +98 -101
- package/dist/storage/index.js.map +1 -1
- package/dist/types/index.d.ts +18 -18
- package/dist/types/index.js +152 -102
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +37 -13
- package/dist/utils/index.js +60 -32
- package/dist/utils/index.js.map +1 -1
- package/package.json +2 -2
package/dist/execution/index.js
CHANGED
|
@@ -16,6 +16,17 @@ function expectDefined(value, label) {
|
|
|
16
16
|
|
|
17
17
|
// src/utils/token-estimate.ts
|
|
18
18
|
var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
|
|
19
|
+
var CALIBRATION_GLOBAL_KEY = "__global__";
|
|
20
|
+
var _cals = /* @__PURE__ */ new Map();
|
|
21
|
+
function calState(key) {
|
|
22
|
+
let state = _cals.get(key);
|
|
23
|
+
if (!state) {
|
|
24
|
+
state = { ratio: 1, count: 0, prevEst: 0 };
|
|
25
|
+
_cals.set(key, state);
|
|
26
|
+
}
|
|
27
|
+
return state;
|
|
28
|
+
}
|
|
29
|
+
var MIN_SAMPLES_FOR_CALIBRATION = 3;
|
|
19
30
|
var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
|
|
20
31
|
var ESTIMATE_CACHE_MAX_SIZE = 1e4;
|
|
21
32
|
function getCachedEstimate(key, compute) {
|
|
@@ -47,10 +58,25 @@ function estimateToolResultTokens(content) {
|
|
|
47
58
|
function estimateTextTokens(text) {
|
|
48
59
|
return RoughTokenEstimate(text);
|
|
49
60
|
}
|
|
61
|
+
function estimateMessageTokens(messages) {
|
|
62
|
+
let total = 0;
|
|
63
|
+
for (const m of messages) {
|
|
64
|
+
if (typeof m.content === "string") {
|
|
65
|
+
total += estimateTextTokens(m.content);
|
|
66
|
+
} else {
|
|
67
|
+
for (const b of m.content) {
|
|
68
|
+
if (b.type === "text") total += estimateTextTokens(b.text);
|
|
69
|
+
else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
|
|
70
|
+
else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return total;
|
|
75
|
+
}
|
|
50
76
|
function estimateToolDefTokens(tool) {
|
|
51
77
|
return RoughTokenEstimate(tool.name) + RoughTokenEstimate(tool.description ?? "") + RoughTokenEstimate(JSON.stringify(tool.inputSchema));
|
|
52
78
|
}
|
|
53
|
-
function estimateRequestTokens(messages, systemPrompt, tools) {
|
|
79
|
+
function estimateRequestTokens(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
|
|
54
80
|
let messagesTokens = 0;
|
|
55
81
|
if (typeof messages === "string") {
|
|
56
82
|
messagesTokens = RoughTokenEstimate(messages);
|
|
@@ -89,6 +115,7 @@ function estimateRequestTokens(messages, systemPrompt, tools) {
|
|
|
89
115
|
toolsTokens += estimateToolDefTokens(t);
|
|
90
116
|
}
|
|
91
117
|
const total = messagesTokens + systemTokens + toolsTokens;
|
|
118
|
+
calState(calibrationKey).prevEst = total;
|
|
92
119
|
return {
|
|
93
120
|
messages: messagesTokens,
|
|
94
121
|
systemPrompt: systemTokens,
|
|
@@ -96,8 +123,18 @@ function estimateRequestTokens(messages, systemPrompt, tools) {
|
|
|
96
123
|
total
|
|
97
124
|
};
|
|
98
125
|
}
|
|
99
|
-
function estimateRequestTokensCalibrated(messages, systemPrompt, tools) {
|
|
100
|
-
const result = estimateRequestTokens(messages, systemPrompt, tools);
|
|
126
|
+
function estimateRequestTokensCalibrated(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
|
|
127
|
+
const result = estimateRequestTokens(messages, systemPrompt, tools, calibrationKey);
|
|
128
|
+
const cal = calState(calibrationKey);
|
|
129
|
+
if (cal.count >= MIN_SAMPLES_FOR_CALIBRATION) {
|
|
130
|
+
const safeRatio = Math.min(1.5, Math.max(0.5, cal.ratio));
|
|
131
|
+
return {
|
|
132
|
+
messages: Math.round(result.messages * safeRatio),
|
|
133
|
+
systemPrompt: Math.round(result.systemPrompt * safeRatio),
|
|
134
|
+
tools: Math.round(result.tools * safeRatio),
|
|
135
|
+
total: Math.round(result.total * safeRatio)
|
|
136
|
+
};
|
|
137
|
+
}
|
|
101
138
|
return result;
|
|
102
139
|
}
|
|
103
140
|
|
|
@@ -193,34 +230,149 @@ function isEmptyMessage(msg) {
|
|
|
193
230
|
return msg.content.length === 0;
|
|
194
231
|
}
|
|
195
232
|
|
|
233
|
+
// src/types/blocks.ts
|
|
234
|
+
function isTextBlock(b) {
|
|
235
|
+
return b.type === "text";
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/execution/compaction-core.ts
|
|
239
|
+
var estimateMessages = estimateMessageTokens;
|
|
240
|
+
function hasTextContent(m) {
|
|
241
|
+
if (typeof m.content === "string") return m.content.trim().length > 0;
|
|
242
|
+
return m.content.some((b) => b.type === "text" && b.text.trim().length > 0);
|
|
243
|
+
}
|
|
244
|
+
function findPreserveStart(messages, preserveK) {
|
|
245
|
+
let pairCount = 0;
|
|
246
|
+
let preserveStart = messages.length;
|
|
247
|
+
for (let i = messages.length - 1; i >= 0 && pairCount < preserveK; i--) {
|
|
248
|
+
const m = messages[i];
|
|
249
|
+
if (!m) continue;
|
|
250
|
+
if (m.role === "user" || m.role === "assistant") {
|
|
251
|
+
pairCount++;
|
|
252
|
+
preserveStart = i;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
for (let i = preserveStart; i < messages.length; i++) {
|
|
256
|
+
const m = messages[i];
|
|
257
|
+
if (!m || typeof m.content === "string" || !Array.isArray(m.content)) continue;
|
|
258
|
+
const hasToolUse2 = m.content.some((b) => b.type === "tool_use");
|
|
259
|
+
if (hasToolUse2 && i + 1 < messages.length) {
|
|
260
|
+
const next = messages[i + 1];
|
|
261
|
+
if (next && next.role === "user" && typeof next.content !== "string" && Array.isArray(next.content) && next.content.some((b) => b.type === "tool_result")) {
|
|
262
|
+
preserveStart = i + 1;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return preserveStart;
|
|
267
|
+
}
|
|
268
|
+
function eliseOldToolResults(messages, opts) {
|
|
269
|
+
const preserveStart = findPreserveStart(messages, opts.preserveK);
|
|
270
|
+
let saved = 0;
|
|
271
|
+
let changed = false;
|
|
272
|
+
const next = new Array(messages.length);
|
|
273
|
+
for (let i = 0; i < messages.length; i++) {
|
|
274
|
+
const msg = messages[i];
|
|
275
|
+
if (i >= preserveStart || !msg || !Array.isArray(msg.content)) {
|
|
276
|
+
next[i] = msg;
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const original = msg.content;
|
|
280
|
+
const newContent = original.map((b) => {
|
|
281
|
+
if (b.type !== "tool_result") return b;
|
|
282
|
+
const tokens = estimateToolResultTokens(b.content);
|
|
283
|
+
if (tokens < opts.eliseThreshold) return b;
|
|
284
|
+
saved += tokens;
|
|
285
|
+
const elided = {
|
|
286
|
+
type: "tool_result",
|
|
287
|
+
tool_use_id: b.tool_use_id,
|
|
288
|
+
content: `[elided: ~${tokens} tokens]`,
|
|
289
|
+
is_error: b.is_error
|
|
290
|
+
};
|
|
291
|
+
return elided;
|
|
292
|
+
});
|
|
293
|
+
if (newContent.every((b, idx) => b === original[idx])) {
|
|
294
|
+
next[i] = msg;
|
|
295
|
+
} else {
|
|
296
|
+
next[i] = { ...msg, content: newContent };
|
|
297
|
+
changed = true;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return { messages: changed ? next : messages, saved, changed };
|
|
301
|
+
}
|
|
302
|
+
function buildLosslessDigest(messages) {
|
|
303
|
+
const lines = [];
|
|
304
|
+
for (const m of messages) {
|
|
305
|
+
let text;
|
|
306
|
+
let omitted = 0;
|
|
307
|
+
if (typeof m.content === "string") {
|
|
308
|
+
text = m.content;
|
|
309
|
+
} else {
|
|
310
|
+
const parts = [];
|
|
311
|
+
for (const b of m.content) {
|
|
312
|
+
if (isTextBlock(b)) parts.push(b.text);
|
|
313
|
+
else if (b.type === "tool_use" || b.type === "tool_result") omitted++;
|
|
314
|
+
}
|
|
315
|
+
text = parts.join(" ");
|
|
316
|
+
}
|
|
317
|
+
if (text.trim().length === 0 && omitted === 0) continue;
|
|
318
|
+
const marker = omitted > 0 ? ` [${omitted} tool call(s) omitted \u2014 see session log]` : "";
|
|
319
|
+
lines.push(`[${m.role}]: ${text}${marker}`);
|
|
320
|
+
}
|
|
321
|
+
return lines.join("\n");
|
|
322
|
+
}
|
|
323
|
+
function findSafeBoundary(messages, from, to) {
|
|
324
|
+
for (let i = to; i >= from; i--) {
|
|
325
|
+
const m = messages[i];
|
|
326
|
+
if (!m) continue;
|
|
327
|
+
if (m.role === "user" && hasTextContent(m)) {
|
|
328
|
+
return findExchangeStart(messages, i);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return -1;
|
|
332
|
+
}
|
|
333
|
+
function findExchangeStart(messages, userIndex) {
|
|
334
|
+
for (let i = userIndex - 1; i >= 0; i--) {
|
|
335
|
+
const m = messages[i];
|
|
336
|
+
if (!m) continue;
|
|
337
|
+
if (m.role === "assistant") {
|
|
338
|
+
const hasToolUse2 = Array.isArray(m.content) ? m.content.some((b) => b.type === "tool_use") : false;
|
|
339
|
+
if (!hasToolUse2) return i + 1;
|
|
340
|
+
} else if (m.role === "user") {
|
|
341
|
+
return i;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return 0;
|
|
345
|
+
}
|
|
346
|
+
|
|
196
347
|
// src/execution/compactor.ts
|
|
197
348
|
var HybridCompactor = class {
|
|
198
349
|
preserveK;
|
|
199
350
|
eliseThreshold;
|
|
200
|
-
estimator;
|
|
201
351
|
constructor(opts = {}) {
|
|
202
352
|
this.preserveK = opts.preserveK ?? 5;
|
|
203
353
|
this.eliseThreshold = opts.eliseThreshold ?? 2e3;
|
|
204
|
-
this.estimator = opts.estimator ?? estimateTextTokens;
|
|
205
354
|
}
|
|
206
355
|
async compact(ctx, opts = {}) {
|
|
207
|
-
const beforeTokens =
|
|
356
|
+
const beforeTokens = estimateMessages(ctx.messages);
|
|
208
357
|
const beforeFull = this.estimateFullRequest(ctx);
|
|
209
358
|
const reductions = [];
|
|
210
359
|
const policy = readContextWindowPolicy(ctx);
|
|
211
360
|
const preserveK = policy?.preserveK ?? this.preserveK;
|
|
212
361
|
const eliseThreshold = policy?.eliseThreshold ?? this.eliseThreshold;
|
|
213
|
-
const
|
|
214
|
-
if (
|
|
362
|
+
const elide = eliseOldToolResults(ctx.messages, { preserveK, eliseThreshold });
|
|
363
|
+
if (elide.changed) ctx.state.replaceMessages(elide.messages);
|
|
364
|
+
if (elide.saved > 0) reductions.push({ phase: "elision", saved: elide.saved });
|
|
365
|
+
let collapsedDigest;
|
|
215
366
|
if (opts.aggressive) {
|
|
216
|
-
const
|
|
217
|
-
if (
|
|
367
|
+
const phase2 = this.collapseAncientTurns(ctx, preserveK);
|
|
368
|
+
if (phase2.saved > 0) reductions.push({ phase: "summary", saved: phase2.saved });
|
|
369
|
+
collapsedDigest = phase2.digest;
|
|
218
370
|
}
|
|
219
371
|
const repaired = repairToolUseAdjacency(ctx.messages);
|
|
220
372
|
if (repaired.report.changed) {
|
|
221
373
|
ctx.state.replaceMessages(repaired.messages);
|
|
222
374
|
}
|
|
223
|
-
const afterTokens =
|
|
375
|
+
const afterTokens = estimateMessages(ctx.messages);
|
|
224
376
|
const afterFull = this.estimateFullRequest(ctx);
|
|
225
377
|
return {
|
|
226
378
|
before: beforeTokens,
|
|
@@ -228,6 +380,7 @@ var HybridCompactor = class {
|
|
|
228
380
|
fullRequestTokensBefore: beforeFull,
|
|
229
381
|
fullRequestTokensAfter: afterFull,
|
|
230
382
|
reductions,
|
|
383
|
+
collapsedDigest,
|
|
231
384
|
repaired: repaired.report.changed ? {
|
|
232
385
|
removedToolUses: repaired.report.removedToolUses,
|
|
233
386
|
removedToolResults: repaired.report.removedToolResults,
|
|
@@ -240,72 +393,23 @@ var HybridCompactor = class {
|
|
|
240
393
|
* This is the accurate figure for context-window pressure monitoring.
|
|
241
394
|
*/
|
|
242
395
|
estimateFullRequest(ctx) {
|
|
243
|
-
|
|
244
|
-
return breakdown.total;
|
|
245
|
-
}
|
|
246
|
-
eliseOldToolResults(ctx, preserveK = this.preserveK, eliseThreshold = this.eliseThreshold) {
|
|
247
|
-
const messages = ctx.messages;
|
|
248
|
-
let pairCount = 0;
|
|
249
|
-
let preserveStart = messages.length;
|
|
250
|
-
for (let i = messages.length - 1; i >= 0 && pairCount < preserveK; i--) {
|
|
251
|
-
const m = messages[i];
|
|
252
|
-
if (!m) continue;
|
|
253
|
-
if (m.role === "user" || m.role === "assistant") {
|
|
254
|
-
pairCount++;
|
|
255
|
-
preserveStart = i;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
for (let i = preserveStart; i < messages.length; i++) {
|
|
259
|
-
const m = messages[i];
|
|
260
|
-
if (!m || typeof m.content === "string" || !Array.isArray(m.content)) continue;
|
|
261
|
-
const hasToolUse2 = m.content.some((b) => b.type === "tool_use");
|
|
262
|
-
if (hasToolUse2 && i + 1 < messages.length) {
|
|
263
|
-
const next = messages[i + 1];
|
|
264
|
-
if (next && next.role === "user" && typeof next.content !== "string" && Array.isArray(next.content) && next.content.some((b) => b.type === "tool_result")) {
|
|
265
|
-
preserveStart = i + 1;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
let saved = 0;
|
|
270
|
-
let changed = false;
|
|
271
|
-
const nextMessages = new Array(messages.length);
|
|
272
|
-
for (let i = 0; i < messages.length; i++) {
|
|
273
|
-
const msg = messages[i];
|
|
274
|
-
if (i >= preserveStart) {
|
|
275
|
-
nextMessages[i] = msg;
|
|
276
|
-
continue;
|
|
277
|
-
}
|
|
278
|
-
if (!msg || !Array.isArray(msg.content)) {
|
|
279
|
-
nextMessages[i] = msg;
|
|
280
|
-
continue;
|
|
281
|
-
}
|
|
282
|
-
const newContent = msg.content.map((b) => {
|
|
283
|
-
if (b.type !== "tool_result") return b;
|
|
284
|
-
const tokens = estimateToolResultTokens(b.content);
|
|
285
|
-
if (tokens < eliseThreshold) return b;
|
|
286
|
-
saved += tokens;
|
|
287
|
-
const elided = {
|
|
288
|
-
type: "tool_result",
|
|
289
|
-
tool_use_id: b.tool_use_id,
|
|
290
|
-
content: `[elided: ~${tokens} tokens]`,
|
|
291
|
-
is_error: b.is_error
|
|
292
|
-
};
|
|
293
|
-
return elided;
|
|
294
|
-
});
|
|
295
|
-
if (newContent.length === msg.content.length && newContent.every((b, idx) => b === msg.content[idx])) {
|
|
296
|
-
nextMessages[i] = msg;
|
|
297
|
-
} else {
|
|
298
|
-
nextMessages[i] = { ...msg, content: newContent };
|
|
299
|
-
changed = true;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
if (changed) ctx.state.replaceMessages(nextMessages);
|
|
303
|
-
return saved;
|
|
396
|
+
return estimateRequestTokens(ctx.messages, ctx.systemPrompt, ctx.tools ?? []).total;
|
|
304
397
|
}
|
|
398
|
+
/**
|
|
399
|
+
* Lossless rule-based collapse of ancient turns into a single digest message.
|
|
400
|
+
*
|
|
401
|
+
* Preserves ALL textual content of the collapsed range — user instructions,
|
|
402
|
+
* assistant decisions/conclusions, and any prior digests (chained forward so
|
|
403
|
+
* the digest stays lossless across repeated compactions). Only `tool_use` /
|
|
404
|
+
* `tool_result` protocol blocks are dropped and replaced with a count marker;
|
|
405
|
+
* their full payload already lives in the session log. No sub-LLM call.
|
|
406
|
+
*
|
|
407
|
+
* Returns the token savings and the digest text (for audit logging).
|
|
408
|
+
*/
|
|
305
409
|
collapseAncientTurns(ctx, preserveK = this.preserveK) {
|
|
306
410
|
const messages = ctx.messages;
|
|
307
411
|
const cutTarget = Math.max(0, messages.length - preserveK * 2);
|
|
308
|
-
if (cutTarget <= 0) return 0;
|
|
412
|
+
if (cutTarget <= 0) return { saved: 0 };
|
|
309
413
|
let boundary = -1;
|
|
310
414
|
for (let i = cutTarget; i < messages.length; i++) {
|
|
311
415
|
const m = messages[i];
|
|
@@ -315,34 +419,20 @@ var HybridCompactor = class {
|
|
|
315
419
|
break;
|
|
316
420
|
}
|
|
317
421
|
}
|
|
318
|
-
if (boundary <= 0) return 0;
|
|
422
|
+
if (boundary <= 0) return { saved: 0 };
|
|
319
423
|
const removed = messages.slice(0, boundary);
|
|
320
|
-
const removedTokens =
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
{ role: "assistant", content: "Continuing from compacted context." }
|
|
327
|
-
];
|
|
424
|
+
const removedTokens = estimateMessages(removed);
|
|
425
|
+
const digest = buildLosslessDigest(removed) || `${removed.length} earlier turns (no textual content; tool I/O omitted \u2014 see session log)`;
|
|
426
|
+
const summaryMsg = {
|
|
427
|
+
role: "system",
|
|
428
|
+
content: `[prior_turns_digest: ${digest}]`
|
|
429
|
+
};
|
|
328
430
|
const tail = ctx.messages.slice(boundary);
|
|
329
|
-
ctx.state.replaceMessages([
|
|
330
|
-
return
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
for (const m of messages) {
|
|
335
|
-
if (typeof m.content === "string") {
|
|
336
|
-
total += this.estimator(m.content);
|
|
337
|
-
} else {
|
|
338
|
-
for (const b of m.content) {
|
|
339
|
-
if (b.type === "text") total += this.estimator(b.text);
|
|
340
|
-
else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
|
|
341
|
-
else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
return total;
|
|
431
|
+
ctx.state.replaceMessages([summaryMsg, ...tail]);
|
|
432
|
+
return {
|
|
433
|
+
saved: Math.max(0, removedTokens - estimateMessages([summaryMsg])),
|
|
434
|
+
digest
|
|
435
|
+
};
|
|
346
436
|
}
|
|
347
437
|
};
|
|
348
438
|
function readContextWindowPolicy(ctx) {
|
|
@@ -354,15 +444,6 @@ function readContextWindowPolicy(ctx) {
|
|
|
354
444
|
}
|
|
355
445
|
return candidate;
|
|
356
446
|
}
|
|
357
|
-
function hasTextContent(m) {
|
|
358
|
-
if (typeof m.content === "string") return m.content.trim().length > 0;
|
|
359
|
-
return m.content.some((b) => b.type === "text" && b.text.trim().length > 0);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// src/types/blocks.ts
|
|
363
|
-
function isTextBlock(b) {
|
|
364
|
-
return b.type === "text";
|
|
365
|
-
}
|
|
366
447
|
|
|
367
448
|
// src/execution/intelligent-compactor.ts
|
|
368
449
|
var IntelligentCompactor = class {
|
|
@@ -387,23 +468,25 @@ var IntelligentCompactor = class {
|
|
|
387
468
|
this.summarizerModel = opts.summarizerModel;
|
|
388
469
|
}
|
|
389
470
|
async compact(ctx, opts = {}) {
|
|
390
|
-
const beforeTokens =
|
|
471
|
+
const beforeTokens = estimateMessages(ctx.messages);
|
|
391
472
|
const beforeFull = this.estimateFullRequest(ctx);
|
|
392
473
|
const reductions = [];
|
|
393
474
|
const load = beforeFull / this.maxContext;
|
|
394
475
|
const aggressive = load >= this.hardThreshold ? true : opts.aggressive ?? load >= this.softThreshold;
|
|
395
|
-
const saved1 = this.
|
|
476
|
+
const saved1 = this.elide(ctx);
|
|
396
477
|
if (saved1 > 0) reductions.push({ phase: "elision", saved: saved1 });
|
|
478
|
+
let collapsedDigest;
|
|
397
479
|
if (aggressive) {
|
|
398
|
-
const
|
|
399
|
-
if (
|
|
480
|
+
const phase2 = await this.summarizeAncientTurns(ctx);
|
|
481
|
+
if (phase2.saved > 0) reductions.push({ phase: "summary", saved: phase2.saved });
|
|
482
|
+
collapsedDigest = phase2.digest;
|
|
400
483
|
} else if (load >= this.warnThreshold) {
|
|
401
|
-
const saved2 = this.
|
|
484
|
+
const saved2 = this.elide(ctx);
|
|
402
485
|
if (saved2 > 0) reductions.push({ phase: "elision", saved: saved2 });
|
|
403
486
|
}
|
|
404
487
|
const repaired = repairToolUseAdjacency(ctx.messages);
|
|
405
488
|
if (repaired.report.changed) ctx.state.replaceMessages(repaired.messages);
|
|
406
|
-
const afterTokens =
|
|
489
|
+
const afterTokens = estimateMessages(ctx.messages);
|
|
407
490
|
const afterFull = this.estimateFullRequest(ctx);
|
|
408
491
|
return {
|
|
409
492
|
before: beforeTokens,
|
|
@@ -411,6 +494,7 @@ var IntelligentCompactor = class {
|
|
|
411
494
|
fullRequestTokensBefore: beforeFull,
|
|
412
495
|
fullRequestTokensAfter: afterFull,
|
|
413
496
|
reductions,
|
|
497
|
+
collapsedDigest,
|
|
414
498
|
repaired: repaired.report.changed ? {
|
|
415
499
|
removedToolUses: repaired.report.removedToolUses,
|
|
416
500
|
removedToolResults: repaired.report.removedToolResults,
|
|
@@ -423,75 +507,39 @@ var IntelligentCompactor = class {
|
|
|
423
507
|
* This is the accurate figure for context-window pressure monitoring.
|
|
424
508
|
*/
|
|
425
509
|
estimateFullRequest(ctx) {
|
|
426
|
-
|
|
427
|
-
|
|
510
|
+
return estimateRequestTokens(ctx.messages, ctx.systemPrompt, ctx.tools ?? []).total;
|
|
511
|
+
}
|
|
512
|
+
/** Run shared tool-result elision and commit through ConversationState. */
|
|
513
|
+
elide(ctx) {
|
|
514
|
+
const result = eliseOldToolResults(ctx.messages, {
|
|
515
|
+
preserveK: this.preserveK,
|
|
516
|
+
eliseThreshold: this.eliseThreshold
|
|
517
|
+
});
|
|
518
|
+
if (result.changed) ctx.state.replaceMessages(result.messages);
|
|
519
|
+
return result.saved;
|
|
428
520
|
}
|
|
429
521
|
async summarizeAncientTurns(ctx) {
|
|
430
522
|
const messages = ctx.messages;
|
|
431
523
|
const cutoff = Math.max(0, messages.length - this.preserveK * 2);
|
|
432
|
-
if (cutoff <= 2) return 0;
|
|
433
|
-
const boundary =
|
|
434
|
-
if (boundary <= 1) return 0;
|
|
524
|
+
if (cutoff <= 2) return { saved: 0 };
|
|
525
|
+
const boundary = findSafeBoundary(messages, 0, cutoff);
|
|
526
|
+
if (boundary <= 1) return { saved: 0 };
|
|
435
527
|
const toSummarize = messages.slice(0, boundary);
|
|
436
|
-
const removedTokens =
|
|
528
|
+
const removedTokens = estimateMessages(toSummarize);
|
|
437
529
|
let summaryText;
|
|
438
530
|
try {
|
|
439
531
|
summaryText = await this.callSummarizer(toSummarize, ctx);
|
|
440
532
|
} catch {
|
|
441
|
-
|
|
442
|
-
for (const m of toSummarize) {
|
|
443
|
-
if (m.role === "system") continue;
|
|
444
|
-
if (typeof m.content === "string") {
|
|
445
|
-
preservedMessages.push(m);
|
|
446
|
-
} else if (Array.isArray(m.content)) {
|
|
447
|
-
const textParts = m.content.filter(isTextBlock);
|
|
448
|
-
const toolParts = m.content.filter((b) => b.type === "tool_use" || b.type === "tool_result");
|
|
449
|
-
const next = {
|
|
450
|
-
role: m.role,
|
|
451
|
-
content: [
|
|
452
|
-
...textParts,
|
|
453
|
-
...toolParts.length > 0 ? [{ type: "text", text: `[${toolParts.length} tool call(s) omitted]` }] : []
|
|
454
|
-
]
|
|
455
|
-
};
|
|
456
|
-
preservedMessages.push(next);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
summaryText = preservedMessages.map((m) => `[${m.role}]: ${typeof m.content === "string" ? m.content : m.content.filter(isTextBlock).map((b) => b.text).join(" ")}`).join("\n");
|
|
460
|
-
if (!summaryText) summaryText = `${toSummarize.length} earlier turns (semantic content preserved)`;
|
|
533
|
+
summaryText = buildLosslessDigest(toSummarize) || `${toSummarize.length} earlier turns (semantic content preserved)`;
|
|
461
534
|
}
|
|
462
535
|
const summaryMsg = {
|
|
463
536
|
role: "system",
|
|
464
537
|
content: `[prior_turns_summary: ${summaryText}]`
|
|
465
538
|
};
|
|
466
|
-
const summaryTokens =
|
|
539
|
+
const summaryTokens = estimateMessages([summaryMsg]);
|
|
467
540
|
const tail = ctx.messages.slice(boundary);
|
|
468
541
|
ctx.state.replaceMessages([summaryMsg, ...tail]);
|
|
469
|
-
return Math.max(0, removedTokens - summaryTokens);
|
|
470
|
-
}
|
|
471
|
-
findSafeBoundary(messages, from, to) {
|
|
472
|
-
for (let i = to; i >= from; i--) {
|
|
473
|
-
const m = messages[i];
|
|
474
|
-
if (!m) continue;
|
|
475
|
-
if (m.role === "user" && this.hasTextContent(m)) {
|
|
476
|
-
return this.findExchangeStart(messages, i);
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
return -1;
|
|
480
|
-
}
|
|
481
|
-
findExchangeStart(messages, userIndex) {
|
|
482
|
-
for (let i = userIndex - 1; i >= 0; i--) {
|
|
483
|
-
const m = messages[i];
|
|
484
|
-
if (!m) continue;
|
|
485
|
-
if (m.role === "assistant") {
|
|
486
|
-
const hasToolUse2 = Array.isArray(m.content) ? m.content.some((b) => b.type === "tool_use") : false;
|
|
487
|
-
if (!hasToolUse2) {
|
|
488
|
-
return i + 1;
|
|
489
|
-
}
|
|
490
|
-
} else if (m.role !== "user") ; else {
|
|
491
|
-
return i;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
return 0;
|
|
542
|
+
return { saved: Math.max(0, removedTokens - summaryTokens), digest: summaryText };
|
|
495
543
|
}
|
|
496
544
|
async callSummarizer(messages, ctx) {
|
|
497
545
|
const prompt = [
|
|
@@ -526,86 +574,6 @@ var IntelligentCompactor = class {
|
|
|
526
574
|
}
|
|
527
575
|
return [{ type: "text", text: lines.join("\n") }];
|
|
528
576
|
}
|
|
529
|
-
lightweightCompact(ctx) {
|
|
530
|
-
return this.eliseOldToolResults(ctx);
|
|
531
|
-
}
|
|
532
|
-
eliseOldToolResults(ctx) {
|
|
533
|
-
const messages = ctx.messages;
|
|
534
|
-
let pairCount = 0;
|
|
535
|
-
let preserveStart = messages.length;
|
|
536
|
-
for (let i = messages.length - 1; i >= 0 && pairCount < this.preserveK; i--) {
|
|
537
|
-
const m = messages[i];
|
|
538
|
-
if (!m) continue;
|
|
539
|
-
if (m.role === "user" || m.role === "assistant") {
|
|
540
|
-
pairCount++;
|
|
541
|
-
preserveStart = i;
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
for (let i = preserveStart; i < messages.length; i++) {
|
|
545
|
-
const m = messages[i];
|
|
546
|
-
if (!m || typeof m.content === "string" || !Array.isArray(m.content)) continue;
|
|
547
|
-
const hasToolUse2 = m.content.some((b) => b.type === "tool_use");
|
|
548
|
-
if (hasToolUse2 && i + 1 < messages.length) {
|
|
549
|
-
const next = messages[i + 1];
|
|
550
|
-
if (next && next.role === "user" && typeof next.content !== "string" && Array.isArray(next.content) && next.content.some((b) => b.type === "tool_result")) {
|
|
551
|
-
preserveStart = i + 1;
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
let saved = 0;
|
|
556
|
-
let changed = false;
|
|
557
|
-
const nextMessages = new Array(messages.length);
|
|
558
|
-
for (let i = 0; i < messages.length; i++) {
|
|
559
|
-
const msg = messages[i];
|
|
560
|
-
if (i >= preserveStart) {
|
|
561
|
-
nextMessages[i] = msg;
|
|
562
|
-
continue;
|
|
563
|
-
}
|
|
564
|
-
if (!msg || !Array.isArray(msg.content)) {
|
|
565
|
-
nextMessages[i] = msg;
|
|
566
|
-
continue;
|
|
567
|
-
}
|
|
568
|
-
const newContent = msg.content.map((b) => {
|
|
569
|
-
if (b.type !== "tool_result") return b;
|
|
570
|
-
const tokens = estimateToolResultTokens(b.content);
|
|
571
|
-
if (tokens < this.eliseThreshold) return b;
|
|
572
|
-
saved += tokens;
|
|
573
|
-
return {
|
|
574
|
-
type: "tool_result",
|
|
575
|
-
tool_use_id: b.tool_use_id,
|
|
576
|
-
content: `[elided: ~${tokens} tokens]`,
|
|
577
|
-
is_error: b.is_error
|
|
578
|
-
};
|
|
579
|
-
});
|
|
580
|
-
if (newContent.length === msg.content.length && newContent.every((b, idx) => b === msg.content[idx])) {
|
|
581
|
-
nextMessages[i] = msg;
|
|
582
|
-
} else {
|
|
583
|
-
nextMessages[i] = { ...msg, content: newContent };
|
|
584
|
-
changed = true;
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
if (changed) ctx.state.replaceMessages(nextMessages);
|
|
588
|
-
return saved;
|
|
589
|
-
}
|
|
590
|
-
hasTextContent(m) {
|
|
591
|
-
if (typeof m.content === "string") return m.content.trim().length > 0;
|
|
592
|
-
return m.content.some((b) => b.type === "text" && b.text.trim().length > 0);
|
|
593
|
-
}
|
|
594
|
-
estimateTokens(messages) {
|
|
595
|
-
let total = 0;
|
|
596
|
-
for (const m of messages) {
|
|
597
|
-
if (typeof m.content === "string") {
|
|
598
|
-
total += estimateTextTokens(m.content);
|
|
599
|
-
} else {
|
|
600
|
-
for (const b of m.content) {
|
|
601
|
-
if (b.type === "text") total += estimateTextTokens(b.text);
|
|
602
|
-
else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
|
|
603
|
-
else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
return total;
|
|
608
|
-
}
|
|
609
577
|
};
|
|
610
578
|
|
|
611
579
|
// src/models/llm-selector.ts
|
|
@@ -631,20 +599,6 @@ Rules:
|
|
|
631
599
|
- If unsure, keep rather than collapse (errors are more costly than waste)
|
|
632
600
|
|
|
633
601
|
Return ONLY the JSON object, no markdown, no explanation outside the JSON.`;
|
|
634
|
-
function estimateTokens(messages) {
|
|
635
|
-
let total = 0;
|
|
636
|
-
for (const m of messages) {
|
|
637
|
-
if (typeof m.content === "string") {
|
|
638
|
-
total += Math.ceil(m.content.length / 4);
|
|
639
|
-
} else if (Array.isArray(m.content)) {
|
|
640
|
-
for (const b of m.content) {
|
|
641
|
-
if (b.type === "text") total += Math.ceil(b.text.length / 4);
|
|
642
|
-
else total += Math.ceil(JSON.stringify(b).length / 4);
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
return total;
|
|
647
|
-
}
|
|
648
602
|
function formatMessages(messages, maxChars = 8e3) {
|
|
649
603
|
const lines = [];
|
|
650
604
|
let used = 0;
|
|
@@ -683,7 +637,7 @@ var LLMSelector = class {
|
|
|
683
637
|
async select(messages, maxToKeep) {
|
|
684
638
|
const effectiveBudget = Math.min(maxToKeep, this.maxContextTokens);
|
|
685
639
|
const historyText = formatMessages(messages);
|
|
686
|
-
const totalTokens =
|
|
640
|
+
const totalTokens = estimateMessageTokens(messages);
|
|
687
641
|
const systemText = `${this.systemPrompt}
|
|
688
642
|
|
|
689
643
|
Conversation (${messages.length} messages, ~${totalTokens} tokens, budget: ${effectiveBudget}):
|
|
@@ -715,10 +669,7 @@ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiv
|
|
|
715
669
|
let startIdx = 0;
|
|
716
670
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
717
671
|
const m = expectDefined(messages[i]);
|
|
718
|
-
const cost =
|
|
719
|
-
(acc, b) => acc + (b.type === "text" ? Math.ceil(b.text.length / 4) : Math.ceil(JSON.stringify(b).length / 4)),
|
|
720
|
-
0
|
|
721
|
-
);
|
|
672
|
+
const cost = estimateMessageTokens([m]);
|
|
722
673
|
if (tokenCount + cost <= budget) {
|
|
723
674
|
tokenCount += cost;
|
|
724
675
|
} else {
|
|
@@ -951,52 +902,12 @@ Summarize the following message range:`;
|
|
|
951
902
|
return Math.floor(this.maxContext * 0.75);
|
|
952
903
|
}
|
|
953
904
|
eliseOldToolResults(ctx) {
|
|
954
|
-
const
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
if (m.role === "user" || m.role === "assistant") {
|
|
961
|
-
pairCount++;
|
|
962
|
-
preserveStart = i;
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
let saved = 0;
|
|
966
|
-
let changed = false;
|
|
967
|
-
const nextMessages = new Array(messages.length);
|
|
968
|
-
for (let i = 0; i < messages.length; i++) {
|
|
969
|
-
const msg = messages[i];
|
|
970
|
-
if (i >= preserveStart) {
|
|
971
|
-
nextMessages[i] = msg;
|
|
972
|
-
continue;
|
|
973
|
-
}
|
|
974
|
-
if (!msg || !Array.isArray(msg.content)) {
|
|
975
|
-
nextMessages[i] = msg;
|
|
976
|
-
continue;
|
|
977
|
-
}
|
|
978
|
-
const newContent = msg.content.map((b) => {
|
|
979
|
-
if (b.type !== "tool_result") return b;
|
|
980
|
-
const text = typeof b.content === "string" ? b.content : JSON.stringify(b.content);
|
|
981
|
-
const tokens = this.roughTokenEstimate(text);
|
|
982
|
-
if (tokens < this.eliseThreshold) return b;
|
|
983
|
-
saved += tokens;
|
|
984
|
-
return {
|
|
985
|
-
type: "tool_result",
|
|
986
|
-
tool_use_id: b.tool_use_id,
|
|
987
|
-
content: `[elided: ~${tokens} tokens]`,
|
|
988
|
-
is_error: b.is_error
|
|
989
|
-
};
|
|
990
|
-
});
|
|
991
|
-
if (newContent.every((b, idx) => b === msg.content[idx])) {
|
|
992
|
-
nextMessages[i] = msg;
|
|
993
|
-
} else {
|
|
994
|
-
nextMessages[i] = { ...msg, content: newContent };
|
|
995
|
-
changed = true;
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
if (changed) ctx.state.replaceMessages(nextMessages);
|
|
999
|
-
return saved;
|
|
905
|
+
const result = eliseOldToolResults(ctx.messages, {
|
|
906
|
+
preserveK: this.preserveK,
|
|
907
|
+
eliseThreshold: this.eliseThreshold
|
|
908
|
+
});
|
|
909
|
+
if (result.changed) ctx.state.replaceMessages(result.messages);
|
|
910
|
+
return result.saved;
|
|
1000
911
|
}
|
|
1001
912
|
hasTextContent(m) {
|
|
1002
913
|
if (typeof m.content === "string") return m.content.trim().length > 0;
|
|
@@ -1026,6 +937,73 @@ Summarize the following message range:`;
|
|
|
1026
937
|
}
|
|
1027
938
|
};
|
|
1028
939
|
|
|
940
|
+
// src/execution/strategy-compactor.ts
|
|
941
|
+
function createStrategyCompactor(opts = {}) {
|
|
942
|
+
const requested = opts.strategy ?? (opts.llmSelector ? "selective" : "hybrid");
|
|
943
|
+
const strategy = requested;
|
|
944
|
+
if (strategy === "intelligent" || strategy === "selective") {
|
|
945
|
+
return new ProviderBackedCompactor(strategy, opts);
|
|
946
|
+
}
|
|
947
|
+
return new HybridCompactor({
|
|
948
|
+
preserveK: opts.preserveK,
|
|
949
|
+
eliseThreshold: opts.eliseThreshold
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
var ProviderBackedCompactor = class {
|
|
953
|
+
constructor(strategy, opts) {
|
|
954
|
+
this.strategy = strategy;
|
|
955
|
+
this.opts = opts;
|
|
956
|
+
}
|
|
957
|
+
strategy;
|
|
958
|
+
opts;
|
|
959
|
+
async compact(ctx, compactOpts = {}) {
|
|
960
|
+
return this.resolveInner(ctx).compact(ctx, compactOpts);
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* Construct the concrete compactor for this run. Rebuilt per call (cheap, no
|
|
964
|
+
* I/O) so a model switch — which changes `ctx.provider.capabilities.maxContext`
|
|
965
|
+
* — is always reflected. Reads the active ContextWindowPolicy from `ctx.meta`
|
|
966
|
+
* so the LLM compactors honor the same thresholds/preserveK as the policy.
|
|
967
|
+
*/
|
|
968
|
+
resolveInner(ctx) {
|
|
969
|
+
const provider = ctx.provider;
|
|
970
|
+
if (!provider) {
|
|
971
|
+
return new HybridCompactor({
|
|
972
|
+
preserveK: this.opts.preserveK,
|
|
973
|
+
eliseThreshold: this.opts.eliseThreshold
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
const policy = readPolicy(ctx);
|
|
977
|
+
const maxContext = provider.capabilities?.maxContext || void 0;
|
|
978
|
+
const thresholds = policy?.thresholds;
|
|
979
|
+
const common = {
|
|
980
|
+
provider,
|
|
981
|
+
maxContext,
|
|
982
|
+
preserveK: this.opts.preserveK ?? policy?.preserveK,
|
|
983
|
+
eliseThreshold: this.opts.eliseThreshold ?? policy?.eliseThreshold,
|
|
984
|
+
...thresholds ? { warnThreshold: thresholds.warn, softThreshold: thresholds.soft, hardThreshold: thresholds.hard } : {}
|
|
985
|
+
};
|
|
986
|
+
if (this.strategy === "selective") {
|
|
987
|
+
return new SelectiveCompactor({
|
|
988
|
+
...common,
|
|
989
|
+
selectorModel: this.opts.summarizerModel,
|
|
990
|
+
summarizerModel: this.opts.summarizerModel
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
return new IntelligentCompactor({
|
|
994
|
+
...common,
|
|
995
|
+
summarizerModel: this.opts.summarizerModel
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
function readPolicy(ctx) {
|
|
1000
|
+
const policy = ctx.meta?.["contextWindowPolicy"];
|
|
1001
|
+
if (!policy || typeof policy !== "object") return null;
|
|
1002
|
+
const candidate = policy;
|
|
1003
|
+
if (typeof candidate.preserveK !== "number" || !candidate.thresholds) return null;
|
|
1004
|
+
return candidate;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1029
1007
|
// src/types/errors.ts
|
|
1030
1008
|
var ERROR_CODES = {
|
|
1031
1009
|
// Provider
|
|
@@ -1113,6 +1091,11 @@ var FsError = class extends WrongStackError {
|
|
|
1113
1091
|
|
|
1114
1092
|
// src/execution/auto-compaction-middleware.ts
|
|
1115
1093
|
var LEVEL_RANK = { warn: 0, soft: 1, hard: 2 };
|
|
1094
|
+
var MAX_DIGEST_LOG_CHARS = 4e3;
|
|
1095
|
+
function truncateDigest(digest) {
|
|
1096
|
+
if (digest.length <= MAX_DIGEST_LOG_CHARS) return digest;
|
|
1097
|
+
return `${digest.slice(0, MAX_DIGEST_LOG_CHARS)}\u2026 [+${digest.length - MAX_DIGEST_LOG_CHARS} chars; full turns in session log]`;
|
|
1098
|
+
}
|
|
1116
1099
|
var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
1117
1100
|
name = "AutoCompaction";
|
|
1118
1101
|
compactor;
|
|
@@ -1173,7 +1156,12 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
1173
1156
|
}
|
|
1174
1157
|
handler() {
|
|
1175
1158
|
return async (ctx, next) => {
|
|
1176
|
-
const tokens = this._estimator ? this._estimator(ctx) : estimateRequestTokensCalibrated(
|
|
1159
|
+
const tokens = this._estimator ? this._estimator(ctx) : estimateRequestTokensCalibrated(
|
|
1160
|
+
ctx.messages,
|
|
1161
|
+
ctx.systemPrompt,
|
|
1162
|
+
ctx.tools ?? [],
|
|
1163
|
+
`${ctx.provider?.id ?? "unknown"}/${ctx.model}`
|
|
1164
|
+
).total;
|
|
1177
1165
|
const load = tokens / this._maxContext;
|
|
1178
1166
|
const policy = this.policyProvider?.(ctx);
|
|
1179
1167
|
const thresholds = policy?.thresholds ?? {
|
|
@@ -1235,7 +1223,11 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
1235
1223
|
after: report.after,
|
|
1236
1224
|
level: pressure.level,
|
|
1237
1225
|
aggressive,
|
|
1238
|
-
reductions: report.reductions?.map((r) => ({ phase: r.phase, saved: r.saved }))
|
|
1226
|
+
reductions: report.reductions?.map((r) => ({ phase: r.phase, saved: r.saved })),
|
|
1227
|
+
// Record what was collapsed so the audit trail shows the preserved
|
|
1228
|
+
// content, not just token counts. Bounded to keep the log line small;
|
|
1229
|
+
// the full original turns are already in the session JSONL.
|
|
1230
|
+
...report.collapsedDigest ? { digest: truncateDigest(report.collapsedDigest) } : {}
|
|
1239
1231
|
});
|
|
1240
1232
|
ctx.clearFileTracking();
|
|
1241
1233
|
} catch (err) {
|
|
@@ -1271,6 +1263,8 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
1271
1263
|
var ToolCapabilities = {
|
|
1272
1264
|
/** Can execute arbitrary commands in the user's shell (the `bash` tool). */
|
|
1273
1265
|
SHELL_ARBITRARY: "shell.arbitrary",
|
|
1266
|
+
/** Can execute a restricted set of commands (the `exec` tool). */
|
|
1267
|
+
SHELL_RESTRICTED: "shell.restricted",
|
|
1274
1268
|
/** Can write / modify / delete files inside the project. */
|
|
1275
1269
|
FS_WRITE: "fs.write",
|
|
1276
1270
|
/** Can write files outside the current project root (very high risk). */
|
|
@@ -1286,6 +1280,7 @@ var ToolCapabilities = {
|
|
|
1286
1280
|
};
|
|
1287
1281
|
var DANGEROUS_FOR_SUBAGENTS = [
|
|
1288
1282
|
ToolCapabilities.SHELL_ARBITRARY,
|
|
1283
|
+
ToolCapabilities.SHELL_RESTRICTED,
|
|
1289
1284
|
ToolCapabilities.FS_WRITE,
|
|
1290
1285
|
ToolCapabilities.FS_WRITE_OUTSIDE_PROJECT,
|
|
1291
1286
|
ToolCapabilities.MCP_PROXY,
|
|
@@ -1863,6 +1858,13 @@ function extractMalformedRaw(input) {
|
|
|
1863
1858
|
}
|
|
1864
1859
|
}
|
|
1865
1860
|
|
|
1861
|
+
// src/utils/assert-never.ts
|
|
1862
|
+
function assertNever(x, message) {
|
|
1863
|
+
throw new Error(
|
|
1864
|
+
`Unhandled case: ${JSON.stringify(x)}`
|
|
1865
|
+
);
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1866
1868
|
// src/utils/regex-guard.ts
|
|
1867
1869
|
var MAX_PATTERN_LEN = 512;
|
|
1868
1870
|
var DANGEROUS_PATTERNS = [
|
|
@@ -1952,6 +1954,12 @@ var DoneConditionChecker = class {
|
|
|
1952
1954
|
return { done: true, reason: `max tool calls (${this.condition.maxToolCalls}) reached`, ...state };
|
|
1953
1955
|
}
|
|
1954
1956
|
break;
|
|
1957
|
+
case "all_tasks_done":
|
|
1958
|
+
break;
|
|
1959
|
+
case "custom":
|
|
1960
|
+
break;
|
|
1961
|
+
default:
|
|
1962
|
+
return assertNever(this.condition.type);
|
|
1955
1963
|
}
|
|
1956
1964
|
return { done: false, iterations: state.iterations, toolCalls: state.toolCalls };
|
|
1957
1965
|
}
|
|
@@ -2232,9 +2240,9 @@ function parseProgressFromText(text) {
|
|
|
2232
2240
|
const re = /\[progress:\s*(\d{1,3})%\]\s*(?:[—\-]\s*(.+))?/i;
|
|
2233
2241
|
const m = text.match(re);
|
|
2234
2242
|
if (!m) return null;
|
|
2235
|
-
const progress = Math.min(100, Math.max(0, Number.parseInt(m[1], 10)));
|
|
2243
|
+
const progress = Math.min(100, Math.max(0, Number.parseInt(m[1] ?? "0", 10)));
|
|
2236
2244
|
const note = m[2]?.trim() || void 0;
|
|
2237
|
-
return { progress, note };
|
|
2245
|
+
return note === void 0 ? { progress } : { progress, note };
|
|
2238
2246
|
}
|
|
2239
2247
|
function recordProgress(goal, progress, note) {
|
|
2240
2248
|
const clamped = Math.min(100, Math.max(0, progress));
|
|
@@ -2999,6 +3007,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
2999
3007
|
note: note.slice(0, 240)
|
|
3000
3008
|
});
|
|
3001
3009
|
await saveGoal(this.goalPath, withEntry);
|
|
3010
|
+
this.opts.onEternalStop?.();
|
|
3002
3011
|
}
|
|
3003
3012
|
/**
|
|
3004
3013
|
* Manually clear the goal — equivalent to `/goal clear` typed by the user.
|
|
@@ -7989,6 +7998,6 @@ function parseDescription(raw) {
|
|
|
7989
7998
|
return { trigger, scope };
|
|
7990
7999
|
}
|
|
7991
8000
|
|
|
7992
|
-
export { AutoCompactionMiddleware, AutonomousRunner, DefaultErrorHandler, DefaultRetryPolicy, DefaultSkillLoader, DoneConditionChecker, EternalAutonomyEngine, HybridCompactor, IntelligentCompactor, ParallelEternalEngine, SelectiveCompactor, ToolExecutor, buildGoalPreamble, createAutonomyBrain, formatDecisionSummary, makeAutonomyPromptContributor };
|
|
8001
|
+
export { AutoCompactionMiddleware, AutonomousRunner, DefaultErrorHandler, DefaultRetryPolicy, DefaultSkillLoader, DoneConditionChecker, EternalAutonomyEngine, HybridCompactor, IntelligentCompactor, ParallelEternalEngine, SelectiveCompactor, ToolExecutor, buildGoalPreamble, createAutonomyBrain, createStrategyCompactor, formatDecisionSummary, makeAutonomyPromptContributor };
|
|
7993
8002
|
//# sourceMappingURL=index.js.map
|
|
7994
8003
|
//# sourceMappingURL=index.js.map
|