@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.
- package/dist/{agent-bridge-DrkBxszZ.d.ts → agent-bridge-UhojbpWx.d.ts} +1 -1
- package/dist/{agent-subagent-runner-DM2pP-B6.d.ts → agent-subagent-runner-Bvtf1o9K.d.ts} +25 -7
- package/dist/{brain-BXd_61kQ.d.ts → brain-69wzMKp1.d.ts} +73 -1
- package/dist/{compactor-B8pOf45Y.d.ts → compactor-CBQAJoDc.d.ts} +19 -1
- package/dist/{config-BMCj_XDs.d.ts → config-VKfOZ-6X.d.ts} +122 -3
- package/dist/{context-MRk5PhNv.d.ts → context-C0U8B9NF.d.ts} +88 -1
- package/dist/coordination/index.d.ts +57 -161
- package/dist/coordination/index.js +471 -177
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +26 -25
- package/dist/defaults/index.js +1818 -844
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +72 -16
- package/dist/execution/index.js +1270 -265
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +1 -1
- package/dist/extension/index.d.ts +7 -6
- package/dist/global-mailbox-KByEFFBa.d.ts +663 -0
- package/dist/{goal-preamble-DvHDSKSe.d.ts → goal-preamble-CrYjmdw4.d.ts} +28 -11
- package/dist/{goal-store-DtLMySNb.d.ts → goal-store-Y_zdLZ3q.d.ts} +1 -1
- package/dist/hq/index.d.ts +195 -0
- package/dist/hq/index.js +1884 -0
- package/dist/hq/index.js.map +1 -0
- package/dist/index-BfaS-f_m.d.ts +82 -0
- package/dist/{index-B-ch8K9C.d.ts → index-CtQnmkaS.d.ts} +8 -8
- package/dist/{index-CEDeNodM.d.ts → index-gCv830d7.d.ts} +5 -5
- package/dist/index.d.ts +124 -47
- package/dist/index.js +5600 -2662
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +117 -19
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +10 -9
- package/dist/kernel/index.js.map +1 -1
- package/dist/{pipeline-DPDxH_7m.d.ts → mailbox-types-Ct2hJq0P.d.ts} +1 -244
- package/dist/{mcp-servers-2x4w6Jn9.d.ts → mcp-servers-HT3Fi7Bl.d.ts} +10 -4
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +33 -3
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-DmJlKuNp.d.ts → models-registry-Bvcl3Vaa.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-DyCkCZnU.d.ts → multi-agent-coordinator-BACjsmkC.d.ts} +1 -1
- package/dist/{null-fleet-bus-CG9QY2aP.d.ts → null-fleet-bus-DA7fvhUg.d.ts} +14 -9
- package/dist/observability/index.d.ts +2 -2
- package/dist/{parallel-eternal-engine-Jw9uhEoT.d.ts → parallel-eternal-engine-Ci71gYu_.d.ts} +11 -15
- package/dist/{path-resolver-Dy2ej-gE.d.ts → path-resolver-O1IJnmKE.d.ts} +4 -3
- package/dist/{permission-B9SB45lp.d.ts → permission-Bd-57Lbl.d.ts} +1 -1
- package/dist/{permission-policy-CkjSXabK.d.ts → permission-policy-uNXC6Kge.d.ts} +2 -3
- package/dist/pipeline-BDNvENyV.d.ts +245 -0
- package/dist/{plan-templates-CzD9GnAU.d.ts → plan-templates-EMsalEtN.d.ts} +5 -5
- package/dist/{llm-selector-C0tfTCUe.d.ts → provider-model-resolve-CEb9x886.d.ts} +40 -3
- package/dist/{provider-runner-DMa70ODu.d.ts → provider-runner-DWJbpo70.d.ts} +3 -3
- package/dist/{retry-policy-CN0khdlj.d.ts → retry-policy-C3s_lvdK.d.ts} +1 -1
- package/dist/sdd/index.d.ts +9 -8
- package/dist/sdd/index.js +44 -14
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-B2yw84VT.d.ts → secret-vault-Cgduf5xL.d.ts} +2 -2
- package/dist/security/index.d.ts +5 -67
- package/dist/security/index.js +129 -99
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-CzHh_igB.d.ts → selector-47LBnBVk.d.ts} +1 -1
- package/dist/{session-event-bridge-BUI6Jf-4.d.ts → session-event-bridge-Cw7oqmW2.d.ts} +1 -1
- package/dist/{session-reader-CMgdMSRP.d.ts → session-reader-DD4v2Obw.d.ts} +1 -1
- package/dist/storage/index.d.ts +14 -12
- package/dist/storage/index.js +144 -120
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/index.d.ts +4 -2
- package/dist/tools/index.js +166 -31
- package/dist/tools/index.js.map +1 -1
- package/dist/types/index.d.ts +20 -19
- package/dist/types/index.js +1358 -476
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +472 -405
- package/dist/utils/index.js +2321 -1193
- package/dist/utils/index.js.map +1 -1
- package/package.json +5 -1
package/dist/execution/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
301
|
-
let
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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 (
|
|
311
|
-
|
|
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
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
|
|
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:
|
|
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(
|
|
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
|
|
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
|
|
1046
|
-
if (this.summarizerModel ===
|
|
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
|
|
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
|
-
|
|
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 =
|
|
1560
|
+
const dir = path3.dirname(targetPath);
|
|
1317
1561
|
await fs.mkdir(dir, { recursive: true });
|
|
1318
|
-
const 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((
|
|
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,
|
|
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:
|
|
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:
|
|
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(
|
|
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(
|
|
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, `${
|
|
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
|
|
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 >=
|
|
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, {
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
2264
|
-
const
|
|
2265
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
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((
|
|
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
|
-
|
|
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: [
|
|
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((
|
|
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
|
-
|
|
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
|
|
8784
|
+
const resolve3 = (d) => {
|
|
7863
8785
|
if (settled) return;
|
|
7864
8786
|
settled = true;
|
|
7865
8787
|
resolveDecision(d);
|
|
7866
8788
|
};
|
|
7867
|
-
const fallback = setTimeout(() =>
|
|
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(() =>
|
|
8805
|
+
queueMicrotask(() => resolve3({ extend: extra }));
|
|
7884
8806
|
},
|
|
7885
8807
|
deny: () => {
|
|
7886
8808
|
clearTimeout(fallback);
|
|
7887
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|