@wrongstack/core 0.1.9 → 0.2.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.
Files changed (69) hide show
  1. package/dist/agent-bridge-DmBiCipY.d.ts +33 -0
  2. package/dist/compactor-DSl2FK7a.d.ts +17 -0
  3. package/dist/config-DXrqb41m.d.ts +193 -0
  4. package/dist/{provider-txgB0Oq9.d.ts → context-u0bryklF.d.ts} +540 -472
  5. package/dist/coordination/index.d.ts +892 -0
  6. package/dist/coordination/index.js +2869 -0
  7. package/dist/coordination/index.js.map +1 -0
  8. package/dist/defaults/index.d.ts +34 -2309
  9. package/dist/defaults/index.js +5610 -4608
  10. package/dist/defaults/index.js.map +1 -1
  11. package/dist/events-B6Q03pTu.d.ts +290 -0
  12. package/dist/execution/index.d.ts +260 -0
  13. package/dist/execution/index.js +1625 -0
  14. package/dist/execution/index.js.map +1 -0
  15. package/dist/index.d.ts +81 -11
  16. package/dist/index.js +7727 -6174
  17. package/dist/index.js.map +1 -1
  18. package/dist/infrastructure/index.d.ts +10 -0
  19. package/dist/infrastructure/index.js +575 -0
  20. package/dist/infrastructure/index.js.map +1 -0
  21. package/dist/input-reader-E-ffP2ee.d.ts +12 -0
  22. package/dist/kernel/index.d.ts +15 -4
  23. package/dist/kernel/index.js.map +1 -1
  24. package/dist/logger-BH6AE0W9.d.ts +24 -0
  25. package/dist/logger-BMQgxvdy.d.ts +12 -0
  26. package/dist/mcp-servers-BA1Ofmfj.d.ts +100 -0
  27. package/dist/memory-CEXuo7sz.d.ts +16 -0
  28. package/dist/mode-CV077NjV.d.ts +27 -0
  29. package/dist/models/index.d.ts +60 -0
  30. package/dist/models/index.js +621 -0
  31. package/dist/models/index.js.map +1 -0
  32. package/dist/models-registry-DqzwpBQy.d.ts +46 -0
  33. package/dist/models-registry-Y2xbog0E.d.ts +95 -0
  34. package/dist/multi-agent-BDfkxL5C.d.ts +351 -0
  35. package/dist/observability/index.d.ts +353 -0
  36. package/dist/observability/index.js +691 -0
  37. package/dist/observability/index.js.map +1 -0
  38. package/dist/observability-BhnVLBLS.d.ts +67 -0
  39. package/dist/path-resolver-CPRj4bFY.d.ts +10 -0
  40. package/dist/path-resolver-Crkt8wTQ.d.ts +54 -0
  41. package/dist/plugin-CoYYZKdn.d.ts +447 -0
  42. package/dist/renderer-0A2ZEtca.d.ts +158 -0
  43. package/dist/sdd/index.d.ts +206 -0
  44. package/dist/sdd/index.js +864 -0
  45. package/dist/sdd/index.js.map +1 -0
  46. package/dist/secret-scrubber-3TLUkiCV.d.ts +31 -0
  47. package/dist/secret-scrubber-CwYliRWd.d.ts +54 -0
  48. package/dist/secret-vault-DoISxaKO.d.ts +19 -0
  49. package/dist/security/index.d.ts +46 -0
  50. package/dist/security/index.js +536 -0
  51. package/dist/security/index.js.map +1 -0
  52. package/dist/selector-BRqzvugb.d.ts +51 -0
  53. package/dist/session-reader-C3x96CDR.d.ts +150 -0
  54. package/dist/skill-Bx8jxznf.d.ts +72 -0
  55. package/dist/storage/index.d.ts +540 -0
  56. package/dist/storage/index.js +1802 -0
  57. package/dist/storage/index.js.map +1 -0
  58. package/dist/{system-prompt-vAB0F54-.d.ts → system-prompt-CG9jU5-5.d.ts} +9 -1
  59. package/dist/task-graph-BITvWt4t.d.ts +160 -0
  60. package/dist/tool-executor-CYdZdtno.d.ts +97 -0
  61. package/dist/types/index.d.ts +26 -4
  62. package/dist/types/index.js +1787 -4
  63. package/dist/types/index.js.map +1 -1
  64. package/dist/utils/index.d.ts +49 -2
  65. package/dist/utils/index.js +100 -2
  66. package/dist/utils/index.js.map +1 -1
  67. package/package.json +34 -2
  68. package/dist/mode-Pjt5vMS6.d.ts +0 -815
  69. package/dist/session-reader-9sOTgmeC.d.ts +0 -1087
