@wrongstack/core 0.265.1 → 0.268.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 (75) hide show
  1. package/dist/{agent-bridge-DrkBxszZ.d.ts → agent-bridge-UhojbpWx.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-DM2pP-B6.d.ts → agent-subagent-runner-Bvtf1o9K.d.ts} +25 -7
  3. package/dist/{brain-BXd_61kQ.d.ts → brain-69wzMKp1.d.ts} +73 -1
  4. package/dist/{compactor-B8pOf45Y.d.ts → compactor-CBQAJoDc.d.ts} +19 -1
  5. package/dist/{config-BMCj_XDs.d.ts → config-VKfOZ-6X.d.ts} +122 -3
  6. package/dist/{context-MRk5PhNv.d.ts → context-C0U8B9NF.d.ts} +88 -1
  7. package/dist/coordination/index.d.ts +57 -161
  8. package/dist/coordination/index.js +471 -177
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +26 -25
  11. package/dist/defaults/index.js +1818 -844
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/execution/index.d.ts +72 -16
  14. package/dist/execution/index.js +1270 -265
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/execution/prompt-enhancer.d.ts +1 -1
  17. package/dist/extension/index.d.ts +7 -6
  18. package/dist/global-mailbox-KByEFFBa.d.ts +663 -0
  19. package/dist/{goal-preamble-DvHDSKSe.d.ts → goal-preamble-CrYjmdw4.d.ts} +28 -11
  20. package/dist/{goal-store-DtLMySNb.d.ts → goal-store-Y_zdLZ3q.d.ts} +1 -1
  21. package/dist/hq/index.d.ts +195 -0
  22. package/dist/hq/index.js +1884 -0
  23. package/dist/hq/index.js.map +1 -0
  24. package/dist/index-BfaS-f_m.d.ts +82 -0
  25. package/dist/{index-B-ch8K9C.d.ts → index-CtQnmkaS.d.ts} +8 -8
  26. package/dist/{index-CEDeNodM.d.ts → index-gCv830d7.d.ts} +5 -5
  27. package/dist/index.d.ts +124 -47
  28. package/dist/index.js +5600 -2662
  29. package/dist/index.js.map +1 -1
  30. package/dist/infrastructure/index.d.ts +6 -6
  31. package/dist/infrastructure/index.js +117 -19
  32. package/dist/infrastructure/index.js.map +1 -1
  33. package/dist/kernel/index.d.ts +10 -9
  34. package/dist/kernel/index.js.map +1 -1
  35. package/dist/{pipeline-DPDxH_7m.d.ts → mailbox-types-Ct2hJq0P.d.ts} +1 -244
  36. package/dist/{mcp-servers-2x4w6Jn9.d.ts → mcp-servers-HT3Fi7Bl.d.ts} +10 -4
  37. package/dist/models/index.d.ts +5 -5
  38. package/dist/models/index.js +33 -3
  39. package/dist/models/index.js.map +1 -1
  40. package/dist/{models-registry-DmJlKuNp.d.ts → models-registry-Bvcl3Vaa.d.ts} +1 -1
  41. package/dist/{multi-agent-coordinator-DyCkCZnU.d.ts → multi-agent-coordinator-BACjsmkC.d.ts} +1 -1
  42. package/dist/{null-fleet-bus-CG9QY2aP.d.ts → null-fleet-bus-DA7fvhUg.d.ts} +14 -9
  43. package/dist/observability/index.d.ts +2 -2
  44. package/dist/{parallel-eternal-engine-Jw9uhEoT.d.ts → parallel-eternal-engine-Ci71gYu_.d.ts} +11 -15
  45. package/dist/{path-resolver-Dy2ej-gE.d.ts → path-resolver-O1IJnmKE.d.ts} +4 -3
  46. package/dist/{permission-B9SB45lp.d.ts → permission-Bd-57Lbl.d.ts} +1 -1
  47. package/dist/{permission-policy-CkjSXabK.d.ts → permission-policy-uNXC6Kge.d.ts} +2 -3
  48. package/dist/pipeline-BDNvENyV.d.ts +245 -0
  49. package/dist/{plan-templates-CzD9GnAU.d.ts → plan-templates-EMsalEtN.d.ts} +5 -5
  50. package/dist/{llm-selector-C0tfTCUe.d.ts → provider-model-resolve-CEb9x886.d.ts} +40 -3
  51. package/dist/{provider-runner-DMa70ODu.d.ts → provider-runner-DWJbpo70.d.ts} +3 -3
  52. package/dist/{retry-policy-CN0khdlj.d.ts → retry-policy-C3s_lvdK.d.ts} +1 -1
  53. package/dist/sdd/index.d.ts +9 -8
  54. package/dist/sdd/index.js +44 -14
  55. package/dist/sdd/index.js.map +1 -1
  56. package/dist/{secret-vault-B2yw84VT.d.ts → secret-vault-Cgduf5xL.d.ts} +2 -2
  57. package/dist/security/index.d.ts +5 -67
  58. package/dist/security/index.js +129 -99
  59. package/dist/security/index.js.map +1 -1
  60. package/dist/{selector-CzHh_igB.d.ts → selector-47LBnBVk.d.ts} +1 -1
  61. package/dist/{session-event-bridge-BUI6Jf-4.d.ts → session-event-bridge-Cw7oqmW2.d.ts} +1 -1
  62. package/dist/{session-reader-CMgdMSRP.d.ts → session-reader-DD4v2Obw.d.ts} +1 -1
  63. package/dist/storage/index.d.ts +14 -12
  64. package/dist/storage/index.js +144 -120
  65. package/dist/storage/index.js.map +1 -1
  66. package/dist/tools/index.d.ts +4 -2
  67. package/dist/tools/index.js +166 -31
  68. package/dist/tools/index.js.map +1 -1
  69. package/dist/types/index.d.ts +20 -19
  70. package/dist/types/index.js +1358 -476
  71. package/dist/types/index.js.map +1 -1
  72. package/dist/utils/index.d.ts +472 -405
  73. package/dist/utils/index.js +2321 -1193
  74. package/dist/utils/index.js.map +1 -1
  75. package/package.json +5 -1
@@ -1,11 +1,81 @@
1
+ import * as path3 from 'path';
1
2
  import { randomUUID, randomBytes, createHash } from 'crypto';
2
3
  import * as fs from 'fs/promises';
3
- import * as path2 from 'path';
4
4
  import * as os from 'os';
5
5
  import { execFile } from 'child_process';
6
6
  import { promisify } from 'util';
7
7
  import { EventEmitter } from 'events';
8
8
 
9
+ // src/utils/tool-wire-compact.ts
10
+ var TOOL_DESCRIPTION_MAX_CHARS = 640;
11
+ var SCHEMA_DESCRIPTION_MAX_CHARS = 180;
12
+ var compactCache = /* @__PURE__ */ new WeakMap();
13
+ function compactToolDefinitionForWire(tool, opts = {}) {
14
+ const useDefaultOptions = opts.descriptionMaxChars === void 0 && opts.schemaDescriptionMaxChars === void 0;
15
+ if (useDefaultOptions && typeof tool === "object" && tool !== null) {
16
+ const cached = compactCache.get(tool);
17
+ if (cached) return cached;
18
+ }
19
+ const compact = {
20
+ name: tool.name,
21
+ description: compactDescription(
22
+ tool.description ?? "",
23
+ opts.descriptionMaxChars ?? TOOL_DESCRIPTION_MAX_CHARS
24
+ ),
25
+ inputSchema: compactSchemaDescriptions(
26
+ tool.inputSchema,
27
+ opts.schemaDescriptionMaxChars ?? SCHEMA_DESCRIPTION_MAX_CHARS
28
+ )
29
+ };
30
+ if (useDefaultOptions && typeof tool === "object" && tool !== null) {
31
+ compactCache.set(tool, compact);
32
+ }
33
+ return compact;
34
+ }
35
+ function compactSchemaDescriptions(schema, maxDescriptionChars = SCHEMA_DESCRIPTION_MAX_CHARS) {
36
+ const compact = compactSchemaNode(schema, maxDescriptionChars);
37
+ return isRecord(compact) ? compact : { type: "object", properties: {} };
38
+ }
39
+ function compactSchemaNode(node, maxDescriptionChars) {
40
+ if (Array.isArray(node)) {
41
+ return node.map((item) => compactSchemaNode(item, maxDescriptionChars));
42
+ }
43
+ if (!isRecord(node)) return node;
44
+ const out = {};
45
+ for (const [key, value] of Object.entries(node)) {
46
+ if (key === "description" && typeof value === "string") {
47
+ out[key] = compactDescription(value, maxDescriptionChars);
48
+ } else {
49
+ out[key] = compactSchemaNode(value, maxDescriptionChars);
50
+ }
51
+ }
52
+ return out;
53
+ }
54
+ function compactDescription(text, maxChars) {
55
+ const normalized = text.replace(/\s+/g, " ").trim();
56
+ if (normalized.length <= maxChars) return normalized;
57
+ if (maxChars <= 20) return normalized.slice(0, maxChars);
58
+ const hardLimit = maxChars - 12;
59
+ const boundary = findSemanticBoundary(normalized, hardLimit);
60
+ const head = normalized.slice(0, boundary > 0 ? boundary : hardLimit).trimEnd();
61
+ return `${head} ...`;
62
+ }
63
+ function findSemanticBoundary(text, limit) {
64
+ const punctuation = Math.max(
65
+ text.lastIndexOf(". ", limit),
66
+ text.lastIndexOf("; ", limit),
67
+ text.lastIndexOf(": ", limit)
68
+ );
69
+ if (punctuation >= Math.floor(limit * 0.45)) return punctuation + 1;
70
+ const comma = text.lastIndexOf(", ", limit);
71
+ if (comma >= Math.floor(limit * 0.6)) return comma + 1;
72
+ const space = text.lastIndexOf(" ", limit);
73
+ return space >= Math.floor(limit * 0.6) ? space : limit;
74
+ }
75
+ function isRecord(value) {
76
+ return !!value && typeof value === "object" && !Array.isArray(value);
77
+ }
78
+
9
79
  // src/utils/token-estimate.ts
10
80
  var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
11
81
  var CALIBRATION_GLOBAL_KEY = "__global__";