@@ -0,0 +1,1625 @@
1
+ import * as fs from 'fs/promises';
2
+ import * as path from 'path';
3
+
4
+ // src/utils/token-estimate.ts
5
+ var RoughTokenEstimate = (text) => Math.max(1, Math.ceil(text.length / 4));
6
+ var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
7
+ var ESTIMATE_CACHE_MAX_SIZE = 1e4;
8
+ function getCachedEstimate(key, compute) {
9
+ const existing = ESTIMATE_CACHE.get(key);
10
+ if (existing !== void 0) return existing;
11
+ if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {
12
+ const keys = [...ESTIMATE_CACHE.keys()];
13
+ for (let i = 0; i < Math.floor(ESTIMATE_CACHE_MAX_SIZE / 4); i++) {
14
+ ESTIMATE_CACHE.delete(keys[i]);
15
+ }
16
+ }
17
+ const estimate = compute();
18
+ ESTIMATE_CACHE.set(key, estimate);
19
+ return estimate;
20
+ }
21
+ function estimateToolInputTokens(input) {
22
+ if (typeof input === "string") return RoughTokenEstimate(input);
23
+ if (input === null || typeof input !== "object") {
24
+ return RoughTokenEstimate(String(input));
25
+ }
26
+ const key = JSON.stringify(input);
27
+ return getCachedEstimate(key, () => RoughTokenEstimate(key));
28
+ }
29
+ function estimateToolResultTokens(content) {
30
+ if (typeof content === "string") return RoughTokenEstimate(content);
31
+ const key = JSON.stringify(content);
32
+ return getCachedEstimate(key, () => RoughTokenEstimate(key));
33
+ }
34
+ function estimateTextTokens(text) {
35
+ return RoughTokenEstimate(text);
36
+ }
37
+
38
+ // src/execution/compactor.ts
39
+ var HybridCompactor = class {
40
+ preserveK;
41
+ eliseThreshold;
42
+ estimator;
43
+ constructor(opts = {}) {
44
+ this.preserveK = opts.preserveK ?? 10;
45
+ this.eliseThreshold = opts.eliseThreshold ?? 2e3;
46
+ this.estimator = opts.estimator ?? estimateTextTokens;
47
+ }
48
+ async compact(ctx, opts = {}) {
49
+ const beforeTokens = this.estimateMessages(ctx.messages);
50
+ const reductions = [];
51
+ const phase1Saved = this.eliseOldToolResults(ctx);
52
+ if (phase1Saved > 0) reductions.push({ phase: "elision", saved: phase1Saved });
53
+ if (opts.aggressive) {
54
+ const phase2Saved = this.collapseAncientTurns(ctx);
55
+ if (phase2Saved > 0) reductions.push({ phase: "summary", saved: phase2Saved });
56
+ }
57
+ const afterTokens = this.estimateMessages(ctx.messages);
58
+ return { before: beforeTokens, after: afterTokens, reductions };
59
+ }
60
+ eliseOldToolResults(ctx) {
61
+ const messages = ctx.messages;
62
+ let pairCount = 0;
63
+ let preserveStart = messages.length;
64
+ for (let i = messages.length - 1; i >= 0 && pairCount < this.preserveK; i--) {
65
+ const m = messages[i];
66
+ if (!m) continue;
67
+ if (m.role === "user" || m.role === "assistant") {
68
+ pairCount++;
69
+ preserveStart = i;
70
+ }
71
+ }
72
+ let saved = 0;
73
+ let changed = false;
74
+ const nextMessages = new Array(messages.length);
75
+ for (let i = 0; i < messages.length; i++) {
76
+ const msg = messages[i];
77
+ if (i >= preserveStart) {
78
+ nextMessages[i] = msg;
79
+ continue;
80
+ }
81
+ if (!msg || !Array.isArray(msg.content)) {
82
+ nextMessages[i] = msg;
83
+ continue;
84
+ }
85
+ const newContent = msg.content.map((b) => {
86
+ if (b.type !== "tool_result") return b;
87
+ const tokens = estimateToolResultTokens(b.content);
88
+ if (tokens < this.eliseThreshold) return b;
89
+ saved += tokens;
90
+ const elided = {
91
+ type: "tool_result",
92
+ tool_use_id: b.tool_use_id,
93
+ content: `[elided: ~${tokens} tokens removed. Call the tool again if needed.]`,
94
+ is_error: b.is_error
95
+ };
96
+ return elided;
97
+ });
98
+ if (newContent.length === msg.content.length && newContent.every((b, idx) => b === msg.content[idx])) {
99
+ nextMessages[i] = msg;
100
+ } else {
101
+ nextMessages[i] = { ...msg, content: newContent };
102
+ changed = true;
103
+ }
104
+ }
105
+ if (changed) ctx.state.replaceMessages(nextMessages);
106
+ return saved;
107
+ }
108
+ collapseAncientTurns(ctx) {
109
+ const messages = ctx.messages;
110
+ const cutTarget = Math.max(0, messages.length - this.preserveK * 2);
111
+ if (cutTarget <= 0) return 0;
112
+ let boundary = -1;
113
+ for (let i = cutTarget; i < messages.length; i++) {
114
+ const m = messages[i];
115
+ if (!m) continue;
116
+ if (m.role === "user" && hasTextContent(m)) {
117
+ boundary = i;
118
+ break;
119
+ }
120
+ }
121
+ if (boundary <= 0) return 0;
122
+ const removed = messages.slice(0, boundary);
123
+ const removedTokens = this.estimateMessages(removed);
124
+ const summary = [
125
+ {
126
+ role: "user",
127
+ content: `[previous_session_summary: ${removed.length} earlier turns compacted. Todo state preserved in context.]`
128
+ },
129
+ { role: "assistant", content: "Continuing from compacted context." }
130
+ ];
131
+ const tail = ctx.messages.slice(boundary);
132
+ ctx.state.replaceMessages([...summary, ...tail]);
133
+ return Math.max(0, removedTokens - this.estimateMessages(summary));
134
+ }
135
+ estimateMessages(messages) {
136
+ let total = 0;
137
+ for (const m of messages) {
138
+ if (typeof m.content === "string") {
139
+ total += this.estimator(m.content);
140
+ } else {
141
+ for (const b of m.content) {
142
+ if (b.type === "text") total += this.estimator(b.text);
143
+ else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
144
+ else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
145
+ }
146
+ }
147
+ }
148
+ return total;
149
+ }
150
+ };
151
+ function hasTextContent(m) {
152
+ if (typeof m.content === "string") return m.content.trim().length > 0;
153
+ return m.content.some((b) => b.type === "text" && b.text.trim().length > 0);
154
+ }
155
+
156
+ // src/types/blocks.ts
157
+ function isTextBlock(b) {
158
+ return b.type === "text";
159
+ }
160
+
161
+ // src/execution/intelligent-compactor.ts
162
+ var IntelligentCompactor = class {
163
+ provider;
164
+ warnThreshold;
165
+ softThreshold;
166
+ hardThreshold;
167
+ maxContext;
168
+ preserveK;
169
+ eliseThreshold;
170
+ summarizerPrompt;
171
+ summarizerModel;
172
+ constructor(opts) {
173
+ this.provider = opts.provider;
174
+ this.warnThreshold = opts.warnThreshold ?? 0.6;
175
+ this.softThreshold = opts.softThreshold ?? 0.75;
176
+ this.hardThreshold = opts.hardThreshold ?? 0.9;
177
+ this.maxContext = opts.maxContext ?? 128e3;
178
+ this.preserveK = opts.preserveK ?? 4;
179
+ this.eliseThreshold = opts.eliseThreshold ?? 500;
180
+ this.summarizerPrompt = opts.summarizerPrompt ?? "You are a context summarizer. Given a list of conversation messages, produce a concise but complete summary that preserves all factual information, decisions made, and any state changes (e.g. file edits, todo updates). Do not add commentary. Output only the summary.";
181
+ this.summarizerModel = opts.summarizerModel;
182
+ }
183
+ async compact(ctx, opts = {}) {
184
+ const beforeTokens = this.estimateTokens(ctx.messages);
185
+ const reductions = [];
186
+ const load = beforeTokens / this.maxContext;
187
+ const aggressive = load >= this.hardThreshold ? true : opts.aggressive ?? load >= this.softThreshold;
188
+ const saved1 = this.eliseOldToolResults(ctx);
189
+ if (saved1 > 0) reductions.push({ phase: "elision", saved: saved1 });
190
+ if (aggressive) {
191
+ const saved2 = await this.summarizeAncientTurns(ctx);
192
+ if (saved2 > 0) reductions.push({ phase: "summary", saved: saved2 });
193
+ } else if (load >= this.warnThreshold) {
194
+ const saved2 = this.lightweightCompact(ctx);
195
+ if (saved2 > 0) reductions.push({ phase: "elision", saved: saved2 });
196
+ }
197
+ const afterTokens = this.estimateTokens(ctx.messages);
198
+ return { before: beforeTokens, after: afterTokens, reductions };
199
+ }
200
+ async summarizeAncientTurns(ctx) {
201
+ const messages = ctx.messages;
202
+ const cutoff = Math.max(0, messages.length - this.preserveK * 2);
203
+ if (cutoff <= 2) return 0;
204
+ const boundary = this.findSafeBoundary(messages, 0, cutoff);
205
+ if (boundary <= 1) return 0;
206
+ const toSummarize = messages.slice(0, boundary);
207
+ const removedTokens = this.estimateTokens(toSummarize);
208
+ let summaryText;
209
+ try {
210
+ summaryText = await this.callSummarizer(toSummarize, ctx);
211
+ } catch {
212
+ summaryText = `[${toSummarize.length} earlier turns omitted \u2014 key decisions and file states preserved in context]`;
213
+ }
214
+ const summaryMsg = {
215
+ role: "system",
216
+ content: `[prior_turns_summary: ${summaryText}]`
217
+ };
218
+ const summaryTokens = this.estimateTokens([summaryMsg]);
219
+ const tail = ctx.messages.slice(boundary);
220
+ ctx.state.replaceMessages([summaryMsg, ...tail]);
221
+ return Math.max(0, removedTokens - summaryTokens);
222
+ }
223
+ findSafeBoundary(messages, from, to) {
224
+ for (let i = to; i >= from; i--) {
225
+ const m = messages[i];
226
+ if (!m) continue;
227
+ if (m.role === "user" && this.hasTextContent(m)) {
228
+ return this.findExchangeStart(messages, i);
229
+ }
230
+ }
231
+ return -1;
232
+ }
233
+ findExchangeStart(messages, userIndex) {
234
+ for (let i = userIndex - 1; i >= 0; i--) {
235
+ const m = messages[i];
236
+ if (!m) continue;
237
+ if (m.role === "assistant") {
238
+ const hasToolUse = Array.isArray(m.content) ? m.content.some((b) => b.type === "tool_use") : false;
239
+ if (!hasToolUse) {
240
+ return i + 1;
241
+ }
242
+ } else if (m.role !== "user") ; else {
243
+ return i;
244
+ }
245
+ }
246
+ return 0;
247
+ }
248
+ async callSummarizer(messages, ctx) {
249
+ const prompt = [
250
+ { type: "text", text: this.summarizerPrompt },
251
+ { type: "text", text: "\n\nConversation to summarize:\n" },
252
+ ...this.messagesToText(messages)
253
+ ];
254
+ const req = {
255
+ model: this.summarizerModel ?? ctx.model,
256
+ system: prompt,
257
+ messages: [],
258
+ maxTokens: 1024
259
+ };
260
+ const ac = ctx.signal ? void 0 : new AbortController();
261
+ const signal = ctx.signal ?? ac.signal;
262
+ const res = await this.provider.complete(req, { signal });
263
+ const textBlocks = res.content.filter(isTextBlock);
264
+ return textBlocks.map((b) => b.text).join("\n").trim() || "(empty summary)";
265
+ }
266
+ messagesToText(messages) {
267
+ const lines = [];
268
+ for (const m of messages) {
269
+ const role = m.role.padEnd(10, " ");
270
+ if (typeof m.content === "string") {
271
+ lines.push(`[${role}]: ${m.content.slice(0, 500)}`);
272
+ } else if (Array.isArray(m.content)) {
273
+ const textParts = m.content.filter(isTextBlock).map((b) => b.text);
274
+ if (textParts.length > 0) {
275
+ lines.push(`[${role}]: ${textParts.join(" ").slice(0, 500)}`);
276
+ }
277
+ }
278
+ }
279
+ return [{ type: "text", text: lines.join("\n") }];
280
+ }
281
+ lightweightCompact(ctx) {
282
+ return this.eliseOldToolResults(ctx);
283
+ }
284
+ eliseOldToolResults(ctx) {
285
+ const messages = ctx.messages;
286
+ let pairCount = 0;
287
+ let preserveStart = messages.length;
288
+ for (let i = messages.length - 1; i >= 0 && pairCount < this.preserveK; i--) {
289
+ const m = messages[i];
290
+ if (!m) continue;
291
+ if (m.role === "user" || m.role === "assistant") {
292
+ pairCount++;
293
+ preserveStart = i;
294
+ }
295
+ }
296
+ let saved = 0;
297
+ let changed = false;
298
+ const nextMessages = new Array(messages.length);
299
+ for (let i = 0; i < messages.length; i++) {
300
+ const msg = messages[i];
301
+ if (i >= preserveStart) {
302
+ nextMessages[i] = msg;
303
+ continue;
304
+ }
305
+ if (!msg || !Array.isArray(msg.content)) {
306
+ nextMessages[i] = msg;
307
+ continue;
308
+ }
309
+ const newContent = msg.content.map((b) => {
310
+ if (b.type !== "tool_result") return b;
311
+ const tokens = estimateToolResultTokens(b.content);
312
+ if (tokens < this.eliseThreshold) return b;
313
+ saved += tokens;
314
+ return {
315
+ type: "tool_result",
316
+ tool_use_id: b.tool_use_id,
317
+ content: `[elided: ~${tokens} tokens]`,
318
+ is_error: b.is_error
319
+ };
320
+ });
321
+ if (newContent.length === msg.content.length && newContent.every((b, idx) => b === msg.content[idx])) {
322
+ nextMessages[i] = msg;
323
+ } else {
324
+ nextMessages[i] = { ...msg, content: newContent };
325
+ changed = true;
326
+ }
327
+ }
328
+ if (changed) ctx.state.replaceMessages(nextMessages);
329
+ return saved;
330
+ }
331
+ hasTextContent(m) {
332
+ if (typeof m.content === "string") return m.content.trim().length > 0;
333
+ return m.content.some((b) => b.type === "text" && b.text.trim().length > 0);
334
+ }
335
+ estimateTokens(messages) {
336
+ let total = 0;
337
+ for (const m of messages) {
338
+ if (typeof m.content === "string") {
339
+ total += estimateTextTokens(m.content);
340
+ } else {
341
+ for (const b of m.content) {
342
+ if (b.type === "text") total += estimateTextTokens(b.text);
343
+ else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
344
+ else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
345
+ }
346
+ }
347
+ }
348
+ return total;
349
+ }
350
+ };
351
+
352
+ // src/models/llm-selector.ts
353
+ var DEFAULT_SYSTEM_PROMPT = `You are a context pruning assistant. Given a conversation history and a token budget, decide which message ranges are worth keeping verbatim and which should be collapsed into summaries.
354
+
355
+ Output a JSON object with this structure:
356
+ {
357
+ "kept": [{"from": 0, "to": 5, "importance": "critical"}],
358
+ "collapsed": [{"from": 6, "to": 20, "summary": "optional summary"}],
359
+ "reasoning": "brief explanation of decisions"
360
+ }
361
+
362
+ Importance tiers:
363
+ - "critical": decisions, file edits, tool results that affect state, final answers
364
+ - "high": substantive tool use, complex reasoning, non-obvious observations
365
+ - "medium": routine exchanges, confirmations, straightforward Q&A
366
+
367
+ Rules:
368
+ - Always keep the most recent K pairs (preserve recency)
369
+ - Never collapse the final 2 user/assistant pairs (working memory)
370
+ - Preserve tool results that modified files or had external effects
371
+ - Collapse old, low-information exchanges (greetings, acknowledgements, etc.)
372
+ - If unsure, keep rather than collapse (errors are more costly than waste)
373
+
374
+ Return ONLY the JSON object, no markdown, no explanation outside the JSON.`;
375
+ function estimateTokens(messages) {
376
+ let total = 0;
377
+ for (const m of messages) {
378
+ if (typeof m.content === "string") {
379
+ total += Math.ceil(m.content.length / 4);
380
+ } else if (Array.isArray(m.content)) {
381
+ for (const b of m.content) {
382
+ if (b.type === "text") total += Math.ceil(b.text.length / 4);
383
+ else total += Math.ceil(JSON.stringify(b).length / 4);
384
+ }
385
+ }
386
+ }
387
+ return total;
388
+ }
389
+ function formatMessages(messages, maxChars = 8e3) {
390
+ const lines = [];
391
+ let used = 0;
392
+ for (let i = 0; i < messages.length; i++) {
393
+ const m = messages[i];
394
+ const role = m.role.padEnd(10, " ");
395
+ let text;
396
+ if (typeof m.content === "string") {
397
+ text = m.content.slice(0, 500);
398
+ } else {
399
+ const content = m.content;
400
+ text = content.filter(isTextBlock).map((b) => b.text).join(" ");
401
+ const toolUses = content.filter((b) => b.type === "tool_use");
402
+ if (toolUses.length > 0) {
403
+ text += ` [tools: ${toolUses.map((b) => b.name).join(", ")}]`;
404
+ }
405
+ }
406
+ const line = `[${i}][${role}]: ${text}`;
407
+ if (used + line.length > maxChars) break;
408
+ lines.push(line);
409
+ used += line.length;
410
+ }
411
+ return lines.join("\n");
412
+ }
413
+ var LLMSelector = class {
414
+ provider;
415
+ model;
416
+ maxContextTokens;
417
+ systemPrompt;
418
+ constructor(opts) {
419
+ this.provider = opts.provider;
420
+ this.model = opts.model ?? "unknown";
421
+ this.maxContextTokens = opts.maxContextTokens ?? 4e4;
422
+ this.systemPrompt = opts.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
423
+ }
424
+ async select(messages, maxToKeep) {
425
+ const effectiveBudget = Math.min(maxToKeep, this.maxContextTokens);
426
+ const historyText = formatMessages(messages);
427
+ const totalTokens = estimateTokens(messages);
428
+ const systemText = `${this.systemPrompt}
429
+
430
+ Conversation (${messages.length} messages, ~${totalTokens} tokens, budget: ${effectiveBudget}):
431
+ `;
432
+ const budgetInstruction = totalTokens > effectiveBudget ? `
433
+
434
+ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiveBudget}). You MUST collapse enough to fit. Prefer collapsing older/lower-importance ranges.` : "";
435
+ const req = {
436
+ model: this.model,
437
+ system: [{ type: "text", text: systemText + budgetInstruction }],
438
+ messages: [{ role: "user", content: historyText }],
439
+ maxTokens: 1024
440
+ };
441
+ let raw;
442
+ try {
443
+ const ac = new AbortController();
444
+ const res = await this.provider.complete(req, { signal: ac.signal });
445
+ const textBlocks = res.content.filter(isTextBlock);
446
+ raw = textBlocks.map((b) => b.text).join("\n").trim();
447
+ } catch (err) {
448
+ return this.fallbackSelect(messages, effectiveBudget);
449
+ }
450
+ return this.parseSelectorOutput(raw, messages.length);
451
+ }
452
+ fallbackSelect(messages, budget) {
453
+ const toKeep = [];
454
+ const toCollapse = [];
455
+ let tokenCount = 0;
456
+ let startIdx = 0;
457
+ for (let i = messages.length - 1; i >= 0; i--) {
458
+ const m = messages[i];
459
+ const cost = typeof m.content === "string" ? Math.ceil(m.content.length / 4) : m.content.reduce(
460
+ (acc, b) => acc + (b.type === "text" ? Math.ceil(b.text.length / 4) : Math.ceil(JSON.stringify(b).length / 4)),
461
+ 0
462
+ );
463
+ if (tokenCount + cost <= budget) {
464
+ tokenCount += cost;
465
+ } else {
466
+ startIdx = i + 1;
467
+ break;
468
+ }
469
+ }
470
+ if (startIdx > 0) {
471
+ toCollapse.push({ from: 0, to: startIdx - 1 });
472
+ }
473
+ toKeep.push({ from: startIdx, to: messages.length - 1, importance: "high" });
474
+ return {
475
+ kept: toKeep,
476
+ collapsed: toCollapse,
477
+ reasoning: `Fallback: kept last ${messages.length - startIdx} messages within ${budget} token budget`
478
+ };
479
+ }
480
+ parseSelectorOutput(raw, messageCount) {
481
+ const jsonStart = raw.indexOf("{");
482
+ const jsonEnd = raw.lastIndexOf("}");
483
+ if (jsonStart === -1 || jsonEnd === -1) {
484
+ return this.fallbackSelect(
485
+ Array.from({ length: messageCount }, () => ({ role: "user", content: "" })),
486
+ this.maxContextTokens
487
+ );
488
+ }
489
+ let parsed;
490
+ try {
491
+ parsed = JSON.parse(raw.slice(jsonStart, jsonEnd + 1));
492
+ } catch {
493
+ return this.fallbackSelect(
494
+ Array.from({ length: messageCount }, () => ({ role: "user", content: "" })),
495
+ this.maxContextTokens
496
+ );
497
+ }
498
+ const obj = parsed;
499
+ const kept = obj.kept ?? [];
500
+ const collapsed = obj.collapsed ?? [];
501
+ return {
502
+ kept: kept.map((k) => ({
503
+ from: k.from,
504
+ to: k.to,
505
+ importance: k.importance ?? "medium"
506
+ })),
507
+ collapsed: collapsed.map((c) => ({ from: c.from, to: c.to, summary: c.summary })),
508
+ reasoning: typeof obj.reasoning === "string" ? obj.reasoning : ""
509
+ };
510
+ }
511
+ };
512
+
513
+ // src/execution/selective-compactor.ts
514
+ var SelectiveCompactor = class {
515
+ provider;
516
+ selector;
517
+ warnThreshold;
518
+ softThreshold;
519
+ hardThreshold;
520
+ maxContext;
521
+ preserveK;
522
+ eliseThreshold;
523
+ summarizerModel;
524
+ summarizerPrompt;
525
+ constructor(opts) {
526
+ this.provider = opts.provider;
527
+ this.selector = opts.selector ?? new LLMSelector({ provider: opts.provider, model: opts.selectorModel });
528
+ this.warnThreshold = opts.warnThreshold ?? 0.6;
529
+ this.softThreshold = opts.softThreshold ?? 0.75;
530
+ this.hardThreshold = opts.hardThreshold ?? 0.9;
531
+ this.maxContext = opts.maxContext ?? 128e3;
532
+ this.preserveK = opts.preserveK ?? 4;
533
+ this.eliseThreshold = opts.eliseThreshold ?? 500;
534
+ this.summarizerModel = opts.summarizerModel ?? opts.selectorModel ?? "unknown";
535
+ this.summarizerPrompt = opts.summarizerPrompt ?? "You are a context summarizer. Given a list of messages, produce a concise summary that preserves all factual information, decisions, file changes, and state changes. Do not add commentary or opinions.";
536
+ }
537
+ async compact(ctx, opts = {}) {
538
+ const beforeTokens = this.estimateTokens(ctx.messages);
539
+ const reductions = [];
540
+ const load = beforeTokens / this.maxContext;
541
+ const shouldCompact = load >= this.warnThreshold || opts.aggressive;
542
+ if (!shouldCompact) {
543
+ const saved = this.eliseOldToolResults(ctx);
544
+ if (saved > 0) reductions.push({ phase: "elision", saved });
545
+ const afterTokens2 = this.estimateTokens(ctx.messages);
546
+ return { before: beforeTokens, after: afterTokens2, reductions };
547
+ }
548
+ const savedElision = this.eliseOldToolResults(ctx);
549
+ if (savedElision > 0) reductions.push({ phase: "elision", saved: savedElision });
550
+ const afterPhase1 = this.estimateTokens(ctx.messages);
551
+ const targetBudget = this.computeTargetBudget(load);
552
+ if (afterPhase1 > targetBudget) {
553
+ const savedSelective = await this.runSelector(ctx, targetBudget);
554
+ if (savedSelective > 0) reductions.push({ phase: "selective", saved: savedSelective });
555
+ }
556
+ const afterTokens = this.estimateTokens(ctx.messages);
557
+ return { before: beforeTokens, after: afterTokens, reductions };
558
+ }
559
+ /**
560
+ * Run the LLM selector to decide what to keep vs collapse.
561
+ * Returns the token savings achieved.
562
+ */
563
+ async runSelector(ctx, targetBudget) {
564
+ const before = this.estimateTokens(ctx.messages);
565
+ let result;
566
+ try {
567
+ result = await this.selector.select(ctx.messages, targetBudget);
568
+ } catch {
569
+ return this.aggressiveRecencyTrim(ctx);
570
+ }
571
+ await this.executePlan(ctx, result);
572
+ const after = this.estimateTokens(ctx.messages);
573
+ return Math.max(0, before - after);
574
+ }
575
+ /**
576
+ * Execute a SelectorResult plan: collapse/remove ranges and
577
+ * insert summaries where the selector provided them.
578
+ */
579
+ async executePlan(ctx, plan) {
580
+ if (ctx.messages.length === 0) return;
581
+ const messages = [...ctx.messages];
582
+ const sortedCollapsed = [...plan.collapsed].sort((a, b) => b.from - a.from);
583
+ for (const range of sortedCollapsed) {
584
+ if (range.from < 0 || range.to >= messages.length || range.from > range.to) continue;
585
+ let summary = range.summary;
586
+ if (!summary) {
587
+ const toSummarize = messages.slice(range.from, range.to + 1);
588
+ summary = await this.summarizeRange(toSummarize, ctx);
589
+ }
590
+ const summaryMsg = {
591
+ role: "system",
592
+ content: `[prior_turns_${range.from}-${range.to}: ${summary}]`
593
+ };
594
+ messages.splice(range.from, range.to - range.from + 1, summaryMsg);
595
+ }
596
+ ctx.state.replaceMessages(messages);
597
+ }
598
+ async summarizeRange(messages, ctx) {
599
+ const systemText = `${this.summarizerPrompt}
600
+
601
+ Summarize the following message range:`;
602
+ const body = messages.map((m, i) => `[${i}] ${m.role}: ${this.messagePreview(m)}`).join("\n");
603
+ const req = {
604
+ model: this.summarizerModel,
605
+ system: [{ type: "text", text: systemText }],
606
+ messages: [{ role: "user", content: body }],
607
+ maxTokens: 512
608
+ };
609
+ try {
610
+ const res = await this.provider.complete(req, {
611
+ signal: ctx.signal ?? new AbortController().signal
612
+ });
613
+ return res.content.filter(isTextBlock).map((b) => b.text).join("\n").trim() || "(empty)";
614
+ } catch {
615
+ return `[${messages.length} earlier turns omitted]`;
616
+ }
617
+ }
618
+ messagePreview(m) {
619
+ if (typeof m.content === "string") return m.content.slice(0, 300);
620
+ return m.content.filter(isTextBlock).map((b) => b.text).join(" ").slice(0, 300);
621
+ }
622
+ /**
623
+ * Fallback when selector fails: aggressively trim from the oldest end
624
+ * until we hit targetBudget.
625
+ */
626
+ aggressiveRecencyTrim(ctx) {
627
+ const messages = ctx.messages;
628
+ const preserveIdx = Math.max(0, messages.length - this.preserveK * 2);
629
+ if (preserveIdx <= 0) return 0;
630
+ let boundary = preserveIdx;
631
+ for (let i = preserveIdx; i < messages.length && i < preserveIdx + 6; i++) {
632
+ const m = messages[i];
633
+ if (m.role === "user" && this.hasTextContent(m)) {
634
+ boundary = i;
635
+ break;
636
+ }
637
+ }
638
+ const removed = messages.slice(0, boundary);
639
+ const removedTokens = this.estimateTokens(removed);
640
+ const summaryMsg = {
641
+ role: "system",
642
+ content: `[${removed.length} earlier turns trimmed \u2014 see session log for details]`
643
+ };
644
+ const tail = messages.slice(boundary);
645
+ ctx.state.replaceMessages([summaryMsg, ...tail]);
646
+ return Math.max(0, removedTokens - this.estimateTokens([summaryMsg]));
647
+ }
648
+ computeTargetBudget(load) {
649
+ if (load >= this.hardThreshold) {
650
+ return Math.floor(this.maxContext * 0.5);
651
+ }
652
+ if (load >= this.softThreshold) {
653
+ return Math.floor(this.maxContext * 0.65);
654
+ }
655
+ return Math.floor(this.maxContext * 0.75);
656
+ }
657
+ eliseOldToolResults(ctx) {
658
+ const messages = ctx.messages;
659
+ let pairCount = 0;
660
+ let preserveStart = messages.length;
661
+ for (let i = messages.length - 1; i >= 0 && pairCount < this.preserveK; i--) {
662
+ const m = messages[i];
663
+ if (!m) continue;
664
+ if (m.role === "user" || m.role === "assistant") {
665
+ pairCount++;
666
+ preserveStart = i;
667
+ }
668
+ }
669
+ let saved = 0;
670
+ let changed = false;
671
+ const nextMessages = new Array(messages.length);
672
+ for (let i = 0; i < messages.length; i++) {
673
+ const msg = messages[i];
674
+ if (i >= preserveStart) {
675
+ nextMessages[i] = msg;
676
+ continue;
677
+ }
678
+ if (!msg || !Array.isArray(msg.content)) {
679
+ nextMessages[i] = msg;
680
+ continue;
681
+ }
682
+ const newContent = msg.content.map((b) => {
683
+ if (b.type !== "tool_result") return b;
684
+ const text = typeof b.content === "string" ? b.content : JSON.stringify(b.content);
685
+ const tokens = this.roughTokenEstimate(text);
686
+ if (tokens < this.eliseThreshold) return b;
687
+ saved += tokens;
688
+ return {
689
+ type: "tool_result",
690
+ tool_use_id: b.tool_use_id,
691
+ content: `[elided: ~${tokens} tokens]`,
692
+ is_error: b.is_error
693
+ };
694
+ });
695
+ if (newContent.every((b, idx) => b === msg.content[idx])) {
696
+ nextMessages[i] = msg;
697
+ } else {
698
+ nextMessages[i] = { ...msg, content: newContent };
699
+ changed = true;
700
+ }
701
+ }
702
+ if (changed) ctx.state.replaceMessages(nextMessages);
703
+ return saved;
704
+ }
705
+ hasTextContent(m) {
706
+ if (typeof m.content === "string") return m.content.trim().length > 0;
707
+ return m.content.some((b) => b.type === "text" && b.text.trim().length > 0);
708
+ }
709
+ estimateTokens(messages) {
710
+ let total = 0;
711
+ for (const m of messages) {
712
+ if (typeof m.content === "string") {
713
+ total += this.roughTokenEstimate(m.content);
714
+ } else {
715
+ for (const b of m.content) {
716
+ if (b.type === "text") total += this.roughTokenEstimate(b.text);
717
+ else if (b.type === "tool_use") total += this.roughTokenEstimate(JSON.stringify(b.input));
718
+ else if (b.type === "tool_result") {
719
+ total += this.roughTokenEstimate(
720
+ typeof b.content === "string" ? b.content : JSON.stringify(b.content)
721
+ );
722
+ }
723
+ }
724
+ }
725
+ }
726
+ return total;
727
+ }
728
+ roughTokenEstimate(text) {
729
+ return Math.max(1, Math.ceil(text.length / 4));
730
+ }
731
+ };
732
+
733
+ // src/types/errors.ts
734
+ var WrongStackError = class extends Error {
735
+ code;
736
+ subsystem;
737
+ severity;
738
+ recoverable;
739
+ context;
740
+ constructor(opts) {
741
+ super(opts.message, { cause: opts.cause });
742
+ this.name = "WrongStackError";
743
+ this.code = opts.code;
744
+ this.subsystem = opts.subsystem;
745
+ this.severity = opts.severity ?? "error";
746
+ this.recoverable = opts.recoverable ?? false;
747
+ this.context = opts.context;
748
+ }
749
+ /**
750
+ * Render a one-line user-facing description.
751
+ * Subclasses should override for domain-specific formatting.
752
+ */
753
+ describe() {
754
+ const ctx = this.context ? ` ${formatContext(this.context)}` : "";
755
+ return `${this.code}: ${this.message}${ctx}`;
756
+ }
757
+ };
758
+ function formatContext(ctx) {
759
+ const parts = Object.entries(ctx).filter(([, v]) => v !== void 0).slice(0, 3).map(([k, v]) => `${k}=${String(v)}`);
760
+ return parts.length > 0 ? `[${parts.join(" ")}]` : "";
761
+ }
762
+ var AgentError = class extends WrongStackError {
763
+ constructor(opts) {
764
+ super({
765
+ message: opts.message,
766
+ code: opts.code,
767
+ subsystem: "agent",
768
+ severity: opts.code === "AGENT_ABORTED" ? "warning" : "error",
769
+ recoverable: opts.recoverable ?? opts.code === "AGENT_ITERATION_LIMIT",
770
+ context: opts.context,
771
+ cause: opts.cause
772
+ });
773
+ this.name = "AgentError";
774
+ }
775
+ };
776
+ function toWrongStackError(err, code = "AGENT_RUN_FAILED") {
777
+ if (err instanceof WrongStackError) return err;
778
+ const message = err instanceof Error ? err.message : String(err);
779
+ return new AgentError({
780
+ message,
781
+ code: code === "UNKNOWN" ? "AGENT_RUN_FAILED" : code,
782
+ cause: err
783
+ });
784
+ }
785
+
786
+ // src/execution/auto-compaction-middleware.ts
787
+ var AutoCompactionMiddleware = class {
788
+ name = "AutoCompaction";
789
+ compactor;
790
+ warnThreshold;
791
+ softThreshold;
792
+ hardThreshold;
793
+ maxContext;
794
+ estimator;
795
+ aggressiveOn;
796
+ events;
797
+ failureMode;
798
+ /**
799
+ * @param compactor Compactor to use for compaction.
800
+ * @param maxContext Provider's max context window in tokens.
801
+ * @param estimator Token estimation function.
802
+ * @param thresholds Threshold fractions (0-1) of maxContext.
803
+ * @param opts Optional behavior. By default, failures at the
804
+ * hard threshold throw AGENT_CONTEXT_OVERFLOW so
805
+ * the agent does not continue into a likely
806
+ * provider context overflow. Warn/soft failures
807
+ * still emit compaction.failed and continue.
808
+ */
809
+ constructor(compactor, maxContext, estimator, thresholds, optsOrAggressiveOn = {}, events) {
810
+ const opts = typeof optsOrAggressiveOn === "string" ? { aggressiveOn: optsOrAggressiveOn, events } : optsOrAggressiveOn;
811
+ this.compactor = compactor;
812
+ this.maxContext = maxContext;
813
+ this.estimator = estimator;
814
+ this.warnThreshold = thresholds.warn;
815
+ this.softThreshold = thresholds.soft;
816
+ this.hardThreshold = thresholds.hard;
817
+ this.aggressiveOn = opts.aggressiveOn ?? "soft";
818
+ this.events = opts.events;
819
+ this.failureMode = opts.failureMode ?? "throw_on_hard";
820
+ }
821
+ handler() {
822
+ return async (ctx, next) => {
823
+ const tokens = this.estimator(ctx);
824
+ const load = tokens / this.maxContext;
825
+ if (load >= this.hardThreshold) {
826
+ await this.compact(ctx, true, { level: "hard", tokens, load });
827
+ } else if (load >= this.softThreshold) {
828
+ await this.compact(ctx, this.aggressiveOn !== "hard", { level: "soft", tokens, load });
829
+ } else if (load >= this.warnThreshold) {
830
+ await this.compact(ctx, false, { level: "warn", tokens, load });
831
+ }
832
+ return next(ctx);
833
+ };
834
+ }
835
+ async compact(ctx, aggressive, pressure) {
836
+ try {
837
+ await this.compactor.compact(ctx, { aggressive });
838
+ } catch (err) {
839
+ const error = err instanceof Error ? err : new Error(String(err));
840
+ const fatal = this.failureMode === "throw" || this.failureMode === "throw_on_hard" && pressure.level === "hard";
841
+ this.events?.emit("compaction.failed", {
842
+ err: error,
843
+ aggressive,
844
+ level: pressure.level,
845
+ tokens: pressure.tokens,
846
+ maxContext: this.maxContext,
847
+ load: pressure.load,
848
+ fatal
849
+ });
850
+ if (fatal) {
851
+ throw new AgentError({
852
+ message: `Auto-compaction failed at ${pressure.level} threshold`,
853
+ code: "AGENT_CONTEXT_OVERFLOW",
854
+ recoverable: true,
855
+ context: {
856
+ level: pressure.level,
857
+ tokens: pressure.tokens,
858
+ maxContext: this.maxContext
859
+ },
860
+ cause: err
861
+ });
862
+ }
863
+ }
864
+ }
865
+ };
866
+
867
+ // src/utils/tool-output-serializer.ts
868
+ function createToolOutputSerializer(opts = {}) {
869
+ const capBytes = opts.perIterationOutputCapBytes ?? 1e5;
870
+ function serialize(value) {
871
+ if (typeof value === "string") return value;
872
+ if (value === null || value === void 0) return "";
873
+ if (typeof value === "object") {
874
+ if (Array.isArray(value)) return value.map(serialize).join("\n");
875
+ if ("text" in value) {
876
+ const t = value.text;
877
+ return typeof t === "string" ? t : JSON.stringify(value, null, 2);
878
+ }
879
+ try {
880
+ return JSON.stringify(value, null, 2);
881
+ } catch {
882
+ return String(value);
883
+ }
884
+ }
885
+ return String(value);
886
+ }
887
+ function enforceCap(text, remainingBudget) {
888
+ if (remainingBudget <= 0) {
889
+ return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
890
+ }
891
+ const textBytes = Buffer.byteLength(text, "utf8");
892
+ if (textBytes <= remainingBudget) {
893
+ return { text, newBudget: remainingBudget - textBytes };
894
+ }
895
+ const marker = `
896
+ \u2026[truncated ${textBytes - remainingBudget} bytes]\u2026
897
+ `;
898
+ const markerBytes = Buffer.byteLength(marker, "utf8");
899
+ const available = remainingBudget - markerBytes;
900
+ if (available <= 0) {
901
+ return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
902
+ }
903
+ const half = Math.floor(available / 2);
904
+ const first = text.slice(0, half);
905
+ const second = text.slice(text.length - half);
906
+ return { text: `${first}${marker}${second}`, newBudget: 0 };
907
+ }
908
+ return { serialize, enforceCap, capBytes };
909
+ }
910
+
911
+ // src/execution/tool-executor.ts
912
+ var ToolExecutor = class {
913
+ constructor(registry, opts) {
914
+ this.registry = registry;
915
+ this.opts = opts;
916
+ this.iterationTimeoutMs = opts.iterationTimeoutMs ?? 3e5;
917
+ this.serializer = createToolOutputSerializer({
918
+ perIterationOutputCapBytes: opts.perIterationOutputCapBytes ?? 1e5
919
+ });
920
+ }
921
+ registry;
922
+ opts;
923
+ serializer;
924
+ iterationTimeoutMs;
925
+ /**
926
+ * Execute a batch of tool uses using the configured strategy.
927
+ * Returns the execution results and the remaining output budget.
928
+ */
929
+ async executeBatch(toolUses, ctx, strategy) {
930
+ let budget = this.opts.perIterationOutputCapBytes ?? 1e5;
931
+ const runOne = async (use) => {
932
+ const start = Date.now();
933
+ const tool = this.registry.get(use.name);
934
+ if (!tool) {
935
+ const result = this.unknownToolResult(use, () => this.registry.list().map((t) => t.name));
936
+ budget = this.decrementBudget(result, budget);
937
+ return { result, tool, durationMs: Date.now() - start };
938
+ }
939
+ const decision = await this.opts.permissionPolicy.evaluate(tool, use.input, ctx);
940
+ if (decision.permission === "deny") {
941
+ const result = this.deniedResult(use, decision.reason);
942
+ budget = this.decrementBudget(result, budget);
943
+ return { result, tool, durationMs: Date.now() - start };
944
+ }
945
+ if (decision.permission === "confirm") {
946
+ if (this.opts.confirmAwaiter) {
947
+ const choice = await this.opts.confirmAwaiter(tool, use.input, use.id, tool.name);
948
+ if (choice !== "yes" && choice !== "always") {
949
+ const result = {
950
+ type: "tool_result",
951
+ tool_use_id: use.id,
952
+ content: `Tool "${tool.name}" denied by user.`,
953
+ is_error: true
954
+ };
955
+ budget = this.decrementBudget(result, budget);
956
+ return { result, tool, durationMs: Date.now() - start };
957
+ }
958
+ } else {
959
+ const suggestedPattern = this.subjectFor(tool.name, use.input, tool.subjectKey) ?? tool.name;
960
+ const pending = {
961
+ type: "tool_confirm_pending",
962
+ toolUseId: use.id,
963
+ toolName: tool.name,
964
+ input: use.input,
965
+ suggestedPattern
966
+ };
967
+ return { result: pending, tool, durationMs: Date.now() - start };
968
+ }
969
+ }
970
+ const span = this.opts.tracer?.startSpan(`tool.${tool.name}`, {
971
+ "tool.name": tool.name,
972
+ "tool.mutating": tool.mutating,
973
+ "tool.permission": tool.permission
974
+ });
975
+ try {
976
+ const result = await this.executeTool(tool, use, ctx, budget);
977
+ budget = this.decrementBudget(result, budget);
978
+ span?.setAttribute("tool.is_error", !!result.is_error);
979
+ span?.setAttribute(
980
+ "tool.output_bytes",
981
+ typeof result.content === "string" ? result.content.length : 0
982
+ );
983
+ return { result, tool, durationMs: Date.now() - start };
984
+ } catch (err) {
985
+ const msg = err instanceof Error ? err.message : String(err);
986
+ const scrubbed = this.opts.secretScrubber.scrub(msg);
987
+ this.opts.renderer?.writeToolResult(tool.name, scrubbed, true);
988
+ const result = {
989
+ type: "tool_result",
990
+ tool_use_id: use.id,
991
+ content: `Tool "${tool.name}" threw: ${scrubbed}`,
992
+ is_error: true
993
+ };
994
+ budget = this.decrementBudget(result, budget);
995
+ if (err instanceof Error) span?.recordError(err);
996
+ span?.setAttribute("tool.is_error", true);
997
+ return { result, tool, durationMs: Date.now() - start };
998
+ } finally {
999
+ span?.end();
1000
+ }
1001
+ };
1002
+ const safeRun = async (use) => {
1003
+ try {
1004
+ return await runOne(use);
1005
+ } catch (err) {
1006
+ const msg = err instanceof Error ? err.message : String(err);
1007
+ const scrubbed = this.opts.secretScrubber.scrub(msg);
1008
+ const result = {
1009
+ type: "tool_result",
1010
+ tool_use_id: use.id,
1011
+ content: `Tool "${use.name}" execution failed: ${scrubbed}`,
1012
+ is_error: true
1013
+ };
1014
+ budget = this.decrementBudget(result, budget);
1015
+ return { result, tool: this.registry.get(use.name), durationMs: 0 };
1016
+ }
1017
+ };
1018
+ if (strategy === "sequential") {
1019
+ const outputs = [];
1020
+ for (const use of toolUses) {
1021
+ if (use) outputs.push(await safeRun(use));
1022
+ }
1023
+ return { outputs, remainingBudget: budget };
1024
+ }
1025
+ if (strategy === "parallel") {
1026
+ const outputs = await Promise.all(toolUses.map((use) => safeRun(use)));
1027
+ return { outputs, remainingBudget: budget };
1028
+ }
1029
+ const nonMutating = [];
1030
+ const mutating = [];
1031
+ for (const use of toolUses) {
1032
+ if (!use) continue;
1033
+ const tool = this.registry.get(use.name);
1034
+ if (tool?.mutating) mutating.push(use);
1035
+ else nonMutating.push(use);
1036
+ }
1037
+ const firstPass = await Promise.all(nonMutating.map((use) => safeRun(use)));
1038
+ const secondPass = [];
1039
+ for (const use of mutating) {
1040
+ secondPass.push(await safeRun(use));
1041
+ }
1042
+ return {
1043
+ outputs: [...firstPass, ...secondPass],
1044
+ remainingBudget: budget
1045
+ };
1046
+ }
1047
+ /**
1048
+ * Execute a single tool with timeout, permission check, and output capping.
1049
+ * Emits `tool.started` via the injected EventBus (if any) right before
1050
+ * invoking the tool — closes the observability gap between "model decided
1051
+ * to call a tool" and "tool.executed".
1052
+ */
1053
+ async executeTool(tool, use, ctx, budget) {
1054
+ this.opts.events?.emit("tool.started", {
1055
+ name: tool.name,
1056
+ id: use.id,
1057
+ input: use.input
1058
+ });
1059
+ this.opts.renderer?.writeToolCall(tool.name, use.input);
1060
+ const output = await this.runWithTimeout(tool, use.input, ctx.signal, ctx, use.id);
1061
+ const text = this.serializer.serialize(output);
1062
+ const scrubbed = this.opts.secretScrubber.scrub(text);
1063
+ const { text: capped } = this.serializer.enforceCap(scrubbed, budget);
1064
+ this.opts.renderer?.writeToolResult(tool.name, capped, false);
1065
+ return {
1066
+ type: "tool_result",
1067
+ tool_use_id: use.id,
1068
+ name: tool.name,
1069
+ content: capped,
1070
+ is_error: false
1071
+ };
1072
+ }
1073
+ async runWithTimeout(tool, input, parentSignal, ctx, toolUseId) {
1074
+ if (parentSignal.aborted) {
1075
+ if (parentSignal.reason instanceof Error) throw parentSignal.reason;
1076
+ throw new Error(typeof parentSignal.reason === "string" ? parentSignal.reason : "aborted");
1077
+ }
1078
+ const timeoutMs = tool.timeoutMs ?? this.iterationTimeoutMs;
1079
+ const ctrl = new AbortController();
1080
+ const timer = setTimeout(() => ctrl.abort(new Error("tool timeout")), timeoutMs);
1081
+ const combined = AbortSignal.any([parentSignal, ctrl.signal]);
1082
+ try {
1083
+ if (typeof tool.executeStream === "function") {
1084
+ return await this.runStreamedTool(tool, input, ctx, combined, toolUseId);
1085
+ }
1086
+ return await tool.execute(input, ctx, { signal: combined });
1087
+ } catch (err) {
1088
+ if (combined.aborted && typeof tool.cleanup === "function") {
1089
+ try {
1090
+ await tool.cleanup(input, ctx);
1091
+ } catch {
1092
+ }
1093
+ }
1094
+ throw err;
1095
+ } finally {
1096
+ clearTimeout(timer);
1097
+ }
1098
+ }
1099
+ async runStreamedTool(tool, input, ctx, signal, toolUseId) {
1100
+ let finalOutput;
1101
+ let sawFinal = false;
1102
+ const stream = tool.executeStream(input, ctx, { signal });
1103
+ for await (const ev of stream) {
1104
+ if (ev.type === "final") {
1105
+ finalOutput = ev.output;
1106
+ sawFinal = true;
1107
+ break;
1108
+ }
1109
+ this.opts.events?.emit("tool.progress", {
1110
+ name: tool.name,
1111
+ id: toolUseId ?? "<unknown>",
1112
+ event: ev
1113
+ });
1114
+ }
1115
+ if (!sawFinal) {
1116
+ throw new Error(`tool "${tool.name}" executeStream completed without a 'final' event`);
1117
+ }
1118
+ return finalOutput;
1119
+ }
1120
+ unknownToolResult(use, listFns) {
1121
+ return {
1122
+ type: "tool_result",
1123
+ tool_use_id: use.id,
1124
+ content: `Tool "${use.name}" is not registered. Available tools: ${listFns().join(", ")}`,
1125
+ is_error: true
1126
+ };
1127
+ }
1128
+ deniedResult(use, reason) {
1129
+ return {
1130
+ type: "tool_result",
1131
+ tool_use_id: use.id,
1132
+ content: `Tool "${use.name}" denied: ${reason ?? "policy"}`,
1133
+ is_error: true
1134
+ };
1135
+ }
1136
+ decrementBudget(result, budget) {
1137
+ const contentBytes = typeof result.content === "string" ? Buffer.byteLength(result.content, "utf8") : Buffer.byteLength(JSON.stringify(result.content), "utf8");
1138
+ return Math.max(0, budget - contentBytes);
1139
+ }
1140
+ /**
1141
+ * Compute the suggestedPattern string for a tool+input pair.
1142
+ * Matches the logic in DefaultPermissionPolicy so the TUI shows the
1143
+ * same subject that the trust file would use.
1144
+ */
1145
+ subjectFor(toolName, input, subjectKey) {
1146
+ if (!input || typeof input !== "object") return void 0;
1147
+ const obj = input;
1148
+ const globChars = /[*?\[\]]/g;
1149
+ const escapeGlob = (s) => s.replace(globChars, (c) => `\\${c}`);
1150
+ const normalizePath = (s) => escapeGlob(s.replace(/\\/g, "/"));
1151
+ if (subjectKey) {
1152
+ const v = obj[subjectKey];
1153
+ if (typeof v === "string") {
1154
+ return subjectKey === "path" || subjectKey === "file" || subjectKey === "files" ? normalizePath(v) : escapeGlob(v);
1155
+ }
1156
+ }
1157
+ if (toolName === "bash" && typeof obj.command === "string") {
1158
+ return escapeGlob(obj.command);
1159
+ }
1160
+ if (typeof obj.path === "string") {
1161
+ return normalizePath(obj.path);
1162
+ }
1163
+ if (typeof obj.url === "string") {
1164
+ return escapeGlob(obj.url);
1165
+ }
1166
+ if (typeof obj.name === "string") {
1167
+ return escapeGlob(obj.name);
1168
+ }
1169
+ return void 0;
1170
+ }
1171
+ };
1172
+
1173
+ // src/execution/autonomous-runner.ts
1174
+ var DoneConditionChecker = class {
1175
+ constructor(condition) {
1176
+ this.condition = condition;
1177
+ this.compiledRegex = condition.type === "output_match" && condition.pattern ? new RegExp(condition.pattern) : null;
1178
+ }
1179
+ condition;
1180
+ compiledRegex;
1181
+ check(state) {
1182
+ switch (this.condition.type) {
1183
+ case "iterations":
1184
+ if (this.condition.maxIterations && state.iterations >= this.condition.maxIterations) {
1185
+ return {
1186
+ done: true,
1187
+ reason: `max iterations (${this.condition.maxIterations}) reached`,
1188
+ ...state
1189
+ };
1190
+ }
1191
+ break;
1192
+ case "tool_calls":
1193
+ if (this.condition.maxToolCalls && state.toolCalls >= this.condition.maxToolCalls) {
1194
+ return {
1195
+ done: true,
1196
+ reason: `max tool calls (${this.condition.maxToolCalls}) reached`,
1197
+ ...state
1198
+ };
1199
+ }
1200
+ break;
1201
+ case "output_match":
1202
+ if (this.compiledRegex && state.lastOutput && this.compiledRegex.test(state.lastOutput)) {
1203
+ return {
1204
+ done: true,
1205
+ reason: `output matched pattern "${this.condition.pattern}"`,
1206
+ ...state
1207
+ };
1208
+ }
1209
+ break;
1210
+ }
1211
+ return { done: false, iterations: state.iterations, toolCalls: state.toolCalls };
1212
+ }
1213
+ };
1214
+ var AutonomousRunner = class {
1215
+ constructor(opts) {
1216
+ this.opts = opts;
1217
+ this.doneChecker = new DoneConditionChecker(opts.doneCondition);
1218
+ }
1219
+ opts;
1220
+ iterations = 0;
1221
+ toolCalls = 0;
1222
+ lastOutput;
1223
+ stopped = false;
1224
+ doneChecker;
1225
+ async run() {
1226
+ const offToolExecuted = this.opts.agent.events?.on?.("tool.executed", () => {
1227
+ this.toolCalls++;
1228
+ });
1229
+ try {
1230
+ return await this.runLoop();
1231
+ } finally {
1232
+ offToolExecuted?.();
1233
+ }
1234
+ }
1235
+ async runLoop() {
1236
+ while (!this.stopped) {
1237
+ const check = this.doneChecker.check({
1238
+ iterations: this.iterations,
1239
+ toolCalls: this.toolCalls,
1240
+ lastOutput: this.lastOutput
1241
+ });
1242
+ if (check.done) {
1243
+ const result = {
1244
+ status: "done",
1245
+ iterations: this.iterations,
1246
+ toolCalls: this.toolCalls,
1247
+ reason: check.reason
1248
+ };
1249
+ this.opts.onDone?.(result);
1250
+ return result;
1251
+ }
1252
+ this.opts.onIteration?.({ iteration: this.iterations, toolCalls: this.toolCalls });
1253
+ const ctrl = new AbortController();
1254
+ const timeout = setTimeout(() => ctrl.abort(), this.opts.iterationTimeoutMs ?? 3e4);
1255
+ try {
1256
+ const result = await this.opts.agent.run("", {
1257
+ signal: ctrl.signal,
1258
+ maxIterations: 1,
1259
+ executionStrategy: "sequential"
1260
+ });
1261
+ this.iterations++;
1262
+ if (result.status === "done") {
1263
+ this.lastOutput = result.finalText;
1264
+ }
1265
+ if (result.status === "failed" || result.status === "aborted") {
1266
+ const failedResult = {
1267
+ status: result.status,
1268
+ error: result.error,
1269
+ iterations: this.iterations,
1270
+ toolCalls: this.toolCalls
1271
+ };
1272
+ this.opts.onDone?.(failedResult);
1273
+ return failedResult;
1274
+ }
1275
+ } catch (e) {
1276
+ const isAbort = e instanceof DOMException && e.name === "AbortError" || e instanceof Error && e.name === "AbortError" || e instanceof Error && e.message.includes("iteration timeout");
1277
+ if (isAbort) {
1278
+ const timeoutResult = {
1279
+ status: "failed",
1280
+ error: toWrongStackError(e),
1281
+ iterations: this.iterations,
1282
+ toolCalls: this.toolCalls,
1283
+ reason: "iteration timeout"
1284
+ };
1285
+ this.opts.onDone?.(timeoutResult);
1286
+ return timeoutResult;
1287
+ }
1288
+ this.stopped = true;
1289
+ const failedResult = {
1290
+ status: "failed",
1291
+ error: toWrongStackError(e),
1292
+ iterations: this.iterations,
1293
+ toolCalls: this.toolCalls,
1294
+ reason: e instanceof Error ? e.message : String(e)
1295
+ };
1296
+ this.opts.onDone?.(failedResult);
1297
+ return failedResult;
1298
+ } finally {
1299
+ clearTimeout(timeout);
1300
+ }
1301
+ }
1302
+ return {
1303
+ status: "aborted",
1304
+ iterations: this.iterations,
1305
+ toolCalls: this.toolCalls,
1306
+ reason: "stopped externally"
1307
+ };
1308
+ }
1309
+ stop() {
1310
+ this.stopped = true;
1311
+ }
1312
+ };
1313
+
1314
+ // src/types/provider.ts
1315
+ var ProviderError = class extends WrongStackError {
1316
+ status;
1317
+ retryable;
1318
+ providerId;
1319
+ body;
1320
+ constructor(message, status, retryable, providerId, opts = {}) {
1321
+ super({
1322
+ message,
1323
+ code: providerStatusToCode(status, opts.body?.type),
1324
+ subsystem: "provider",
1325
+ severity: status >= 500 ? "error" : "warning",
1326
+ recoverable: retryable,
1327
+ context: { providerId, status },
1328
+ cause: opts.cause
1329
+ });
1330
+ this.name = "ProviderError";
1331
+ this.status = status;
1332
+ this.retryable = retryable;
1333
+ this.providerId = providerId;
1334
+ this.body = opts.body;
1335
+ }
1336
+ /**
1337
+ * Render a one-line, user-facing description. Designed for the CLI/TUI
1338
+ * status line and the agent's retry warning. Avoids dumping raw JSON
1339
+ * (which is what users see today when a 529 lands and the log message
1340
+ * includes the full `{"type":"error",...}` body).
1341
+ *
1342
+ * Examples:
1343
+ * "minimax-coding-plan overloaded (529): High traffic detected. Upgrade for highspeed model. [req 06534785201de9c0…]"
1344
+ * "openai rate limited (429): Retry after 12s"
1345
+ * "anthropic invalid request (400): messages.0.role must be one of 'user'|'assistant'"
1346
+ * "groq HTTP 500 (server error)"
1347
+ */
1348
+ describe() {
1349
+ const kind = describeStatus(this.status, this.body?.type);
1350
+ const head = `${this.providerId} ${kind}`;
1351
+ const detail = this.body?.message?.trim();
1352
+ const reqId = this.body?.requestId ? ` [req ${this.body.requestId.slice(0, 16)}${this.body.requestId.length > 16 ? "\u2026" : ""}]` : "";
1353
+ if (detail && detail.length > 0) {
1354
+ return `${head}: ${truncate(detail, 240)}${reqId}`;
1355
+ }
1356
+ return `${head}${reqId}`;
1357
+ }
1358
+ };
1359
+ function describeStatus(status, type) {
1360
+ if (status === 0) return "network error";
1361
+ if (type === "overloaded_error" || status === 529) return `overloaded (${status})`;
1362
+ if (type === "rate_limit_error" || status === 429) return `rate limited (${status})`;
1363
+ if (type === "authentication_error" || status === 401) return `auth failed (${status})`;
1364
+ if (type === "permission_error" || status === 403) return `forbidden (${status})`;
1365
+ if (type === "not_found_error" || status === 404) return `not found (${status})`;
1366
+ if (type === "invalid_request_error" || status === 400) return `invalid request (${status})`;
1367
+ if (status === 408) return `timeout (${status})`;
1368
+ if (status >= 500 && status < 600) return `HTTP ${status} (server error)`;
1369
+ if (type) return `${type} (${status})`;
1370
+ return `HTTP ${status}`;
1371
+ }
1372
+ function truncate(s, n) {
1373
+ return s.length <= n ? s : `${s.slice(0, n - 1)}\u2026`;
1374
+ }
1375
+ function providerStatusToCode(status, type) {
1376
+ if (status === 0) return "PROVIDER_NETWORK_ERROR";
1377
+ if (type === "rate_limit_error" || status === 429) return "PROVIDER_RATE_LIMITED";
1378
+ if (type === "authentication_error" || status === 401) return "PROVIDER_AUTH_FAILED";
1379
+ if (type === "overloaded_error" || status === 529) return "PROVIDER_OVERLOADED";
1380
+ if (type === "invalid_request_error" || status === 400) return "PROVIDER_INVALID_REQUEST";
1381
+ if (status === 408) return "PROVIDER_NETWORK_ERROR";
1382
+ if (status >= 500) return "PROVIDER_SERVER_ERROR";
1383
+ return "PROVIDER_INVALID_REQUEST";
1384
+ }
1385
+
1386
+ // src/execution/retry-policy.ts
1387
+ var DefaultRetryPolicy = class _DefaultRetryPolicy {
1388
+ static NETWORK_ERR_RE = /ECONN|ETIMEDOUT|ETIME|ENOTFOUND|EAI_AGAIN|fetch failed/i;
1389
+ shouldRetry(err, attempt) {
1390
+ if (err instanceof ProviderError) {
1391
+ if (!err.retryable) return false;
1392
+ return attempt < this.maxAttempts(err);
1393
+ }
1394
+ const msg = err.message ?? "";
1395
+ const isNetwork = _DefaultRetryPolicy.NETWORK_ERR_RE.test(msg);
1396
+ if (isNetwork) return attempt < 2;
1397
+ return false;
1398
+ }
1399
+ maxAttempts(err) {
1400
+ if (err instanceof ProviderError) {
1401
+ if (err.status === 429) return 5;
1402
+ if (err.status === 529) return 3;
1403
+ if (err.status >= 500) return 3;
1404
+ return 0;
1405
+ }
1406
+ return 2;
1407
+ }
1408
+ delayMs(attempt) {
1409
+ const base = 1e3;
1410
+ const exp = base * 2 ** attempt;
1411
+ const jitter = Math.random() * base;
1412
+ return Math.min(3e4, exp + jitter);
1413
+ }
1414
+ };
1415
+
1416
+ // src/execution/error-handler.ts
1417
+ var CONTEXT_OVERFLOW_RE = /context|too long|tokens/i;
1418
+ var NETWORK_ERR_RE = /ECONN|ETIMEDOUT|ETIME|ENOTFOUND|EAI_AGAIN|fetch failed/i;
1419
+ function buildRecoveryStrategies(opts) {
1420
+ return [
1421
+ {
1422
+ label: "context_overflow_reduce",
1423
+ compactor: opts?.compactor,
1424
+ async attempt(err, ctx) {
1425
+ if (!(err instanceof ProviderError)) return null;
1426
+ if (err.status !== 413 && !CONTEXT_OVERFLOW_RE.test(err.message)) return null;
1427
+ if (this.compactor) {
1428
+ try {
1429
+ const report = await this.compactor.compact(ctx, { aggressive: true });
1430
+ if (report.after < report.before) {
1431
+ return { action: "retry", reason: "context_compacted" };
1432
+ }
1433
+ } catch {
1434
+ }
1435
+ }
1436
+ return null;
1437
+ }
1438
+ },
1439
+ {
1440
+ label: "rate_limit_backoff",
1441
+ async attempt(err) {
1442
+ if (!(err instanceof ProviderError) || err.status !== 429) return null;
1443
+ const delayMs = err.body?.retryAfterMs ?? 5e3;
1444
+ const delay = Math.max(1e3, Math.min(delayMs, 6e4));
1445
+ await new Promise((r) => setTimeout(r, delay));
1446
+ return { action: "retry", reason: "rate_limit_backoff" };
1447
+ }
1448
+ },
1449
+ {
1450
+ label: "downgrade_model",
1451
+ async attempt(err, ctx) {
1452
+ if (!(err instanceof ProviderError)) return null;
1453
+ if (err.status !== 429 && err.status !== 529 && err.status < 500) return null;
1454
+ return null;
1455
+ }
1456
+ }
1457
+ ];
1458
+ }
1459
+ var DEFAULT_RECOVERY_STRATEGIES = buildRecoveryStrategies();
1460
+ var DefaultErrorHandler = class {
1461
+ strategies;
1462
+ constructor(strategies = DEFAULT_RECOVERY_STRATEGIES) {
1463
+ this.strategies = strategies;
1464
+ }
1465
+ classify(err) {
1466
+ if (typeof DOMException !== "undefined" && err instanceof DOMException && err.name === "AbortError") {
1467
+ return { kind: "abort", retryable: false };
1468
+ }
1469
+ if (err instanceof Error && err.name === "AbortError") {
1470
+ return { kind: "abort", retryable: false };
1471
+ }
1472
+ if (err instanceof ProviderError) {
1473
+ if (err.status === 429) return { kind: "rate_limit", retryable: true };
1474
+ if (err.status === 529) return { kind: "overloaded", retryable: true };
1475
+ if (err.status >= 500) return { kind: "server", retryable: true };
1476
+ if (err.status === 413 || CONTEXT_OVERFLOW_RE.test(err.message)) {
1477
+ return { kind: "context_overflow", retryable: false };
1478
+ }
1479
+ if (err.status >= 400) return { kind: "client", retryable: false };
1480
+ }
1481
+ if (err instanceof Error && NETWORK_ERR_RE.test(err.message)) {
1482
+ return { kind: "network", retryable: true };
1483
+ }
1484
+ return { kind: "unknown", retryable: false };
1485
+ }
1486
+ async recover(err, ctx) {
1487
+ for (const strategy of this.strategies) {
1488
+ const result = await strategy.attempt(err, ctx);
1489
+ if (result !== null) return result;
1490
+ }
1491
+ return null;
1492
+ }
1493
+ };
1494
+ var DefaultSkillLoader = class {
1495
+ dirs;
1496
+ cache;
1497
+ constructor(opts) {
1498
+ this.dirs = [
1499
+ { dir: opts.paths.inProjectSkills, source: "project" },
1500
+ { dir: opts.paths.globalSkills, source: "user" }
1501
+ ];
1502
+ if (opts.bundledDir) {
1503
+ this.dirs.push({ dir: opts.bundledDir, source: "bundled" });
1504
+ }
1505
+ }
1506
+ async list() {
1507
+ if (this.cache) return this.cache;
1508
+ const found = [];
1509
+ const seen = /* @__PURE__ */ new Set();
1510
+ for (const { dir, source } of this.dirs) {
1511
+ try {
1512
+ const entries = await fs.readdir(dir, { withFileTypes: true });
1513
+ for (const e of entries) {
1514
+ if (!e.isDirectory()) continue;
1515
+ const skillFile = path.join(dir, e.name, "SKILL.md");
1516
+ try {
1517
+ const raw = await fs.readFile(skillFile, "utf8");
1518
+ const meta = parseFrontmatter(raw);
1519
+ if (!meta.name || !meta.description) continue;
1520
+ if (seen.has(meta.name)) continue;
1521
+ seen.add(meta.name);
1522
+ found.push({
1523
+ name: meta.name,
1524
+ description: meta.description,
1525
+ version: meta.version,
1526
+ path: skillFile,
1527
+ source
1528
+ });
1529
+ } catch {
1530
+ }
1531
+ }
1532
+ } catch {
1533
+ }
1534
+ }
1535
+ this.cache = found;
1536
+ return found;
1537
+ }
1538
+ async find(name) {
1539
+ const all = await this.list();
1540
+ return all.find((s) => s.name === name);
1541
+ }
1542
+ async manifestText() {
1543
+ const skills = await this.list();
1544
+ if (skills.length === 0) return "";
1545
+ const entries = await this.listEntries();
1546
+ const lines = ["## Available skills"];
1547
+ for (const e of entries) {
1548
+ const scopeTag = e.scope.length > 0 ? ` \u2014 ${e.scope.slice(0, 3).join(", ")}` : "";
1549
+ lines.push(`- **${e.name}**${scopeTag}`);
1550
+ lines.push(` Use when: ${e.trigger}`);
1551
+ }
1552
+ return lines.join("\n");
1553
+ }
1554
+ async listEntries() {
1555
+ const skills = await this.list();
1556
+ const entries = [];
1557
+ for (const s of skills) {
1558
+ try {
1559
+ const raw = await fs.readFile(s.path, "utf8");
1560
+ const { trigger, scope } = parseDescription(raw);
1561
+ entries.push({ name: s.name, trigger, scope, source: s.source, path: s.path });
1562
+ } catch {
1563
+ }
1564
+ }
1565
+ return entries;
1566
+ }
1567
+ async readBody(name) {
1568
+ const m = await this.find(name);
1569
+ if (!m) throw new Error(`Skill "${name}" not found`);
1570
+ return fs.readFile(m.path, "utf8");
1571
+ }
1572
+ };
1573
+ function parseFrontmatter(raw) {
1574
+ if (!raw.startsWith("---")) return {};
1575
+ const end = raw.indexOf("\n---", 4);
1576
+ if (end === -1) return {};
1577
+ const block = raw.slice(4, end);
1578
+ const out = {};
1579
+ let key = null;
1580
+ let value = [];
1581
+ const flush = () => {
1582
+ if (key) {
1583
+ out[key] = value.join("\n").trim();
1584
+ }
1585
+ key = null;
1586
+ value = [];
1587
+ };
1588
+ for (const line of block.split("\n")) {
1589
+ const m = /^([a-zA-Z_]+):\s*(\|?)\s*(.*)$/.exec(line);
1590
+ if (m) {
1591
+ flush();
1592
+ key = m[1] ?? "";
1593
+ const pipe = m[2];
1594
+ const rest = m[3] ?? "";
1595
+ if (pipe === "|") {
1596
+ value = [];
1597
+ } else if (rest) {
1598
+ value = [rest];
1599
+ } else {
1600
+ value = [];
1601
+ }
1602
+ } else if (key) {
1603
+ value.push(line.replace(/^\s+/, ""));
1604
+ }
1605
+ }
1606
+ flush();
1607
+ return out;
1608
+ }
1609
+ function parseDescription(raw) {
1610
+ const fm = parseFrontmatter(raw);
1611
+ const desc = fm.description ?? "";
1612
+ const firstSentenceEnd = desc.indexOf(". ");
1613
+ const trigger = firstSentenceEnd !== -1 ? desc.slice(0, firstSentenceEnd + 1).trim() : desc.trim().split("\n")[0] ?? "";
1614
+ const scope = [];
1615
+ const coversMatch = /(?:covers|for|including)\s+([^.]+)/i.exec(desc);
1616
+ if (coversMatch) {
1617
+ const items = coversMatch[1].replace(/[·•]/g, ",").split(",").map((s) => s.trim()).filter(Boolean);
1618
+ scope.push(...items);
1619
+ }
1620
+ return { trigger, scope };
1621
+ }
1622
+
1623
+ export { AutoCompactionMiddleware, AutonomousRunner, DefaultErrorHandler, DefaultRetryPolicy, DefaultSkillLoader, DoneConditionChecker, HybridCompactor, IntelligentCompactor, SelectiveCompactor, ToolExecutor };
1624
+ //# sourceMappingURL=index.js.map
1625
+ //# sourceMappingURL=index.js.map