@@ -73,7 +143,8 @@ function estimateMessageTokens(messages) {
73
143
  function estimateToolDefTokens(tool) {
74
144
  const cached = tool._estDefTokens;
75
145
  if (typeof cached === "number" && cached > 0) return cached;
76
- return RoughTokenEstimate(tool.name) + RoughTokenEstimate(tool.description ?? "") + RoughTokenEstimate(JSON.stringify(tool.inputSchema));
146
+ const compact = compactToolDefinitionForWire(tool);
147
+ return RoughTokenEstimate(tool.name) + RoughTokenEstimate(compact.description) + RoughTokenEstimate(JSON.stringify(compact.inputSchema));
77
148
  }
78
149
  function estimateRequestTokens(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
79
150
  let messagesTokens = 0;
@@ -251,6 +322,79 @@ function isEmptyMessage(msg) {
251
322
  if (typeof msg.content === "string") return msg.content.trim().length === 0;
252
323
  return msg.content.length === 0;
253
324
  }
325
+ var MAX_DIGEST_CHARS = 4e3;
326
+ function createContextEvidenceState() {
327
+ return {
328
+ sessionGoals: [],
329
+ implicitFacts: [],
330
+ activeErrors: [],
331
+ toolCalls: [],
332
+ fileGraph: {},
333
+ repeatedReads: [],
334
+ updatedAt: Date.now()
335
+ };
336
+ }
337
+ function buildContextEvidenceDigest(ctx) {
338
+ const state = ensureEvidence(ctx);
339
+ const lines = [];
340
+ if (state.currentIntent?.text) {
341
+ lines.push(`intent: ${state.currentIntent.text}`);
342
+ }
343
+ const goals = state.sessionGoals.slice(-3);
344
+ if (goals.length > 0) {
345
+ lines.push("session_goals:");
346
+ for (const goal of goals) lines.push(`- ${goal}`);
347
+ }
348
+ const activeErrors = state.activeErrors.slice(-5);
349
+ if (activeErrors.length > 0) {
350
+ lines.push("active_errors:");
351
+ for (const err of activeErrors) lines.push(`- ${err}`);
352
+ }
353
+ const files = Object.values(state.fileGraph).sort((a, b) => b.writes - a.writes || b.reads - a.reads || a.path.localeCompare(b.path)).slice(0, 12);
354
+ if (files.length > 0) {
355
+ lines.push("dependency_graph:");
356
+ for (const file of files) {
357
+ const actions = [
358
+ file.reads > 0 ? `read ${file.reads}x` : "",
359
+ file.writes > 0 ? `write ${file.writes}x` : ""
360
+ ].filter(Boolean).join(", ");
361
+ const refs = file.referenced ? "; referenced by assistant" : "";
362
+ const via = file.lastToolUseId ? `; last via ${file.lastToolUseId}` : "";
363
+ lines.push(`- ${file.path} (${actions || "seen"}${refs}${via})`);
364
+ }
365
+ }
366
+ const referenced = state.toolCalls.filter((tool) => tool.status === "referenced").slice(-10);
367
+ const recentSeen = state.toolCalls.filter((tool) => tool.status === "seen").slice(-5);
368
+ const trail = [...referenced, ...recentSeen];
369
+ if (trail.length > 0) {
370
+ lines.push("tool_trail:");
371
+ for (const tool of trail) {
372
+ const size = tool.outputTokens ? `; ~${tool.outputTokens} tokens` : "";
373
+ const filesText = tool.files.length > 0 ? `; files=${tool.files.slice(0, 4).join(", ")}` : "";
374
+ const symbolsText = tool.symbols.length > 0 ? `; symbols=${tool.symbols.slice(0, 4).join(", ")}` : "";
375
+ lines.push(
376
+ `- ${tool.toolUseId} ${tool.toolName} ${tool.status}: ${tool.summary}${filesText}${symbolsText}${size}`
377
+ );
378
+ }
379
+ }
380
+ const facts = state.implicitFacts.slice(-8);
381
+ if (facts.length > 0) {
382
+ lines.push("implicit_facts:");
383
+ for (const fact of facts) lines.push(`- ${fact}`);
384
+ }
385
+ const digest = lines.join("\n");
386
+ if (digest.length <= MAX_DIGEST_CHARS) return digest;
387
+ return `${digest.slice(0, MAX_DIGEST_CHARS)}... [+${digest.length - MAX_DIGEST_CHARS} chars]`;
388
+ }
389
+ function repeatedReadPressure(ctx) {
390
+ return ensureEvidence(ctx).repeatedReads.reduce((max, item) => Math.max(max, item.count), 0);
391
+ }
392
+ function ensureEvidence(ctx) {
393
+ if (!ctx.contextEvidence) {
394
+ ctx.contextEvidence = createContextEvidenceState();
395
+ }
396
+ return ctx.contextEvidence;
397
+ }
254
398
 
255
399
  // src/types/blocks.ts
256
400
  function isTextBlock(b) {
@@ -297,25 +441,26 @@ function findPreserveStart(messages, preserveK) {
297
441
  preserveStart = i;
298
442
  }
299
443
  }
300
- let forwardWalkIterations = 0;
301
- let forwardWalkInnerIterations = 0;
302
- for (let i = preserveStart; i < messages.length; i++) {
303
- forwardWalkIterations++;
304
- const m = messages[i];
305
- if (!m || typeof m.content === "string" || !Array.isArray(m.content)) continue;
306
- const hasToolUse3 = m.content.some((b) => {
307
- forwardWalkInnerIterations++;
308
- return b.type === "tool_use";
444
+ let pairRepairIterations = 0;
445
+ let pairRepairInnerIterations = 0;
446
+ while (preserveStart > 0) {
447
+ pairRepairIterations++;
448
+ const first = messages[preserveStart];
449
+ const prev = messages[preserveStart - 1];
450
+ if (!first || !prev || first.role !== "user" || prev.role !== "assistant") break;
451
+ if (typeof first.content === "string" || typeof prev.content === "string") break;
452
+ const resultIds = /* @__PURE__ */ new Set();
453
+ for (const block of first.content) {
454
+ pairRepairInnerIterations++;
455
+ if (block.type === "tool_result") resultIds.add(block.tool_use_id);
456
+ }
457
+ if (resultIds.size === 0) break;
458
+ const hasMatchingUse = prev.content.some((block) => {
459
+ pairRepairInnerIterations++;
460
+ return block.type === "tool_use" && resultIds.has(block.id);
309
461
  });
310
- if (hasToolUse3 && i + 1 < messages.length) {
311
- const next = messages[i + 1];
312
- if (next && next.role === "user" && typeof next.content !== "string" && Array.isArray(next.content) && next.content.some((b) => {
313
- forwardWalkInnerIterations++;
314
- return b.type === "tool_result";
315
- })) {
316
- preserveStart = i + 1;
317
- }
318
- }
462
+ if (!hasMatchingUse) break;
463
+ preserveStart--;
319
464
  }
320
465
  if (compactionDebugEnabled()) {
321
466
  console.log(
@@ -325,9 +470,9 @@ function findPreserveStart(messages, preserveK) {
325
470
  messageCount: messages.length,
326
471
  preserveK,
327
472
  preserveStart,
328
- forwardWalkIterations,
329
- forwardWalkInnerIterations,
330
- forwardWalkInnerPerOuter: forwardWalkIterations > 0 ? forwardWalkInnerIterations / forwardWalkIterations : 0
473
+ pairRepairIterations,
474
+ pairRepairInnerIterations,
475
+ pairRepairInnerPerOuter: pairRepairIterations > 0 ? pairRepairInnerIterations / pairRepairIterations : 0
331
476
  })
332
477
  );
333
478
  }
@@ -344,7 +489,8 @@ function eliseOldToolResults(messages, opts) {
344
489
  if (!msg || !Array.isArray(msg.content)) continue;
345
490
  for (const b of msg.content) {
346
491
  fastPathInnerIterations++;
347
- if (b.type === "tool_result" && estimateToolResultTokens(b.content) >= opts.eliseThreshold) {
492
+ const oversized = b.type === "tool_result" && estimateToolResultTokens(b.content) >= opts.eliseThreshold || b.type === "tool_use" && estimateToolInputTokens(b.input) >= opts.eliseThreshold;
493
+ if (oversized) {
348
494
  hasOversized = true;
349
495
  break;
350
496
  }
@@ -378,6 +524,13 @@ function eliseOldToolResults(messages, opts) {
378
524
  }
379
525
  const original = msg.content;
380
526
  const newContent = original.map((b) => {
527
+ if (b.type === "tool_use") {
528
+ const tokens2 = estimateToolInputTokens(b.input);
529
+ if (tokens2 < opts.eliseThreshold) return b;
530
+ const elidedInput = summarizeToolUseInputElision(b, tokens2);
531
+ saved += Math.max(0, tokens2 - estimateToolInputTokens(elidedInput));
532
+ return { ...b, input: elidedInput };
533
+ }
381
534
  if (b.type !== "tool_result") return b;
382
535
  const tokens = estimateToolResultTokens(b.content);
383
536
  if (tokens < opts.eliseThreshold) return b;
@@ -385,7 +538,7 @@ function eliseOldToolResults(messages, opts) {
385
538
  const elided = {
386
539
  type: "tool_result",
387
540
  tool_use_id: b.tool_use_id,
388
- content: `[elided: ~${tokens} tokens]`,
541
+ content: summarizeToolResultElision(b, tokens),
389
542
  is_error: b.is_error
390
543
  };
391
544
  return elided;
@@ -425,6 +578,65 @@ function eliseOldToolResults(messages, opts) {
425
578
  });
426
579
  return { messages: changed ? next : messages, saved, changed };
427
580
  }
581
+ function summarizeToolUseInputElision(block, tokens) {
582
+ const fields = {};
583
+ for (const [key, value] of Object.entries(block.input ?? {})) {
584
+ fields[key] = summarizeToolUseInputValue(value);
585
+ }
586
+ return {
587
+ __elided_tool_input: `~${tokens} tokens; original arguments are in the session log`,
588
+ tool: block.name,
589
+ fields
590
+ };
591
+ }
592
+ function summarizeToolUseInputValue(value) {
593
+ if (value === null || value === void 0) return value;
594
+ if (typeof value === "number" || typeof value === "boolean") return value;
595
+ if (typeof value === "string") {
596
+ const oneLine = value.replace(/\s+/g, " ").trim();
597
+ return oneLine.length <= 160 ? oneLine : `${oneLine.slice(0, 120)}...(${oneLine.length} chars)`;
598
+ }
599
+ if (Array.isArray(value)) {
600
+ return `[array:${value.length}]`;
601
+ }
602
+ if (typeof value === "object") {
603
+ const keys = Object.keys(value);
604
+ return `[object:${keys.slice(0, 8).join(",")}${keys.length > 8 ? ",..." : ""}]`;
605
+ }
606
+ return String(value);
607
+ }
608
+ function summarizeToolResultElision(block, tokens) {
609
+ const parts = [`elided: ~${tokens} tokens`];
610
+ if (block.name) parts.push(`tool=${block.name}`);
611
+ const files = extractPathHints(block.content).slice(0, 5);
612
+ if (files.length > 0) parts.push(`files=${files.join(", ")}`);
613
+ const error = firstErrorLine(block.content);
614
+ if (error) parts.push(`error=${error}`);
615
+ return `[${parts.join("; ")}]`;
616
+ }
617
+ function extractPathHints(content) {
618
+ const text = typeof content === "string" ? content : JSON.stringify(content);
619
+ const out = /* @__PURE__ */ new Set();
620
+ const re = /(?:(?:[A-Za-z]:)?[./\\]?[\w@.-]+(?:[\\/][\w@(). -]+)+\.[A-Za-z0-9]{1,12})/g;
621
+ for (const match of text.matchAll(re)) {
622
+ const clean = match[0]?.replace(/\\/g, "/").replace(/^["'`]+|["'`),;:]+$/g, "");
623
+ if (clean && clean.length <= 220) out.add(clean);
624
+ if (out.size >= 5) break;
625
+ }
626
+ return [...out];
627
+ }
628
+ function firstErrorLine(content) {
629
+ const text = typeof content === "string" ? content : JSON.stringify(content);
630
+ for (const line of text.split(/\r?\n/)) {
631
+ if (!/\b(error|exception|failed|failure|fatal|panic|timeout|denied|enoent|eacces|eperm)\b/i.test(
632
+ line
633
+ ))
634
+ continue;
635
+ const trimmed = line.replace(/\s+/g, " ").trim();
636
+ if (trimmed) return trimmed.slice(0, 180);
637
+ }
638
+ return void 0;
639
+ }
428
640
  function buildLosslessDigest(messages) {
429
641
  const lines = [];
430
642
  for (const m of messages) {
@@ -535,15 +747,15 @@ function buildSmartDigest(messages) {
535
747
  lines.push(`[${m.role}]: ${display}${marker}`);
536
748
  }
537
749
  if (noiseCount > 0) {
538
- lines.push(`[system]: ${noiseCount} low-importance turn(s) collapsed (repeated failures / pure tool I/O)`);
750
+ lines.push(
751
+ `[system]: ${noiseCount} low-importance turn(s) collapsed (repeated failures / pure tool I/O)`
752
+ );
539
753
  }
540
754
  return lines.join("\n");
541
755
  }
542
756
  function countToolBlocks(m) {
543
757
  if (typeof m.content === "string") return 0;
544
- return m.content.filter(
545
- (b) => b.type === "tool_use" || b.type === "tool_result"
546
- ).length;
758
+ return m.content.filter((b) => b.type === "tool_use" || b.type === "tool_result").length;
547
759
  }
548
760
  function firstSentence(text) {
549
761
  const trimmed = text.trim();
@@ -612,10 +824,12 @@ var HybridCompactor = class {
612
824
  if (elide.changed) ctx.state.replaceMessages(elide.messages);
613
825
  if (elide.saved > 0) reductions.push({ phase: "elision", saved: elide.saved });
614
826
  let collapsedDigest;
827
+ let evidenceDigest;
615
828
  if (opts.aggressive) {
616
829
  const phase2 = this.collapseAncientTurns(ctx, preserveK);
617
830
  if (phase2.saved > 0) reductions.push({ phase: "summary", saved: phase2.saved });
618
831
  collapsedDigest = phase2.digest;
832
+ evidenceDigest = phase2.evidenceDigest;
619
833
  }
620
834
  const repaired = repairToolUseAdjacency(ctx.messages);
621
835
  if (repaired.report.changed) {
@@ -623,6 +837,11 @@ var HybridCompactor = class {
623
837
  }
624
838
  const afterTokens = estimateMessages(ctx.messages);
625
839
  const afterFull = this.estimateFullRequest(ctx);
840
+ const quality = checkCompactionQuality(ctx, {
841
+ collapsedDigest,
842
+ evidenceDigest,
843
+ reduced: beforeTokens > afterTokens || beforeFull > afterFull
844
+ });
626
845
  return {
627
846
  before: beforeTokens,
628
847
  after: afterTokens,
@@ -630,6 +849,8 @@ var HybridCompactor = class {
630
849
  fullRequestTokensAfter: afterFull,
631
850
  reductions,
632
851
  collapsedDigest,
852
+ evidenceDigest,
853
+ quality,
633
854
  repaired: repaired.report.changed ? {
634
855
  removedToolUses: repaired.report.removedToolUses,
635
856
  removedToolResults: repaired.report.removedToolResults,
@@ -671,7 +892,13 @@ var HybridCompactor = class {
671
892
  if (boundary <= 0) return { saved: 0 };
672
893
  const removed = messages.slice(0, boundary);
673
894
  const removedTokens = estimateMessages(removed);
674
- const digest = this.smart ? buildSmartDigest(removed) || `${removed.length} earlier turns (no textual content; tool I/O omitted \u2014 see session log)` : buildLosslessDigest(removed) || `${removed.length} earlier turns (no textual content; tool I/O omitted \u2014 see session log)`;
895
+ const historyDigest = this.smart ? buildSmartDigest(removed) || `${removed.length} earlier turns (no textual content; tool I/O omitted; see session log)` : buildLosslessDigest(removed) || `${removed.length} earlier turns (no textual content; tool I/O omitted; see session log)`;
896
+ const evidenceDigest = buildContextEvidenceDigest(ctx);
897
+ const digest = evidenceDigest ? `[context_state]
898
+ ${evidenceDigest}
899
+
900
+ [prior_history]
901
+ ${historyDigest}` : historyDigest;
675
902
  const summaryMsg = {
676
903
  role: "system",
677
904
  content: `[prior_turns_digest: ${digest}]`
@@ -680,10 +907,29 @@ var HybridCompactor = class {
680
907
  ctx.state.replaceMessages([summaryMsg, ...tail]);
681
908
  return {
682
909
  saved: Math.max(0, removedTokens - estimateMessages([summaryMsg])),
683
- digest
910
+ digest,
911
+ evidenceDigest: evidenceDigest || void 0
684
912
  };
685
913
  }
686
914
  };
915
+ function checkCompactionQuality(ctx, opts) {
916
+ const evidence = ctx.contextEvidence;
917
+ const digest = `${opts.collapsedDigest ?? ""}
918
+ ${opts.evidenceDigest ?? ""}`;
919
+ const hasIntent = Boolean(evidence?.currentIntent?.text || /\b(intent|goal|session_goals)\b/i.test(digest));
920
+ const hasPathTrail = Boolean(
921
+ Object.keys(evidence?.fileGraph ?? {}).length > 0 || (evidence?.toolCalls.length ?? 0) > 0 || /\b(dependency_graph|tool_trail|files=)\b/i.test(digest)
922
+ );
923
+ const issues = [];
924
+ if (opts.reduced && !hasIntent) issues.push("missing intent anchor");
925
+ if (opts.reduced && !hasPathTrail) issues.push("missing tool/path trail");
926
+ return {
927
+ ok: issues.length === 0,
928
+ hasIntent,
929
+ hasPathTrail,
930
+ issues
931
+ };
932
+ }
687
933
  function readContextWindowPolicy(ctx) {
688
934
  const policy = ctx.meta?.["contextWindowPolicy"];
689
935
  if (!policy || typeof policy !== "object") return null;
@@ -1042,10 +1288,10 @@ var SelectiveCompactor = class {
1042
1288
  this.maxContext = opts.maxContext ?? 128e3;
1043
1289
  this.preserveK = opts.preserveK ?? 4;
1044
1290
  this.eliseThreshold = opts.eliseThreshold ?? 500;
1045
- this.summarizerModel = opts.summarizerModel ?? opts.selectorModel ?? "unknown";
1046
- if (this.summarizerModel === "unknown" && (process.env["NODE_ENV"] === "development" || process.env["WRONGSTACK_DEBUG"] === "1")) {
1291
+ this.summarizerModel = opts.summarizerModel ?? opts.selectorModel;
1292
+ if (this.summarizerModel === void 0 && (process.env["NODE_ENV"] === "development" || process.env["WRONGSTACK_DEBUG"] === "1")) {
1047
1293
  console.warn(
1048
- "[SelectiveCompactor] summarizerModel not set \u2014 will use provider default. Set `summarizerModel` explicitly to silence this warning."
1294
+ "[SelectiveCompactor] summarizerModel not set \u2014 will fall back to ctx.model at summarize time. Set `summarizerModel` explicitly to silence this warning."
1049
1295
  );
1050
1296
  }
1051
1297
  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.";
@@ -1153,7 +1399,7 @@ var SelectiveCompactor = class {
1153
1399
  Summarize the following message range:`;
1154
1400
  const body = messages.map((m, i) => `[${i}] ${m.role}: ${this.messagePreview(m)}`).join("\n");
1155
1401
  const req = {
1156
- model: this.summarizerModel,
1402
+ model: this.summarizerModel ?? ctx.model,
1157
1403
  system: [{ type: "text", text: systemText }],
1158
1404
  messages: [{ role: "user", content: body }],
1159
1405
  maxTokens: 512
@@ -1220,27 +1466,16 @@ Summarize the following message range:`;
1220
1466
  if (typeof m.content === "string") return m.content.trim().length > 0;
1221
1467
  return m.content.some((b) => b.type === "text" && b.text.trim().length > 0);
1222
1468
  }
1469
+ /**
1470
+ * Estimate message-array tokens via the shared `estimateMessages` primitive
1471
+ * so SelectiveCompactor's before/after/load figures agree with the
1472
+ * middleware threshold math and the other compactors. Previously this used a
1473
+ * private `ceil(len/3.5)` walk that diverged from the calibrated shared
1474
+ * estimator, causing the selective `load`/`targetBudget` comparison to mix
1475
+ * two incompatible token scales.
1476
+ */
1223
1477
  estimateTokens(messages) {
1224
- let total = 0;
1225
- for (const m of messages) {
1226
- if (typeof m.content === "string") {
1227
- total += this.roughTokenEstimate(m.content);
1228
- } else {
1229
- for (const b of m.content) {
1230
- if (b.type === "text") total += this.roughTokenEstimate(b.text);
1231
- else if (b.type === "tool_use") total += this.roughTokenEstimate(JSON.stringify(b.input));
1232
- else if (b.type === "tool_result") {
1233
- total += this.roughTokenEstimate(
1234
- typeof b.content === "string" ? b.content : JSON.stringify(b.content)
1235
- );
1236
- }
1237
- }
1238
- }
1239
- }
1240
- return total;
1241
- }
1242
- roughTokenEstimate(text) {
1243
- return Math.max(1, Math.ceil(text.length / 3.5));
1478
+ return estimateMessages(messages);
1244
1479
  }
1245
1480
  };
1246
1481
 
@@ -1312,10 +1547,19 @@ function readPolicy(ctx) {
1312
1547
  if (typeof candidate.preserveK !== "number" || !candidate.thresholds) return null;
1313
1548
  return candidate;
1314
1549
  }
1550
+
1551
+ // src/utils/assert-never.ts
1552
+ function assertNever(x, message) {
1553
+ const err = new Error(
1554
+ `Unhandled case: ${JSON.stringify(x)}`
1555
+ );
1556
+ err.name = "AssertNeverError";
1557
+ throw err;
1558
+ }
1315
1559
  async function atomicWrite(targetPath, content, opts = {}) {
1316
- const dir = path2.dirname(targetPath);
1560
+ const dir = path3.dirname(targetPath);
1317
1561
  await fs.mkdir(dir, { recursive: true });
1318
- const tmp = path2.join(dir, `.${path2.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
1562
+ const tmp = path3.join(dir, `.${path3.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
1319
1563
  try {
1320
1564
  if (typeof content === "string") {
1321
1565
  await fs.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
@@ -1368,7 +1612,7 @@ async function renameWithRetry(from, to) {
1368
1612
  if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
1369
1613
  throw err;
1370
1614
  }
1371
- await new Promise((resolve2) => setTimeout(resolve2, delays[i]));
1615
+ await new Promise((resolve3) => setTimeout(resolve3, delays[i]));
1372
1616
  }
1373
1617
  }
1374
1618
  throw lastErr;
@@ -1379,137 +1623,17 @@ function toErrorMessage(err) {
1379
1623
  return err instanceof Error ? err.message : String(err);
1380
1624
  }
1381
1625
 
1382
- // src/utils/string.ts
1383
- function truncate(s, max) {
1384
- return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
1385
- }
1386
- function projectHash(absRoot) {
1387
- return createHash("sha256").update(path2.resolve(absRoot)).digest("hex").slice(0, 12);
1388
- }
1389
- function projectSlug(absRoot) {
1390
- const base = slugify(path2.basename(absRoot));
1391
- const hash = createHash("sha256").update(path2.resolve(absRoot)).digest("hex").slice(0, 6);
1392
- return `${base}-${hash}`;
1393
- }
1394
- function slugify(name) {
1395
- return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
1396
- }
1397
- function wstackGlobalRoot() {
1398
- const fromEnv = process.env["WRONGSTACK_HOME"];
1399
- if (fromEnv && fromEnv.trim().length > 0) return path2.resolve(fromEnv);
1400
- return path2.join(os.homedir(), ".wrongstack");
1401
- }
1402
- function resolveWstackPaths(opts) {
1403
- const globalRoot = opts.globalRoot ?? (opts.userHome ? path2.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
1404
- const hash = projectHash(opts.projectRoot);
1405
- const slug = projectSlug(opts.projectRoot);
1406
- const projectDir = path2.join(globalRoot, "projects", slug);
1407
- return {
1408
- globalRoot,
1409
- configDir: globalRoot,
1410
- globalConfig: path2.join(globalRoot, "config.json"),
1411
- secretsKey: path2.join(globalRoot, ".key"),
1412
- globalMemory: path2.join(globalRoot, "memory.md"),
1413
- globalSkills: path2.join(globalRoot, "skills"),
1414
- globalPrompts: path2.join(globalRoot, "prompts"),
1415
- cacheDir: path2.join(globalRoot, "cache"),
1416
- modelsCache: path2.join(globalRoot, "cache", "models.dev.json"),
1417
- modelsOverlayCache: path2.join(globalRoot, "cache", "models-overlay.json"),
1418
- historyFile: path2.join(globalRoot, "history"),
1419
- logFile: path2.join(globalRoot, "logs", "wrongstack.log"),
1420
- projectDir,
1421
- projectCodebaseIndex: path2.join(projectDir, "codebase-index"),
1422
- projectMemory: path2.join(projectDir, "memory.md"),
1423
- projectSessions: path2.join(projectDir, "sessions"),
1424
- projectTrust: path2.join(projectDir, "trust.json"),
1425
- projectMeta: path2.join(projectDir, "meta.json"),
1426
- projectLocalConfig: path2.join(projectDir, "config.local.json"),
1427
- inProjectConfig: path2.join(opts.projectRoot, ".wrongstack", "config.json"),
1428
- inProjectAgentsFile: path2.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
1429
- inProjectSkills: path2.join(opts.projectRoot, ".wrongstack", "skills"),
1430
- inProjectWorktrees: path2.join(opts.projectRoot, ".wrongstack", "worktrees"),
1431
- projectHash: hash,
1432
- projectSlug: slug,
1433
- projectGoal: path2.join(projectDir, "goal.json"),
1434
- projectSpecs: path2.join(projectDir, "specs"),
1435
- projectTaskGraphs: path2.join(projectDir, "task-graphs"),
1436
- projectSddSession: path2.join(projectDir, "sdd-session.json"),
1437
- projectPlan: path2.join(projectDir, "plan.json"),
1438
- projectAutophase: path2.join(projectDir, "autophase"),
1439
- syncConfig: path2.join(globalRoot, "sync.json"),
1440
- projectStatus: (projectHash2) => path2.join(globalRoot, "projects", projectHash2, "status.json")
1441
- };
1442
- }
1443
-
1444
- // src/utils/sleep.ts
1445
- function sleep(ms) {
1446
- return new Promise((resolve2) => setTimeout(resolve2, ms));
1447
- }
1448
-
1449
- // src/utils/assert-never.ts
1450
- function assertNever(x, message) {
1451
- const err = new Error(
1452
- `Unhandled case: ${JSON.stringify(x)}`
1453
- );
1454
- err.name = "AssertNeverError";
1455
- throw err;
1456
- }
1457
-
1458
- // src/utils/tool-output-serializer.ts
1459
- function createToolOutputSerializer(opts = {}) {
1460
- const capBytes = opts.perIterationOutputCapBytes ?? 1e5;
1461
- function serialize(value) {
1462
- if (typeof value === "string") return value;
1463
- if (value === null || value === void 0) return "";
1464
- if (typeof value === "object") {
1465
- if (Array.isArray(value)) return value.map(serialize).join("\n");
1466
- if ("text" in value) {
1467
- const t = value.text;
1468
- return typeof t === "string" ? t : JSON.stringify(value, null, 2);
1469
- }
1470
- try {
1471
- return JSON.stringify(value, null, 2);
1472
- } catch {
1473
- return String(value);
1474
- }
1475
- }
1476
- return String(value);
1477
- }
1478
- function enforceCap(text, remainingBudget) {
1479
- if (remainingBudget <= 0) {
1480
- return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
1481
- }
1482
- const textBytes = Buffer.byteLength(text, "utf8");
1483
- if (textBytes <= remainingBudget) {
1484
- return { text, newBudget: remainingBudget - textBytes };
1485
- }
1486
- const marker = `
1487
- \u2026[truncated ${textBytes - remainingBudget} bytes]\u2026
1488
- `;
1489
- const markerBytes = Buffer.byteLength(marker, "utf8");
1490
- const available = remainingBudget - markerBytes;
1491
- if (available <= 0) {
1492
- return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
1493
- }
1494
- const half = Math.floor(available / 2);
1495
- const first = text.slice(0, half);
1496
- const second = text.slice(text.length - half);
1497
- return { text: `${first}${marker}${second}`, newBudget: 0 };
1498
- }
1499
- return { serialize, enforceCap, capBytes };
1500
- }
1501
-
1502
1626
  // src/utils/json-schema-validate.ts
1503
1627
  function validateAgainstSchema(value, schema) {
1504
1628
  const errors = [];
1505
1629
  walk(value, schema, "", errors);
1506
1630
  return { ok: errors.length === 0, errors };
1507
1631
  }
1508
- function walk(value, schema, path4, errors) {
1632
+ function walk(value, schema, path6, errors) {
1509
1633
  if (schema.enum !== void 0) {
1510
1634
  if (!schema.enum.some((e) => deepEqual(e, value))) {
1511
1635
  errors.push({
1512
- path: path4 || "<root>",
1636
+ path: path6 || "<root>",
1513
1637
  message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
1514
1638
  });
1515
1639
  return;
@@ -1518,7 +1642,7 @@ function walk(value, schema, path4, errors) {
1518
1642
  if (typeof schema.type === "string") {
1519
1643
  if (!checkType(value, schema.type)) {
1520
1644
  errors.push({
1521
- path: path4 || "<root>",
1645
+ path: path6 || "<root>",
1522
1646
  message: `expected ${schema.type}, got ${describeType(value)}`
1523
1647
  });
1524
1648
  return;
@@ -1528,20 +1652,20 @@ function walk(value, schema, path4, errors) {
1528
1652
  const obj = value;
1529
1653
  for (const req of schema.required ?? []) {
1530
1654
  if (!(req in obj)) {
1531
- errors.push({ path: joinPath(path4, req), message: "required property missing" });
1655
+ errors.push({ path: joinPath(path6, req), message: "required property missing" });
1532
1656
  }
1533
1657
  }
1534
1658
  if (schema.properties) {
1535
1659
  for (const [key, subSchema] of Object.entries(schema.properties)) {
1536
1660
  if (key in obj) {
1537
- walk(obj[key], subSchema, joinPath(path4, key), errors);
1661
+ walk(obj[key], subSchema, joinPath(path6, key), errors);
1538
1662
  }
1539
1663
  }
1540
1664
  }
1541
1665
  }
1542
1666
  if (schema.type === "array" && Array.isArray(value) && schema.items) {
1543
1667
  for (let i = 0; i < value.length; i++) {
1544
- walk(value[i], schema.items, `${path4}[${i}]`, errors);
1668
+ walk(value[i], schema.items, `${path6}[${i}]`, errors);
1545
1669
  }
1546
1670
  }
1547
1671
  }
@@ -1631,6 +1755,749 @@ function compileUserRegex(pattern, flags) {
1631
1755
  }
1632
1756
  }
1633
1757
 
1758
+ // src/utils/sleep.ts
1759
+ function sleep(ms) {
1760
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
1761
+ }
1762
+
1763
+ // src/utils/string.ts
1764
+ function truncate(s, max) {
1765
+ return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
1766
+ }
1767
+
1768
+ // src/utils/tool-subject.ts
1769
+ var GLOB_METACHARACTERS = /[*?[\]]/g;
1770
+ function escapeGlobSubject(value) {
1771
+ return value.replace(GLOB_METACHARACTERS, (char) => `\\${char}`);
1772
+ }
1773
+ function normalizePathSubject(value) {
1774
+ return escapeGlobSubject(value.replace(/\\/g, "/"));
1775
+ }
1776
+ function isPathSubjectKey(subjectKey) {
1777
+ return subjectKey === "path" || subjectKey === "file" || subjectKey === "files";
1778
+ }
1779
+ function subjectForToolInput(toolName, input, subjectKey) {
1780
+ if (!input || typeof input !== "object") return void 0;
1781
+ const obj = input;
1782
+ if (subjectKey) {
1783
+ const value = obj[subjectKey];
1784
+ if (typeof value === "string") {
1785
+ return isPathSubjectKey(subjectKey) ? normalizePathSubject(value) : escapeGlobSubject(value);
1786
+ }
1787
+ }
1788
+ if (toolName === "bash" && typeof obj.command === "string") {
1789
+ return escapeGlobSubject(obj.command);
1790
+ }
1791
+ if (typeof obj.path === "string") {
1792
+ return normalizePathSubject(obj.path);
1793
+ }
1794
+ if (typeof obj.url === "string") {
1795
+ return escapeGlobSubject(obj.url);
1796
+ }
1797
+ if (typeof obj.name === "string") {
1798
+ return escapeGlobSubject(obj.name);
1799
+ }
1800
+ return void 0;
1801
+ }
1802
+
1803
+ // src/utils/tool-output-serializer.ts
1804
+ var DEFAULT_LIST_LIMIT = 500;
1805
+ var LOG_ENTRY_LIMIT = 200;
1806
+ var INLINE_LIMIT = 240;
1807
+ var GREP_FILE_LIMIT = 80;
1808
+ var GREP_MATCHES_PER_FILE = 3;
1809
+ var DIFF_INLINE_LINE_LIMIT = 260;
1810
+ var DIFF_HUNK_LIMIT = 8;
1811
+ var DIFF_HUNK_CONTEXT = 14;
1812
+ function createToolOutputSerializer(opts = {}) {
1813
+ const capBytes = opts.perIterationOutputCapBytes ?? 1e5;
1814
+ function serialize(value, context = {}) {
1815
+ if (typeof value === "string") return value;
1816
+ if (value === null || value === void 0) return "";
1817
+ if (typeof value === "object") {
1818
+ if (Array.isArray(value)) return value.map((item) => serialize(item)).join("\n");
1819
+ if (context.toolName) {
1820
+ const compact = renderToolObject(context.toolName, value, context.input);
1821
+ if (compact !== void 0) return compact;
1822
+ return renderGenericToolObject(context.toolName, value);
1823
+ }
1824
+ if ("text" in value) {
1825
+ const t = value.text;
1826
+ return typeof t === "string" ? t : JSON.stringify(value, null, 2);
1827
+ }
1828
+ try {
1829
+ return JSON.stringify(value, null, 2);
1830
+ } catch {
1831
+ return String(value);
1832
+ }
1833
+ }
1834
+ return String(value);
1835
+ }
1836
+ function enforceCap(text, remainingBudget) {
1837
+ if (remainingBudget <= 0) {
1838
+ return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
1839
+ }
1840
+ const textBytes = Buffer.byteLength(text, "utf8");
1841
+ if (textBytes <= remainingBudget) {
1842
+ return { text, newBudget: remainingBudget - textBytes };
1843
+ }
1844
+ const marker = `
1845
+ \u2026[truncated ${textBytes - remainingBudget} bytes]\u2026
1846
+ `;
1847
+ const markerBytes = Buffer.byteLength(marker, "utf8");
1848
+ const available = remainingBudget - markerBytes;
1849
+ if (available <= 0) {
1850
+ return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
1851
+ }
1852
+ const half = Math.floor(available / 2);
1853
+ const first = text.slice(0, half);
1854
+ const second = text.slice(text.length - half);
1855
+ return { text: `${first}${marker}${second}`, newBudget: 0 };
1856
+ }
1857
+ return { serialize, enforceCap, capBytes };
1858
+ }
1859
+ function renderToolObject(toolName, obj, input) {
1860
+ if (toolName === "read" && typeof obj["text"] === "string") {
1861
+ return joinSections([
1862
+ renderHeader(
1863
+ `read: ${stringFromInput(input, "path") ?? stringField(obj, "path") ?? "<unknown>"}`,
1864
+ {
1865
+ offset: numberFromInput(input, "offset"),
1866
+ limit: numberFromInput(input, "limit"),
1867
+ total_lines: obj["total_lines"],
1868
+ encoding: obj["encoding"],
1869
+ truncated: obj["truncated"],
1870
+ cached: obj["cached"],
1871
+ note: obj["note"]
1872
+ }
1873
+ ),
1874
+ obj["text"]
1875
+ ]);
1876
+ }
1877
+ if (toolName === "grep" && Array.isArray(obj["matches"])) {
1878
+ const matches = stringArrayField(obj, "matches");
1879
+ return joinSections([
1880
+ renderHeader(`grep: ${stringFromInput(input, "pattern") ?? "<pattern>"}`, {
1881
+ path: stringFromInput(input, "path"),
1882
+ glob: stringFromInput(input, "glob"),
1883
+ mode: stringFromInput(input, "output_mode"),
1884
+ count: obj["count"],
1885
+ shown: matches.length,
1886
+ truncated: obj["truncated"],
1887
+ used: obj["used"]
1888
+ }),
1889
+ renderGrepMatches(matches, stringFromInput(input, "output_mode"))
1890
+ ]);
1891
+ }
1892
+ if (toolName === "patch" && Array.isArray(obj["files"])) {
1893
+ const files = stringArrayField(obj, "files");
1894
+ return joinSections([
1895
+ renderHeader("patch", {
1896
+ applied: obj["applied"],
1897
+ rejected: obj["rejected"],
1898
+ files: files.length,
1899
+ dry_run: obj["dry_run"]
1900
+ }),
1901
+ typeof obj["message"] === "string" ? `message:
1902
+ ${obj["message"]}` : void 0,
1903
+ files.length > 0 ? `files:
1904
+ ${renderStringList(files)}` : void 0
1905
+ ]);
1906
+ }
1907
+ if (toolName === "glob" && Array.isArray(obj["files"])) {
1908
+ const files = stringArrayField(obj, "files");
1909
+ return joinSections([
1910
+ renderHeader(
1911
+ `${toolName}: ${stringFromInput(input, "pattern") ?? stringFromInput(input, "files") ?? stringFromInput(input, "path") ?? ""}`.trim(),
1912
+ {
1913
+ path: stringFromInput(input, "path"),
1914
+ files: files.length,
1915
+ truncated: obj["truncated"]
1916
+ }
1917
+ ),
1918
+ renderStringList(files, "(no files)")
1919
+ ]);
1920
+ }
1921
+ if (toolName === "tree" && typeof obj["tree"] === "string") {
1922
+ return joinSections([
1923
+ renderHeader(
1924
+ `tree: ${stringField(obj, "path") ?? stringFromInput(input, "path") ?? "<cwd>"}`,
1925
+ {
1926
+ total_files: obj["total_files"],
1927
+ total_dirs: obj["total_dirs"],
1928
+ truncated: obj["truncated"]
1929
+ }
1930
+ ),
1931
+ obj["tree"]
1932
+ ]);
1933
+ }
1934
+ if (toolName === "fetch" && typeof obj["content"] === "string") {
1935
+ return joinSections([
1936
+ renderHeader(
1937
+ `fetch: ${stringField(obj, "url") ?? stringFromInput(input, "url") ?? "<url>"}`,
1938
+ {
1939
+ status: obj["status"],
1940
+ content_type: obj["content_type"]
1941
+ }
1942
+ ),
1943
+ obj["content"]
1944
+ ]);
1945
+ }
1946
+ if (toolName === "replace" && Array.isArray(obj["results"])) {
1947
+ const results = obj["results"].filter(isRecord2);
1948
+ const sections = [
1949
+ renderHeader("replace", {
1950
+ files_modified: obj["files_modified"],
1951
+ total_replacements: obj["total_replacements"],
1952
+ dry_run: obj["dry_run"]
1953
+ })
1954
+ ];
1955
+ for (const r of results.slice(0, DEFAULT_LIST_LIMIT)) {
1956
+ sections.push(
1957
+ joinSections([
1958
+ renderHeader(`file: ${stringField(r, "path") ?? "<unknown>"}`, {
1959
+ replacements: r["replacements"]
1960
+ }),
1961
+ typeof r["diff"] === "string" ? r["diff"] : void 0
1962
+ ])
1963
+ );
1964
+ }
1965
+ if (results.length > DEFAULT_LIST_LIMIT) {
1966
+ sections.push(`[serializer omitted ${results.length - DEFAULT_LIST_LIMIT} result item(s)]`);
1967
+ }
1968
+ return joinSections(sections);
1969
+ }
1970
+ if (typeof obj["diff"] === "string") {
1971
+ const diff = obj["diff"];
1972
+ return joinSections([
1973
+ renderHeader(toolName, {
1974
+ path: obj["path"],
1975
+ replacements: obj["replacements"],
1976
+ bytes_written: obj["bytes_written"],
1977
+ created: obj["created"],
1978
+ note: obj["note"],
1979
+ files: Array.isArray(obj["files"]) ? obj["files"].length : void 0,
1980
+ truncated: obj["truncated"],
1981
+ mode: obj["mode"]
1982
+ }),
1983
+ compactDiff(diff)
1984
+ ]);
1985
+ }
1986
+ if (toolName === "test" && typeof obj["output"] === "string") {
1987
+ return renderTestOutput(obj, input);
1988
+ }
1989
+ if ((toolName === "typecheck" || toolName === "lint" || toolName === "format") && typeof obj["output"] === "string") {
1990
+ return renderVerifierOutput(toolName, obj, input);
1991
+ }
1992
+ if (hasCommandOutputShape(obj)) {
1993
+ return renderCommandOutput(toolName, obj, input);
1994
+ }
1995
+ if (toolName === "json" && typeof obj["formatted"] === "string") {
1996
+ return joinSections([
1997
+ renderHeader("json", {
1998
+ type: obj["type"],
1999
+ keys: Array.isArray(obj["keys"]) ? obj["keys"].length : void 0,
2000
+ query: stringFromInput(input, "query"),
2001
+ error: obj["error"]
2002
+ }),
2003
+ obj["formatted"]
2004
+ ]);
2005
+ }
2006
+ if (toolName === "logs" && Array.isArray(obj["entries"])) {
2007
+ const entries = obj["entries"].filter(isRecord2);
2008
+ const lines = entries.slice(0, LOG_ENTRY_LIMIT).map((entry) => {
2009
+ const ts = stringField(entry, "timestamp") ?? "";
2010
+ const level = stringField(entry, "level") ?? "info";
2011
+ const message = stringField(entry, "message") ?? "";
2012
+ const source = stringField(entry, "source");
2013
+ return [ts, level, source, message].filter(Boolean).join(" ");
2014
+ });
2015
+ if (entries.length > LOG_ENTRY_LIMIT) {
2016
+ lines.push(`[serializer omitted ${entries.length - LOG_ENTRY_LIMIT} log entry item(s)]`);
2017
+ }
2018
+ return joinSections([
2019
+ renderHeader(`logs: ${stringField(obj, "source") ?? "<source>"}`, {
2020
+ total: obj["total"],
2021
+ shown: Math.min(entries.length, LOG_ENTRY_LIMIT),
2022
+ truncated: obj["truncated"],
2023
+ stream_mode: obj["stream_mode"]
2024
+ }),
2025
+ lines.length > 0 ? lines.join("\n") : "(no log entries)"
2026
+ ]);
2027
+ }
2028
+ if (toolName === "audit" && Array.isArray(obj["vulnerabilities"])) {
2029
+ const vulns = obj["vulnerabilities"].filter(isRecord2);
2030
+ const lines = vulns.slice(0, DEFAULT_LIST_LIMIT).map((v) => {
2031
+ const severity = stringField(v, "severity") ?? "unknown";
2032
+ const pkg = stringField(v, "package") ?? "<package>";
2033
+ const title = stringField(v, "title") ?? "";
2034
+ const url = stringField(v, "url");
2035
+ return [severity, pkg, title, url].filter(Boolean).join(" | ");
2036
+ });
2037
+ if (vulns.length > DEFAULT_LIST_LIMIT) {
2038
+ lines.push(`[serializer omitted ${vulns.length - DEFAULT_LIST_LIMIT} vulnerability item(s)]`);
2039
+ }
2040
+ return joinSections([
2041
+ renderHeader("audit", {
2042
+ exit_code: obj["exit_code"],
2043
+ total: obj["total"],
2044
+ summary: obj["summary"],
2045
+ truncated: obj["truncated"]
2046
+ }),
2047
+ lines.length > 0 ? lines.join("\n") : stringField(obj, "output")
2048
+ ]);
2049
+ }
2050
+ if (toolName === "outdated" && Array.isArray(obj["packages"])) {
2051
+ const packages = obj["packages"].filter(isRecord2);
2052
+ const lines = packages.slice(0, DEFAULT_LIST_LIMIT).map(
2053
+ (p) => [
2054
+ stringField(p, "name") ?? "<package>",
2055
+ `current=${stringField(p, "current") ?? "unknown"}`,
2056
+ `wanted=${stringField(p, "wanted") ?? "unknown"}`,
2057
+ `latest=${stringField(p, "latest") ?? "unknown"}`,
2058
+ stringField(p, "type")
2059
+ ].filter(Boolean).join(" | ")
2060
+ );
2061
+ if (packages.length > DEFAULT_LIST_LIMIT) {
2062
+ lines.push(`[serializer omitted ${packages.length - DEFAULT_LIST_LIMIT} package item(s)]`);
2063
+ }
2064
+ return joinSections([
2065
+ renderHeader("outdated", {
2066
+ exit_code: obj["exit_code"],
2067
+ total: obj["total"],
2068
+ truncated: obj["truncated"]
2069
+ }),
2070
+ lines.length > 0 ? lines.join("\n") : stringField(obj, "output")
2071
+ ]);
2072
+ }
2073
+ return void 0;
2074
+ }
2075
+ function renderTestOutput(obj, input) {
2076
+ const exitCode = numberField(obj, "exit_code") ?? 0;
2077
+ const failed = numberField(obj, "failed") ?? 0;
2078
+ const output = stringField(obj, "output") ?? "";
2079
+ const header = renderHeader(`test: ${stringField(obj, "runner") ?? "runner"}`, {
2080
+ exit_code: obj["exit_code"],
2081
+ tests_run: obj["tests_run"],
2082
+ passed: obj["passed"],
2083
+ failed: obj["failed"],
2084
+ duration_ms: obj["duration_ms"],
2085
+ truncated: obj["truncated"],
2086
+ files: inputListSummary(input, "files"),
2087
+ grep: stringFromInput(input, "grep")
2088
+ });
2089
+ if (exitCode === 0 && failed === 0) {
2090
+ return joinSections([
2091
+ header,
2092
+ joinSections([
2093
+ "report:",
2094
+ `status=passed`,
2095
+ `tests_run=${obj["tests_run"] ?? 0}`,
2096
+ `passed=${obj["passed"] ?? 0}`,
2097
+ `failed=${obj["failed"] ?? 0}`,
2098
+ `duration_ms=${obj["duration_ms"] ?? 0}`,
2099
+ extractSpoolNote(output)
2100
+ ])
2101
+ ]);
2102
+ }
2103
+ return joinSections([
2104
+ header,
2105
+ `error_context:
2106
+ ${compactFailureOutput(output || "(no runner output)")}`
2107
+ ]);
2108
+ }
2109
+ function renderVerifierOutput(toolName, obj, input) {
2110
+ const exitCode = numberField(obj, "exit_code") ?? 0;
2111
+ const errors = numberField(obj, "errors") ?? 0;
2112
+ const warnings = numberField(obj, "warnings") ?? 0;
2113
+ const output = stringField(obj, "output") ?? "";
2114
+ const changed = numberField(obj, "files_changed") ?? 0;
2115
+ const header = renderHeader(toolName, {
2116
+ exit_code: obj["exit_code"],
2117
+ errors: obj["errors"],
2118
+ warnings: obj["warnings"],
2119
+ files_checked: obj["files_checked"],
2120
+ files_changed: obj["files_changed"],
2121
+ fix_applied: obj["fix_applied"],
2122
+ fixer: obj["fixer"],
2123
+ linter: obj["linter"],
2124
+ project: obj["project"],
2125
+ truncated: obj["truncated"],
2126
+ files: inputListSummary(input, "files"),
2127
+ cwd: stringFromInput(input, "cwd")
2128
+ });
2129
+ if (exitCode === 0 && errors === 0 && (toolName !== "format" || changed === 0)) {
2130
+ return joinSections([
2131
+ header,
2132
+ joinSections([
2133
+ "report:",
2134
+ "status=passed",
2135
+ `errors=${errors}`,
2136
+ `warnings=${warnings}`,
2137
+ toolName === "format" ? `files_changed=${changed}` : void 0,
2138
+ extractSpoolNote(output)
2139
+ ])
2140
+ ]);
2141
+ }
2142
+ if (exitCode === 0 && toolName === "format") {
2143
+ return joinSections([
2144
+ header,
2145
+ joinSections([
2146
+ "report:",
2147
+ "status=changed",
2148
+ `files_changed=${changed}`,
2149
+ extractSpoolNote(output)
2150
+ ])
2151
+ ]);
2152
+ }
2153
+ return joinSections([
2154
+ header,
2155
+ `error_context:
2156
+ ${compactFailureOutput(output || "(no verifier output)")}`
2157
+ ]);
2158
+ }
2159
+ function renderGrepMatches(matches, mode) {
2160
+ if (matches.length === 0) return "(no matches)";
2161
+ if (mode === "files_with_matches") return renderStringList(matches, "(no files)");
2162
+ if (mode === "count") return renderStringList(matches, "(no counts)");
2163
+ const groups = /* @__PURE__ */ new Map();
2164
+ const passthrough = [];
2165
+ for (const match of matches) {
2166
+ const parsed = parseGrepContentLine(match);
2167
+ if (!parsed) {
2168
+ passthrough.push(match);
2169
+ continue;
2170
+ }
2171
+ const list = groups.get(parsed.file) ?? [];
2172
+ list.push(`${parsed.line}:${parsed.text}`);
2173
+ groups.set(parsed.file, list);
2174
+ }
2175
+ if (groups.size === 0) return renderStringList(matches, "(no matches)");
2176
+ const sections = [];
2177
+ let fileIndex = 0;
2178
+ for (const [file, lines] of groups) {
2179
+ fileIndex++;
2180
+ if (fileIndex > GREP_FILE_LIMIT) break;
2181
+ const shown = lines.slice(0, GREP_MATCHES_PER_FILE);
2182
+ sections.push(
2183
+ `${file} (${lines.length} match(es), showing ${shown.length})
2184
+ ${shown.join("\n")}`
2185
+ );
2186
+ }
2187
+ if (groups.size > GREP_FILE_LIMIT) {
2188
+ sections.push(`[serializer omitted ${groups.size - GREP_FILE_LIMIT} file group(s)]`);
2189
+ }
2190
+ if (passthrough.length > 0) {
2191
+ sections.push(`ungrouped:
2192
+ ${renderStringList(passthrough, "", 50)}`);
2193
+ }
2194
+ return sections.join("\n");
2195
+ }
2196
+ function parseGrepContentLine(line) {
2197
+ const match = /^(.+?):(\d+):(.*)$/.exec(line);
2198
+ if (!match?.[1] || !match[2]) return void 0;
2199
+ return { file: match[1], line: match[2], text: match[3] ?? "" };
2200
+ }
2201
+ function compactDiff(diff) {
2202
+ const lines = diff.split(/\r?\n/);
2203
+ if (lines.length <= DIFF_INLINE_LINE_LIMIT) return diff;
2204
+ const fileCount = Math.max(
2205
+ new Set(
2206
+ lines.map(
2207
+ (line) => /^diff --git\s+a\/(.+?)\s+b\//.exec(line)?.[1] ?? /^---\s+(.+)/.exec(line)?.[1]
2208
+ ).filter(Boolean)
2209
+ ).size,
2210
+ 0
2211
+ );
2212
+ const hunks = lines.filter((line) => line.startsWith("@@")).length;
2213
+ const added = lines.filter((line) => line.startsWith("+") && !line.startsWith("+++")).length;
2214
+ const removed = lines.filter((line) => line.startsWith("-") && !line.startsWith("---")).length;
2215
+ const selected = /* @__PURE__ */ new Set();
2216
+ let hunkCount = 0;
2217
+ for (let i = 0; i < lines.length; i++) {
2218
+ const line = lines[i] ?? "";
2219
+ if (line.startsWith("diff --git") || line.startsWith("--- ") || line.startsWith("+++ ")) {
2220
+ selected.add(i);
2221
+ continue;
2222
+ }
2223
+ if (!line.startsWith("@@")) continue;
2224
+ if (hunkCount >= DIFF_HUNK_LIMIT) continue;
2225
+ hunkCount++;
2226
+ for (let j = i; j <= Math.min(lines.length - 1, i + DIFF_HUNK_CONTEXT); j++) {
2227
+ selected.add(j);
2228
+ }
2229
+ }
2230
+ if (selected.size === 0) {
2231
+ return joinSections([
2232
+ renderHeader("diff_summary", {
2233
+ files: fileCount,
2234
+ hunks,
2235
+ added,
2236
+ removed,
2237
+ lines: lines.length
2238
+ }),
2239
+ lines.slice(0, DIFF_INLINE_LINE_LIMIT).join("\n"),
2240
+ `[serializer omitted ${Math.max(0, lines.length - DIFF_INLINE_LINE_LIMIT)} diff line(s)]`
2241
+ ]);
2242
+ }
2243
+ const excerpt = [];
2244
+ let previous = -1;
2245
+ for (const index of [...selected].sort((a, b) => a - b)) {
2246
+ if (index > previous + 1) {
2247
+ const omitted = previous === -1 ? index : index - previous - 1;
2248
+ excerpt.push(`[serializer omitted ${omitted} diff line(s)]`);
2249
+ }
2250
+ excerpt.push(lines[index] ?? "");
2251
+ previous = index;
2252
+ }
2253
+ const trailing = lines.length - previous - 1;
2254
+ if (trailing > 0) excerpt.push(`[serializer omitted ${trailing} trailing diff line(s)]`);
2255
+ return joinSections([
2256
+ renderHeader("diff_summary", {
2257
+ files: fileCount,
2258
+ hunks,
2259
+ shown_hunks: Math.min(hunks, DIFF_HUNK_LIMIT),
2260
+ added,
2261
+ removed,
2262
+ lines: lines.length
2263
+ }),
2264
+ excerpt.join("\n")
2265
+ ]);
2266
+ }
2267
+ function compactFailureOutput(output) {
2268
+ const lines = output.split(/\r?\n/);
2269
+ if (lines.length <= 260) return output.trimEnd();
2270
+ const selected = /* @__PURE__ */ new Set();
2271
+ const marker = /\b(fail|failed|failure|error|exception|assertionerror|expected|received|actual|timeout|stack)\b/i;
2272
+ let markerHits = 0;
2273
+ for (let i = 0; i < lines.length; i++) {
2274
+ if (!marker.test(lines[i] ?? "")) continue;
2275
+ markerHits++;
2276
+ for (let j = Math.max(0, i - 4); j <= Math.min(lines.length - 1, i + 10); j++) {
2277
+ selected.add(j);
2278
+ }
2279
+ }
2280
+ if (markerHits === 0) {
2281
+ return lines.slice(-220).join("\n").trimEnd();
2282
+ }
2283
+ const ordered = [...selected].sort((a, b) => a - b);
2284
+ const out = [];
2285
+ let previous = -1;
2286
+ for (const index of ordered) {
2287
+ if (index > previous + 1) {
2288
+ const omitted = previous === -1 ? index : index - previous - 1;
2289
+ out.push(`[serializer omitted ${omitted} line(s)]`);
2290
+ }
2291
+ out.push(lines[index] ?? "");
2292
+ previous = index;
2293
+ }
2294
+ return out.join("\n").trimEnd();
2295
+ }
2296
+ function extractSpoolNote(output) {
2297
+ return output.split(/\r?\n/).find((line) => line.startsWith("[output truncated") && line.includes("full"));
2298
+ }
2299
+ function hasCommandOutputShape(obj) {
2300
+ return typeof obj["stdout"] === "string" || typeof obj["stderr"] === "string" || typeof obj["output"] === "string" || typeof obj["exitCode"] === "number" || typeof obj["exit_code"] === "number";
2301
+ }
2302
+ function renderCommandOutput(toolName, obj, input) {
2303
+ const command = stringField(obj, "command") ?? stringFromInput(input, "command");
2304
+ const args = stringArrayField(obj, "args");
2305
+ const commandLine = command ? [command, ...args].join(" ") : void 0;
2306
+ const output = stringField(obj, "output");
2307
+ const stdout = stringField(obj, "stdout");
2308
+ const stderr = stringField(obj, "stderr");
2309
+ return joinSections([
2310
+ renderHeader(commandLine ? `${toolName}: ${commandLine}` : toolName, {
2311
+ exit_code: obj["exit_code"] ?? obj["exitCode"],
2312
+ timed_out: obj["timed_out"],
2313
+ pid: obj["pid"],
2314
+ allowed: obj["allowed"],
2315
+ truncated: obj["truncated"],
2316
+ runner: obj["runner"],
2317
+ linter: obj["linter"],
2318
+ fixer: obj["fixer"],
2319
+ project: obj["project"],
2320
+ tests_run: obj["tests_run"],
2321
+ passed: obj["passed"],
2322
+ failed: obj["failed"],
2323
+ duration_ms: obj["duration_ms"],
2324
+ errors: obj["errors"],
2325
+ warnings: obj["warnings"],
2326
+ files_checked: obj["files_checked"],
2327
+ files_changed: obj["files_changed"],
2328
+ fix_applied: obj["fix_applied"]
2329
+ }),
2330
+ stringField(obj, "error") ? `error:
2331
+ ${stringField(obj, "error")}` : void 0,
2332
+ output ? `output:
2333
+ ${output}` : void 0,
2334
+ stdout ? `stdout:
2335
+ ${stdout}` : void 0,
2336
+ stderr ? `stderr:
2337
+ ${stderr}` : void 0
2338
+ ]);
2339
+ }
2340
+ function renderGenericToolObject(toolName, obj) {
2341
+ const scalars = {};
2342
+ const blocks = [];
2343
+ for (const [key, value] of Object.entries(obj)) {
2344
+ if (value === void 0) continue;
2345
+ if (isScalar(value)) {
2346
+ const inline = String(value);
2347
+ if (inline.length <= INLINE_LIMIT && !inline.includes("\n")) {
2348
+ scalars[key] = value;
2349
+ } else {
2350
+ blocks.push(`${key}:
2351
+ ${inline}`);
2352
+ }
2353
+ continue;
2354
+ }
2355
+ if (Array.isArray(value)) {
2356
+ if (value.every((item) => typeof item === "string")) {
2357
+ blocks.push(`${key}:
2358
+ ${renderStringList(value)}`);
2359
+ } else {
2360
+ blocks.push(`${key}:
2361
+ ${renderUnknownList(value)}`);
2362
+ }
2363
+ continue;
2364
+ }
2365
+ blocks.push(`${key}: ${clipInline(oneLineJson(value))}`);
2366
+ }
2367
+ return joinSections([renderHeader(toolName, scalars), ...blocks]);
2368
+ }
2369
+ function renderHeader(label, fields) {
2370
+ const parts = Object.entries(fields).filter(([, value]) => value !== void 0 && value !== null && value !== "").map(([key, value]) => `${key}=${clipInline(formatInlineValue(value))}`);
2371
+ return parts.length > 0 ? `${label} (${parts.join(" ")})` : label;
2372
+ }
2373
+ function renderStringList(items, empty = "", limit = DEFAULT_LIST_LIMIT) {
2374
+ if (items.length === 0) return empty;
2375
+ const shown = items.slice(0, limit);
2376
+ const omitted = items.length - shown.length;
2377
+ return [
2378
+ ...shown,
2379
+ ...omitted > 0 ? [`[serializer omitted ${omitted} item(s); narrow the request for more]`] : []
2380
+ ].join("\n");
2381
+ }
2382
+ function renderUnknownList(items, limit = DEFAULT_LIST_LIMIT) {
2383
+ const shown = items.slice(0, limit).map((item) => clipInline(oneLineJson(item), 1e3));
2384
+ const omitted = items.length - shown.length;
2385
+ if (omitted > 0)
2386
+ shown.push(`[serializer omitted ${omitted} item(s); narrow the request for more]`);
2387
+ return shown.join("\n");
2388
+ }
2389
+ function joinSections(sections) {
2390
+ return sections.map((section) => typeof section === "string" ? section.trimEnd() : void 0).filter((section) => !!section).join("\n");
2391
+ }
2392
+ function formatInlineValue(value) {
2393
+ if (Array.isArray(value)) return `[${value.map(formatInlineValue).join(",")}]`;
2394
+ if (isScalar(value)) return String(value);
2395
+ return oneLineJson(value);
2396
+ }
2397
+ function clipInline(value, max = INLINE_LIMIT) {
2398
+ const compact = value.replace(/\s+/g, " ").trim();
2399
+ return compact.length <= max ? compact : `${compact.slice(0, max - 15)}...(${compact.length} chars)`;
2400
+ }
2401
+ function oneLineJson(value) {
2402
+ try {
2403
+ return JSON.stringify(value);
2404
+ } catch {
2405
+ return String(value);
2406
+ }
2407
+ }
2408
+ function stringField(obj, key) {
2409
+ const value = obj[key];
2410
+ return typeof value === "string" ? value : void 0;
2411
+ }
2412
+ function numberField(obj, key) {
2413
+ const value = obj[key];
2414
+ return typeof value === "number" ? value : void 0;
2415
+ }
2416
+ function stringArrayField(obj, key) {
2417
+ const value = obj[key];
2418
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
2419
+ }
2420
+ function stringFromInput(input, key) {
2421
+ if (!isRecord2(input)) return void 0;
2422
+ const value = input[key];
2423
+ return typeof value === "string" ? value : void 0;
2424
+ }
2425
+ function numberFromInput(input, key) {
2426
+ if (!isRecord2(input)) return void 0;
2427
+ const value = input[key];
2428
+ return typeof value === "number" ? value : void 0;
2429
+ }
2430
+ function inputListSummary(input, key) {
2431
+ if (!isRecord2(input)) return void 0;
2432
+ const value = input[key];
2433
+ if (typeof value === "string") return value;
2434
+ if (Array.isArray(value)) return value.filter((item) => typeof item === "string").join(",");
2435
+ return void 0;
2436
+ }
2437
+ function isRecord2(value) {
2438
+ return !!value && typeof value === "object" && !Array.isArray(value);
2439
+ }
2440
+ function isScalar(value) {
2441
+ return value === null || ["string", "number", "boolean"].includes(typeof value);
2442
+ }
2443
+ function projectHash(absRoot) {
2444
+ return createHash("sha256").update(path3.resolve(absRoot)).digest("hex").slice(0, 12);
2445
+ }
2446
+ function projectSlug(absRoot) {
2447
+ const base = slugify(path3.basename(absRoot));
2448
+ const hash = createHash("sha256").update(path3.resolve(absRoot)).digest("hex").slice(0, 6);
2449
+ return `${base}-${hash}`;
2450
+ }
2451
+ function slugify(name) {
2452
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
2453
+ }
2454
+ function wstackGlobalRoot() {
2455
+ const fromEnv = process.env["WRONGSTACK_HOME"];
2456
+ if (fromEnv && fromEnv.trim().length > 0) return path3.resolve(fromEnv);
2457
+ return path3.join(os.homedir(), ".wrongstack");
2458
+ }
2459
+ function resolveWstackPaths(opts) {
2460
+ const globalRoot = opts.globalRoot ?? (opts.userHome ? path3.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
2461
+ const hash = projectHash(opts.projectRoot);
2462
+ const slug = projectSlug(opts.projectRoot);
2463
+ const projectDir = path3.join(globalRoot, "projects", slug);
2464
+ return {
2465
+ globalRoot,
2466
+ configDir: globalRoot,
2467
+ globalConfig: path3.join(globalRoot, "config.json"),
2468
+ secretsKey: path3.join(globalRoot, ".key"),
2469
+ globalMemory: path3.join(globalRoot, "memory.md"),
2470
+ globalSkills: path3.join(globalRoot, "skills"),
2471
+ globalPrompts: path3.join(globalRoot, "prompts"),
2472
+ cacheDir: path3.join(globalRoot, "cache"),
2473
+ modelsCache: path3.join(globalRoot, "cache", "models.dev.json"),
2474
+ modelsOverlayCache: path3.join(globalRoot, "cache", "models-overlay.json"),
2475
+ historyFile: path3.join(globalRoot, "history"),
2476
+ logFile: path3.join(globalRoot, "logs", "wrongstack.log"),
2477
+ projectDir,
2478
+ projectCodebaseIndex: path3.join(projectDir, "codebase-index"),
2479
+ projectMemory: path3.join(projectDir, "memory.md"),
2480
+ projectSessions: path3.join(projectDir, "sessions"),
2481
+ projectTrust: path3.join(projectDir, "trust.json"),
2482
+ projectMeta: path3.join(projectDir, "meta.json"),
2483
+ projectLocalConfig: path3.join(projectDir, "config.local.json"),
2484
+ inProjectConfig: path3.join(opts.projectRoot, ".wrongstack", "config.json"),
2485
+ inProjectAgentsFile: path3.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
2486
+ inProjectSkills: path3.join(opts.projectRoot, ".wrongstack", "skills"),
2487
+ inProjectWorktrees: path3.join(opts.projectRoot, ".wrongstack", "worktrees"),
2488
+ projectHash: hash,
2489
+ projectSlug: slug,
2490
+ projectGoal: path3.join(projectDir, "goal.json"),
2491
+ projectSpecs: path3.join(projectDir, "specs"),
2492
+ projectTaskGraphs: path3.join(projectDir, "task-graphs"),
2493
+ projectSddSession: path3.join(projectDir, "sdd-session.json"),
2494
+ projectPlan: path3.join(projectDir, "plan.json"),
2495
+ projectAutophase: path3.join(projectDir, "autophase"),
2496
+ syncConfig: path3.join(globalRoot, "sync.json"),
2497
+ projectStatus: (projectHash2) => path3.join(globalRoot, "projects", projectHash2, "status.json")
2498
+ };
2499
+ }
2500
+
1634
2501
  // src/types/errors.ts
1635
2502
  var ERROR_CODES = {
1636
2503
  // Provider
@@ -1834,15 +2701,20 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
1834
2701
  this._cachedMsgCount = msgCount;
1835
2702
  this._cachedToolCount = toolCount;
1836
2703
  }
1837
- const load = tokens / this._maxContext;
2704
+ const budget = computeContextWindowBudget(ctx, tokens, this._maxContext);
2705
+ const load = budget.load;
1838
2706
  const policy = this.policyProvider?.(ctx);
1839
2707
  const thresholds = policy?.thresholds ?? {
1840
2708
  warn: this.warnThreshold,
1841
2709
  soft: this.softThreshold,
1842
2710
  hard: this.hardThreshold
1843
2711
  };
2712
+ const repetition = repeatedReadPressure(ctx);
2713
+ const adaptiveThresholds = adaptThresholdsForSignals(thresholds, {
2714
+ repeatedReadCount: repetition
2715
+ });
1844
2716
  const aggressiveOn = policy?.aggressiveOn ?? this.aggressiveOn;
1845
- const level = load >= thresholds.hard ? "hard" : load >= thresholds.soft ? "soft" : load >= thresholds.warn ? "warn" : null;
2717
+ const level = load >= adaptiveThresholds.hard ? "hard" : load >= adaptiveThresholds.soft ? "soft" : load >= adaptiveThresholds.warn ? "warn" : null;
1846
2718
  if (!level) {
1847
2719
  this.lastNoopAttempt = null;
1848
2720
  return next(ctx);
@@ -1851,7 +2723,13 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
1851
2723
  return next(ctx);
1852
2724
  }
1853
2725
  const aggressive = level === "hard" ? true : level === "soft" ? aggressiveOn !== "hard" : aggressiveOn === "warn";
1854
- await this.compact(ctx, aggressive, { level, tokens, load });
2726
+ await this.compact(ctx, aggressive, {
2727
+ level,
2728
+ tokens,
2729
+ load,
2730
+ budget,
2731
+ signals: { repeatedReadCount: repetition }
2732
+ });
1855
2733
  return next(ctx);
1856
2734
  };
1857
2735
  }
@@ -1904,6 +2782,8 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
1904
2782
  tokens: pressure.tokens,
1905
2783
  load: pressure.load,
1906
2784
  maxContext: this._maxContext,
2785
+ budget: pressure.budget,
2786
+ signals: pressure.signals,
1907
2787
  report,
1908
2788
  aggressive
1909
2789
  });
@@ -1915,6 +2795,8 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
1915
2795
  level: pressure.level,
1916
2796
  aggressive,
1917
2797
  reductions: report.reductions?.map((r) => ({ phase: r.phase, saved: r.saved })),
2798
+ budget: pressure.budget,
2799
+ signals: pressure.signals,
1918
2800
  // Record what was collapsed so the audit trail shows the preserved
1919
2801
  // content, not just token counts. Bounded to keep the log line small;
1920
2802
  // the full original turns are already in the session JSONL.
@@ -1930,6 +2812,8 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
1930
2812
  level: pressure.level,
1931
2813
  tokens: pressure.tokens,
1932
2814
  maxContext: this._maxContext,
2815
+ budget: pressure.budget,
2816
+ signals: pressure.signals,
1933
2817
  load: pressure.load,
1934
2818
  fatal
1935
2819
  });
@@ -1949,6 +2833,37 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
1949
2833
  }
1950
2834
  }
1951
2835
  };
2836
+ function computeContextWindowBudget(ctx, inputTokens, maxContext) {
2837
+ const reservedOutputTokens = readPositiveMetaNumber(ctx, "contextOutputReserveTokens") ?? Math.floor(Math.min(8192, maxContext * 0.08));
2838
+ const reservedSafetyTokens = readPositiveMetaNumber(ctx, "contextSafetyBufferTokens") ?? Math.floor(Math.min(4096, maxContext * 0.02));
2839
+ const availableInputTokens = Math.max(
2840
+ 1,
2841
+ maxContext - reservedOutputTokens - reservedSafetyTokens
2842
+ );
2843
+ const remainingInputTokens = availableInputTokens - inputTokens;
2844
+ return {
2845
+ maxContext,
2846
+ inputTokens,
2847
+ availableInputTokens,
2848
+ remainingInputTokens,
2849
+ reservedOutputTokens,
2850
+ reservedSafetyTokens,
2851
+ load: inputTokens / availableInputTokens,
2852
+ overflowTokens: Math.max(0, -remainingInputTokens)
2853
+ };
2854
+ }
2855
+ function readPositiveMetaNumber(ctx, key) {
2856
+ const value = ctx.meta?.[key];
2857
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : void 0;
2858
+ }
2859
+ function adaptThresholdsForSignals(thresholds, signals) {
2860
+ if (signals.repeatedReadCount < 3) return thresholds;
2861
+ return {
2862
+ warn: Math.max(0.25, thresholds.warn - 0.08),
2863
+ soft: Math.max(0.35, thresholds.soft - 0.04),
2864
+ hard: thresholds.hard
2865
+ };
2866
+ }
1952
2867
 
1953
2868
  // src/security/capabilities.ts
1954
2869
  var ToolCapabilities = {
@@ -2091,7 +3006,8 @@ ${errorDetails}`,
2091
3006
  let effectivePermission = decision.permission;
2092
3007
  const policy = this.opts.permissionPolicy;
2093
3008
  const yolo = policy.getYolo?.() === true || policy.getYoloDestructive?.() === true;
2094
- if (toolDangerousCaps.length > 0 && effectivePermission === "auto" && !yolo) {
3009
+ const authoritativeAuto = decision.source === "yolo";
3010
+ if (toolDangerousCaps.length > 0 && effectivePermission === "auto" && !yolo && !authoritativeAuto) {
2095
3011
  effectivePermission = "confirm";
2096
3012
  }
2097
3013
  if (effectivePermission === "deny") {
@@ -2113,7 +3029,7 @@ ${errorDetails}`,
2113
3029
  return { result, tool, durationMs: Date.now() - start };
2114
3030
  }
2115
3031
  } else {
2116
- const suggestedPattern = this.subjectFor(tool.name, use.input, tool.subjectKey) ?? tool.name;
3032
+ const suggestedPattern = subjectForToolInput(tool.name, use.input, tool.subjectKey) ?? tool.name;
2117
3033
  const pending = {
2118
3034
  type: "tool_confirm_pending",
2119
3035
  toolUseId: use.id,
@@ -2233,9 +3149,10 @@ ${post.additionalContext}`;
2233
3149
  });
2234
3150
  this.opts.renderer?.writeToolCall(tool.name, use.input);
2235
3151
  const output = await this.runWithTimeout(tool, use.input, ctx.signal, ctx, use.id);
2236
- const text = this.serializer.serialize(output);
3152
+ const text = this.serializer.serialize(output, { toolName: tool.name, input: use.input });
2237
3153
  const scrubbed = this.opts.secretScrubber.scrub(text);
2238
- const { text: capped, newBudget } = this.serializer.enforceCap(scrubbed, budget);
3154
+ const withArtifact = await maybePersistLargeToolOutput(tool.name, scrubbed, budget);
3155
+ const { text: capped, newBudget } = this.serializer.enforceCap(withArtifact, budget);
2239
3156
  this.opts.renderer?.writeToolResult(tool.name, capped, false);
2240
3157
  return {
2241
3158
  block: {
@@ -2260,38 +3177,27 @@ ${post.additionalContext}`;
2260
3177
  tool.timeoutMs ?? this.iterationTimeoutMs,
2261
3178
  this.maxToolTimeoutMs
2262
3179
  );
2263
- const ctrl = new AbortController();
2264
- const timer = setTimeout(() => ctrl.abort(new Error("tool timeout")), timeoutMs);
2265
- const combined = AbortSignal.any([parentSignal, ctrl.signal]);
2266
- let cleanupCalled = false;
2267
- let caught = false;
3180
+ const timeoutSignal = AbortSignal.timeout(timeoutMs);
3181
+ const combined = AbortSignal.any([parentSignal, timeoutSignal]);
3182
+ let output;
2268
3183
  try {
2269
- if (typeof tool.executeStream === "function") {
2270
- return await this.runStreamedTool(tool, input, ctx, combined, toolUseId);
2271
- }
2272
- return await tool.execute(input, ctx, { signal: combined });
3184
+ output = typeof tool.executeStream === "function" ? await this.runStreamedTool(tool, input, ctx, combined, toolUseId) : await tool.execute(input, ctx, { signal: combined });
2273
3185
  } catch (err) {
2274
- caught = true;
2275
- if (combined.aborted && typeof tool.cleanup === "function") {
2276
- cleanupCalled = true;
2277
- try {
2278
- await tool.cleanup(input, ctx);
2279
- } catch {
2280
- }
2281
- }
3186
+ if (combined.aborted) await this.runToolCleanup(tool, input, ctx);
2282
3187
  throw err;
2283
- } finally {
2284
- clearTimeout(timer);
2285
- if (combined.aborted && !caught) {
2286
- if (!cleanupCalled && typeof tool.cleanup === "function") {
2287
- try {
2288
- await tool.cleanup(input, ctx);
2289
- } catch {
2290
- }
2291
- }
2292
- const reason = combined.reason instanceof Error ? combined.reason : new Error(typeof combined.reason === "string" ? combined.reason : "aborted");
2293
- throw reason;
2294
- }
3188
+ }
3189
+ if (combined.aborted) {
3190
+ await this.runToolCleanup(tool, input, ctx);
3191
+ throw combined.reason instanceof Error ? combined.reason : new Error(typeof combined.reason === "string" ? combined.reason : "tool timeout");
3192
+ }
3193
+ return output;
3194
+ }
3195
+ /** Best-effort tool cleanup; never let it mask the original error. */
3196
+ async runToolCleanup(tool, input, ctx) {
3197
+ if (typeof tool.cleanup !== "function") return;
3198
+ try {
3199
+ await tool.cleanup(input, ctx);
3200
+ } catch {
2295
3201
  }
2296
3202
  }
2297
3203
  async runStreamedTool(tool, input, ctx, signal, toolUseId) {
@@ -2402,38 +3308,6 @@ ${excerpt}`;
2402
3308
  budgetForString(content, budget) {
2403
3309
  return Math.max(0, budget - Buffer.byteLength(content, "utf8"));
2404
3310
  }
2405
- /**
2406
- * Compute the suggestedPattern string for a tool+input pair.
2407
- * Matches the logic in DefaultPermissionPolicy so the TUI shows the
2408
- * same subject that the trust file would use.
2409
- */
2410
- subjectFor(toolName, input, subjectKey) {
2411
- if (!input || typeof input !== "object") return void 0;
2412
- const obj = input;
2413
- const globChars = /[*?[\]]/g;
2414
- const escapeGlob = (s) => s.replace(globChars, (c) => `\\${c}`);
2415
- const normalizePath = (s) => escapeGlob(s.replace(/\\/g, "/"));
2416
- if (subjectKey) {
2417
- const v = obj[subjectKey];
2418
- if (typeof v === "string") {
2419
- const isPathKey = subjectKey === "path" || subjectKey === "file" || subjectKey === "files";
2420
- return isPathKey ? normalizePath(v) : escapeGlob(v);
2421
- }
2422
- }
2423
- if (toolName === "bash" && typeof obj.command === "string") {
2424
- return escapeGlob(obj.command);
2425
- }
2426
- if (typeof obj.path === "string") {
2427
- return normalizePath(obj.path);
2428
- }
2429
- if (typeof obj.url === "string") {
2430
- return escapeGlob(obj.url);
2431
- }
2432
- if (typeof obj.name === "string") {
2433
- return escapeGlob(obj.name);
2434
- }
2435
- return void 0;
2436
- }
2437
3311
  };
2438
3312
  function clampTimeoutMs(timeoutMs, maxTimeoutMs) {
2439
3313
  const fallback = 3e5;
@@ -2460,6 +3334,25 @@ function extractMalformedRaw(input) {
2460
3334
  return String(value);
2461
3335
  }
2462
3336
  }
3337
+ var TOOL_OUTPUT_ARTIFACT_THRESHOLD_BYTES = 64 * 1024;
3338
+ async function maybePersistLargeToolOutput(toolName, content, budget) {
3339
+ const bytes = Buffer.byteLength(content, "utf8");
3340
+ if (bytes <= Math.min(TOOL_OUTPUT_ARTIFACT_THRESHOLD_BYTES, Math.max(0, budget))) {
3341
+ return content;
3342
+ }
3343
+ try {
3344
+ const dir = path3.join(wstackGlobalRoot(), "tool-output");
3345
+ await fs.mkdir(dir, { recursive: true });
3346
+ const safeTool = toolName.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40) || "tool";
3347
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3348
+ const filePath = path3.join(dir, `${stamp}-${safeTool}-${randomUUID()}.log`);
3349
+ await fs.writeFile(filePath, content, "utf8");
3350
+ return content + `
3351
+ [full tool output: ${bytes} bytes at ${filePath}; read/grep that file selectively instead of re-running or requesting more output]`;
3352
+ } catch {
3353
+ return content;
3354
+ }
3355
+ }
2463
3356
 
2464
3357
  // src/execution/autonomous-runner.ts
2465
3358
  var DoneConditionChecker = class {
@@ -3954,13 +4847,13 @@ var SubagentBudget = class _SubagentBudget {
3954
4847
  if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
3955
4848
  return Promise.resolve("stop");
3956
4849
  }
3957
- return new Promise((resolve2) => {
4850
+ return new Promise((resolve3) => {
3958
4851
  let resolved = false;
3959
4852
  const respond = (d) => {
3960
4853
  if (resolved) return;
3961
4854
  resolved = true;
3962
4855
  clearTimeout(fallback);
3963
- resolve2(d);
4856
+ resolve3(d);
3964
4857
  };
3965
4858
  const fallback = setTimeout(() => respond("stop"), _SubagentBudget.DECISION_TIMEOUT_MS);
3966
4859
  bus.emit("budget.threshold_reached", {
@@ -6423,7 +7316,27 @@ Working rules:
6423
7316
  id: "devops",
6424
7317
  name: "DevOps",
6425
7318
  role: "devops",
6426
- tools: [...TOOLS.build],
7319
+ tools: [
7320
+ ...TOOLS.build,
7321
+ "mcp__ssh__ssh_list_servers",
7322
+ "mcp__ssh__ssh_connection_status",
7323
+ "mcp__ssh__ssh_execute",
7324
+ "mcp__ssh__ssh_execute_sudo",
7325
+ "mcp__ssh__ssh_upload",
7326
+ "mcp__ssh__ssh_download",
7327
+ "mcp__ssh__ssh_sync",
7328
+ "mcp__ssh__ssh_deploy",
7329
+ "mcp__ssh__ssh_health_check",
7330
+ "mcp__ssh__ssh_service_status",
7331
+ "mcp__ssh__ssh_process_manager",
7332
+ "mcp__ssh__ssh_tunnel",
7333
+ "mcp__ssh__ssh_backup_create",
7334
+ "mcp__ssh__ssh_backup_list",
7335
+ "mcp__ssh__ssh_backup_restore",
7336
+ "mcp__ssh__ssh_db_list",
7337
+ "mcp__ssh__ssh_db_query",
7338
+ "mcp__ssh__ssh_profile"
7339
+ ],
6427
7340
  prompt: `You are the DevOps agent. Your job is CI/CD, containerization, and
6428
7341
  deployment configuration: make builds reproducible and deploys safe.
6429
7342
 
@@ -6432,6 +7345,7 @@ Scope:
6432
7345
  - Write Dockerfiles/compose and optimize image size and layer caching
6433
7346
  - Configure deployment (env, secrets handling, health checks, rollback)
6434
7347
  - Diagnose flaky/broken pipelines
7348
+ - Use optional SSH MCP tools for remote hosts when the 'ssh' MCP server is enabled: list servers, run health checks, inspect services, transfer/deploy files, and open tunnels
6435
7349
 
6436
7350
  Input format you accept:
6437
7351
  { "task": "ci | container | deploy | fix-pipeline", "platform": "github-actions | gitlab | docker | k8s", "target": "<what>" }
@@ -6446,7 +7360,8 @@ Working rules:
6446
7360
  - Never hardcode secrets in config; reference the secret store
6447
7361
  - Pin versions for reproducible builds; avoid floating :latest
6448
7362
  - Every deploy path needs a rollback and a health check
6449
- - Treat CI/CD changes as high-risk \u2014 explain blast radius before applying`
7363
+ - Treat CI/CD changes as high-risk \u2014 explain blast radius before applying
7364
+ - For remote SSH work, start with ssh_list_servers / ssh_connection_status, prefer read-only checks first, and do not run destructive commands without explicit user approval`
6450
7365
  },
6451
7366
  budget: MEDIUM_BUDGET,
6452
7367
  capability: {
@@ -6463,6 +7378,13 @@ Working rules:
6463
7378
  "kubernetes",
6464
7379
  "k8s",
6465
7380
  "deploy",
7381
+ "ssh",
7382
+ "remote ssh",
7383
+ "remote server",
7384
+ "sftp",
7385
+ "tunnel",
7386
+ "bastion",
7387
+ "jump host",
6466
7388
  "github actions",
6467
7389
  "container"
6468
7390
  ]
@@ -7582,7 +8504,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
7582
8504
  taskIds.map((id) => {
7583
8505
  const cached = this.completedResults.find((r) => r.taskId === id);
7584
8506
  if (cached) return cached;
7585
- return new Promise((resolve2, reject) => {
8507
+ return new Promise((resolve3, reject) => {
7586
8508
  const timeout = setTimeout(() => {
7587
8509
  this.off("task.completed", handler);
7588
8510
  reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
@@ -7591,7 +8513,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
7591
8513
  if (result.taskId === id) {
7592
8514
  clearTimeout(timeout);
7593
8515
  this.off("task.completed", handler);
7594
- resolve2(result);
8516
+ resolve3(result);
7595
8517
  }
7596
8518
  };
7597
8519
  this.on("task.completed", handler);
@@ -7859,12 +8781,12 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
7859
8781
  }
7860
8782
  return new Promise((resolveDecision) => {
7861
8783
  let settled = false;
7862
- const resolve2 = (d) => {
8784
+ const resolve3 = (d) => {
7863
8785
  if (settled) return;
7864
8786
  settled = true;
7865
8787
  resolveDecision(d);
7866
8788
  };
7867
- const fallback = setTimeout(() => resolve2("stop"), DECISION_TIMEOUT_MS);
8789
+ const fallback = setTimeout(() => resolve3("stop"), DECISION_TIMEOUT_MS);
7868
8790
  budget._events?.emit("budget.threshold_reached", {
7869
8791
  kind: "timeout",
7870
8792
  used,
@@ -7880,11 +8802,11 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
7880
8802
  // disagreeing, resolves as a stop). Async grants still resolve.
7881
8803
  extend: (extra) => {
7882
8804
  clearTimeout(fallback);
7883
- queueMicrotask(() => resolve2({ extend: extra }));
8805
+ queueMicrotask(() => resolve3({ extend: extra }));
7884
8806
  },
7885
8807
  deny: () => {
7886
8808
  clearTimeout(fallback);
7887
- resolve2("stop");
8809
+ resolve3("stop");
7888
8810
  }
7889
8811
  });
7890
8812
  });
@@ -8045,6 +8967,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
8045
8967
  subagentId: result.subagentId,
8046
8968
  taskId: result.taskId,
8047
8969
  status: result.status,
8970
+ result: result.result,
8048
8971
  iterations: result.iterations,
8049
8972
  toolCalls: result.toolCalls,
8050
8973
  durationMs: result.durationMs
@@ -8839,7 +9762,7 @@ var DefaultSkillLoader = class {
8839
9762
  const entries = await fs.readdir(dir, { withFileTypes: true });
8840
9763
  for (const e of entries) {
8841
9764
  if (!e.isDirectory()) continue;
8842
- const skillFile = path2.join(dir, e.name, "SKILL.md");
9765
+ const skillFile = path3.join(dir, e.name, "SKILL.md");
8843
9766
  try {
8844
9767
  const raw = await fs.readFile(skillFile, "utf8");
8845
9768
  const meta = parseFrontmatter(raw);
@@ -8910,7 +9833,7 @@ var DefaultSkillLoader = class {
8910
9833
  if (cached !== void 0) return cached;
8911
9834
  const m = await this.find(name);
8912
9835
  if (!m) throw new Error(`Skill "${name}" not found`);
8913
- const savePath = path2.join(path2.dirname(m.path), "SKILL.save.md");
9836
+ const savePath = path3.join(path3.dirname(m.path), "SKILL.save.md");
8914
9837
  let result;
8915
9838
  try {
8916
9839
  result = await fs.readFile(savePath, "utf8");
@@ -8978,6 +9901,88 @@ function parseDescriptionFromText(desc) {
8978
9901
  return { trigger, scope };
8979
9902
  }
8980
9903
 
8981
- export { AutoCompactionMiddleware, AutonomousRunner, DefaultErrorHandler, DefaultRetryPolicy, DefaultSkillLoader, DoneConditionChecker, EternalAutonomyEngine, HybridCompactor, IntelligentCompactor, ParallelEternalEngine, SelectiveCompactor, ToolExecutor, buildGoalPreamble, createAutonomyBrain, createStrategyCompactor, createTieredBrainArbiter, formatDecisionSummary, makeAutonomyPromptContributor };
9904
+ // src/execution/model-runtime.ts
9905
+ function resolveModelRuntime(settings, reasoning) {
9906
+ const warnings = [];
9907
+ if (!settings) {
9908
+ return { reasoning: void 0, cache: void 0, warnings };
9909
+ }
9910
+ const reasoningField = resolveReasoningForRequest(settings, reasoning, warnings);
9911
+ const cacheField = resolveCacheForRequest(settings);
9912
+ return { reasoning: reasoningField, cache: cacheField, warnings };
9913
+ }
9914
+ function resolveReasoningForRequest(settings, rc, warnings) {
9915
+ const cfg = settings.reasoning;
9916
+ if (!cfg) return void 0;
9917
+ const capKnown = rc !== void 0;
9918
+ const supportsReasoning = rc ? rc.default !== "disabled" || rc.disableSupported || rc.effortSupported : false;
9919
+ const out = {};
9920
+ if (cfg.mode === "off") {
9921
+ if (capKnown && rc?.disableSupported) {
9922
+ out.enabled = false;
9923
+ } else if (capKnown && rc && rc.default === "always_on") {
9924
+ warnings.push(
9925
+ 'reasoning "off" requested, but this model has thinking always on; the disable field was omitted to avoid a provider error.'
9926
+ );
9927
+ } else if (capKnown && rc && !rc.disableSupported) {
9928
+ warnings.push('reasoning "off" requested, but this model does not support disabling thinking; the setting was omitted.');
9929
+ } else {
9930
+ warnings.push('reasoning "off" requested, but model capabilities are unknown; the setting was omitted.');
9931
+ }
9932
+ } else if (cfg.mode === "on") {
9933
+ if (!capKnown) {
9934
+ warnings.push('reasoning "on" requested, but model capabilities are unknown; the setting was omitted.');
9935
+ } else if (!supportsReasoning && rc?.default === "disabled") {
9936
+ warnings.push('reasoning "on" requested, but this model has reasoning disabled by default and does not advertise support; the setting was omitted.');
9937
+ } else {
9938
+ out.enabled = true;
9939
+ }
9940
+ }
9941
+ const effort = cfg.effort;
9942
+ if (effort !== void 0) {
9943
+ if (capKnown && rc?.effortSupported && rc.effortLevels.includes(effort)) {
9944
+ out.effort = effort;
9945
+ } else if (capKnown && rc?.effortSupported) {
9946
+ warnings.push(
9947
+ `reasoning effort "${effort}" not supported by this model (supported: ${rc.effortLevels.join(", ")}); the setting was omitted.`
9948
+ );
9949
+ } else if (capKnown) {
9950
+ warnings.push(`reasoning effort "${effort}" requested, but this model does not support effort; the setting was omitted.`);
9951
+ } else {
9952
+ warnings.push(`reasoning effort "${effort}" requested, but model capabilities are unknown; the setting was omitted.`);
9953
+ }
9954
+ }
9955
+ if (cfg.preserve !== void 0) {
9956
+ if (capKnown && rc && rc.preserveThinking !== "unsupported") {
9957
+ out.preserve = cfg.preserve;
9958
+ } else if (capKnown) {
9959
+ warnings.push("reasoning preserve requested, but this model does not support preserved thinking; the setting was omitted.");
9960
+ }
9961
+ }
9962
+ return Object.keys(out).length > 0 ? out : void 0;
9963
+ }
9964
+ function resolveCacheForRequest(settings, _warnings) {
9965
+ const ttl = settings.cache?.ttl;
9966
+ if (ttl === void 0) return void 0;
9967
+ const out = { ttl };
9968
+ return out;
9969
+ }
9970
+ function applyModelRuntime(req, opts) {
9971
+ const settings = opts.getSettings();
9972
+ if (!settings) return req;
9973
+ const rc = opts.getReasoningConfig();
9974
+ const resolved = resolveModelRuntime(settings, rc);
9975
+ for (const w of resolved.warnings) opts.onWarning?.(w);
9976
+ const next = { ...req };
9977
+ if (resolved.reasoning !== void 0) {
9978
+ next.reasoning = resolved.reasoning;
9979
+ }
9980
+ if (resolved.cache !== void 0) {
9981
+ next.cache = resolved.cache;
9982
+ }
9983
+ return next;
9984
+ }
9985
+
9986
+ export { AutoCompactionMiddleware, AutonomousRunner, DefaultErrorHandler, DefaultRetryPolicy, DefaultSkillLoader, DoneConditionChecker, EternalAutonomyEngine, HybridCompactor, IntelligentCompactor, ParallelEternalEngine, SelectiveCompactor, ToolExecutor, applyModelRuntime, buildGoalPreamble, createAutonomyBrain, createStrategyCompactor, createTieredBrainArbiter, formatDecisionSummary, makeAutonomyPromptContributor, resolveCacheForRequest, resolveModelRuntime, resolveReasoningForRequest };
8982
9987
  //# sourceMappingURL=index.js.map
8983
9988
  //# sourceMappingURL=index.js.map