@wrongstack/core 0.265.1 → 0.267.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-STJ3JwwK.d.ts} +1 -1
- package/dist/{agent-subagent-runner-DM2pP-B6.d.ts → agent-subagent-runner-CzPGP3jA.d.ts} +25 -7
- package/dist/{brain-BXd_61kQ.d.ts → brain-Cdg77tVN.d.ts} +73 -1
- package/dist/{compactor-B8pOf45Y.d.ts → compactor-iMZ84CXq.d.ts} +19 -1
- package/dist/{config-BMCj_XDs.d.ts → config-Du3pYYln.d.ts} +54 -3
- package/dist/{context-MRk5PhNv.d.ts → context-dT5Ueund.d.ts} +65 -1
- package/dist/coordination/index.d.ts +17 -17
- package/dist/coordination/index.js +138 -114
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +25 -25
- package/dist/defaults/index.js +1729 -781
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +15 -15
- package/dist/execution/index.js +1119 -229
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/{goal-preamble-DvHDSKSe.d.ts → goal-preamble-SulMTowG.d.ts} +28 -11
- package/dist/{goal-store-DtLMySNb.d.ts → goal-store-CABDwdFE.d.ts} +1 -1
- package/dist/{index-CEDeNodM.d.ts → index-Bms0m4oy.d.ts} +5 -5
- package/dist/{index-B-ch8K9C.d.ts → index-DtCVWel4.d.ts} +8 -8
- package/dist/index-IEuxQd-E.d.ts +82 -0
- package/dist/index.d.ts +118 -45
- package/dist/index.js +3083 -1602
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +72 -1
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +9 -9
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mcp-servers-2x4w6Jn9.d.ts → mcp-servers-C2cBTxUR.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +30 -1
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-DmJlKuNp.d.ts → models-registry-BqGZNJQ-.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-DyCkCZnU.d.ts → multi-agent-coordinator-B8R43uPz.d.ts} +1 -1
- package/dist/{null-fleet-bus-CG9QY2aP.d.ts → null-fleet-bus-CnXa5oTH.d.ts} +14 -9
- package/dist/observability/index.d.ts +2 -2
- package/dist/{parallel-eternal-engine-Jw9uhEoT.d.ts → parallel-eternal-engine-DdNnw9BQ.d.ts} +11 -9
- package/dist/{path-resolver-Dy2ej-gE.d.ts → path-resolver-COIMLCQL.d.ts} +3 -3
- package/dist/{permission-B9SB45lp.d.ts → permission-B75JAi3-.d.ts} +1 -1
- package/dist/{permission-policy-CkjSXabK.d.ts → permission-policy-DlR9eJAM.d.ts} +2 -2
- package/dist/{pipeline-DPDxH_7m.d.ts → pipeline-BfD2k1rT.d.ts} +2 -2
- package/dist/{plan-templates-CzD9GnAU.d.ts → plan-templates-DSIKCXZN.d.ts} +5 -5
- package/dist/{llm-selector-C0tfTCUe.d.ts → provider-model-resolve-BNRsNuJx.d.ts} +40 -3
- package/dist/{provider-runner-DMa70ODu.d.ts → provider-runner-CX7iIvox.d.ts} +3 -3
- package/dist/{retry-policy-CN0khdlj.d.ts → retry-policy-BilV1ujH.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +12 -12
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-B2yw84VT.d.ts → secret-vault-gkvEZZfE.d.ts} +2 -2
- package/dist/security/index.d.ts +5 -67
- package/dist/security/index.js +96 -76
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-CzHh_igB.d.ts → selector-Bc7eWtT3.d.ts} +1 -1
- package/dist/{session-event-bridge-BUI6Jf-4.d.ts → session-event-bridge-D-araDEz.d.ts} +1 -1
- package/dist/{session-reader-CMgdMSRP.d.ts → session-reader-D7Dapswh.d.ts} +1 -1
- package/dist/storage/index.d.ts +11 -11
- package/dist/storage/index.js +81 -84
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/index.d.ts +4 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/types/index.d.ts +19 -19
- package/dist/types/index.js +1265 -400
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +454 -406
- package/dist/utils/index.js +2191 -1201
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
package/dist/defaults/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as crypto2 from 'crypto';
|
|
2
2
|
import { randomBytes, createCipheriv, createDecipheriv, randomUUID, createHash } from 'crypto';
|
|
3
3
|
import * as fsp2 from 'fs/promises';
|
|
4
|
-
import * as
|
|
4
|
+
import * as path4 from 'path';
|
|
5
5
|
import { isAbsolute, resolve } from 'path';
|
|
6
6
|
import * as fs4 from 'fs';
|
|
7
7
|
import * as os from 'os';
|
|
@@ -33,9 +33,9 @@ __export(atomic_write_exports, {
|
|
|
33
33
|
withFileLock: () => withFileLock
|
|
34
34
|
});
|
|
35
35
|
async function atomicWrite(targetPath, content, opts = {}) {
|
|
36
|
-
const dir =
|
|
36
|
+
const dir = path4.dirname(targetPath);
|
|
37
37
|
await fsp2.mkdir(dir, { recursive: true });
|
|
38
|
-
const tmp =
|
|
38
|
+
const tmp = path4.join(dir, `.${path4.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
39
39
|
try {
|
|
40
40
|
if (typeof content === "string") {
|
|
41
41
|
await fsp2.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
@@ -74,9 +74,9 @@ async function ensureDir(dir) {
|
|
|
74
74
|
await fsp2.mkdir(dir, { recursive: true });
|
|
75
75
|
}
|
|
76
76
|
async function withFileLock(targetPath, fn, opts = {}) {
|
|
77
|
-
const dir =
|
|
77
|
+
const dir = path4.dirname(targetPath);
|
|
78
78
|
await fsp2.mkdir(dir, { recursive: true });
|
|
79
|
-
const lockPath =
|
|
79
|
+
const lockPath = path4.join(dir, `.${path4.basename(targetPath)}.lock`);
|
|
80
80
|
const timeoutMs = opts.timeoutMs ?? 5e3;
|
|
81
81
|
const staleMs = opts.staleMs ?? 3e4;
|
|
82
82
|
const started = Date.now();
|
|
@@ -105,7 +105,7 @@ async function withFileLock(targetPath, fn, opts = {}) {
|
|
|
105
105
|
if (Date.now() - started >= timeoutMs) {
|
|
106
106
|
throw new Error(`Timed out waiting for file lock: ${targetPath}`);
|
|
107
107
|
}
|
|
108
|
-
await new Promise((
|
|
108
|
+
await new Promise((resolve6) => setTimeout(resolve6, 25));
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
try {
|
|
@@ -138,7 +138,7 @@ async function renameWithRetry(from, to) {
|
|
|
138
138
|
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
139
139
|
throw err;
|
|
140
140
|
}
|
|
141
|
-
await new Promise((
|
|
141
|
+
await new Promise((resolve6) => setTimeout(resolve6, delays[i]));
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
throw lastErr;
|
|
@@ -234,7 +234,7 @@ var DefaultLogger = class _DefaultLogger {
|
|
|
234
234
|
this.maxFileBytes = opts.maxFileBytes ?? 10 * 1024 * 1024;
|
|
235
235
|
if (this.file) {
|
|
236
236
|
try {
|
|
237
|
-
fs4.mkdirSync(
|
|
237
|
+
fs4.mkdirSync(path4.dirname(this.file), { recursive: true });
|
|
238
238
|
} catch {
|
|
239
239
|
}
|
|
240
240
|
}
|
|
@@ -436,32 +436,268 @@ function isEmptyMessage(msg) {
|
|
|
436
436
|
return msg.content.length === 0;
|
|
437
437
|
}
|
|
438
438
|
|
|
439
|
+
// src/utils/assert-never.ts
|
|
440
|
+
function assertNever(x, message) {
|
|
441
|
+
const err = new Error(
|
|
442
|
+
`Unhandled case: ${JSON.stringify(x)}`
|
|
443
|
+
);
|
|
444
|
+
err.name = "AssertNeverError";
|
|
445
|
+
throw err;
|
|
446
|
+
}
|
|
447
|
+
|
|
439
448
|
// src/utils/index.ts
|
|
440
449
|
init_atomic_write();
|
|
450
|
+
var MAX_DIGEST_CHARS = 4e3;
|
|
451
|
+
function createContextEvidenceState() {
|
|
452
|
+
return {
|
|
453
|
+
sessionGoals: [],
|
|
454
|
+
implicitFacts: [],
|
|
455
|
+
activeErrors: [],
|
|
456
|
+
toolCalls: [],
|
|
457
|
+
fileGraph: {},
|
|
458
|
+
repeatedReads: [],
|
|
459
|
+
updatedAt: Date.now()
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
function buildContextEvidenceDigest(ctx) {
|
|
463
|
+
const state = ensureEvidence(ctx);
|
|
464
|
+
const lines = [];
|
|
465
|
+
if (state.currentIntent?.text) {
|
|
466
|
+
lines.push(`intent: ${state.currentIntent.text}`);
|
|
467
|
+
}
|
|
468
|
+
const goals = state.sessionGoals.slice(-3);
|
|
469
|
+
if (goals.length > 0) {
|
|
470
|
+
lines.push("session_goals:");
|
|
471
|
+
for (const goal of goals) lines.push(`- ${goal}`);
|
|
472
|
+
}
|
|
473
|
+
const activeErrors = state.activeErrors.slice(-5);
|
|
474
|
+
if (activeErrors.length > 0) {
|
|
475
|
+
lines.push("active_errors:");
|
|
476
|
+
for (const err of activeErrors) lines.push(`- ${err}`);
|
|
477
|
+
}
|
|
478
|
+
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);
|
|
479
|
+
if (files.length > 0) {
|
|
480
|
+
lines.push("dependency_graph:");
|
|
481
|
+
for (const file of files) {
|
|
482
|
+
const actions = [
|
|
483
|
+
file.reads > 0 ? `read ${file.reads}x` : "",
|
|
484
|
+
file.writes > 0 ? `write ${file.writes}x` : ""
|
|
485
|
+
].filter(Boolean).join(", ");
|
|
486
|
+
const refs = file.referenced ? "; referenced by assistant" : "";
|
|
487
|
+
const via = file.lastToolUseId ? `; last via ${file.lastToolUseId}` : "";
|
|
488
|
+
lines.push(`- ${file.path} (${actions || "seen"}${refs}${via})`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
const referenced = state.toolCalls.filter((tool) => tool.status === "referenced").slice(-10);
|
|
492
|
+
const recentSeen = state.toolCalls.filter((tool) => tool.status === "seen").slice(-5);
|
|
493
|
+
const trail = [...referenced, ...recentSeen];
|
|
494
|
+
if (trail.length > 0) {
|
|
495
|
+
lines.push("tool_trail:");
|
|
496
|
+
for (const tool of trail) {
|
|
497
|
+
const size = tool.outputTokens ? `; ~${tool.outputTokens} tokens` : "";
|
|
498
|
+
const filesText = tool.files.length > 0 ? `; files=${tool.files.slice(0, 4).join(", ")}` : "";
|
|
499
|
+
const symbolsText = tool.symbols.length > 0 ? `; symbols=${tool.symbols.slice(0, 4).join(", ")}` : "";
|
|
500
|
+
lines.push(
|
|
501
|
+
`- ${tool.toolUseId} ${tool.toolName} ${tool.status}: ${tool.summary}${filesText}${symbolsText}${size}`
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
const facts = state.implicitFacts.slice(-8);
|
|
506
|
+
if (facts.length > 0) {
|
|
507
|
+
lines.push("implicit_facts:");
|
|
508
|
+
for (const fact of facts) lines.push(`- ${fact}`);
|
|
509
|
+
}
|
|
510
|
+
const digest = lines.join("\n");
|
|
511
|
+
if (digest.length <= MAX_DIGEST_CHARS) return digest;
|
|
512
|
+
return `${digest.slice(0, MAX_DIGEST_CHARS)}... [+${digest.length - MAX_DIGEST_CHARS} chars]`;
|
|
513
|
+
}
|
|
514
|
+
function repeatedReadPressure(ctx) {
|
|
515
|
+
return ensureEvidence(ctx).repeatedReads.reduce((max, item) => Math.max(max, item.count), 0);
|
|
516
|
+
}
|
|
517
|
+
function ensureEvidence(ctx) {
|
|
518
|
+
if (!ctx.contextEvidence) {
|
|
519
|
+
ctx.contextEvidence = createContextEvidenceState();
|
|
520
|
+
}
|
|
521
|
+
return ctx.contextEvidence;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// src/utils/deep-merge.ts
|
|
525
|
+
var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
|
|
526
|
+
"__proto__",
|
|
527
|
+
"constructor",
|
|
528
|
+
"prototype",
|
|
529
|
+
"__defineGetter__",
|
|
530
|
+
"__defineSetter__",
|
|
531
|
+
"__lookupGetter__",
|
|
532
|
+
"__lookupSetter__"
|
|
533
|
+
]);
|
|
534
|
+
function isPrimitiveArray(a) {
|
|
535
|
+
return a.every((v) => v === null || typeof v !== "object" && typeof v !== "function");
|
|
536
|
+
}
|
|
537
|
+
function deepMerge(base, patch, options = {}) {
|
|
538
|
+
const {
|
|
539
|
+
conflictResolution = "prefer-patch",
|
|
540
|
+
arrayMode = "replace",
|
|
541
|
+
protectProto = true,
|
|
542
|
+
onNonPrimitiveArrayReplace
|
|
543
|
+
} = options;
|
|
544
|
+
if (typeof base !== "object" || base === null) {
|
|
545
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
546
|
+
}
|
|
547
|
+
if (typeof patch !== "object" || patch === null) {
|
|
548
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
549
|
+
}
|
|
550
|
+
if (Array.isArray(base) && Array.isArray(patch)) {
|
|
551
|
+
if (arrayMode === "concat-primitives" && isPrimitiveArray(base) && isPrimitiveArray(patch)) {
|
|
552
|
+
return [.../* @__PURE__ */ new Set([...base, ...patch])];
|
|
553
|
+
}
|
|
554
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
555
|
+
}
|
|
556
|
+
if (Array.isArray(base) || Array.isArray(patch)) {
|
|
557
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
558
|
+
}
|
|
559
|
+
const baseObj = base;
|
|
560
|
+
const patchObj = patch;
|
|
561
|
+
const out = { ...baseObj };
|
|
562
|
+
for (const [k, v] of Object.entries(patchObj)) {
|
|
563
|
+
if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;
|
|
564
|
+
const existing = out[k];
|
|
565
|
+
if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
|
|
566
|
+
out[k] = deepMerge(existing, v, options);
|
|
567
|
+
} else if (Array.isArray(v) && Array.isArray(existing)) {
|
|
568
|
+
if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {
|
|
569
|
+
onNonPrimitiveArrayReplace(k, existing.length, v.length);
|
|
570
|
+
}
|
|
571
|
+
out[k] = deepMerge(existing, v, options);
|
|
572
|
+
} else if (v !== void 0) {
|
|
573
|
+
if (onNonPrimitiveArrayReplace && Array.isArray(v) && !isPrimitiveArray(v)) {
|
|
574
|
+
const existingLen = Array.isArray(existing) ? existing.length : 0;
|
|
575
|
+
onNonPrimitiveArrayReplace(k, existingLen, v.length);
|
|
576
|
+
}
|
|
577
|
+
out[k] = v;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return out;
|
|
581
|
+
}
|
|
441
582
|
|
|
442
583
|
// src/utils/error.ts
|
|
443
584
|
function toErrorMessage(err) {
|
|
444
585
|
return err instanceof Error ? err.message : String(err);
|
|
445
586
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
587
|
+
var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
|
|
588
|
+
var IS_WINDOWS = process.platform === "win32";
|
|
589
|
+
var SEP = IS_WINDOWS ? "\\" : "/";
|
|
590
|
+
function isGlob(p) {
|
|
591
|
+
for (const c of p) {
|
|
592
|
+
if (GLOB_CHARS.has(c)) return true;
|
|
451
593
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
594
|
+
return false;
|
|
595
|
+
}
|
|
596
|
+
function globToRegex(pat) {
|
|
597
|
+
let i = 0;
|
|
598
|
+
let re = "^";
|
|
599
|
+
while (i < pat.length) {
|
|
600
|
+
const c = expectDefined(pat[i]);
|
|
601
|
+
if (c === "*") {
|
|
602
|
+
if (pat[i + 1] === "*") {
|
|
603
|
+
re += ".*";
|
|
604
|
+
i += 2;
|
|
605
|
+
if (pat[i] === "/") i++;
|
|
606
|
+
} else {
|
|
607
|
+
re += "[^/\\\\]*";
|
|
608
|
+
i++;
|
|
609
|
+
}
|
|
610
|
+
} else if (c === "?") {
|
|
611
|
+
re += "[^/\\\\]";
|
|
612
|
+
i++;
|
|
613
|
+
} else if (c === "[") {
|
|
614
|
+
let cls = "[";
|
|
615
|
+
i++;
|
|
616
|
+
if (pat[i] === "!" || pat[i] === "^") {
|
|
617
|
+
cls += "^";
|
|
618
|
+
i++;
|
|
619
|
+
}
|
|
620
|
+
while (i < pat.length && pat[i] !== "]") {
|
|
621
|
+
const ch = pat[i] ?? "";
|
|
622
|
+
if (ch === "\\") cls += "\\\\";
|
|
623
|
+
else if (ch === "]" || ch === "^") cls += `\\${ch}`;
|
|
624
|
+
else cls += ch;
|
|
625
|
+
i++;
|
|
626
|
+
}
|
|
627
|
+
cls += "]";
|
|
628
|
+
re += cls;
|
|
629
|
+
i++;
|
|
630
|
+
} else {
|
|
631
|
+
re += c.replace(/[.+^${}()|\\]/g, "\\$&");
|
|
632
|
+
i++;
|
|
633
|
+
}
|
|
459
634
|
}
|
|
635
|
+
return new RegExp(re + "$");
|
|
460
636
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
637
|
+
function baseDir(pat) {
|
|
638
|
+
let i = pat.length - 1;
|
|
639
|
+
while (i >= 0 && !GLOB_CHARS.has(expectDefined(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
|
|
640
|
+
const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
|
|
641
|
+
return cut < 0 ? "." : pat.slice(0, cut);
|
|
642
|
+
}
|
|
643
|
+
async function expandGlob(pattern) {
|
|
644
|
+
if (!isGlob(pattern)) return [pattern];
|
|
645
|
+
const results = /* @__PURE__ */ new Set();
|
|
646
|
+
const abs = isAbsolute(pattern);
|
|
647
|
+
const base = abs ? baseDir(pattern) : baseDir(pattern);
|
|
648
|
+
const relPat = base === "." ? pattern : pattern.slice(base.length + 1);
|
|
649
|
+
async function walk3(dir, pat) {
|
|
650
|
+
let entries;
|
|
651
|
+
try {
|
|
652
|
+
entries = await fsp2.readdir(dir);
|
|
653
|
+
} catch {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
const firstGlob = pat.search(/[*?[[]/);
|
|
657
|
+
if (firstGlob < 0) {
|
|
658
|
+
const re = globToRegex(pat);
|
|
659
|
+
for (const e of entries) {
|
|
660
|
+
if (re.test(e)) {
|
|
661
|
+
const full = `${dir}${SEP}${e}`;
|
|
662
|
+
results.add(abs ? resolve(full) : full);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
const before = pat.slice(0, firstGlob);
|
|
668
|
+
const rest = pat.slice(firstGlob);
|
|
669
|
+
if (before.endsWith("**")) {
|
|
670
|
+
await walk3(dir, rest);
|
|
671
|
+
for (const e of entries) {
|
|
672
|
+
const full = `${dir}${SEP}${e}`;
|
|
673
|
+
try {
|
|
674
|
+
const stat6 = await fsp2.stat(full);
|
|
675
|
+
if (stat6.isDirectory()) await walk3(full, rest);
|
|
676
|
+
} catch {
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
} else if (before === "") {
|
|
680
|
+
const re = globToRegex(rest);
|
|
681
|
+
for (const e of entries) {
|
|
682
|
+
if (re.test(e)) {
|
|
683
|
+
const full = `${dir}${SEP}${e}`;
|
|
684
|
+
results.add(abs ? resolve(full) : full);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
} else {
|
|
688
|
+
const seg = before.replace(/[*?[\]]/g, "").replace(/\/$/, "");
|
|
689
|
+
if (entries.includes(seg)) {
|
|
690
|
+
const full = `${dir}${SEP}${seg}`;
|
|
691
|
+
try {
|
|
692
|
+
const stat6 = await fsp2.stat(full);
|
|
693
|
+
if (stat6.isDirectory()) await walk3(full, rest);
|
|
694
|
+
} catch {
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
await walk3(base === "." ? "." : base, relPat);
|
|
700
|
+
return [...results];
|
|
465
701
|
}
|
|
466
702
|
|
|
467
703
|
// src/utils/glob-match.ts
|
|
@@ -539,232 +775,440 @@ function matchGlob(pattern, input) {
|
|
|
539
775
|
function matchAny(patterns, input) {
|
|
540
776
|
return patterns.some((p) => matchGlob(p, input));
|
|
541
777
|
}
|
|
542
|
-
function projectHash(absRoot) {
|
|
543
|
-
return createHash("sha256").update(path3.resolve(absRoot)).digest("hex").slice(0, 12);
|
|
544
|
-
}
|
|
545
|
-
function projectSlug(absRoot) {
|
|
546
|
-
const base = slugify(path3.basename(absRoot));
|
|
547
|
-
const hash = createHash("sha256").update(path3.resolve(absRoot)).digest("hex").slice(0, 6);
|
|
548
|
-
return `${base}-${hash}`;
|
|
549
|
-
}
|
|
550
|
-
function slugify(name) {
|
|
551
|
-
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
|
|
552
|
-
}
|
|
553
|
-
function wstackGlobalRoot() {
|
|
554
|
-
const fromEnv = process.env["WRONGSTACK_HOME"];
|
|
555
|
-
if (fromEnv && fromEnv.trim().length > 0) return path3.resolve(fromEnv);
|
|
556
|
-
return path3.join(os.homedir(), ".wrongstack");
|
|
557
|
-
}
|
|
558
|
-
function resolveWstackPaths(opts) {
|
|
559
|
-
const globalRoot = opts.globalRoot ?? (opts.userHome ? path3.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
|
|
560
|
-
const hash = projectHash(opts.projectRoot);
|
|
561
|
-
const slug = projectSlug(opts.projectRoot);
|
|
562
|
-
const projectDir = path3.join(globalRoot, "projects", slug);
|
|
563
|
-
return {
|
|
564
|
-
globalRoot,
|
|
565
|
-
configDir: globalRoot,
|
|
566
|
-
globalConfig: path3.join(globalRoot, "config.json"),
|
|
567
|
-
secretsKey: path3.join(globalRoot, ".key"),
|
|
568
|
-
globalMemory: path3.join(globalRoot, "memory.md"),
|
|
569
|
-
globalSkills: path3.join(globalRoot, "skills"),
|
|
570
|
-
globalPrompts: path3.join(globalRoot, "prompts"),
|
|
571
|
-
cacheDir: path3.join(globalRoot, "cache"),
|
|
572
|
-
modelsCache: path3.join(globalRoot, "cache", "models.dev.json"),
|
|
573
|
-
modelsOverlayCache: path3.join(globalRoot, "cache", "models-overlay.json"),
|
|
574
|
-
historyFile: path3.join(globalRoot, "history"),
|
|
575
|
-
logFile: path3.join(globalRoot, "logs", "wrongstack.log"),
|
|
576
|
-
projectDir,
|
|
577
|
-
projectCodebaseIndex: path3.join(projectDir, "codebase-index"),
|
|
578
|
-
projectMemory: path3.join(projectDir, "memory.md"),
|
|
579
|
-
projectSessions: path3.join(projectDir, "sessions"),
|
|
580
|
-
projectTrust: path3.join(projectDir, "trust.json"),
|
|
581
|
-
projectMeta: path3.join(projectDir, "meta.json"),
|
|
582
|
-
projectLocalConfig: path3.join(projectDir, "config.local.json"),
|
|
583
|
-
inProjectConfig: path3.join(opts.projectRoot, ".wrongstack", "config.json"),
|
|
584
|
-
inProjectAgentsFile: path3.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
|
|
585
|
-
inProjectSkills: path3.join(opts.projectRoot, ".wrongstack", "skills"),
|
|
586
|
-
inProjectWorktrees: path3.join(opts.projectRoot, ".wrongstack", "worktrees"),
|
|
587
|
-
projectHash: hash,
|
|
588
|
-
projectSlug: slug,
|
|
589
|
-
projectGoal: path3.join(projectDir, "goal.json"),
|
|
590
|
-
projectSpecs: path3.join(projectDir, "specs"),
|
|
591
|
-
projectTaskGraphs: path3.join(projectDir, "task-graphs"),
|
|
592
|
-
projectSddSession: path3.join(projectDir, "sdd-session.json"),
|
|
593
|
-
projectPlan: path3.join(projectDir, "plan.json"),
|
|
594
|
-
projectAutophase: path3.join(projectDir, "autophase"),
|
|
595
|
-
syncConfig: path3.join(globalRoot, "sync.json"),
|
|
596
|
-
projectStatus: (projectHash2) => path3.join(globalRoot, "projects", projectHash2, "status.json")
|
|
597
|
-
};
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
// src/utils/sleep.ts
|
|
601
|
-
function sleep(ms) {
|
|
602
|
-
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
603
|
-
}
|
|
604
778
|
|
|
605
|
-
// src/utils/
|
|
606
|
-
function
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
);
|
|
610
|
-
err.name = "AssertNeverError";
|
|
611
|
-
throw err;
|
|
779
|
+
// src/utils/json-repair.ts
|
|
780
|
+
function completePartialObject(s) {
|
|
781
|
+
if (!s.trim().startsWith("{")) return s;
|
|
782
|
+
if (tryParse(s).ok) return s;
|
|
783
|
+
return repairTruncated(s);
|
|
612
784
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
"
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
785
|
+
function repairTruncated(s) {
|
|
786
|
+
const stack = [];
|
|
787
|
+
let inString = false;
|
|
788
|
+
let escaped = false;
|
|
789
|
+
let sawKey = false;
|
|
790
|
+
let prevSig = "";
|
|
791
|
+
let contentEnd = 0;
|
|
792
|
+
let stringBraceDepth = 0;
|
|
793
|
+
for (let i = 0; i < s.length; i++) {
|
|
794
|
+
const ch = expectDefined(s[i]);
|
|
795
|
+
if (inString) {
|
|
796
|
+
contentEnd = i + 1;
|
|
797
|
+
if (escaped) {
|
|
798
|
+
escaped = false;
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
801
|
+
if (ch === "\\") {
|
|
802
|
+
escaped = true;
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
if (ch === '"') {
|
|
806
|
+
inString = false;
|
|
807
|
+
prevSig = '"';
|
|
808
|
+
stringBraceDepth = 0;
|
|
809
|
+
continue;
|
|
810
|
+
}
|
|
811
|
+
if (ch === "{") stringBraceDepth++;
|
|
812
|
+
else if (ch === "}" && stringBraceDepth > 0) stringBraceDepth--;
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
if (ch === " " || ch === " " || ch === "\n" || ch === "\r") continue;
|
|
816
|
+
contentEnd = i + 1;
|
|
817
|
+
if (ch === '"') {
|
|
818
|
+
inString = true;
|
|
819
|
+
sawKey = true;
|
|
820
|
+
stringBraceDepth = 0;
|
|
821
|
+
prevSig = '"';
|
|
822
|
+
} else if (ch === "{" || ch === "[") {
|
|
823
|
+
stack.push(ch);
|
|
824
|
+
prevSig = ch;
|
|
825
|
+
} else if (ch === "}" || ch === "]") {
|
|
826
|
+
stack.pop();
|
|
827
|
+
prevSig = ch;
|
|
828
|
+
} else {
|
|
829
|
+
prevSig = ch;
|
|
830
|
+
}
|
|
639
831
|
}
|
|
640
|
-
if (
|
|
641
|
-
|
|
642
|
-
|
|
832
|
+
if (!sawKey && !inString) return s;
|
|
833
|
+
let result = s.slice(0, contentEnd);
|
|
834
|
+
if (inString) {
|
|
835
|
+
if (escaped) {
|
|
836
|
+
result = result.slice(0, -1);
|
|
837
|
+
} else if (endsWithInvalidEscape(result)) {
|
|
838
|
+
result = result.slice(0, -2);
|
|
643
839
|
}
|
|
644
|
-
|
|
840
|
+
if (stringBraceDepth > 0) result += "}".repeat(stringBraceDepth);
|
|
841
|
+
result += '"';
|
|
842
|
+
} else if (prevSig === ":") {
|
|
843
|
+
result += "null";
|
|
645
844
|
}
|
|
646
|
-
|
|
647
|
-
|
|
845
|
+
for (let k = stack.length - 1; k >= 0; k--) {
|
|
846
|
+
result += stack[k] === "{" ? "}" : "]";
|
|
648
847
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
}
|
|
848
|
+
if (!tryParse(result).ok) {
|
|
849
|
+
const patched = result.replace(/:(\s*)([}\]])/g, ":null$2");
|
|
850
|
+
if (tryParse(patched).ok) result = patched;
|
|
851
|
+
}
|
|
852
|
+
return result;
|
|
853
|
+
}
|
|
854
|
+
var VALID_ESCAPE = /* @__PURE__ */ new Set(['"', "\\", "/", "b", "f", "n", "r", "t", "u"]);
|
|
855
|
+
function endsWithInvalidEscape(str) {
|
|
856
|
+
const last = str[str.length - 1];
|
|
857
|
+
if (str[str.length - 2] !== "\\" || last === void 0) return false;
|
|
858
|
+
if (VALID_ESCAPE.has(last)) return false;
|
|
859
|
+
let backslashes = 0;
|
|
860
|
+
for (let k = str.length - 2; k >= 0 && str[k] === "\\"; k--) backslashes++;
|
|
861
|
+
return backslashes % 2 === 1;
|
|
862
|
+
}
|
|
863
|
+
function tryParse(s) {
|
|
864
|
+
try {
|
|
865
|
+
return { ok: true, value: JSON.parse(s) };
|
|
866
|
+
} catch {
|
|
867
|
+
return { ok: false };
|
|
669
868
|
}
|
|
670
|
-
return out;
|
|
671
869
|
}
|
|
672
870
|
|
|
673
|
-
// src/utils/
|
|
674
|
-
function
|
|
675
|
-
const
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
} catch {
|
|
688
|
-
return String(value);
|
|
689
|
-
}
|
|
871
|
+
// src/utils/json-schema-validate.ts
|
|
872
|
+
function validateAgainstSchema(value, schema) {
|
|
873
|
+
const errors = [];
|
|
874
|
+
walk(value, schema, "", errors);
|
|
875
|
+
return { ok: errors.length === 0, errors };
|
|
876
|
+
}
|
|
877
|
+
function walk(value, schema, path21, errors) {
|
|
878
|
+
if (schema.enum !== void 0) {
|
|
879
|
+
if (!schema.enum.some((e) => deepEqual(e, value))) {
|
|
880
|
+
errors.push({
|
|
881
|
+
path: path21 || "<root>",
|
|
882
|
+
message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
|
|
883
|
+
});
|
|
884
|
+
return;
|
|
690
885
|
}
|
|
691
|
-
return String(value);
|
|
692
886
|
}
|
|
693
|
-
|
|
694
|
-
if (
|
|
695
|
-
|
|
887
|
+
if (typeof schema.type === "string") {
|
|
888
|
+
if (!checkType(value, schema.type)) {
|
|
889
|
+
errors.push({
|
|
890
|
+
path: path21 || "<root>",
|
|
891
|
+
message: `expected ${schema.type}, got ${describeType(value)}`
|
|
892
|
+
});
|
|
893
|
+
return;
|
|
696
894
|
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
895
|
+
}
|
|
896
|
+
if (schema.type === "object" && isPlainObject(value)) {
|
|
897
|
+
const obj = value;
|
|
898
|
+
for (const req of schema.required ?? []) {
|
|
899
|
+
if (!(req in obj)) {
|
|
900
|
+
errors.push({ path: joinPath(path21, req), message: "required property missing" });
|
|
901
|
+
}
|
|
700
902
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
|
|
903
|
+
if (schema.properties) {
|
|
904
|
+
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
905
|
+
if (key in obj) {
|
|
906
|
+
walk(obj[key], subSchema, joinPath(path21, key), errors);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
708
909
|
}
|
|
709
|
-
const half = Math.floor(available / 2);
|
|
710
|
-
const first = text.slice(0, half);
|
|
711
|
-
const second = text.slice(text.length - half);
|
|
712
|
-
return { text: `${first}${marker}${second}`, newBudget: 0 };
|
|
713
|
-
}
|
|
714
|
-
return { serialize, enforceCap, capBytes };
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
// src/utils/token-estimate.ts
|
|
718
|
-
var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
|
|
719
|
-
var CALIBRATION_GLOBAL_KEY = "__global__";
|
|
720
|
-
var _cals = /* @__PURE__ */ new Map();
|
|
721
|
-
function calState(key) {
|
|
722
|
-
let state = _cals.get(key);
|
|
723
|
-
if (!state) {
|
|
724
|
-
state = { ratio: 1, count: 0, prevEst: 0 };
|
|
725
|
-
_cals.set(key, state);
|
|
726
910
|
}
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
|
|
731
|
-
var ESTIMATE_CACHE_MAX_SIZE = 1e4;
|
|
732
|
-
function getCachedEstimate(key, compute) {
|
|
733
|
-
const existing = ESTIMATE_CACHE.get(key);
|
|
734
|
-
if (existing !== void 0) return existing;
|
|
735
|
-
if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {
|
|
736
|
-
for (const k of ESTIMATE_CACHE.keys()) {
|
|
737
|
-
if (ESTIMATE_CACHE.size <= Math.floor(ESTIMATE_CACHE_MAX_SIZE / 2)) break;
|
|
738
|
-
ESTIMATE_CACHE.delete(k);
|
|
911
|
+
if (schema.type === "array" && Array.isArray(value) && schema.items) {
|
|
912
|
+
for (let i = 0; i < value.length; i++) {
|
|
913
|
+
walk(value[i], schema.items, `${path21}[${i}]`, errors);
|
|
739
914
|
}
|
|
740
915
|
}
|
|
741
|
-
const estimate = compute(key);
|
|
742
|
-
ESTIMATE_CACHE.set(key, estimate);
|
|
743
|
-
return estimate;
|
|
744
916
|
}
|
|
745
|
-
function
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
917
|
+
function checkType(value, type) {
|
|
918
|
+
switch (type) {
|
|
919
|
+
case "string":
|
|
920
|
+
return typeof value === "string";
|
|
921
|
+
case "number":
|
|
922
|
+
return typeof value === "number" && !Number.isNaN(value);
|
|
923
|
+
case "integer":
|
|
924
|
+
return typeof value === "number" && Number.isInteger(value);
|
|
925
|
+
case "boolean":
|
|
926
|
+
return typeof value === "boolean";
|
|
927
|
+
case "null":
|
|
928
|
+
return value === null;
|
|
929
|
+
case "array":
|
|
930
|
+
return Array.isArray(value);
|
|
931
|
+
case "object":
|
|
932
|
+
return isPlainObject(value);
|
|
933
|
+
default:
|
|
934
|
+
return true;
|
|
749
935
|
}
|
|
750
|
-
return getCachedEstimate(JSON.stringify(input), (key) => RoughTokenEstimate(key));
|
|
751
936
|
}
|
|
752
|
-
function
|
|
753
|
-
|
|
754
|
-
return getCachedEstimate(JSON.stringify(content), (key) => RoughTokenEstimate(key));
|
|
937
|
+
function isPlainObject(v) {
|
|
938
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
755
939
|
}
|
|
756
|
-
function
|
|
757
|
-
|
|
940
|
+
function describeType(v) {
|
|
941
|
+
if (v === null) return "null";
|
|
942
|
+
if (Array.isArray(v)) return "array";
|
|
943
|
+
return typeof v;
|
|
758
944
|
}
|
|
759
|
-
function
|
|
760
|
-
if (
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
945
|
+
function joinPath(parent, key) {
|
|
946
|
+
if (!parent) return key;
|
|
947
|
+
return `${parent}.${key}`;
|
|
948
|
+
}
|
|
949
|
+
function deepEqual(a, b) {
|
|
950
|
+
if (a === b) return true;
|
|
951
|
+
if (typeof a !== typeof b) return false;
|
|
952
|
+
if (a === null || b === null) return a === b;
|
|
953
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
954
|
+
return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
|
|
955
|
+
}
|
|
956
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
957
|
+
const ak = Object.keys(a);
|
|
958
|
+
const bk = Object.keys(b);
|
|
959
|
+
if (ak.length !== bk.length) return false;
|
|
960
|
+
return ak.every(
|
|
961
|
+
(k) => deepEqual(a[k], b[k])
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
return false;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// src/utils/merge-models-payload.ts
|
|
968
|
+
function mergeModelsPayload(base, overlay) {
|
|
969
|
+
const out = {};
|
|
970
|
+
for (const [id, provider] of Object.entries(base)) {
|
|
971
|
+
out[id] = cloneProvider(provider);
|
|
972
|
+
}
|
|
973
|
+
for (const [id, ovProvider] of Object.entries(overlay)) {
|
|
974
|
+
const existing = out[id];
|
|
975
|
+
out[id] = existing ? mergeProvider(existing, ovProvider) : cloneProvider(ovProvider);
|
|
976
|
+
}
|
|
977
|
+
return out;
|
|
978
|
+
}
|
|
979
|
+
function mergeProvider(base, overlay) {
|
|
980
|
+
const models = {};
|
|
981
|
+
for (const [mid, m] of Object.entries(base.models ?? {})) {
|
|
982
|
+
models[mid] = { ...m };
|
|
983
|
+
}
|
|
984
|
+
for (const [mid, ovModel] of Object.entries(overlay.models ?? {})) {
|
|
985
|
+
const existing = models[mid];
|
|
986
|
+
models[mid] = existing ? mergeModel(existing, ovModel) : { ...ovModel };
|
|
987
|
+
}
|
|
988
|
+
return {
|
|
989
|
+
...base,
|
|
990
|
+
// Overlay scalar fields win when explicitly provided; otherwise keep base.
|
|
991
|
+
...stripUndefined({
|
|
992
|
+
id: overlay.id,
|
|
993
|
+
name: overlay.name,
|
|
994
|
+
npm: overlay.npm,
|
|
995
|
+
api: overlay.api,
|
|
996
|
+
env: overlay.env,
|
|
997
|
+
doc: overlay.doc
|
|
998
|
+
}),
|
|
999
|
+
models
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
function mergeModel(base, overlay) {
|
|
1003
|
+
const merged = { ...base, ...overlay };
|
|
1004
|
+
if (base.limit || overlay.limit) {
|
|
1005
|
+
merged.limit = { ...base.limit, ...overlay.limit };
|
|
1006
|
+
}
|
|
1007
|
+
if (base.cost || overlay.cost) {
|
|
1008
|
+
merged.cost = { ...base.cost, ...overlay.cost };
|
|
1009
|
+
}
|
|
1010
|
+
if (base.modalities || overlay.modalities) {
|
|
1011
|
+
merged.modalities = { ...base.modalities, ...overlay.modalities };
|
|
1012
|
+
}
|
|
1013
|
+
return merged;
|
|
1014
|
+
}
|
|
1015
|
+
function cloneProvider(p) {
|
|
1016
|
+
const models = {};
|
|
1017
|
+
for (const [mid, m] of Object.entries(p.models ?? {})) {
|
|
1018
|
+
models[mid] = { ...m };
|
|
1019
|
+
}
|
|
1020
|
+
return { ...p, models };
|
|
1021
|
+
}
|
|
1022
|
+
function stripUndefined(obj) {
|
|
1023
|
+
const out = {};
|
|
1024
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
1025
|
+
if (v !== void 0) out[k] = v;
|
|
1026
|
+
}
|
|
1027
|
+
return out;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
// src/utils/regex-guard.ts
|
|
1031
|
+
var MAX_PATTERN_LEN = 512;
|
|
1032
|
+
var DANGEROUS_PATTERNS = [
|
|
1033
|
+
/(\([^)]*[+*][^)]*\))[+*]/,
|
|
1034
|
+
// (a+)+, (.*)+, etc
|
|
1035
|
+
/(\(\?:[^)]*[+*][^)]*\))[+*]/
|
|
1036
|
+
// same, with non-capturing group
|
|
1037
|
+
];
|
|
1038
|
+
function compileUserRegex(pattern, flags) {
|
|
1039
|
+
if (typeof pattern !== "string") {
|
|
1040
|
+
return { ok: false, reason: "pattern must be a string" };
|
|
1041
|
+
}
|
|
1042
|
+
if (pattern.length === 0) {
|
|
1043
|
+
return { ok: false, reason: "pattern is empty" };
|
|
1044
|
+
}
|
|
1045
|
+
if (pattern.length > MAX_PATTERN_LEN) {
|
|
1046
|
+
return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };
|
|
1047
|
+
}
|
|
1048
|
+
for (const rx of DANGEROUS_PATTERNS) {
|
|
1049
|
+
if (rx.test(pattern)) {
|
|
1050
|
+
return {
|
|
1051
|
+
ok: false,
|
|
1052
|
+
reason: "pattern looks vulnerable to catastrophic backtracking \u2014 rewrite without nested quantifiers"
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
try {
|
|
1057
|
+
return { ok: true, regex: new RegExp(pattern, flags) };
|
|
1058
|
+
} catch (err) {
|
|
1059
|
+
return {
|
|
1060
|
+
ok: false,
|
|
1061
|
+
reason: err instanceof Error ? err.message : "invalid regex"
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// src/utils/safe-json.ts
|
|
1067
|
+
function safeParse(input, maxBytes = 5e6) {
|
|
1068
|
+
if (input.length > maxBytes) {
|
|
1069
|
+
return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
|
|
1070
|
+
}
|
|
1071
|
+
try {
|
|
1072
|
+
return { ok: true, value: JSON.parse(input) };
|
|
1073
|
+
} catch (err) {
|
|
1074
|
+
return {
|
|
1075
|
+
ok: false,
|
|
1076
|
+
error: toErrorMessage(err)
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// src/utils/sleep.ts
|
|
1082
|
+
function sleep(ms) {
|
|
1083
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
// src/utils/string.ts
|
|
1087
|
+
function truncate(s, max) {
|
|
1088
|
+
return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// src/utils/tool-wire-compact.ts
|
|
1092
|
+
var TOOL_DESCRIPTION_MAX_CHARS = 640;
|
|
1093
|
+
var SCHEMA_DESCRIPTION_MAX_CHARS = 180;
|
|
1094
|
+
var compactCache = /* @__PURE__ */ new WeakMap();
|
|
1095
|
+
function compactToolDefinitionForWire(tool, opts = {}) {
|
|
1096
|
+
const useDefaultOptions = opts.descriptionMaxChars === void 0 && opts.schemaDescriptionMaxChars === void 0;
|
|
1097
|
+
if (useDefaultOptions && typeof tool === "object" && tool !== null) {
|
|
1098
|
+
const cached = compactCache.get(tool);
|
|
1099
|
+
if (cached) return cached;
|
|
1100
|
+
}
|
|
1101
|
+
const compact = {
|
|
1102
|
+
name: tool.name,
|
|
1103
|
+
description: compactDescription(
|
|
1104
|
+
tool.description ?? "",
|
|
1105
|
+
opts.descriptionMaxChars ?? TOOL_DESCRIPTION_MAX_CHARS
|
|
1106
|
+
),
|
|
1107
|
+
inputSchema: compactSchemaDescriptions(
|
|
1108
|
+
tool.inputSchema,
|
|
1109
|
+
opts.schemaDescriptionMaxChars ?? SCHEMA_DESCRIPTION_MAX_CHARS
|
|
1110
|
+
)
|
|
1111
|
+
};
|
|
1112
|
+
if (useDefaultOptions && typeof tool === "object" && tool !== null) {
|
|
1113
|
+
compactCache.set(tool, compact);
|
|
1114
|
+
}
|
|
1115
|
+
return compact;
|
|
1116
|
+
}
|
|
1117
|
+
function compactSchemaDescriptions(schema, maxDescriptionChars = SCHEMA_DESCRIPTION_MAX_CHARS) {
|
|
1118
|
+
const compact = compactSchemaNode(schema, maxDescriptionChars);
|
|
1119
|
+
return isRecord(compact) ? compact : { type: "object", properties: {} };
|
|
1120
|
+
}
|
|
1121
|
+
function compactSchemaNode(node, maxDescriptionChars) {
|
|
1122
|
+
if (Array.isArray(node)) {
|
|
1123
|
+
return node.map((item) => compactSchemaNode(item, maxDescriptionChars));
|
|
1124
|
+
}
|
|
1125
|
+
if (!isRecord(node)) return node;
|
|
1126
|
+
const out = {};
|
|
1127
|
+
for (const [key, value] of Object.entries(node)) {
|
|
1128
|
+
if (key === "description" && typeof value === "string") {
|
|
1129
|
+
out[key] = compactDescription(value, maxDescriptionChars);
|
|
1130
|
+
} else {
|
|
1131
|
+
out[key] = compactSchemaNode(value, maxDescriptionChars);
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
return out;
|
|
1135
|
+
}
|
|
1136
|
+
function compactDescription(text, maxChars) {
|
|
1137
|
+
const normalized = text.replace(/\s+/g, " ").trim();
|
|
1138
|
+
if (normalized.length <= maxChars) return normalized;
|
|
1139
|
+
if (maxChars <= 20) return normalized.slice(0, maxChars);
|
|
1140
|
+
const hardLimit = maxChars - 12;
|
|
1141
|
+
const boundary = findSemanticBoundary(normalized, hardLimit);
|
|
1142
|
+
const head = normalized.slice(0, boundary > 0 ? boundary : hardLimit).trimEnd();
|
|
1143
|
+
return `${head} ...`;
|
|
1144
|
+
}
|
|
1145
|
+
function findSemanticBoundary(text, limit) {
|
|
1146
|
+
const punctuation = Math.max(
|
|
1147
|
+
text.lastIndexOf(". ", limit),
|
|
1148
|
+
text.lastIndexOf("; ", limit),
|
|
1149
|
+
text.lastIndexOf(": ", limit)
|
|
1150
|
+
);
|
|
1151
|
+
if (punctuation >= Math.floor(limit * 0.45)) return punctuation + 1;
|
|
1152
|
+
const comma = text.lastIndexOf(", ", limit);
|
|
1153
|
+
if (comma >= Math.floor(limit * 0.6)) return comma + 1;
|
|
1154
|
+
const space = text.lastIndexOf(" ", limit);
|
|
1155
|
+
return space >= Math.floor(limit * 0.6) ? space : limit;
|
|
1156
|
+
}
|
|
1157
|
+
function isRecord(value) {
|
|
1158
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
// src/utils/token-estimate.ts
|
|
1162
|
+
var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
|
|
1163
|
+
var CALIBRATION_GLOBAL_KEY = "__global__";
|
|
1164
|
+
var _cals = /* @__PURE__ */ new Map();
|
|
1165
|
+
function calState(key) {
|
|
1166
|
+
let state = _cals.get(key);
|
|
1167
|
+
if (!state) {
|
|
1168
|
+
state = { ratio: 1, count: 0, prevEst: 0 };
|
|
1169
|
+
_cals.set(key, state);
|
|
1170
|
+
}
|
|
1171
|
+
return state;
|
|
1172
|
+
}
|
|
1173
|
+
var MIN_SAMPLES_FOR_CALIBRATION = 3;
|
|
1174
|
+
var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
|
|
1175
|
+
var ESTIMATE_CACHE_MAX_SIZE = 1e4;
|
|
1176
|
+
function getCachedEstimate(key, compute) {
|
|
1177
|
+
const existing = ESTIMATE_CACHE.get(key);
|
|
1178
|
+
if (existing !== void 0) return existing;
|
|
1179
|
+
if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {
|
|
1180
|
+
for (const k of ESTIMATE_CACHE.keys()) {
|
|
1181
|
+
if (ESTIMATE_CACHE.size <= Math.floor(ESTIMATE_CACHE_MAX_SIZE / 2)) break;
|
|
1182
|
+
ESTIMATE_CACHE.delete(k);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
const estimate = compute(key);
|
|
1186
|
+
ESTIMATE_CACHE.set(key, estimate);
|
|
1187
|
+
return estimate;
|
|
1188
|
+
}
|
|
1189
|
+
function estimateToolInputTokens(input) {
|
|
1190
|
+
if (typeof input === "string") return RoughTokenEstimate(input);
|
|
1191
|
+
if (input === null || typeof input !== "object") {
|
|
1192
|
+
return RoughTokenEstimate(String(input));
|
|
1193
|
+
}
|
|
1194
|
+
return getCachedEstimate(JSON.stringify(input), (key) => RoughTokenEstimate(key));
|
|
1195
|
+
}
|
|
1196
|
+
function estimateToolResultTokens(content) {
|
|
1197
|
+
if (typeof content === "string") return RoughTokenEstimate(content);
|
|
1198
|
+
return getCachedEstimate(JSON.stringify(content), (key) => RoughTokenEstimate(key));
|
|
1199
|
+
}
|
|
1200
|
+
function estimateTextTokens(text) {
|
|
1201
|
+
return RoughTokenEstimate(text);
|
|
1202
|
+
}
|
|
1203
|
+
function computeMessageTokens(msg) {
|
|
1204
|
+
if (typeof msg.content === "string") return estimateTextTokens(msg.content);
|
|
1205
|
+
let total = 0;
|
|
1206
|
+
for (const b of msg.content) {
|
|
1207
|
+
if (b.type === "text") total += estimateTextTokens(b.text);
|
|
1208
|
+
else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
|
|
1209
|
+
else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
|
|
1210
|
+
else total += RoughTokenEstimate(JSON.stringify(b));
|
|
1211
|
+
}
|
|
768
1212
|
return total;
|
|
769
1213
|
}
|
|
770
1214
|
function estimateMessageTokens(messages) {
|
|
@@ -781,7 +1225,8 @@ function estimateMessageTokens(messages) {
|
|
|
781
1225
|
function estimateToolDefTokens(tool) {
|
|
782
1226
|
const cached = tool._estDefTokens;
|
|
783
1227
|
if (typeof cached === "number" && cached > 0) return cached;
|
|
784
|
-
|
|
1228
|
+
const compact = compactToolDefinitionForWire(tool);
|
|
1229
|
+
return RoughTokenEstimate(tool.name) + RoughTokenEstimate(compact.description) + RoughTokenEstimate(JSON.stringify(compact.inputSchema));
|
|
785
1230
|
}
|
|
786
1231
|
function estimateRequestTokens(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
|
|
787
1232
|
let messagesTokens = 0;
|
|
@@ -858,406 +1303,702 @@ function estimateRequestTokensCalibrated(messages, systemPrompt, tools, calibrat
|
|
|
858
1303
|
return result;
|
|
859
1304
|
}
|
|
860
1305
|
|
|
861
|
-
// src/utils/
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
1306
|
+
// src/utils/tool-output-serializer.ts
|
|
1307
|
+
var DEFAULT_LIST_LIMIT = 500;
|
|
1308
|
+
var LOG_ENTRY_LIMIT = 200;
|
|
1309
|
+
var INLINE_LIMIT = 240;
|
|
1310
|
+
var GREP_FILE_LIMIT = 80;
|
|
1311
|
+
var GREP_MATCHES_PER_FILE = 3;
|
|
1312
|
+
var DIFF_INLINE_LINE_LIMIT = 260;
|
|
1313
|
+
var DIFF_HUNK_LIMIT = 8;
|
|
1314
|
+
var DIFF_HUNK_CONTEXT = 14;
|
|
1315
|
+
function createToolOutputSerializer(opts = {}) {
|
|
1316
|
+
const capBytes = opts.perIterationOutputCapBytes ?? 1e5;
|
|
1317
|
+
function serialize(value, context = {}) {
|
|
1318
|
+
if (typeof value === "string") return value;
|
|
1319
|
+
if (value === null || value === void 0) return "";
|
|
1320
|
+
if (typeof value === "object") {
|
|
1321
|
+
if (Array.isArray(value)) return value.map((item) => serialize(item)).join("\n");
|
|
1322
|
+
if (context.toolName) {
|
|
1323
|
+
const compact = renderToolObject(context.toolName, value, context.input);
|
|
1324
|
+
if (compact !== void 0) return compact;
|
|
1325
|
+
return renderGenericToolObject(context.toolName, value);
|
|
1326
|
+
}
|
|
1327
|
+
if ("text" in value) {
|
|
1328
|
+
const t = value.text;
|
|
1329
|
+
return typeof t === "string" ? t : JSON.stringify(value, null, 2);
|
|
1330
|
+
}
|
|
1331
|
+
try {
|
|
1332
|
+
return JSON.stringify(value, null, 2);
|
|
1333
|
+
} catch {
|
|
1334
|
+
return String(value);
|
|
1335
|
+
}
|
|
875
1336
|
}
|
|
1337
|
+
return String(value);
|
|
876
1338
|
}
|
|
877
|
-
|
|
878
|
-
if (
|
|
879
|
-
|
|
880
|
-
path: path19 || "<root>",
|
|
881
|
-
message: `expected ${schema.type}, got ${describeType(value)}`
|
|
882
|
-
});
|
|
883
|
-
return;
|
|
1339
|
+
function enforceCap(text, remainingBudget) {
|
|
1340
|
+
if (remainingBudget <= 0) {
|
|
1341
|
+
return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
|
|
884
1342
|
}
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
for (const req of schema.required ?? []) {
|
|
889
|
-
if (!(req in obj)) {
|
|
890
|
-
errors.push({ path: joinPath(path19, req), message: "required property missing" });
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
if (schema.properties) {
|
|
894
|
-
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
895
|
-
if (key in obj) {
|
|
896
|
-
walk(obj[key], subSchema, joinPath(path19, key), errors);
|
|
897
|
-
}
|
|
898
|
-
}
|
|
1343
|
+
const textBytes = Buffer.byteLength(text, "utf8");
|
|
1344
|
+
if (textBytes <= remainingBudget) {
|
|
1345
|
+
return { text, newBudget: remainingBudget - textBytes };
|
|
899
1346
|
}
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
1347
|
+
const marker = `
|
|
1348
|
+
\u2026[truncated ${textBytes - remainingBudget} bytes]\u2026
|
|
1349
|
+
`;
|
|
1350
|
+
const markerBytes = Buffer.byteLength(marker, "utf8");
|
|
1351
|
+
const available = remainingBudget - markerBytes;
|
|
1352
|
+
if (available <= 0) {
|
|
1353
|
+
return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
|
|
904
1354
|
}
|
|
1355
|
+
const half = Math.floor(available / 2);
|
|
1356
|
+
const first = text.slice(0, half);
|
|
1357
|
+
const second = text.slice(text.length - half);
|
|
1358
|
+
return { text: `${first}${marker}${second}`, newBudget: 0 };
|
|
905
1359
|
}
|
|
1360
|
+
return { serialize, enforceCap, capBytes };
|
|
906
1361
|
}
|
|
907
|
-
function
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
return true;
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
function isPlainObject(v) {
|
|
928
|
-
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
929
|
-
}
|
|
930
|
-
function describeType(v) {
|
|
931
|
-
if (v === null) return "null";
|
|
932
|
-
if (Array.isArray(v)) return "array";
|
|
933
|
-
return typeof v;
|
|
934
|
-
}
|
|
935
|
-
function joinPath(parent, key) {
|
|
936
|
-
if (!parent) return key;
|
|
937
|
-
return `${parent}.${key}`;
|
|
938
|
-
}
|
|
939
|
-
function deepEqual(a, b) {
|
|
940
|
-
if (a === b) return true;
|
|
941
|
-
if (typeof a !== typeof b) return false;
|
|
942
|
-
if (a === null || b === null) return a === b;
|
|
943
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
944
|
-
return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
|
|
945
|
-
}
|
|
946
|
-
if (typeof a === "object" && typeof b === "object") {
|
|
947
|
-
const ak = Object.keys(a);
|
|
948
|
-
const bk = Object.keys(b);
|
|
949
|
-
if (ak.length !== bk.length) return false;
|
|
950
|
-
return ak.every(
|
|
951
|
-
(k) => deepEqual(a[k], b[k])
|
|
952
|
-
);
|
|
953
|
-
}
|
|
954
|
-
return false;
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
// src/utils/regex-guard.ts
|
|
958
|
-
var MAX_PATTERN_LEN = 512;
|
|
959
|
-
var DANGEROUS_PATTERNS = [
|
|
960
|
-
/(\([^)]*[+*][^)]*\))[+*]/,
|
|
961
|
-
// (a+)+, (.*)+, etc
|
|
962
|
-
/(\(\?:[^)]*[+*][^)]*\))[+*]/
|
|
963
|
-
// same, with non-capturing group
|
|
964
|
-
];
|
|
965
|
-
function compileUserRegex(pattern, flags) {
|
|
966
|
-
if (typeof pattern !== "string") {
|
|
967
|
-
return { ok: false, reason: "pattern must be a string" };
|
|
968
|
-
}
|
|
969
|
-
if (pattern.length === 0) {
|
|
970
|
-
return { ok: false, reason: "pattern is empty" };
|
|
971
|
-
}
|
|
972
|
-
if (pattern.length > MAX_PATTERN_LEN) {
|
|
973
|
-
return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };
|
|
974
|
-
}
|
|
975
|
-
for (const rx of DANGEROUS_PATTERNS) {
|
|
976
|
-
if (rx.test(pattern)) {
|
|
977
|
-
return {
|
|
978
|
-
ok: false,
|
|
979
|
-
reason: "pattern looks vulnerable to catastrophic backtracking \u2014 rewrite without nested quantifiers"
|
|
980
|
-
};
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
try {
|
|
984
|
-
return { ok: true, regex: new RegExp(pattern, flags) };
|
|
985
|
-
} catch (err) {
|
|
986
|
-
return {
|
|
987
|
-
ok: false,
|
|
988
|
-
reason: err instanceof Error ? err.message : "invalid regex"
|
|
989
|
-
};
|
|
1362
|
+
function renderToolObject(toolName, obj, input) {
|
|
1363
|
+
if (toolName === "read" && typeof obj["text"] === "string") {
|
|
1364
|
+
return joinSections([
|
|
1365
|
+
renderHeader(
|
|
1366
|
+
`read: ${stringFromInput(input, "path") ?? stringField(obj, "path") ?? "<unknown>"}`,
|
|
1367
|
+
{
|
|
1368
|
+
offset: numberFromInput(input, "offset"),
|
|
1369
|
+
limit: numberFromInput(input, "limit"),
|
|
1370
|
+
total_lines: obj["total_lines"],
|
|
1371
|
+
encoding: obj["encoding"],
|
|
1372
|
+
truncated: obj["truncated"],
|
|
1373
|
+
cached: obj["cached"],
|
|
1374
|
+
note: obj["note"]
|
|
1375
|
+
}
|
|
1376
|
+
),
|
|
1377
|
+
obj["text"]
|
|
1378
|
+
]);
|
|
990
1379
|
}
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1380
|
+
if (toolName === "grep" && Array.isArray(obj["matches"])) {
|
|
1381
|
+
const matches = stringArrayField(obj, "matches");
|
|
1382
|
+
return joinSections([
|
|
1383
|
+
renderHeader(`grep: ${stringFromInput(input, "pattern") ?? "<pattern>"}`, {
|
|
1384
|
+
path: stringFromInput(input, "path"),
|
|
1385
|
+
glob: stringFromInput(input, "glob"),
|
|
1386
|
+
mode: stringFromInput(input, "output_mode"),
|
|
1387
|
+
count: obj["count"],
|
|
1388
|
+
shown: matches.length,
|
|
1389
|
+
truncated: obj["truncated"],
|
|
1390
|
+
used: obj["used"]
|
|
1391
|
+
}),
|
|
1392
|
+
renderGrepMatches(matches, stringFromInput(input, "output_mode"))
|
|
1393
|
+
]);
|
|
998
1394
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
i++;
|
|
1014
|
-
}
|
|
1015
|
-
} else if (c === "?") {
|
|
1016
|
-
re += "[^/\\\\]";
|
|
1017
|
-
i++;
|
|
1018
|
-
} else if (c === "[") {
|
|
1019
|
-
let cls = "[";
|
|
1020
|
-
i++;
|
|
1021
|
-
if (pat[i] === "!" || pat[i] === "^") {
|
|
1022
|
-
cls += "^";
|
|
1023
|
-
i++;
|
|
1024
|
-
}
|
|
1025
|
-
while (i < pat.length && pat[i] !== "]") {
|
|
1026
|
-
const ch = pat[i] ?? "";
|
|
1027
|
-
if (ch === "\\") cls += "\\\\";
|
|
1028
|
-
else if (ch === "]" || ch === "^") cls += `\\${ch}`;
|
|
1029
|
-
else cls += ch;
|
|
1030
|
-
i++;
|
|
1031
|
-
}
|
|
1032
|
-
cls += "]";
|
|
1033
|
-
re += cls;
|
|
1034
|
-
i++;
|
|
1035
|
-
} else {
|
|
1036
|
-
re += c.replace(/[.+^${}()|\\]/g, "\\$&");
|
|
1037
|
-
i++;
|
|
1038
|
-
}
|
|
1395
|
+
if (toolName === "patch" && Array.isArray(obj["files"])) {
|
|
1396
|
+
const files = stringArrayField(obj, "files");
|
|
1397
|
+
return joinSections([
|
|
1398
|
+
renderHeader("patch", {
|
|
1399
|
+
applied: obj["applied"],
|
|
1400
|
+
rejected: obj["rejected"],
|
|
1401
|
+
files: files.length,
|
|
1402
|
+
dry_run: obj["dry_run"]
|
|
1403
|
+
}),
|
|
1404
|
+
typeof obj["message"] === "string" ? `message:
|
|
1405
|
+
${obj["message"]}` : void 0,
|
|
1406
|
+
files.length > 0 ? `files:
|
|
1407
|
+
${renderStringList(files)}` : void 0
|
|
1408
|
+
]);
|
|
1039
1409
|
}
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
if (!isGlob(pattern)) return [pattern];
|
|
1050
|
-
const results = /* @__PURE__ */ new Set();
|
|
1051
|
-
const abs = isAbsolute(pattern);
|
|
1052
|
-
const base = abs ? baseDir(pattern) : baseDir(pattern);
|
|
1053
|
-
const relPat = base === "." ? pattern : pattern.slice(base.length + 1);
|
|
1054
|
-
async function walk3(dir, pat) {
|
|
1055
|
-
let entries;
|
|
1056
|
-
try {
|
|
1057
|
-
entries = await fsp2.readdir(dir);
|
|
1058
|
-
} catch {
|
|
1059
|
-
return;
|
|
1060
|
-
}
|
|
1061
|
-
const firstGlob = pat.search(/[*?[[]/);
|
|
1062
|
-
if (firstGlob < 0) {
|
|
1063
|
-
const re = globToRegex(pat);
|
|
1064
|
-
for (const e of entries) {
|
|
1065
|
-
if (re.test(e)) {
|
|
1066
|
-
const full = `${dir}${SEP}${e}`;
|
|
1067
|
-
results.add(abs ? resolve(full) : full);
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
return;
|
|
1071
|
-
}
|
|
1072
|
-
const before = pat.slice(0, firstGlob);
|
|
1073
|
-
const rest = pat.slice(firstGlob);
|
|
1074
|
-
if (before.endsWith("**")) {
|
|
1075
|
-
await walk3(dir, rest);
|
|
1076
|
-
for (const e of entries) {
|
|
1077
|
-
const full = `${dir}${SEP}${e}`;
|
|
1078
|
-
try {
|
|
1079
|
-
const stat6 = await fsp2.stat(full);
|
|
1080
|
-
if (stat6.isDirectory()) await walk3(full, rest);
|
|
1081
|
-
} catch {
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
} else if (before === "") {
|
|
1085
|
-
const re = globToRegex(rest);
|
|
1086
|
-
for (const e of entries) {
|
|
1087
|
-
if (re.test(e)) {
|
|
1088
|
-
const full = `${dir}${SEP}${e}`;
|
|
1089
|
-
results.add(abs ? resolve(full) : full);
|
|
1410
|
+
if (toolName === "glob" && Array.isArray(obj["files"])) {
|
|
1411
|
+
const files = stringArrayField(obj, "files");
|
|
1412
|
+
return joinSections([
|
|
1413
|
+
renderHeader(
|
|
1414
|
+
`${toolName}: ${stringFromInput(input, "pattern") ?? stringFromInput(input, "files") ?? stringFromInput(input, "path") ?? ""}`.trim(),
|
|
1415
|
+
{
|
|
1416
|
+
path: stringFromInput(input, "path"),
|
|
1417
|
+
files: files.length,
|
|
1418
|
+
truncated: obj["truncated"]
|
|
1090
1419
|
}
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1420
|
+
),
|
|
1421
|
+
renderStringList(files, "(no files)")
|
|
1422
|
+
]);
|
|
1423
|
+
}
|
|
1424
|
+
if (toolName === "tree" && typeof obj["tree"] === "string") {
|
|
1425
|
+
return joinSections([
|
|
1426
|
+
renderHeader(
|
|
1427
|
+
`tree: ${stringField(obj, "path") ?? stringFromInput(input, "path") ?? "<cwd>"}`,
|
|
1428
|
+
{
|
|
1429
|
+
total_files: obj["total_files"],
|
|
1430
|
+
total_dirs: obj["total_dirs"],
|
|
1431
|
+
truncated: obj["truncated"]
|
|
1100
1432
|
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1433
|
+
),
|
|
1434
|
+
obj["tree"]
|
|
1435
|
+
]);
|
|
1103
1436
|
}
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
const stack = [];
|
|
1116
|
-
let inString = false;
|
|
1117
|
-
let escaped = false;
|
|
1118
|
-
let sawKey = false;
|
|
1119
|
-
let prevSig = "";
|
|
1120
|
-
let contentEnd = 0;
|
|
1121
|
-
let stringBraceDepth = 0;
|
|
1122
|
-
for (let i = 0; i < s.length; i++) {
|
|
1123
|
-
const ch = expectDefined(s[i]);
|
|
1124
|
-
if (inString) {
|
|
1125
|
-
contentEnd = i + 1;
|
|
1126
|
-
if (escaped) {
|
|
1127
|
-
escaped = false;
|
|
1128
|
-
continue;
|
|
1129
|
-
}
|
|
1130
|
-
if (ch === "\\") {
|
|
1131
|
-
escaped = true;
|
|
1132
|
-
continue;
|
|
1133
|
-
}
|
|
1134
|
-
if (ch === '"') {
|
|
1135
|
-
inString = false;
|
|
1136
|
-
prevSig = '"';
|
|
1137
|
-
stringBraceDepth = 0;
|
|
1138
|
-
continue;
|
|
1139
|
-
}
|
|
1140
|
-
if (ch === "{") stringBraceDepth++;
|
|
1141
|
-
else if (ch === "}" && stringBraceDepth > 0) stringBraceDepth--;
|
|
1142
|
-
continue;
|
|
1143
|
-
}
|
|
1144
|
-
if (ch === " " || ch === " " || ch === "\n" || ch === "\r") continue;
|
|
1145
|
-
contentEnd = i + 1;
|
|
1146
|
-
if (ch === '"') {
|
|
1147
|
-
inString = true;
|
|
1148
|
-
sawKey = true;
|
|
1149
|
-
stringBraceDepth = 0;
|
|
1150
|
-
prevSig = '"';
|
|
1151
|
-
} else if (ch === "{" || ch === "[") {
|
|
1152
|
-
stack.push(ch);
|
|
1153
|
-
prevSig = ch;
|
|
1154
|
-
} else if (ch === "}" || ch === "]") {
|
|
1155
|
-
stack.pop();
|
|
1156
|
-
prevSig = ch;
|
|
1157
|
-
} else {
|
|
1158
|
-
prevSig = ch;
|
|
1159
|
-
}
|
|
1437
|
+
if (toolName === "fetch" && typeof obj["content"] === "string") {
|
|
1438
|
+
return joinSections([
|
|
1439
|
+
renderHeader(
|
|
1440
|
+
`fetch: ${stringField(obj, "url") ?? stringFromInput(input, "url") ?? "<url>"}`,
|
|
1441
|
+
{
|
|
1442
|
+
status: obj["status"],
|
|
1443
|
+
content_type: obj["content_type"]
|
|
1444
|
+
}
|
|
1445
|
+
),
|
|
1446
|
+
obj["content"]
|
|
1447
|
+
]);
|
|
1160
1448
|
}
|
|
1161
|
-
if (
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1449
|
+
if (toolName === "replace" && Array.isArray(obj["results"])) {
|
|
1450
|
+
const results = obj["results"].filter(isRecord2);
|
|
1451
|
+
const sections = [
|
|
1452
|
+
renderHeader("replace", {
|
|
1453
|
+
files_modified: obj["files_modified"],
|
|
1454
|
+
total_replacements: obj["total_replacements"],
|
|
1455
|
+
dry_run: obj["dry_run"]
|
|
1456
|
+
})
|
|
1457
|
+
];
|
|
1458
|
+
for (const r of results.slice(0, DEFAULT_LIST_LIMIT)) {
|
|
1459
|
+
sections.push(
|
|
1460
|
+
joinSections([
|
|
1461
|
+
renderHeader(`file: ${stringField(r, "path") ?? "<unknown>"}`, {
|
|
1462
|
+
replacements: r["replacements"]
|
|
1463
|
+
}),
|
|
1464
|
+
typeof r["diff"] === "string" ? r["diff"] : void 0
|
|
1465
|
+
])
|
|
1466
|
+
);
|
|
1467
|
+
}
|
|
1468
|
+
if (results.length > DEFAULT_LIST_LIMIT) {
|
|
1469
|
+
sections.push(`[serializer omitted ${results.length - DEFAULT_LIST_LIMIT} result item(s)]`);
|
|
1470
|
+
}
|
|
1471
|
+
return joinSections(sections);
|
|
1472
|
+
}
|
|
1473
|
+
if (typeof obj["diff"] === "string") {
|
|
1474
|
+
const diff = obj["diff"];
|
|
1475
|
+
return joinSections([
|
|
1476
|
+
renderHeader(toolName, {
|
|
1477
|
+
path: obj["path"],
|
|
1478
|
+
replacements: obj["replacements"],
|
|
1479
|
+
bytes_written: obj["bytes_written"],
|
|
1480
|
+
created: obj["created"],
|
|
1481
|
+
note: obj["note"],
|
|
1482
|
+
files: Array.isArray(obj["files"]) ? obj["files"].length : void 0,
|
|
1483
|
+
truncated: obj["truncated"],
|
|
1484
|
+
mode: obj["mode"]
|
|
1485
|
+
}),
|
|
1486
|
+
compactDiff(diff)
|
|
1487
|
+
]);
|
|
1173
1488
|
}
|
|
1174
|
-
|
|
1175
|
-
|
|
1489
|
+
if (toolName === "test" && typeof obj["output"] === "string") {
|
|
1490
|
+
return renderTestOutput(obj, input);
|
|
1176
1491
|
}
|
|
1177
|
-
if (
|
|
1178
|
-
|
|
1179
|
-
if (tryParse(patched).ok) result = patched;
|
|
1492
|
+
if ((toolName === "typecheck" || toolName === "lint" || toolName === "format") && typeof obj["output"] === "string") {
|
|
1493
|
+
return renderVerifierOutput(toolName, obj, input);
|
|
1180
1494
|
}
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
var VALID_ESCAPE = /* @__PURE__ */ new Set(['"', "\\", "/", "b", "f", "n", "r", "t", "u"]);
|
|
1184
|
-
function endsWithInvalidEscape(str) {
|
|
1185
|
-
const last = str[str.length - 1];
|
|
1186
|
-
if (str[str.length - 2] !== "\\" || last === void 0) return false;
|
|
1187
|
-
if (VALID_ESCAPE.has(last)) return false;
|
|
1188
|
-
let backslashes = 0;
|
|
1189
|
-
for (let k = str.length - 2; k >= 0 && str[k] === "\\"; k--) backslashes++;
|
|
1190
|
-
return backslashes % 2 === 1;
|
|
1191
|
-
}
|
|
1192
|
-
function tryParse(s) {
|
|
1193
|
-
try {
|
|
1194
|
-
return { ok: true, value: JSON.parse(s) };
|
|
1195
|
-
} catch {
|
|
1196
|
-
return { ok: false };
|
|
1495
|
+
if (hasCommandOutputShape(obj)) {
|
|
1496
|
+
return renderCommandOutput(toolName, obj, input);
|
|
1197
1497
|
}
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1498
|
+
if (toolName === "json" && typeof obj["formatted"] === "string") {
|
|
1499
|
+
return joinSections([
|
|
1500
|
+
renderHeader("json", {
|
|
1501
|
+
type: obj["type"],
|
|
1502
|
+
keys: Array.isArray(obj["keys"]) ? obj["keys"].length : void 0,
|
|
1503
|
+
query: stringFromInput(input, "query"),
|
|
1504
|
+
error: obj["error"]
|
|
1505
|
+
}),
|
|
1506
|
+
obj["formatted"]
|
|
1507
|
+
]);
|
|
1205
1508
|
}
|
|
1206
|
-
|
|
1207
|
-
const
|
|
1208
|
-
|
|
1509
|
+
if (toolName === "logs" && Array.isArray(obj["entries"])) {
|
|
1510
|
+
const entries = obj["entries"].filter(isRecord2);
|
|
1511
|
+
const lines = entries.slice(0, LOG_ENTRY_LIMIT).map((entry) => {
|
|
1512
|
+
const ts = stringField(entry, "timestamp") ?? "";
|
|
1513
|
+
const level = stringField(entry, "level") ?? "info";
|
|
1514
|
+
const message = stringField(entry, "message") ?? "";
|
|
1515
|
+
const source = stringField(entry, "source");
|
|
1516
|
+
return [ts, level, source, message].filter(Boolean).join(" ");
|
|
1517
|
+
});
|
|
1518
|
+
if (entries.length > LOG_ENTRY_LIMIT) {
|
|
1519
|
+
lines.push(`[serializer omitted ${entries.length - LOG_ENTRY_LIMIT} log entry item(s)]`);
|
|
1520
|
+
}
|
|
1521
|
+
return joinSections([
|
|
1522
|
+
renderHeader(`logs: ${stringField(obj, "source") ?? "<source>"}`, {
|
|
1523
|
+
total: obj["total"],
|
|
1524
|
+
shown: Math.min(entries.length, LOG_ENTRY_LIMIT),
|
|
1525
|
+
truncated: obj["truncated"],
|
|
1526
|
+
stream_mode: obj["stream_mode"]
|
|
1527
|
+
}),
|
|
1528
|
+
lines.length > 0 ? lines.join("\n") : "(no log entries)"
|
|
1529
|
+
]);
|
|
1209
1530
|
}
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1531
|
+
if (toolName === "audit" && Array.isArray(obj["vulnerabilities"])) {
|
|
1532
|
+
const vulns = obj["vulnerabilities"].filter(isRecord2);
|
|
1533
|
+
const lines = vulns.slice(0, DEFAULT_LIST_LIMIT).map((v) => {
|
|
1534
|
+
const severity = stringField(v, "severity") ?? "unknown";
|
|
1535
|
+
const pkg = stringField(v, "package") ?? "<package>";
|
|
1536
|
+
const title = stringField(v, "title") ?? "";
|
|
1537
|
+
const url = stringField(v, "url");
|
|
1538
|
+
return [severity, pkg, title, url].filter(Boolean).join(" | ");
|
|
1539
|
+
});
|
|
1540
|
+
if (vulns.length > DEFAULT_LIST_LIMIT) {
|
|
1541
|
+
lines.push(`[serializer omitted ${vulns.length - DEFAULT_LIST_LIMIT} vulnerability item(s)]`);
|
|
1542
|
+
}
|
|
1543
|
+
return joinSections([
|
|
1544
|
+
renderHeader("audit", {
|
|
1545
|
+
exit_code: obj["exit_code"],
|
|
1546
|
+
total: obj["total"],
|
|
1547
|
+
summary: obj["summary"],
|
|
1548
|
+
truncated: obj["truncated"]
|
|
1549
|
+
}),
|
|
1550
|
+
lines.length > 0 ? lines.join("\n") : stringField(obj, "output")
|
|
1551
|
+
]);
|
|
1216
1552
|
}
|
|
1217
|
-
|
|
1218
|
-
const
|
|
1219
|
-
|
|
1553
|
+
if (toolName === "outdated" && Array.isArray(obj["packages"])) {
|
|
1554
|
+
const packages = obj["packages"].filter(isRecord2);
|
|
1555
|
+
const lines = packages.slice(0, DEFAULT_LIST_LIMIT).map(
|
|
1556
|
+
(p) => [
|
|
1557
|
+
stringField(p, "name") ?? "<package>",
|
|
1558
|
+
`current=${stringField(p, "current") ?? "unknown"}`,
|
|
1559
|
+
`wanted=${stringField(p, "wanted") ?? "unknown"}`,
|
|
1560
|
+
`latest=${stringField(p, "latest") ?? "unknown"}`,
|
|
1561
|
+
stringField(p, "type")
|
|
1562
|
+
].filter(Boolean).join(" | ")
|
|
1563
|
+
);
|
|
1564
|
+
if (packages.length > DEFAULT_LIST_LIMIT) {
|
|
1565
|
+
lines.push(`[serializer omitted ${packages.length - DEFAULT_LIST_LIMIT} package item(s)]`);
|
|
1566
|
+
}
|
|
1567
|
+
return joinSections([
|
|
1568
|
+
renderHeader("outdated", {
|
|
1569
|
+
exit_code: obj["exit_code"],
|
|
1570
|
+
total: obj["total"],
|
|
1571
|
+
truncated: obj["truncated"]
|
|
1572
|
+
}),
|
|
1573
|
+
lines.length > 0 ? lines.join("\n") : stringField(obj, "output")
|
|
1574
|
+
]);
|
|
1220
1575
|
}
|
|
1221
|
-
return
|
|
1222
|
-
...base,
|
|
1223
|
-
// Overlay scalar fields win when explicitly provided; otherwise keep base.
|
|
1224
|
-
...stripUndefined({
|
|
1225
|
-
id: overlay.id,
|
|
1226
|
-
name: overlay.name,
|
|
1227
|
-
npm: overlay.npm,
|
|
1228
|
-
api: overlay.api,
|
|
1229
|
-
env: overlay.env,
|
|
1230
|
-
doc: overlay.doc
|
|
1231
|
-
}),
|
|
1232
|
-
models
|
|
1233
|
-
};
|
|
1576
|
+
return void 0;
|
|
1234
1577
|
}
|
|
1235
|
-
function
|
|
1236
|
-
const
|
|
1237
|
-
|
|
1238
|
-
|
|
1578
|
+
function renderTestOutput(obj, input) {
|
|
1579
|
+
const exitCode = numberField(obj, "exit_code") ?? 0;
|
|
1580
|
+
const failed = numberField(obj, "failed") ?? 0;
|
|
1581
|
+
const output = stringField(obj, "output") ?? "";
|
|
1582
|
+
const header = renderHeader(`test: ${stringField(obj, "runner") ?? "runner"}`, {
|
|
1583
|
+
exit_code: obj["exit_code"],
|
|
1584
|
+
tests_run: obj["tests_run"],
|
|
1585
|
+
passed: obj["passed"],
|
|
1586
|
+
failed: obj["failed"],
|
|
1587
|
+
duration_ms: obj["duration_ms"],
|
|
1588
|
+
truncated: obj["truncated"],
|
|
1589
|
+
files: inputListSummary(input, "files"),
|
|
1590
|
+
grep: stringFromInput(input, "grep")
|
|
1591
|
+
});
|
|
1592
|
+
if (exitCode === 0 && failed === 0) {
|
|
1593
|
+
return joinSections([
|
|
1594
|
+
header,
|
|
1595
|
+
joinSections([
|
|
1596
|
+
"report:",
|
|
1597
|
+
`status=passed`,
|
|
1598
|
+
`tests_run=${obj["tests_run"] ?? 0}`,
|
|
1599
|
+
`passed=${obj["passed"] ?? 0}`,
|
|
1600
|
+
`failed=${obj["failed"] ?? 0}`,
|
|
1601
|
+
`duration_ms=${obj["duration_ms"] ?? 0}`,
|
|
1602
|
+
extractSpoolNote(output)
|
|
1603
|
+
])
|
|
1604
|
+
]);
|
|
1239
1605
|
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1606
|
+
return joinSections([
|
|
1607
|
+
header,
|
|
1608
|
+
`error_context:
|
|
1609
|
+
${compactFailureOutput(output || "(no runner output)")}`
|
|
1610
|
+
]);
|
|
1611
|
+
}
|
|
1612
|
+
function renderVerifierOutput(toolName, obj, input) {
|
|
1613
|
+
const exitCode = numberField(obj, "exit_code") ?? 0;
|
|
1614
|
+
const errors = numberField(obj, "errors") ?? 0;
|
|
1615
|
+
const warnings = numberField(obj, "warnings") ?? 0;
|
|
1616
|
+
const output = stringField(obj, "output") ?? "";
|
|
1617
|
+
const changed = numberField(obj, "files_changed") ?? 0;
|
|
1618
|
+
const header = renderHeader(toolName, {
|
|
1619
|
+
exit_code: obj["exit_code"],
|
|
1620
|
+
errors: obj["errors"],
|
|
1621
|
+
warnings: obj["warnings"],
|
|
1622
|
+
files_checked: obj["files_checked"],
|
|
1623
|
+
files_changed: obj["files_changed"],
|
|
1624
|
+
fix_applied: obj["fix_applied"],
|
|
1625
|
+
fixer: obj["fixer"],
|
|
1626
|
+
linter: obj["linter"],
|
|
1627
|
+
project: obj["project"],
|
|
1628
|
+
truncated: obj["truncated"],
|
|
1629
|
+
files: inputListSummary(input, "files"),
|
|
1630
|
+
cwd: stringFromInput(input, "cwd")
|
|
1631
|
+
});
|
|
1632
|
+
if (exitCode === 0 && errors === 0 && (toolName !== "format" || changed === 0)) {
|
|
1633
|
+
return joinSections([
|
|
1634
|
+
header,
|
|
1635
|
+
joinSections([
|
|
1636
|
+
"report:",
|
|
1637
|
+
"status=passed",
|
|
1638
|
+
`errors=${errors}`,
|
|
1639
|
+
`warnings=${warnings}`,
|
|
1640
|
+
toolName === "format" ? `files_changed=${changed}` : void 0,
|
|
1641
|
+
extractSpoolNote(output)
|
|
1642
|
+
])
|
|
1643
|
+
]);
|
|
1242
1644
|
}
|
|
1243
|
-
if (
|
|
1244
|
-
|
|
1645
|
+
if (exitCode === 0 && toolName === "format") {
|
|
1646
|
+
return joinSections([
|
|
1647
|
+
header,
|
|
1648
|
+
joinSections([
|
|
1649
|
+
"report:",
|
|
1650
|
+
"status=changed",
|
|
1651
|
+
`files_changed=${changed}`,
|
|
1652
|
+
extractSpoolNote(output)
|
|
1653
|
+
])
|
|
1654
|
+
]);
|
|
1245
1655
|
}
|
|
1246
|
-
return
|
|
1656
|
+
return joinSections([
|
|
1657
|
+
header,
|
|
1658
|
+
`error_context:
|
|
1659
|
+
${compactFailureOutput(output || "(no verifier output)")}`
|
|
1660
|
+
]);
|
|
1247
1661
|
}
|
|
1248
|
-
function
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1662
|
+
function renderGrepMatches(matches, mode) {
|
|
1663
|
+
if (matches.length === 0) return "(no matches)";
|
|
1664
|
+
if (mode === "files_with_matches") return renderStringList(matches, "(no files)");
|
|
1665
|
+
if (mode === "count") return renderStringList(matches, "(no counts)");
|
|
1666
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1667
|
+
const passthrough = [];
|
|
1668
|
+
for (const match of matches) {
|
|
1669
|
+
const parsed = parseGrepContentLine(match);
|
|
1670
|
+
if (!parsed) {
|
|
1671
|
+
passthrough.push(match);
|
|
1672
|
+
continue;
|
|
1673
|
+
}
|
|
1674
|
+
const list = groups.get(parsed.file) ?? [];
|
|
1675
|
+
list.push(`${parsed.line}:${parsed.text}`);
|
|
1676
|
+
groups.set(parsed.file, list);
|
|
1252
1677
|
}
|
|
1253
|
-
return
|
|
1678
|
+
if (groups.size === 0) return renderStringList(matches, "(no matches)");
|
|
1679
|
+
const sections = [];
|
|
1680
|
+
let fileIndex = 0;
|
|
1681
|
+
for (const [file, lines] of groups) {
|
|
1682
|
+
fileIndex++;
|
|
1683
|
+
if (fileIndex > GREP_FILE_LIMIT) break;
|
|
1684
|
+
const shown = lines.slice(0, GREP_MATCHES_PER_FILE);
|
|
1685
|
+
sections.push(
|
|
1686
|
+
`${file} (${lines.length} match(es), showing ${shown.length})
|
|
1687
|
+
${shown.join("\n")}`
|
|
1688
|
+
);
|
|
1689
|
+
}
|
|
1690
|
+
if (groups.size > GREP_FILE_LIMIT) {
|
|
1691
|
+
sections.push(`[serializer omitted ${groups.size - GREP_FILE_LIMIT} file group(s)]`);
|
|
1692
|
+
}
|
|
1693
|
+
if (passthrough.length > 0) {
|
|
1694
|
+
sections.push(`ungrouped:
|
|
1695
|
+
${renderStringList(passthrough, "", 50)}`);
|
|
1696
|
+
}
|
|
1697
|
+
return sections.join("\n");
|
|
1698
|
+
}
|
|
1699
|
+
function parseGrepContentLine(line) {
|
|
1700
|
+
const match = /^(.+?):(\d+):(.*)$/.exec(line);
|
|
1701
|
+
if (!match?.[1] || !match[2]) return void 0;
|
|
1702
|
+
return { file: match[1], line: match[2], text: match[3] ?? "" };
|
|
1703
|
+
}
|
|
1704
|
+
function compactDiff(diff) {
|
|
1705
|
+
const lines = diff.split(/\r?\n/);
|
|
1706
|
+
if (lines.length <= DIFF_INLINE_LINE_LIMIT) return diff;
|
|
1707
|
+
const fileCount = Math.max(
|
|
1708
|
+
new Set(
|
|
1709
|
+
lines.map(
|
|
1710
|
+
(line) => /^diff --git\s+a\/(.+?)\s+b\//.exec(line)?.[1] ?? /^---\s+(.+)/.exec(line)?.[1]
|
|
1711
|
+
).filter(Boolean)
|
|
1712
|
+
).size,
|
|
1713
|
+
0
|
|
1714
|
+
);
|
|
1715
|
+
const hunks = lines.filter((line) => line.startsWith("@@")).length;
|
|
1716
|
+
const added = lines.filter((line) => line.startsWith("+") && !line.startsWith("+++")).length;
|
|
1717
|
+
const removed = lines.filter((line) => line.startsWith("-") && !line.startsWith("---")).length;
|
|
1718
|
+
const selected = /* @__PURE__ */ new Set();
|
|
1719
|
+
let hunkCount = 0;
|
|
1720
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1721
|
+
const line = lines[i] ?? "";
|
|
1722
|
+
if (line.startsWith("diff --git") || line.startsWith("--- ") || line.startsWith("+++ ")) {
|
|
1723
|
+
selected.add(i);
|
|
1724
|
+
continue;
|
|
1725
|
+
}
|
|
1726
|
+
if (!line.startsWith("@@")) continue;
|
|
1727
|
+
if (hunkCount >= DIFF_HUNK_LIMIT) continue;
|
|
1728
|
+
hunkCount++;
|
|
1729
|
+
for (let j = i; j <= Math.min(lines.length - 1, i + DIFF_HUNK_CONTEXT); j++) {
|
|
1730
|
+
selected.add(j);
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
if (selected.size === 0) {
|
|
1734
|
+
return joinSections([
|
|
1735
|
+
renderHeader("diff_summary", {
|
|
1736
|
+
files: fileCount,
|
|
1737
|
+
hunks,
|
|
1738
|
+
added,
|
|
1739
|
+
removed,
|
|
1740
|
+
lines: lines.length
|
|
1741
|
+
}),
|
|
1742
|
+
lines.slice(0, DIFF_INLINE_LINE_LIMIT).join("\n"),
|
|
1743
|
+
`[serializer omitted ${Math.max(0, lines.length - DIFF_INLINE_LINE_LIMIT)} diff line(s)]`
|
|
1744
|
+
]);
|
|
1745
|
+
}
|
|
1746
|
+
const excerpt = [];
|
|
1747
|
+
let previous = -1;
|
|
1748
|
+
for (const index of [...selected].sort((a, b) => a - b)) {
|
|
1749
|
+
if (index > previous + 1) {
|
|
1750
|
+
const omitted = previous === -1 ? index : index - previous - 1;
|
|
1751
|
+
excerpt.push(`[serializer omitted ${omitted} diff line(s)]`);
|
|
1752
|
+
}
|
|
1753
|
+
excerpt.push(lines[index] ?? "");
|
|
1754
|
+
previous = index;
|
|
1755
|
+
}
|
|
1756
|
+
const trailing = lines.length - previous - 1;
|
|
1757
|
+
if (trailing > 0) excerpt.push(`[serializer omitted ${trailing} trailing diff line(s)]`);
|
|
1758
|
+
return joinSections([
|
|
1759
|
+
renderHeader("diff_summary", {
|
|
1760
|
+
files: fileCount,
|
|
1761
|
+
hunks,
|
|
1762
|
+
shown_hunks: Math.min(hunks, DIFF_HUNK_LIMIT),
|
|
1763
|
+
added,
|
|
1764
|
+
removed,
|
|
1765
|
+
lines: lines.length
|
|
1766
|
+
}),
|
|
1767
|
+
excerpt.join("\n")
|
|
1768
|
+
]);
|
|
1769
|
+
}
|
|
1770
|
+
function compactFailureOutput(output) {
|
|
1771
|
+
const lines = output.split(/\r?\n/);
|
|
1772
|
+
if (lines.length <= 260) return output.trimEnd();
|
|
1773
|
+
const selected = /* @__PURE__ */ new Set();
|
|
1774
|
+
const marker = /\b(fail|failed|failure|error|exception|assertionerror|expected|received|actual|timeout|stack)\b/i;
|
|
1775
|
+
let markerHits = 0;
|
|
1776
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1777
|
+
if (!marker.test(lines[i] ?? "")) continue;
|
|
1778
|
+
markerHits++;
|
|
1779
|
+
for (let j = Math.max(0, i - 4); j <= Math.min(lines.length - 1, i + 10); j++) {
|
|
1780
|
+
selected.add(j);
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
if (markerHits === 0) {
|
|
1784
|
+
return lines.slice(-220).join("\n").trimEnd();
|
|
1785
|
+
}
|
|
1786
|
+
const ordered = [...selected].sort((a, b) => a - b);
|
|
1787
|
+
const out = [];
|
|
1788
|
+
let previous = -1;
|
|
1789
|
+
for (const index of ordered) {
|
|
1790
|
+
if (index > previous + 1) {
|
|
1791
|
+
const omitted = previous === -1 ? index : index - previous - 1;
|
|
1792
|
+
out.push(`[serializer omitted ${omitted} line(s)]`);
|
|
1793
|
+
}
|
|
1794
|
+
out.push(lines[index] ?? "");
|
|
1795
|
+
previous = index;
|
|
1796
|
+
}
|
|
1797
|
+
return out.join("\n").trimEnd();
|
|
1798
|
+
}
|
|
1799
|
+
function extractSpoolNote(output) {
|
|
1800
|
+
return output.split(/\r?\n/).find((line) => line.startsWith("[output truncated") && line.includes("full"));
|
|
1801
|
+
}
|
|
1802
|
+
function hasCommandOutputShape(obj) {
|
|
1803
|
+
return typeof obj["stdout"] === "string" || typeof obj["stderr"] === "string" || typeof obj["output"] === "string" || typeof obj["exitCode"] === "number" || typeof obj["exit_code"] === "number";
|
|
1804
|
+
}
|
|
1805
|
+
function renderCommandOutput(toolName, obj, input) {
|
|
1806
|
+
const command = stringField(obj, "command") ?? stringFromInput(input, "command");
|
|
1807
|
+
const args = stringArrayField(obj, "args");
|
|
1808
|
+
const commandLine = command ? [command, ...args].join(" ") : void 0;
|
|
1809
|
+
const output = stringField(obj, "output");
|
|
1810
|
+
const stdout = stringField(obj, "stdout");
|
|
1811
|
+
const stderr = stringField(obj, "stderr");
|
|
1812
|
+
return joinSections([
|
|
1813
|
+
renderHeader(commandLine ? `${toolName}: ${commandLine}` : toolName, {
|
|
1814
|
+
exit_code: obj["exit_code"] ?? obj["exitCode"],
|
|
1815
|
+
timed_out: obj["timed_out"],
|
|
1816
|
+
pid: obj["pid"],
|
|
1817
|
+
allowed: obj["allowed"],
|
|
1818
|
+
truncated: obj["truncated"],
|
|
1819
|
+
runner: obj["runner"],
|
|
1820
|
+
linter: obj["linter"],
|
|
1821
|
+
fixer: obj["fixer"],
|
|
1822
|
+
project: obj["project"],
|
|
1823
|
+
tests_run: obj["tests_run"],
|
|
1824
|
+
passed: obj["passed"],
|
|
1825
|
+
failed: obj["failed"],
|
|
1826
|
+
duration_ms: obj["duration_ms"],
|
|
1827
|
+
errors: obj["errors"],
|
|
1828
|
+
warnings: obj["warnings"],
|
|
1829
|
+
files_checked: obj["files_checked"],
|
|
1830
|
+
files_changed: obj["files_changed"],
|
|
1831
|
+
fix_applied: obj["fix_applied"]
|
|
1832
|
+
}),
|
|
1833
|
+
stringField(obj, "error") ? `error:
|
|
1834
|
+
${stringField(obj, "error")}` : void 0,
|
|
1835
|
+
output ? `output:
|
|
1836
|
+
${output}` : void 0,
|
|
1837
|
+
stdout ? `stdout:
|
|
1838
|
+
${stdout}` : void 0,
|
|
1839
|
+
stderr ? `stderr:
|
|
1840
|
+
${stderr}` : void 0
|
|
1841
|
+
]);
|
|
1842
|
+
}
|
|
1843
|
+
function renderGenericToolObject(toolName, obj) {
|
|
1844
|
+
const scalars = {};
|
|
1845
|
+
const blocks = [];
|
|
1846
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1847
|
+
if (value === void 0) continue;
|
|
1848
|
+
if (isScalar(value)) {
|
|
1849
|
+
const inline = String(value);
|
|
1850
|
+
if (inline.length <= INLINE_LIMIT && !inline.includes("\n")) {
|
|
1851
|
+
scalars[key] = value;
|
|
1852
|
+
} else {
|
|
1853
|
+
blocks.push(`${key}:
|
|
1854
|
+
${inline}`);
|
|
1855
|
+
}
|
|
1856
|
+
continue;
|
|
1857
|
+
}
|
|
1858
|
+
if (Array.isArray(value)) {
|
|
1859
|
+
if (value.every((item) => typeof item === "string")) {
|
|
1860
|
+
blocks.push(`${key}:
|
|
1861
|
+
${renderStringList(value)}`);
|
|
1862
|
+
} else {
|
|
1863
|
+
blocks.push(`${key}:
|
|
1864
|
+
${renderUnknownList(value)}`);
|
|
1865
|
+
}
|
|
1866
|
+
continue;
|
|
1867
|
+
}
|
|
1868
|
+
blocks.push(`${key}: ${clipInline(oneLineJson(value))}`);
|
|
1869
|
+
}
|
|
1870
|
+
return joinSections([renderHeader(toolName, scalars), ...blocks]);
|
|
1254
1871
|
}
|
|
1255
|
-
function
|
|
1256
|
-
const
|
|
1257
|
-
|
|
1258
|
-
|
|
1872
|
+
function renderHeader(label, fields) {
|
|
1873
|
+
const parts = Object.entries(fields).filter(([, value]) => value !== void 0 && value !== null && value !== "").map(([key, value]) => `${key}=${clipInline(formatInlineValue(value))}`);
|
|
1874
|
+
return parts.length > 0 ? `${label} (${parts.join(" ")})` : label;
|
|
1875
|
+
}
|
|
1876
|
+
function renderStringList(items, empty = "", limit = DEFAULT_LIST_LIMIT) {
|
|
1877
|
+
if (items.length === 0) return empty;
|
|
1878
|
+
const shown = items.slice(0, limit);
|
|
1879
|
+
const omitted = items.length - shown.length;
|
|
1880
|
+
return [
|
|
1881
|
+
...shown,
|
|
1882
|
+
...omitted > 0 ? [`[serializer omitted ${omitted} item(s); narrow the request for more]`] : []
|
|
1883
|
+
].join("\n");
|
|
1884
|
+
}
|
|
1885
|
+
function renderUnknownList(items, limit = DEFAULT_LIST_LIMIT) {
|
|
1886
|
+
const shown = items.slice(0, limit).map((item) => clipInline(oneLineJson(item), 1e3));
|
|
1887
|
+
const omitted = items.length - shown.length;
|
|
1888
|
+
if (omitted > 0)
|
|
1889
|
+
shown.push(`[serializer omitted ${omitted} item(s); narrow the request for more]`);
|
|
1890
|
+
return shown.join("\n");
|
|
1891
|
+
}
|
|
1892
|
+
function joinSections(sections) {
|
|
1893
|
+
return sections.map((section) => typeof section === "string" ? section.trimEnd() : void 0).filter((section) => !!section).join("\n");
|
|
1894
|
+
}
|
|
1895
|
+
function formatInlineValue(value) {
|
|
1896
|
+
if (Array.isArray(value)) return `[${value.map(formatInlineValue).join(",")}]`;
|
|
1897
|
+
if (isScalar(value)) return String(value);
|
|
1898
|
+
return oneLineJson(value);
|
|
1899
|
+
}
|
|
1900
|
+
function clipInline(value, max = INLINE_LIMIT) {
|
|
1901
|
+
const compact = value.replace(/\s+/g, " ").trim();
|
|
1902
|
+
return compact.length <= max ? compact : `${compact.slice(0, max - 15)}...(${compact.length} chars)`;
|
|
1903
|
+
}
|
|
1904
|
+
function oneLineJson(value) {
|
|
1905
|
+
try {
|
|
1906
|
+
return JSON.stringify(value);
|
|
1907
|
+
} catch {
|
|
1908
|
+
return String(value);
|
|
1259
1909
|
}
|
|
1260
|
-
|
|
1910
|
+
}
|
|
1911
|
+
function stringField(obj, key) {
|
|
1912
|
+
const value = obj[key];
|
|
1913
|
+
return typeof value === "string" ? value : void 0;
|
|
1914
|
+
}
|
|
1915
|
+
function numberField(obj, key) {
|
|
1916
|
+
const value = obj[key];
|
|
1917
|
+
return typeof value === "number" ? value : void 0;
|
|
1918
|
+
}
|
|
1919
|
+
function stringArrayField(obj, key) {
|
|
1920
|
+
const value = obj[key];
|
|
1921
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
1922
|
+
}
|
|
1923
|
+
function stringFromInput(input, key) {
|
|
1924
|
+
if (!isRecord2(input)) return void 0;
|
|
1925
|
+
const value = input[key];
|
|
1926
|
+
return typeof value === "string" ? value : void 0;
|
|
1927
|
+
}
|
|
1928
|
+
function numberFromInput(input, key) {
|
|
1929
|
+
if (!isRecord2(input)) return void 0;
|
|
1930
|
+
const value = input[key];
|
|
1931
|
+
return typeof value === "number" ? value : void 0;
|
|
1932
|
+
}
|
|
1933
|
+
function inputListSummary(input, key) {
|
|
1934
|
+
if (!isRecord2(input)) return void 0;
|
|
1935
|
+
const value = input[key];
|
|
1936
|
+
if (typeof value === "string") return value;
|
|
1937
|
+
if (Array.isArray(value)) return value.filter((item) => typeof item === "string").join(",");
|
|
1938
|
+
return void 0;
|
|
1939
|
+
}
|
|
1940
|
+
function isRecord2(value) {
|
|
1941
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
1942
|
+
}
|
|
1943
|
+
function isScalar(value) {
|
|
1944
|
+
return value === null || ["string", "number", "boolean"].includes(typeof value);
|
|
1945
|
+
}
|
|
1946
|
+
function projectHash(absRoot) {
|
|
1947
|
+
return createHash("sha256").update(path4.resolve(absRoot)).digest("hex").slice(0, 12);
|
|
1948
|
+
}
|
|
1949
|
+
function projectSlug(absRoot) {
|
|
1950
|
+
const base = slugify(path4.basename(absRoot));
|
|
1951
|
+
const hash = createHash("sha256").update(path4.resolve(absRoot)).digest("hex").slice(0, 6);
|
|
1952
|
+
return `${base}-${hash}`;
|
|
1953
|
+
}
|
|
1954
|
+
function slugify(name) {
|
|
1955
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
|
|
1956
|
+
}
|
|
1957
|
+
function wstackGlobalRoot() {
|
|
1958
|
+
const fromEnv = process.env["WRONGSTACK_HOME"];
|
|
1959
|
+
if (fromEnv && fromEnv.trim().length > 0) return path4.resolve(fromEnv);
|
|
1960
|
+
return path4.join(os.homedir(), ".wrongstack");
|
|
1961
|
+
}
|
|
1962
|
+
function resolveWstackPaths(opts) {
|
|
1963
|
+
const globalRoot = opts.globalRoot ?? (opts.userHome ? path4.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
|
|
1964
|
+
const hash = projectHash(opts.projectRoot);
|
|
1965
|
+
const slug = projectSlug(opts.projectRoot);
|
|
1966
|
+
const projectDir = path4.join(globalRoot, "projects", slug);
|
|
1967
|
+
return {
|
|
1968
|
+
globalRoot,
|
|
1969
|
+
configDir: globalRoot,
|
|
1970
|
+
globalConfig: path4.join(globalRoot, "config.json"),
|
|
1971
|
+
secretsKey: path4.join(globalRoot, ".key"),
|
|
1972
|
+
globalMemory: path4.join(globalRoot, "memory.md"),
|
|
1973
|
+
globalSkills: path4.join(globalRoot, "skills"),
|
|
1974
|
+
globalPrompts: path4.join(globalRoot, "prompts"),
|
|
1975
|
+
cacheDir: path4.join(globalRoot, "cache"),
|
|
1976
|
+
modelsCache: path4.join(globalRoot, "cache", "models.dev.json"),
|
|
1977
|
+
modelsOverlayCache: path4.join(globalRoot, "cache", "models-overlay.json"),
|
|
1978
|
+
historyFile: path4.join(globalRoot, "history"),
|
|
1979
|
+
logFile: path4.join(globalRoot, "logs", "wrongstack.log"),
|
|
1980
|
+
projectDir,
|
|
1981
|
+
projectCodebaseIndex: path4.join(projectDir, "codebase-index"),
|
|
1982
|
+
projectMemory: path4.join(projectDir, "memory.md"),
|
|
1983
|
+
projectSessions: path4.join(projectDir, "sessions"),
|
|
1984
|
+
projectTrust: path4.join(projectDir, "trust.json"),
|
|
1985
|
+
projectMeta: path4.join(projectDir, "meta.json"),
|
|
1986
|
+
projectLocalConfig: path4.join(projectDir, "config.local.json"),
|
|
1987
|
+
inProjectConfig: path4.join(opts.projectRoot, ".wrongstack", "config.json"),
|
|
1988
|
+
inProjectAgentsFile: path4.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
|
|
1989
|
+
inProjectSkills: path4.join(opts.projectRoot, ".wrongstack", "skills"),
|
|
1990
|
+
inProjectWorktrees: path4.join(opts.projectRoot, ".wrongstack", "worktrees"),
|
|
1991
|
+
projectHash: hash,
|
|
1992
|
+
projectSlug: slug,
|
|
1993
|
+
projectGoal: path4.join(projectDir, "goal.json"),
|
|
1994
|
+
projectSpecs: path4.join(projectDir, "specs"),
|
|
1995
|
+
projectTaskGraphs: path4.join(projectDir, "task-graphs"),
|
|
1996
|
+
projectSddSession: path4.join(projectDir, "sdd-session.json"),
|
|
1997
|
+
projectPlan: path4.join(projectDir, "plan.json"),
|
|
1998
|
+
projectAutophase: path4.join(projectDir, "autophase"),
|
|
1999
|
+
syncConfig: path4.join(globalRoot, "sync.json"),
|
|
2000
|
+
projectStatus: (projectHash2) => path4.join(globalRoot, "projects", projectHash2, "status.json")
|
|
2001
|
+
};
|
|
1261
2002
|
}
|
|
1262
2003
|
|
|
1263
2004
|
// src/storage/session-store.ts
|
|
@@ -1339,11 +2080,11 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1339
2080
|
}
|
|
1340
2081
|
/** Absolute path to the session index file. */
|
|
1341
2082
|
get indexFile() {
|
|
1342
|
-
return
|
|
2083
|
+
return path4.join(this.dir, "_index.jsonl");
|
|
1343
2084
|
}
|
|
1344
2085
|
/** Join session ID to its absolute path within the store directory. */
|
|
1345
2086
|
sessionPath(id, ext) {
|
|
1346
|
-
return
|
|
2087
|
+
return path4.join(this.dir, `${id}${ext}`);
|
|
1347
2088
|
}
|
|
1348
2089
|
/**
|
|
1349
2090
|
* Ensure the directory implied by the session ID exists. When the ID
|
|
@@ -1351,7 +2092,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1351
2092
|
* subdirectory so sessions group naturally by day.
|
|
1352
2093
|
*/
|
|
1353
2094
|
async ensureShardDir(id) {
|
|
1354
|
-
const dirPath =
|
|
2095
|
+
const dirPath = path4.dirname(path4.join(this.dir, id));
|
|
1355
2096
|
await ensureDir(dirPath);
|
|
1356
2097
|
return dirPath;
|
|
1357
2098
|
}
|
|
@@ -1359,7 +2100,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1359
2100
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1360
2101
|
const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt, meta.model ?? meta.provider);
|
|
1361
2102
|
const shardDir = await this.ensureShardDir(id);
|
|
1362
|
-
const file =
|
|
2103
|
+
const file = path4.join(shardDir, `${path4.basename(id)}.jsonl`);
|
|
1363
2104
|
const t0 = Date.now();
|
|
1364
2105
|
let handle;
|
|
1365
2106
|
try {
|
|
@@ -1421,7 +2162,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1421
2162
|
// Shard directory (sessions/<date>/) — must match create() so the
|
|
1422
2163
|
// .summary.json sidecar lands next to the JSONL instead of the
|
|
1423
2164
|
// sessions root (where summaryFor() would never find it).
|
|
1424
|
-
dir:
|
|
2165
|
+
dir: path4.dirname(file),
|
|
1425
2166
|
filePath: file,
|
|
1426
2167
|
secretScrubber: this.secretScrubber,
|
|
1427
2168
|
onClose: (s) => this.appendToIndex(s)
|
|
@@ -1645,7 +2386,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1645
2386
|
continue;
|
|
1646
2387
|
if (entry.isDirectory()) {
|
|
1647
2388
|
const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
|
|
1648
|
-
ids.push(...await this.collectSessionIds(
|
|
2389
|
+
ids.push(...await this.collectSessionIds(path4.join(dir, entry.name), childPrefix, depth + 1));
|
|
1649
2390
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
1650
2391
|
if (entry.name === "_index.jsonl") continue;
|
|
1651
2392
|
const base = entry.name.replace(/\.jsonl$/, "");
|
|
@@ -1696,14 +2437,14 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1696
2437
|
async deleteSession(id) {
|
|
1697
2438
|
const jsonlPath = this.sessionPath(id, ".jsonl");
|
|
1698
2439
|
const summaryPath = this.sessionPath(id, ".summary.json");
|
|
1699
|
-
const shardDir =
|
|
1700
|
-
const base =
|
|
1701
|
-
const sessDir =
|
|
2440
|
+
const shardDir = path4.dirname(path4.join(this.dir, id));
|
|
2441
|
+
const base = path4.basename(id);
|
|
2442
|
+
const sessDir = path4.join(shardDir, base);
|
|
1702
2443
|
const deletions = [
|
|
1703
2444
|
fsp2.unlink(jsonlPath),
|
|
1704
2445
|
fsp2.unlink(summaryPath),
|
|
1705
|
-
fsp2.unlink(
|
|
1706
|
-
fsp2.unlink(
|
|
2446
|
+
fsp2.unlink(path4.join(shardDir, `${base}.plan.json`)),
|
|
2447
|
+
fsp2.unlink(path4.join(shardDir, `${base}.todos.json`))
|
|
1707
2448
|
];
|
|
1708
2449
|
const results = await Promise.allSettled(deletions);
|
|
1709
2450
|
for (const r of results) {
|
|
@@ -1739,14 +2480,14 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1739
2480
|
let deleted = 0;
|
|
1740
2481
|
let activeSessionId = null;
|
|
1741
2482
|
try {
|
|
1742
|
-
const raw = await fsp2.readFile(
|
|
2483
|
+
const raw = await fsp2.readFile(path4.join(this.dir, "active.json"), "utf8");
|
|
1743
2484
|
const active = JSON.parse(raw);
|
|
1744
2485
|
activeSessionId = active.sessionId ?? null;
|
|
1745
2486
|
} catch {
|
|
1746
2487
|
}
|
|
1747
2488
|
const isPrunableJsonl = (name) => name.endsWith(".jsonl") && name !== "_index.jsonl" && name !== "_mailbox.jsonl" && !name.endsWith(".replay.jsonl") && !name.endsWith(".audit.jsonl");
|
|
1748
2489
|
const pruneFile = async (dir, name, prefix) => {
|
|
1749
|
-
const jsonlPath =
|
|
2490
|
+
const jsonlPath = path4.join(dir, name);
|
|
1750
2491
|
try {
|
|
1751
2492
|
const stat6 = await fsp2.stat(jsonlPath);
|
|
1752
2493
|
if (stat6.mtimeMs >= cutoff) return;
|
|
@@ -1766,7 +2507,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1766
2507
|
continue;
|
|
1767
2508
|
}
|
|
1768
2509
|
if (!entry.isDirectory()) continue;
|
|
1769
|
-
const dateDir =
|
|
2510
|
+
const dateDir = path4.join(this.dir, entry.name);
|
|
1770
2511
|
const files = await fsp2.readdir(dateDir, { withFileTypes: true }).catch(() => []);
|
|
1771
2512
|
for (const file of files) {
|
|
1772
2513
|
if (!file.isFile() || !isPrunableJsonl(file.name)) continue;
|
|
@@ -1778,7 +2519,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
1778
2519
|
}
|
|
1779
2520
|
for (const entry of entries) {
|
|
1780
2521
|
if (!entry.isDirectory()) continue;
|
|
1781
|
-
const dateDir =
|
|
2522
|
+
const dateDir = path4.join(this.dir, entry.name);
|
|
1782
2523
|
try {
|
|
1783
2524
|
const remaining = await fsp2.readdir(dateDir);
|
|
1784
2525
|
if (remaining.length === 0) {
|
|
@@ -1953,7 +2694,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1953
2694
|
this.meta = meta;
|
|
1954
2695
|
this.events = events;
|
|
1955
2696
|
this.resumed = opts.resumed ?? false;
|
|
1956
|
-
this.manifestFile = opts.dir ?
|
|
2697
|
+
this.manifestFile = opts.dir ? path4.join(opts.dir, `${path4.basename(id)}.summary.json`) : "";
|
|
1957
2698
|
this.filePath = opts.filePath ?? "";
|
|
1958
2699
|
this.secretScrubber = opts.secretScrubber;
|
|
1959
2700
|
this.onCloseCb = opts.onClose;
|
|
@@ -2457,7 +3198,7 @@ var QueueStore = class {
|
|
|
2457
3198
|
events;
|
|
2458
3199
|
traceId;
|
|
2459
3200
|
constructor(opts) {
|
|
2460
|
-
this.file =
|
|
3201
|
+
this.file = path4.join(opts.dir, "queue.json");
|
|
2461
3202
|
this.events = opts.events;
|
|
2462
3203
|
this.traceId = opts.traceId;
|
|
2463
3204
|
}
|
|
@@ -2644,7 +3385,7 @@ var DefaultAttachmentStore = class {
|
|
|
2644
3385
|
let data = input.data;
|
|
2645
3386
|
if (this.spoolDir && bytes >= this.spoolThreshold) {
|
|
2646
3387
|
await fsp2.mkdir(this.spoolDir, { recursive: true });
|
|
2647
|
-
spooledPath =
|
|
3388
|
+
spooledPath = path4.join(this.spoolDir, `${id}.bin`);
|
|
2648
3389
|
await atomicWrite(spooledPath, input.data, {
|
|
2649
3390
|
encoding: input.kind === "image" ? "base64" : "utf8"
|
|
2650
3391
|
});
|
|
@@ -2856,7 +3597,7 @@ var FileMemoryBackend = class {
|
|
|
2856
3597
|
}
|
|
2857
3598
|
async remember(scope, entry, filePath) {
|
|
2858
3599
|
const file = this.resolveFile(filePath, scope);
|
|
2859
|
-
await ensureDir(
|
|
3600
|
+
await ensureDir(path4.dirname(file));
|
|
2860
3601
|
let existing = "";
|
|
2861
3602
|
try {
|
|
2862
3603
|
existing = await fsp2.readFile(file, "utf8");
|
|
@@ -3472,9 +4213,9 @@ ${body.trim()}`);
|
|
|
3472
4213
|
if (!this.persistBackup || scope === "project-agents") return;
|
|
3473
4214
|
try {
|
|
3474
4215
|
const content = await this.backend.readAll(scope, this.files[scope]);
|
|
3475
|
-
const { writeFile:
|
|
3476
|
-
await
|
|
3477
|
-
await
|
|
4216
|
+
const { writeFile: writeFile5, mkdir: mkdir7 } = await import('fs/promises');
|
|
4217
|
+
await mkdir7(this.backupDir, { recursive: true });
|
|
4218
|
+
await writeFile5(`${this.backupDir}/${scope}.md`, content, "utf8");
|
|
3478
4219
|
} catch {
|
|
3479
4220
|
}
|
|
3480
4221
|
}
|
|
@@ -3824,7 +4565,7 @@ var DefaultSecretVault = class {
|
|
|
3824
4565
|
KEY_FILE_MAGIC.copy(keyFileBuf, 0);
|
|
3825
4566
|
keyFileBuf[KEY_FILE_MAGIC.length] = newVersion;
|
|
3826
4567
|
newKey.copy(keyFileBuf, KEY_FILE_MAGIC.length + 1);
|
|
3827
|
-
fs4.mkdirSync(
|
|
4568
|
+
fs4.mkdirSync(path4.dirname(this.keyFile), { recursive: true });
|
|
3828
4569
|
fs4.writeFileSync(this.keyFile, keyFileBuf, { mode: 384 });
|
|
3829
4570
|
checkKeyFilePermissions(this.keyFile);
|
|
3830
4571
|
this.key = newKey;
|
|
@@ -3872,7 +4613,7 @@ var DefaultSecretVault = class {
|
|
|
3872
4613
|
} catch (err) {
|
|
3873
4614
|
if (err.code !== "ENOENT") throw err;
|
|
3874
4615
|
}
|
|
3875
|
-
fs4.mkdirSync(
|
|
4616
|
+
fs4.mkdirSync(path4.dirname(this.keyFile), { recursive: true });
|
|
3876
4617
|
const key = randomBytes(KEY_BYTES);
|
|
3877
4618
|
try {
|
|
3878
4619
|
fs4.writeFileSync(this.keyFile, key, { mode: 384, flag: "wx" });
|
|
@@ -3962,7 +4703,7 @@ async function rewriteConfigEncrypted(configPath, vault, patch) {
|
|
|
3962
4703
|
}
|
|
3963
4704
|
const merged = deepMerge(current, patch ?? {});
|
|
3964
4705
|
const encrypted = encryptConfigSecrets(merged, vault);
|
|
3965
|
-
await fsp2.mkdir(
|
|
4706
|
+
await fsp2.mkdir(path4.dirname(configPath), { recursive: true });
|
|
3966
4707
|
await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
3967
4708
|
await restrictFilePermissions(configPath);
|
|
3968
4709
|
}
|
|
@@ -4592,7 +5333,7 @@ var RecoveryLock = class {
|
|
|
4592
5333
|
sessionStore;
|
|
4593
5334
|
probe;
|
|
4594
5335
|
constructor(opts) {
|
|
4595
|
-
this.file =
|
|
5336
|
+
this.file = path4.join(opts.dir, LOCK_FILE);
|
|
4596
5337
|
this.pid = opts.pid ?? process.pid;
|
|
4597
5338
|
this.hostname = opts.hostname ?? os.hostname();
|
|
4598
5339
|
this.maxAgeMs = opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
|
|
@@ -4653,7 +5394,7 @@ var RecoveryLock = class {
|
|
|
4653
5394
|
* null return before calling this.
|
|
4654
5395
|
*/
|
|
4655
5396
|
async write(sessionId) {
|
|
4656
|
-
await ensureDir(
|
|
5397
|
+
await ensureDir(path4.dirname(this.file));
|
|
4657
5398
|
const lock = {
|
|
4658
5399
|
v: 1,
|
|
4659
5400
|
sessionId,
|
|
@@ -6073,9 +6814,9 @@ function getInputString(input, key) {
|
|
|
6073
6814
|
function pathLooksInsideProject(rawPath, projectRoot) {
|
|
6074
6815
|
if (!projectRoot) return false;
|
|
6075
6816
|
if (rawPath === "~" || rawPath.startsWith("~/") || rawPath.startsWith("~\\")) return false;
|
|
6076
|
-
const resolved =
|
|
6077
|
-
const
|
|
6078
|
-
return !!
|
|
6817
|
+
const resolved = path4.resolve(projectRoot, rawPath);
|
|
6818
|
+
const relative3 = path4.relative(projectRoot, resolved);
|
|
6819
|
+
return !!relative3 && !relative3.startsWith("..") && !path4.isAbsolute(relative3);
|
|
6079
6820
|
}
|
|
6080
6821
|
function tokenizeShell(command) {
|
|
6081
6822
|
return command.match(/"[^"]*"|'[^']*'|\S+/g)?.map((token) => token.replace(/^['"]|['"]$/g, "")) ?? [];
|
|
@@ -6085,7 +6826,7 @@ function pathTokenIsOutsideProject(token, projectRoot) {
|
|
|
6085
6826
|
if (token === "/" || token === "~" || token === "." || token === "..") return token !== ".";
|
|
6086
6827
|
if (token.includes("*")) return true;
|
|
6087
6828
|
if (token.startsWith("..") || token.includes("../") || token.includes("..\\")) return true;
|
|
6088
|
-
if (
|
|
6829
|
+
if (path4.isAbsolute(token) || token.startsWith("~/")) return !pathLooksInsideProject(token, projectRoot);
|
|
6089
6830
|
return false;
|
|
6090
6831
|
}
|
|
6091
6832
|
function hasDangerousDeleteTarget(tokens, start, projectRoot) {
|
|
@@ -6480,9 +7221,12 @@ var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
|
|
|
6480
7221
|
const caps = tool.capabilities ?? [];
|
|
6481
7222
|
const hasAllowedCap = caps.some((c) => this.allowedCapabilities.includes(c));
|
|
6482
7223
|
const isMcp = _AutoApprovePermissionPolicy.isMcpTool(tool.name);
|
|
6483
|
-
const
|
|
7224
|
+
const dangerousNotAllowed = getDangerousCapabilities(tool).filter(
|
|
7225
|
+
(c) => !this.allowedCapabilities.includes(c)
|
|
7226
|
+
);
|
|
7227
|
+
const blocked = tool.permission === "deny" || isMcp || !hasAllowedCap || dangerousNotAllowed.length > 0;
|
|
6484
7228
|
if (blocked) {
|
|
6485
|
-
const reason = isMcp ? `MCP tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow it explicitly` : tool.permission === "deny" ? "tool default deny" : `tool lacks allowed capability (has: ${caps.join(", ") || "none"}, allowed: ${this.allowedCapabilities.join(", ")})`;
|
|
7229
|
+
const reason = isMcp ? `MCP tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow it explicitly` : tool.permission === "deny" ? "tool default deny" : dangerousNotAllowed.length > 0 ? `tool requires un-granted dangerous capability (needs: ${dangerousNotAllowed.join(", ")}, allowed: ${this.allowedCapabilities.join(", ")})` : `tool lacks allowed capability (has: ${caps.join(", ") || "none"}, allowed: ${this.allowedCapabilities.join(", ")})`;
|
|
6486
7230
|
return {
|
|
6487
7231
|
permission: "deny",
|
|
6488
7232
|
source: "subagent_guard",
|
|
@@ -6742,7 +7486,7 @@ var DefaultSkillLoader = class {
|
|
|
6742
7486
|
const entries = await fsp2.readdir(dir, { withFileTypes: true });
|
|
6743
7487
|
for (const e of entries) {
|
|
6744
7488
|
if (!e.isDirectory()) continue;
|
|
6745
|
-
const skillFile =
|
|
7489
|
+
const skillFile = path4.join(dir, e.name, "SKILL.md");
|
|
6746
7490
|
try {
|
|
6747
7491
|
const raw = await fsp2.readFile(skillFile, "utf8");
|
|
6748
7492
|
const meta = parseFrontmatter(raw);
|
|
@@ -6813,7 +7557,7 @@ var DefaultSkillLoader = class {
|
|
|
6813
7557
|
if (cached !== void 0) return cached;
|
|
6814
7558
|
const m = await this.find(name);
|
|
6815
7559
|
if (!m) throw new Error(`Skill "${name}" not found`);
|
|
6816
|
-
const savePath =
|
|
7560
|
+
const savePath = path4.join(path4.dirname(m.path), "SKILL.save.md");
|
|
6817
7561
|
let result;
|
|
6818
7562
|
try {
|
|
6819
7563
|
result = await fsp2.readFile(savePath, "utf8");
|
|
@@ -7127,8 +7871,8 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
|
|
|
7127
7871
|
});
|
|
7128
7872
|
await Promise.race([
|
|
7129
7873
|
drainPromise,
|
|
7130
|
-
new Promise((
|
|
7131
|
-
drainTimer = setTimeout(
|
|
7874
|
+
new Promise((resolve6) => {
|
|
7875
|
+
drainTimer = setTimeout(resolve6, STREAM_DRAIN_TIMEOUT_MS);
|
|
7132
7876
|
})
|
|
7133
7877
|
]);
|
|
7134
7878
|
} finally {
|
|
@@ -7234,26 +7978,29 @@ async function runProviderWithRetry(opts) {
|
|
|
7234
7978
|
description
|
|
7235
7979
|
});
|
|
7236
7980
|
}
|
|
7237
|
-
await new Promise((
|
|
7981
|
+
await new Promise((resolve6, reject) => {
|
|
7238
7982
|
let settled = false;
|
|
7983
|
+
const cleanup = () => {
|
|
7984
|
+
clearTimeout(t);
|
|
7985
|
+
signal.removeEventListener("abort", onAbort);
|
|
7986
|
+
};
|
|
7239
7987
|
const onAbort = () => {
|
|
7240
7988
|
if (settled) return;
|
|
7241
7989
|
settled = true;
|
|
7242
|
-
|
|
7990
|
+
cleanup();
|
|
7243
7991
|
reject(new Error("aborted"));
|
|
7244
7992
|
};
|
|
7245
7993
|
const t = setTimeout(() => {
|
|
7246
7994
|
if (settled) return;
|
|
7247
7995
|
settled = true;
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
resolve5();
|
|
7996
|
+
cleanup();
|
|
7997
|
+
resolve6();
|
|
7251
7998
|
}, delay);
|
|
7252
7999
|
if (signal.aborted) {
|
|
7253
8000
|
onAbort();
|
|
7254
8001
|
return;
|
|
7255
8002
|
}
|
|
7256
|
-
signal.addEventListener("abort", onAbort
|
|
8003
|
+
signal.addEventListener("abort", onAbort);
|
|
7257
8004
|
});
|
|
7258
8005
|
attempt++;
|
|
7259
8006
|
}
|
|
@@ -7312,25 +8059,26 @@ function findPreserveStart(messages, preserveK) {
|
|
|
7312
8059
|
preserveStart = i;
|
|
7313
8060
|
}
|
|
7314
8061
|
}
|
|
7315
|
-
let
|
|
7316
|
-
let
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
const
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
8062
|
+
let pairRepairIterations = 0;
|
|
8063
|
+
let pairRepairInnerIterations = 0;
|
|
8064
|
+
while (preserveStart > 0) {
|
|
8065
|
+
pairRepairIterations++;
|
|
8066
|
+
const first = messages[preserveStart];
|
|
8067
|
+
const prev = messages[preserveStart - 1];
|
|
8068
|
+
if (!first || !prev || first.role !== "user" || prev.role !== "assistant") break;
|
|
8069
|
+
if (typeof first.content === "string" || typeof prev.content === "string") break;
|
|
8070
|
+
const resultIds = /* @__PURE__ */ new Set();
|
|
8071
|
+
for (const block of first.content) {
|
|
8072
|
+
pairRepairInnerIterations++;
|
|
8073
|
+
if (block.type === "tool_result") resultIds.add(block.tool_use_id);
|
|
8074
|
+
}
|
|
8075
|
+
if (resultIds.size === 0) break;
|
|
8076
|
+
const hasMatchingUse = prev.content.some((block) => {
|
|
8077
|
+
pairRepairInnerIterations++;
|
|
8078
|
+
return block.type === "tool_use" && resultIds.has(block.id);
|
|
7324
8079
|
});
|
|
7325
|
-
if (
|
|
7326
|
-
|
|
7327
|
-
if (next && next.role === "user" && typeof next.content !== "string" && Array.isArray(next.content) && next.content.some((b) => {
|
|
7328
|
-
forwardWalkInnerIterations++;
|
|
7329
|
-
return b.type === "tool_result";
|
|
7330
|
-
})) {
|
|
7331
|
-
preserveStart = i + 1;
|
|
7332
|
-
}
|
|
7333
|
-
}
|
|
8080
|
+
if (!hasMatchingUse) break;
|
|
8081
|
+
preserveStart--;
|
|
7334
8082
|
}
|
|
7335
8083
|
if (compactionDebugEnabled()) {
|
|
7336
8084
|
console.log(
|
|
@@ -7340,9 +8088,9 @@ function findPreserveStart(messages, preserveK) {
|
|
|
7340
8088
|
messageCount: messages.length,
|
|
7341
8089
|
preserveK,
|
|
7342
8090
|
preserveStart,
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
8091
|
+
pairRepairIterations,
|
|
8092
|
+
pairRepairInnerIterations,
|
|
8093
|
+
pairRepairInnerPerOuter: pairRepairIterations > 0 ? pairRepairInnerIterations / pairRepairIterations : 0
|
|
7346
8094
|
})
|
|
7347
8095
|
);
|
|
7348
8096
|
}
|
|
@@ -7359,7 +8107,8 @@ function eliseOldToolResults(messages, opts) {
|
|
|
7359
8107
|
if (!msg || !Array.isArray(msg.content)) continue;
|
|
7360
8108
|
for (const b of msg.content) {
|
|
7361
8109
|
fastPathInnerIterations++;
|
|
7362
|
-
|
|
8110
|
+
const oversized = b.type === "tool_result" && estimateToolResultTokens(b.content) >= opts.eliseThreshold || b.type === "tool_use" && estimateToolInputTokens(b.input) >= opts.eliseThreshold;
|
|
8111
|
+
if (oversized) {
|
|
7363
8112
|
hasOversized = true;
|
|
7364
8113
|
break;
|
|
7365
8114
|
}
|
|
@@ -7393,6 +8142,13 @@ function eliseOldToolResults(messages, opts) {
|
|
|
7393
8142
|
}
|
|
7394
8143
|
const original = msg.content;
|
|
7395
8144
|
const newContent = original.map((b) => {
|
|
8145
|
+
if (b.type === "tool_use") {
|
|
8146
|
+
const tokens2 = estimateToolInputTokens(b.input);
|
|
8147
|
+
if (tokens2 < opts.eliseThreshold) return b;
|
|
8148
|
+
const elidedInput = summarizeToolUseInputElision(b, tokens2);
|
|
8149
|
+
saved += Math.max(0, tokens2 - estimateToolInputTokens(elidedInput));
|
|
8150
|
+
return { ...b, input: elidedInput };
|
|
8151
|
+
}
|
|
7396
8152
|
if (b.type !== "tool_result") return b;
|
|
7397
8153
|
const tokens = estimateToolResultTokens(b.content);
|
|
7398
8154
|
if (tokens < opts.eliseThreshold) return b;
|
|
@@ -7400,7 +8156,7 @@ function eliseOldToolResults(messages, opts) {
|
|
|
7400
8156
|
const elided = {
|
|
7401
8157
|
type: "tool_result",
|
|
7402
8158
|
tool_use_id: b.tool_use_id,
|
|
7403
|
-
content:
|
|
8159
|
+
content: summarizeToolResultElision(b, tokens),
|
|
7404
8160
|
is_error: b.is_error
|
|
7405
8161
|
};
|
|
7406
8162
|
return elided;
|
|
@@ -7440,6 +8196,65 @@ function eliseOldToolResults(messages, opts) {
|
|
|
7440
8196
|
});
|
|
7441
8197
|
return { messages: changed ? next : messages, saved, changed };
|
|
7442
8198
|
}
|
|
8199
|
+
function summarizeToolUseInputElision(block, tokens) {
|
|
8200
|
+
const fields = {};
|
|
8201
|
+
for (const [key, value] of Object.entries(block.input ?? {})) {
|
|
8202
|
+
fields[key] = summarizeToolUseInputValue(value);
|
|
8203
|
+
}
|
|
8204
|
+
return {
|
|
8205
|
+
__elided_tool_input: `~${tokens} tokens; original arguments are in the session log`,
|
|
8206
|
+
tool: block.name,
|
|
8207
|
+
fields
|
|
8208
|
+
};
|
|
8209
|
+
}
|
|
8210
|
+
function summarizeToolUseInputValue(value) {
|
|
8211
|
+
if (value === null || value === void 0) return value;
|
|
8212
|
+
if (typeof value === "number" || typeof value === "boolean") return value;
|
|
8213
|
+
if (typeof value === "string") {
|
|
8214
|
+
const oneLine = value.replace(/\s+/g, " ").trim();
|
|
8215
|
+
return oneLine.length <= 160 ? oneLine : `${oneLine.slice(0, 120)}...(${oneLine.length} chars)`;
|
|
8216
|
+
}
|
|
8217
|
+
if (Array.isArray(value)) {
|
|
8218
|
+
return `[array:${value.length}]`;
|
|
8219
|
+
}
|
|
8220
|
+
if (typeof value === "object") {
|
|
8221
|
+
const keys = Object.keys(value);
|
|
8222
|
+
return `[object:${keys.slice(0, 8).join(",")}${keys.length > 8 ? ",..." : ""}]`;
|
|
8223
|
+
}
|
|
8224
|
+
return String(value);
|
|
8225
|
+
}
|
|
8226
|
+
function summarizeToolResultElision(block, tokens) {
|
|
8227
|
+
const parts = [`elided: ~${tokens} tokens`];
|
|
8228
|
+
if (block.name) parts.push(`tool=${block.name}`);
|
|
8229
|
+
const files = extractPathHints(block.content).slice(0, 5);
|
|
8230
|
+
if (files.length > 0) parts.push(`files=${files.join(", ")}`);
|
|
8231
|
+
const error = firstErrorLine(block.content);
|
|
8232
|
+
if (error) parts.push(`error=${error}`);
|
|
8233
|
+
return `[${parts.join("; ")}]`;
|
|
8234
|
+
}
|
|
8235
|
+
function extractPathHints(content) {
|
|
8236
|
+
const text = typeof content === "string" ? content : JSON.stringify(content);
|
|
8237
|
+
const out = /* @__PURE__ */ new Set();
|
|
8238
|
+
const re = /(?:(?:[A-Za-z]:)?[./\\]?[\w@.-]+(?:[\\/][\w@(). -]+)+\.[A-Za-z0-9]{1,12})/g;
|
|
8239
|
+
for (const match of text.matchAll(re)) {
|
|
8240
|
+
const clean = match[0]?.replace(/\\/g, "/").replace(/^["'`]+|["'`),;:]+$/g, "");
|
|
8241
|
+
if (clean && clean.length <= 220) out.add(clean);
|
|
8242
|
+
if (out.size >= 5) break;
|
|
8243
|
+
}
|
|
8244
|
+
return [...out];
|
|
8245
|
+
}
|
|
8246
|
+
function firstErrorLine(content) {
|
|
8247
|
+
const text = typeof content === "string" ? content : JSON.stringify(content);
|
|
8248
|
+
for (const line of text.split(/\r?\n/)) {
|
|
8249
|
+
if (!/\b(error|exception|failed|failure|fatal|panic|timeout|denied|enoent|eacces|eperm)\b/i.test(
|
|
8250
|
+
line
|
|
8251
|
+
))
|
|
8252
|
+
continue;
|
|
8253
|
+
const trimmed = line.replace(/\s+/g, " ").trim();
|
|
8254
|
+
if (trimmed) return trimmed.slice(0, 180);
|
|
8255
|
+
}
|
|
8256
|
+
return void 0;
|
|
8257
|
+
}
|
|
7443
8258
|
function buildLosslessDigest(messages) {
|
|
7444
8259
|
const lines = [];
|
|
7445
8260
|
for (const m of messages) {
|
|
@@ -7550,15 +8365,15 @@ function buildSmartDigest(messages) {
|
|
|
7550
8365
|
lines.push(`[${m.role}]: ${display}${marker}`);
|
|
7551
8366
|
}
|
|
7552
8367
|
if (noiseCount > 0) {
|
|
7553
|
-
lines.push(
|
|
8368
|
+
lines.push(
|
|
8369
|
+
`[system]: ${noiseCount} low-importance turn(s) collapsed (repeated failures / pure tool I/O)`
|
|
8370
|
+
);
|
|
7554
8371
|
}
|
|
7555
8372
|
return lines.join("\n");
|
|
7556
8373
|
}
|
|
7557
8374
|
function countToolBlocks(m) {
|
|
7558
8375
|
if (typeof m.content === "string") return 0;
|
|
7559
|
-
return m.content.filter(
|
|
7560
|
-
(b) => b.type === "tool_use" || b.type === "tool_result"
|
|
7561
|
-
).length;
|
|
8376
|
+
return m.content.filter((b) => b.type === "tool_use" || b.type === "tool_result").length;
|
|
7562
8377
|
}
|
|
7563
8378
|
function firstSentence(text) {
|
|
7564
8379
|
const trimmed = text.trim();
|
|
@@ -7627,10 +8442,12 @@ var HybridCompactor = class {
|
|
|
7627
8442
|
if (elide.changed) ctx.state.replaceMessages(elide.messages);
|
|
7628
8443
|
if (elide.saved > 0) reductions.push({ phase: "elision", saved: elide.saved });
|
|
7629
8444
|
let collapsedDigest;
|
|
8445
|
+
let evidenceDigest;
|
|
7630
8446
|
if (opts.aggressive) {
|
|
7631
8447
|
const phase2 = this.collapseAncientTurns(ctx, preserveK);
|
|
7632
8448
|
if (phase2.saved > 0) reductions.push({ phase: "summary", saved: phase2.saved });
|
|
7633
8449
|
collapsedDigest = phase2.digest;
|
|
8450
|
+
evidenceDigest = phase2.evidenceDigest;
|
|
7634
8451
|
}
|
|
7635
8452
|
const repaired = repairToolUseAdjacency(ctx.messages);
|
|
7636
8453
|
if (repaired.report.changed) {
|
|
@@ -7638,6 +8455,11 @@ var HybridCompactor = class {
|
|
|
7638
8455
|
}
|
|
7639
8456
|
const afterTokens = estimateMessages(ctx.messages);
|
|
7640
8457
|
const afterFull = this.estimateFullRequest(ctx);
|
|
8458
|
+
const quality = checkCompactionQuality(ctx, {
|
|
8459
|
+
collapsedDigest,
|
|
8460
|
+
evidenceDigest,
|
|
8461
|
+
reduced: beforeTokens > afterTokens || beforeFull > afterFull
|
|
8462
|
+
});
|
|
7641
8463
|
return {
|
|
7642
8464
|
before: beforeTokens,
|
|
7643
8465
|
after: afterTokens,
|
|
@@ -7645,6 +8467,8 @@ var HybridCompactor = class {
|
|
|
7645
8467
|
fullRequestTokensAfter: afterFull,
|
|
7646
8468
|
reductions,
|
|
7647
8469
|
collapsedDigest,
|
|
8470
|
+
evidenceDigest,
|
|
8471
|
+
quality,
|
|
7648
8472
|
repaired: repaired.report.changed ? {
|
|
7649
8473
|
removedToolUses: repaired.report.removedToolUses,
|
|
7650
8474
|
removedToolResults: repaired.report.removedToolResults,
|
|
@@ -7686,7 +8510,13 @@ var HybridCompactor = class {
|
|
|
7686
8510
|
if (boundary <= 0) return { saved: 0 };
|
|
7687
8511
|
const removed = messages.slice(0, boundary);
|
|
7688
8512
|
const removedTokens = estimateMessages(removed);
|
|
7689
|
-
const
|
|
8513
|
+
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)`;
|
|
8514
|
+
const evidenceDigest = buildContextEvidenceDigest(ctx);
|
|
8515
|
+
const digest = evidenceDigest ? `[context_state]
|
|
8516
|
+
${evidenceDigest}
|
|
8517
|
+
|
|
8518
|
+
[prior_history]
|
|
8519
|
+
${historyDigest}` : historyDigest;
|
|
7690
8520
|
const summaryMsg = {
|
|
7691
8521
|
role: "system",
|
|
7692
8522
|
content: `[prior_turns_digest: ${digest}]`
|
|
@@ -7695,10 +8525,29 @@ var HybridCompactor = class {
|
|
|
7695
8525
|
ctx.state.replaceMessages([summaryMsg, ...tail]);
|
|
7696
8526
|
return {
|
|
7697
8527
|
saved: Math.max(0, removedTokens - estimateMessages([summaryMsg])),
|
|
7698
|
-
digest
|
|
8528
|
+
digest,
|
|
8529
|
+
evidenceDigest: evidenceDigest || void 0
|
|
7699
8530
|
};
|
|
7700
8531
|
}
|
|
7701
8532
|
};
|
|
8533
|
+
function checkCompactionQuality(ctx, opts) {
|
|
8534
|
+
const evidence = ctx.contextEvidence;
|
|
8535
|
+
const digest = `${opts.collapsedDigest ?? ""}
|
|
8536
|
+
${opts.evidenceDigest ?? ""}`;
|
|
8537
|
+
const hasIntent = Boolean(evidence?.currentIntent?.text || /\b(intent|goal|session_goals)\b/i.test(digest));
|
|
8538
|
+
const hasPathTrail = Boolean(
|
|
8539
|
+
Object.keys(evidence?.fileGraph ?? {}).length > 0 || (evidence?.toolCalls.length ?? 0) > 0 || /\b(dependency_graph|tool_trail|files=)\b/i.test(digest)
|
|
8540
|
+
);
|
|
8541
|
+
const issues = [];
|
|
8542
|
+
if (opts.reduced && !hasIntent) issues.push("missing intent anchor");
|
|
8543
|
+
if (opts.reduced && !hasPathTrail) issues.push("missing tool/path trail");
|
|
8544
|
+
return {
|
|
8545
|
+
ok: issues.length === 0,
|
|
8546
|
+
hasIntent,
|
|
8547
|
+
hasPathTrail,
|
|
8548
|
+
issues
|
|
8549
|
+
};
|
|
8550
|
+
}
|
|
7702
8551
|
function readContextWindowPolicy(ctx) {
|
|
7703
8552
|
const policy = ctx.meta?.["contextWindowPolicy"];
|
|
7704
8553
|
if (!policy || typeof policy !== "object") return null;
|
|
@@ -8057,10 +8906,10 @@ var SelectiveCompactor = class {
|
|
|
8057
8906
|
this.maxContext = opts.maxContext ?? 128e3;
|
|
8058
8907
|
this.preserveK = opts.preserveK ?? 4;
|
|
8059
8908
|
this.eliseThreshold = opts.eliseThreshold ?? 500;
|
|
8060
|
-
this.summarizerModel = opts.summarizerModel ?? opts.selectorModel
|
|
8061
|
-
if (this.summarizerModel ===
|
|
8909
|
+
this.summarizerModel = opts.summarizerModel ?? opts.selectorModel;
|
|
8910
|
+
if (this.summarizerModel === void 0 && (process.env["NODE_ENV"] === "development" || process.env["WRONGSTACK_DEBUG"] === "1")) {
|
|
8062
8911
|
console.warn(
|
|
8063
|
-
"[SelectiveCompactor] summarizerModel not set \u2014 will
|
|
8912
|
+
"[SelectiveCompactor] summarizerModel not set \u2014 will fall back to ctx.model at summarize time. Set `summarizerModel` explicitly to silence this warning."
|
|
8064
8913
|
);
|
|
8065
8914
|
}
|
|
8066
8915
|
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.";
|
|
@@ -8168,7 +9017,7 @@ var SelectiveCompactor = class {
|
|
|
8168
9017
|
Summarize the following message range:`;
|
|
8169
9018
|
const body = messages.map((m, i) => `[${i}] ${m.role}: ${this.messagePreview(m)}`).join("\n");
|
|
8170
9019
|
const req = {
|
|
8171
|
-
model: this.summarizerModel,
|
|
9020
|
+
model: this.summarizerModel ?? ctx.model,
|
|
8172
9021
|
system: [{ type: "text", text: systemText }],
|
|
8173
9022
|
messages: [{ role: "user", content: body }],
|
|
8174
9023
|
maxTokens: 512
|
|
@@ -8235,27 +9084,16 @@ Summarize the following message range:`;
|
|
|
8235
9084
|
if (typeof m.content === "string") return m.content.trim().length > 0;
|
|
8236
9085
|
return m.content.some((b) => b.type === "text" && b.text.trim().length > 0);
|
|
8237
9086
|
}
|
|
9087
|
+
/**
|
|
9088
|
+
* Estimate message-array tokens via the shared `estimateMessages` primitive
|
|
9089
|
+
* so SelectiveCompactor's before/after/load figures agree with the
|
|
9090
|
+
* middleware threshold math and the other compactors. Previously this used a
|
|
9091
|
+
* private `ceil(len/3.5)` walk that diverged from the calibrated shared
|
|
9092
|
+
* estimator, causing the selective `load`/`targetBudget` comparison to mix
|
|
9093
|
+
* two incompatible token scales.
|
|
9094
|
+
*/
|
|
8238
9095
|
estimateTokens(messages) {
|
|
8239
|
-
|
|
8240
|
-
for (const m of messages) {
|
|
8241
|
-
if (typeof m.content === "string") {
|
|
8242
|
-
total += this.roughTokenEstimate(m.content);
|
|
8243
|
-
} else {
|
|
8244
|
-
for (const b of m.content) {
|
|
8245
|
-
if (b.type === "text") total += this.roughTokenEstimate(b.text);
|
|
8246
|
-
else if (b.type === "tool_use") total += this.roughTokenEstimate(JSON.stringify(b.input));
|
|
8247
|
-
else if (b.type === "tool_result") {
|
|
8248
|
-
total += this.roughTokenEstimate(
|
|
8249
|
-
typeof b.content === "string" ? b.content : JSON.stringify(b.content)
|
|
8250
|
-
);
|
|
8251
|
-
}
|
|
8252
|
-
}
|
|
8253
|
-
}
|
|
8254
|
-
}
|
|
8255
|
-
return total;
|
|
8256
|
-
}
|
|
8257
|
-
roughTokenEstimate(text) {
|
|
8258
|
-
return Math.max(1, Math.ceil(text.length / 3.5));
|
|
9096
|
+
return estimateMessages(messages);
|
|
8259
9097
|
}
|
|
8260
9098
|
};
|
|
8261
9099
|
|
|
@@ -8446,15 +9284,20 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
8446
9284
|
this._cachedMsgCount = msgCount;
|
|
8447
9285
|
this._cachedToolCount = toolCount;
|
|
8448
9286
|
}
|
|
8449
|
-
const
|
|
9287
|
+
const budget = computeContextWindowBudget(ctx, tokens, this._maxContext);
|
|
9288
|
+
const load = budget.load;
|
|
8450
9289
|
const policy = this.policyProvider?.(ctx);
|
|
8451
9290
|
const thresholds = policy?.thresholds ?? {
|
|
8452
9291
|
warn: this.warnThreshold,
|
|
8453
9292
|
soft: this.softThreshold,
|
|
8454
9293
|
hard: this.hardThreshold
|
|
8455
9294
|
};
|
|
9295
|
+
const repetition = repeatedReadPressure(ctx);
|
|
9296
|
+
const adaptiveThresholds = adaptThresholdsForSignals(thresholds, {
|
|
9297
|
+
repeatedReadCount: repetition
|
|
9298
|
+
});
|
|
8456
9299
|
const aggressiveOn = policy?.aggressiveOn ?? this.aggressiveOn;
|
|
8457
|
-
const level = load >=
|
|
9300
|
+
const level = load >= adaptiveThresholds.hard ? "hard" : load >= adaptiveThresholds.soft ? "soft" : load >= adaptiveThresholds.warn ? "warn" : null;
|
|
8458
9301
|
if (!level) {
|
|
8459
9302
|
this.lastNoopAttempt = null;
|
|
8460
9303
|
return next(ctx);
|
|
@@ -8463,7 +9306,13 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
8463
9306
|
return next(ctx);
|
|
8464
9307
|
}
|
|
8465
9308
|
const aggressive = level === "hard" ? true : level === "soft" ? aggressiveOn !== "hard" : aggressiveOn === "warn";
|
|
8466
|
-
await this.compact(ctx, aggressive, {
|
|
9309
|
+
await this.compact(ctx, aggressive, {
|
|
9310
|
+
level,
|
|
9311
|
+
tokens,
|
|
9312
|
+
load,
|
|
9313
|
+
budget,
|
|
9314
|
+
signals: { repeatedReadCount: repetition }
|
|
9315
|
+
});
|
|
8467
9316
|
return next(ctx);
|
|
8468
9317
|
};
|
|
8469
9318
|
}
|
|
@@ -8516,6 +9365,8 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
8516
9365
|
tokens: pressure.tokens,
|
|
8517
9366
|
load: pressure.load,
|
|
8518
9367
|
maxContext: this._maxContext,
|
|
9368
|
+
budget: pressure.budget,
|
|
9369
|
+
signals: pressure.signals,
|
|
8519
9370
|
report,
|
|
8520
9371
|
aggressive
|
|
8521
9372
|
});
|
|
@@ -8527,6 +9378,8 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
8527
9378
|
level: pressure.level,
|
|
8528
9379
|
aggressive,
|
|
8529
9380
|
reductions: report.reductions?.map((r) => ({ phase: r.phase, saved: r.saved })),
|
|
9381
|
+
budget: pressure.budget,
|
|
9382
|
+
signals: pressure.signals,
|
|
8530
9383
|
// Record what was collapsed so the audit trail shows the preserved
|
|
8531
9384
|
// content, not just token counts. Bounded to keep the log line small;
|
|
8532
9385
|
// the full original turns are already in the session JSONL.
|
|
@@ -8542,6 +9395,8 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
8542
9395
|
level: pressure.level,
|
|
8543
9396
|
tokens: pressure.tokens,
|
|
8544
9397
|
maxContext: this._maxContext,
|
|
9398
|
+
budget: pressure.budget,
|
|
9399
|
+
signals: pressure.signals,
|
|
8545
9400
|
load: pressure.load,
|
|
8546
9401
|
fatal
|
|
8547
9402
|
});
|
|
@@ -8561,8 +9416,37 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
8561
9416
|
}
|
|
8562
9417
|
}
|
|
8563
9418
|
};
|
|
8564
|
-
|
|
8565
|
-
|
|
9419
|
+
function computeContextWindowBudget(ctx, inputTokens, maxContext) {
|
|
9420
|
+
const reservedOutputTokens = readPositiveMetaNumber(ctx, "contextOutputReserveTokens") ?? Math.floor(Math.min(8192, maxContext * 0.08));
|
|
9421
|
+
const reservedSafetyTokens = readPositiveMetaNumber(ctx, "contextSafetyBufferTokens") ?? Math.floor(Math.min(4096, maxContext * 0.02));
|
|
9422
|
+
const availableInputTokens = Math.max(
|
|
9423
|
+
1,
|
|
9424
|
+
maxContext - reservedOutputTokens - reservedSafetyTokens
|
|
9425
|
+
);
|
|
9426
|
+
const remainingInputTokens = availableInputTokens - inputTokens;
|
|
9427
|
+
return {
|
|
9428
|
+
maxContext,
|
|
9429
|
+
inputTokens,
|
|
9430
|
+
availableInputTokens,
|
|
9431
|
+
remainingInputTokens,
|
|
9432
|
+
reservedOutputTokens,
|
|
9433
|
+
reservedSafetyTokens,
|
|
9434
|
+
load: inputTokens / availableInputTokens,
|
|
9435
|
+
overflowTokens: Math.max(0, -remainingInputTokens)
|
|
9436
|
+
};
|
|
9437
|
+
}
|
|
9438
|
+
function readPositiveMetaNumber(ctx, key) {
|
|
9439
|
+
const value = ctx.meta?.[key];
|
|
9440
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : void 0;
|
|
9441
|
+
}
|
|
9442
|
+
function adaptThresholdsForSignals(thresholds, signals) {
|
|
9443
|
+
if (signals.repeatedReadCount < 3) return thresholds;
|
|
9444
|
+
return {
|
|
9445
|
+
warn: Math.max(0.25, thresholds.warn - 0.08),
|
|
9446
|
+
soft: Math.max(0.35, thresholds.soft - 0.04),
|
|
9447
|
+
hard: thresholds.hard
|
|
9448
|
+
};
|
|
9449
|
+
}
|
|
8566
9450
|
var ToolExecutor = class _ToolExecutor {
|
|
8567
9451
|
constructor(registry, opts) {
|
|
8568
9452
|
this.registry = registry;
|
|
@@ -8659,7 +9543,8 @@ ${errorDetails}`,
|
|
|
8659
9543
|
let effectivePermission = decision.permission;
|
|
8660
9544
|
const policy = this.opts.permissionPolicy;
|
|
8661
9545
|
const yolo = policy.getYolo?.() === true || policy.getYoloDestructive?.() === true;
|
|
8662
|
-
|
|
9546
|
+
const authoritativeAuto = decision.source === "yolo";
|
|
9547
|
+
if (toolDangerousCaps.length > 0 && effectivePermission === "auto" && !yolo && !authoritativeAuto) {
|
|
8663
9548
|
effectivePermission = "confirm";
|
|
8664
9549
|
}
|
|
8665
9550
|
if (effectivePermission === "deny") {
|
|
@@ -8801,9 +9686,10 @@ ${post.additionalContext}`;
|
|
|
8801
9686
|
});
|
|
8802
9687
|
this.opts.renderer?.writeToolCall(tool.name, use.input);
|
|
8803
9688
|
const output = await this.runWithTimeout(tool, use.input, ctx.signal, ctx, use.id);
|
|
8804
|
-
const text = this.serializer.serialize(output);
|
|
9689
|
+
const text = this.serializer.serialize(output, { toolName: tool.name, input: use.input });
|
|
8805
9690
|
const scrubbed = this.opts.secretScrubber.scrub(text);
|
|
8806
|
-
const
|
|
9691
|
+
const withArtifact = await maybePersistLargeToolOutput(tool.name, scrubbed, budget);
|
|
9692
|
+
const { text: capped, newBudget } = this.serializer.enforceCap(withArtifact, budget);
|
|
8807
9693
|
this.opts.renderer?.writeToolResult(tool.name, capped, false);
|
|
8808
9694
|
return {
|
|
8809
9695
|
block: {
|
|
@@ -8828,38 +9714,27 @@ ${post.additionalContext}`;
|
|
|
8828
9714
|
tool.timeoutMs ?? this.iterationTimeoutMs,
|
|
8829
9715
|
this.maxToolTimeoutMs
|
|
8830
9716
|
);
|
|
8831
|
-
const
|
|
8832
|
-
const
|
|
8833
|
-
|
|
8834
|
-
let cleanupCalled = false;
|
|
8835
|
-
let caught = false;
|
|
9717
|
+
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
9718
|
+
const combined = AbortSignal.any([parentSignal, timeoutSignal]);
|
|
9719
|
+
let output;
|
|
8836
9720
|
try {
|
|
8837
|
-
|
|
8838
|
-
return await this.runStreamedTool(tool, input, ctx, combined, toolUseId);
|
|
8839
|
-
}
|
|
8840
|
-
return await tool.execute(input, ctx, { signal: combined });
|
|
9721
|
+
output = typeof tool.executeStream === "function" ? await this.runStreamedTool(tool, input, ctx, combined, toolUseId) : await tool.execute(input, ctx, { signal: combined });
|
|
8841
9722
|
} catch (err) {
|
|
8842
|
-
|
|
8843
|
-
if (combined.aborted && typeof tool.cleanup === "function") {
|
|
8844
|
-
cleanupCalled = true;
|
|
8845
|
-
try {
|
|
8846
|
-
await tool.cleanup(input, ctx);
|
|
8847
|
-
} catch {
|
|
8848
|
-
}
|
|
8849
|
-
}
|
|
9723
|
+
if (combined.aborted) await this.runToolCleanup(tool, input, ctx);
|
|
8850
9724
|
throw err;
|
|
8851
|
-
}
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
8855
|
-
|
|
8856
|
-
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
|
|
8862
|
-
|
|
9725
|
+
}
|
|
9726
|
+
if (combined.aborted) {
|
|
9727
|
+
await this.runToolCleanup(tool, input, ctx);
|
|
9728
|
+
throw combined.reason instanceof Error ? combined.reason : new Error(typeof combined.reason === "string" ? combined.reason : "tool timeout");
|
|
9729
|
+
}
|
|
9730
|
+
return output;
|
|
9731
|
+
}
|
|
9732
|
+
/** Best-effort tool cleanup; never let it mask the original error. */
|
|
9733
|
+
async runToolCleanup(tool, input, ctx) {
|
|
9734
|
+
if (typeof tool.cleanup !== "function") return;
|
|
9735
|
+
try {
|
|
9736
|
+
await tool.cleanup(input, ctx);
|
|
9737
|
+
} catch {
|
|
8863
9738
|
}
|
|
8864
9739
|
}
|
|
8865
9740
|
async runStreamedTool(tool, input, ctx, signal, toolUseId) {
|
|
@@ -9028,6 +9903,25 @@ function extractMalformedRaw(input) {
|
|
|
9028
9903
|
return String(value);
|
|
9029
9904
|
}
|
|
9030
9905
|
}
|
|
9906
|
+
var TOOL_OUTPUT_ARTIFACT_THRESHOLD_BYTES = 64 * 1024;
|
|
9907
|
+
async function maybePersistLargeToolOutput(toolName, content, budget) {
|
|
9908
|
+
const bytes = Buffer.byteLength(content, "utf8");
|
|
9909
|
+
if (bytes <= Math.min(TOOL_OUTPUT_ARTIFACT_THRESHOLD_BYTES, Math.max(0, budget))) {
|
|
9910
|
+
return content;
|
|
9911
|
+
}
|
|
9912
|
+
try {
|
|
9913
|
+
const dir = path4.join(wstackGlobalRoot(), "tool-output");
|
|
9914
|
+
await fsp2.mkdir(dir, { recursive: true });
|
|
9915
|
+
const safeTool = toolName.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40) || "tool";
|
|
9916
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
9917
|
+
const filePath = path4.join(dir, `${stamp}-${safeTool}-${randomUUID()}.log`);
|
|
9918
|
+
await fsp2.writeFile(filePath, content, "utf8");
|
|
9919
|
+
return content + `
|
|
9920
|
+
[full tool output: ${bytes} bytes at ${filePath}; read/grep that file selectively instead of re-running or requesting more output]`;
|
|
9921
|
+
} catch {
|
|
9922
|
+
return content;
|
|
9923
|
+
}
|
|
9924
|
+
}
|
|
9031
9925
|
|
|
9032
9926
|
// src/execution/autonomous-runner.ts
|
|
9033
9927
|
var DoneConditionChecker = class {
|
|
@@ -10339,13 +11233,13 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
10339
11233
|
if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
|
|
10340
11234
|
return Promise.resolve("stop");
|
|
10341
11235
|
}
|
|
10342
|
-
return new Promise((
|
|
11236
|
+
return new Promise((resolve6) => {
|
|
10343
11237
|
let resolved = false;
|
|
10344
11238
|
const respond = (d) => {
|
|
10345
11239
|
if (resolved) return;
|
|
10346
11240
|
resolved = true;
|
|
10347
11241
|
clearTimeout(fallback);
|
|
10348
|
-
|
|
11242
|
+
resolve6(d);
|
|
10349
11243
|
};
|
|
10350
11244
|
const fallback = setTimeout(() => respond("stop"), _SubagentBudget.DECISION_TIMEOUT_MS);
|
|
10351
11245
|
bus.emit("budget.threshold_reached", {
|
|
@@ -14156,7 +15050,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
14156
15050
|
taskIds.map((id) => {
|
|
14157
15051
|
const cached = this.completedResults.find((r) => r.taskId === id);
|
|
14158
15052
|
if (cached) return cached;
|
|
14159
|
-
return new Promise((
|
|
15053
|
+
return new Promise((resolve6, reject) => {
|
|
14160
15054
|
const timeout = setTimeout(() => {
|
|
14161
15055
|
this.off("task.completed", handler);
|
|
14162
15056
|
reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
|
|
@@ -14165,7 +15059,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
14165
15059
|
if (result.taskId === id) {
|
|
14166
15060
|
clearTimeout(timeout);
|
|
14167
15061
|
this.off("task.completed", handler);
|
|
14168
|
-
|
|
15062
|
+
resolve6(result);
|
|
14169
15063
|
}
|
|
14170
15064
|
};
|
|
14171
15065
|
this.on("task.completed", handler);
|
|
@@ -14433,12 +15327,12 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
14433
15327
|
}
|
|
14434
15328
|
return new Promise((resolveDecision) => {
|
|
14435
15329
|
let settled = false;
|
|
14436
|
-
const
|
|
15330
|
+
const resolve6 = (d) => {
|
|
14437
15331
|
if (settled) return;
|
|
14438
15332
|
settled = true;
|
|
14439
15333
|
resolveDecision(d);
|
|
14440
15334
|
};
|
|
14441
|
-
const fallback = setTimeout(() =>
|
|
15335
|
+
const fallback = setTimeout(() => resolve6("stop"), DECISION_TIMEOUT_MS);
|
|
14442
15336
|
budget._events?.emit("budget.threshold_reached", {
|
|
14443
15337
|
kind: "timeout",
|
|
14444
15338
|
used,
|
|
@@ -14454,11 +15348,11 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
14454
15348
|
// disagreeing, resolves as a stop). Async grants still resolve.
|
|
14455
15349
|
extend: (extra) => {
|
|
14456
15350
|
clearTimeout(fallback);
|
|
14457
|
-
queueMicrotask(() =>
|
|
15351
|
+
queueMicrotask(() => resolve6({ extend: extra }));
|
|
14458
15352
|
},
|
|
14459
15353
|
deny: () => {
|
|
14460
15354
|
clearTimeout(fallback);
|
|
14461
|
-
|
|
15355
|
+
resolve6("stop");
|
|
14462
15356
|
}
|
|
14463
15357
|
});
|
|
14464
15358
|
});
|
|
@@ -15346,7 +16240,7 @@ var InMemoryAgentBridge = class {
|
|
|
15346
16240
|
});
|
|
15347
16241
|
}
|
|
15348
16242
|
this.inflightGuards.add(correlationId);
|
|
15349
|
-
return new Promise((
|
|
16243
|
+
return new Promise((resolve6, reject) => {
|
|
15350
16244
|
const timer = setTimeout(() => {
|
|
15351
16245
|
this.inflightGuards.delete(correlationId);
|
|
15352
16246
|
this.pendingRequests.delete(correlationId);
|
|
@@ -15365,7 +16259,7 @@ var InMemoryAgentBridge = class {
|
|
|
15365
16259
|
return;
|
|
15366
16260
|
}
|
|
15367
16261
|
this.pendingRequests.set(correlationId, {
|
|
15368
|
-
resolve:
|
|
16262
|
+
resolve: resolve6,
|
|
15369
16263
|
reject,
|
|
15370
16264
|
timer
|
|
15371
16265
|
});
|
|
@@ -16037,6 +16931,24 @@ Working rules:
|
|
|
16037
16931
|
var DEFAULT_SUBAGENT_BASELINE = `You are a subagent operating under a Director. You were spawned to handle
|
|
16038
16932
|
a specific slice of a larger plan \u2014 do that slice well and report back.
|
|
16039
16933
|
|
|
16934
|
+
Capabilities & operating rules:
|
|
16935
|
+
- You have full developer tools for your task: read, write/edit, search,
|
|
16936
|
+
shell + build (lint, format, typecheck, test), and dependency install.
|
|
16937
|
+
Use them directly to finish the task end-to-end. You run non-interactively
|
|
16938
|
+
\u2014 there is no human to approve individual tool calls, so routine work is
|
|
16939
|
+
pre-authorized; do not stop to ask for permission to read, edit, or build.
|
|
16940
|
+
- Stay inside the project root. Do not write files outside the repository,
|
|
16941
|
+
and do not touch machine config, credentials, or global state \u2014 those
|
|
16942
|
+
require an explicit grant you do not have.
|
|
16943
|
+
- Prefer the least-destructive path. Do not run irreversible or destructive
|
|
16944
|
+
commands (e.g. \`rm -rf\`, \`git push --force\`, history rewrites, dropping
|
|
16945
|
+
databases, mass deletes) unless the task explicitly requires it and names
|
|
16946
|
+
the target.
|
|
16947
|
+
- When you change code, verify it: run the relevant build / typecheck / tests
|
|
16948
|
+
and fix what you broke before reporting done.
|
|
16949
|
+
- Make only the changes the task calls for \u2014 don't refactor or reformat
|
|
16950
|
+
unrelated code.
|
|
16951
|
+
|
|
16040
16952
|
Bridge contract:
|
|
16041
16953
|
- You have a parent (the Director). You may call \`request\` on the
|
|
16042
16954
|
parent bridge to ask a clarifying question. Use this sparingly; the
|
|
@@ -16994,6 +17906,8 @@ var Director = class _Director {
|
|
|
16994
17906
|
sessionsRoot;
|
|
16995
17907
|
/** Director run id for JSONL path resolution. */
|
|
16996
17908
|
directorRunId;
|
|
17909
|
+
/** Optional logger for structured logging. Falls back to noop when omitted. */
|
|
17910
|
+
logger;
|
|
16997
17911
|
/** Resolves task descriptions back from `assign()` so completion events
|
|
16998
17912
|
* can also carry a human-readable title. */
|
|
16999
17913
|
taskDescriptions = /* @__PURE__ */ new Map();
|
|
@@ -17082,6 +17996,7 @@ var Director = class _Director {
|
|
|
17082
17996
|
opts.checkpointDebounceMs ?? 250
|
|
17083
17997
|
) : null;
|
|
17084
17998
|
this.fleetManager = opts.fleetManager;
|
|
17999
|
+
this.logger = opts.logger;
|
|
17085
18000
|
if (this.sharedScratchpadPath) {
|
|
17086
18001
|
void fsp2.mkdir(this.sharedScratchpadPath, { recursive: true }).catch((err) => this.logShutdownError("shared_scratchpad_mkdir", err));
|
|
17087
18002
|
}
|
|
@@ -17381,7 +18296,10 @@ var Director = class _Director {
|
|
|
17381
18296
|
entry.session.cancel(reason);
|
|
17382
18297
|
for (const [_role, subagentId] of entry.session.getSubagentIds()) {
|
|
17383
18298
|
this.coordinator.stop(subagentId).catch((err) => {
|
|
17384
|
-
|
|
18299
|
+
this.logger?.debug(`stop subagent ${subagentId} failed (may have already completed)`, {
|
|
18300
|
+
subagentId,
|
|
18301
|
+
err: err instanceof Error ? err.message : String(err)
|
|
18302
|
+
});
|
|
17385
18303
|
});
|
|
17386
18304
|
}
|
|
17387
18305
|
}
|
|
@@ -17441,7 +18359,7 @@ var Director = class _Director {
|
|
|
17441
18359
|
* Caller-supplied `priceLookup` is optional but recommended — without
|
|
17442
18360
|
* it the `cost` column in `usage.snapshot()` stays at 0.
|
|
17443
18361
|
*/
|
|
17444
|
-
async spawn(
|
|
18362
|
+
async spawn(callerConfig, priceLookup) {
|
|
17445
18363
|
if (this.workCompleteFlag) {
|
|
17446
18364
|
throw new FleetSpawnBudgetError(
|
|
17447
18365
|
"max_spawns",
|
|
@@ -17450,6 +18368,7 @@ var Director = class _Director {
|
|
|
17450
18368
|
"workComplete() has been called \u2014 director closed further spawning"
|
|
17451
18369
|
);
|
|
17452
18370
|
}
|
|
18371
|
+
const config = { ...callerConfig };
|
|
17453
18372
|
if (!config.model && this.modelMatrix) {
|
|
17454
18373
|
const matrix = typeof this.modelMatrix === "function" ? this.modelMatrix() : this.modelMatrix;
|
|
17455
18374
|
const entry = resolveModelMatrix(matrix, config.role);
|
|
@@ -17675,7 +18594,7 @@ var Director = class _Director {
|
|
|
17675
18594
|
})),
|
|
17676
18595
|
usage: this.usage.snapshot()
|
|
17677
18596
|
};
|
|
17678
|
-
await fsp2.mkdir(
|
|
18597
|
+
await fsp2.mkdir(path4.dirname(this.manifestPath), { recursive: true });
|
|
17679
18598
|
await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
17680
18599
|
return this.manifestPath;
|
|
17681
18600
|
}
|
|
@@ -17802,11 +18721,11 @@ var Director = class _Director {
|
|
|
17802
18721
|
if (cached) return cached;
|
|
17803
18722
|
const existing = this.taskWaiters.get(id);
|
|
17804
18723
|
if (existing) return existing.promise;
|
|
17805
|
-
let
|
|
18724
|
+
let resolve6;
|
|
17806
18725
|
const promise = new Promise((res) => {
|
|
17807
|
-
|
|
18726
|
+
resolve6 = res;
|
|
17808
18727
|
});
|
|
17809
|
-
this.taskWaiters.set(id, { promise, resolve:
|
|
18728
|
+
this.taskWaiters.set(id, { promise, resolve: resolve6 });
|
|
17810
18729
|
return promise;
|
|
17811
18730
|
})
|
|
17812
18731
|
);
|
|
@@ -17889,7 +18808,7 @@ var Director = class _Director {
|
|
|
17889
18808
|
*/
|
|
17890
18809
|
async readSession(subagentId, tail) {
|
|
17891
18810
|
if (!this.sessionsRoot) return null;
|
|
17892
|
-
const filePath =
|
|
18811
|
+
const filePath = path4.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
|
|
17893
18812
|
let raw;
|
|
17894
18813
|
try {
|
|
17895
18814
|
raw = await fsp2.readFile(filePath, "utf8");
|
|
@@ -18202,7 +19121,7 @@ function createDelegateTool(opts) {
|
|
|
18202
19121
|
subagentId
|
|
18203
19122
|
});
|
|
18204
19123
|
const dir = director;
|
|
18205
|
-
const result = await new Promise((
|
|
19124
|
+
const result = await new Promise((resolve6) => {
|
|
18206
19125
|
let settled = false;
|
|
18207
19126
|
let timer;
|
|
18208
19127
|
const finish = (value) => {
|
|
@@ -18212,7 +19131,7 @@ function createDelegateTool(opts) {
|
|
|
18212
19131
|
offTool();
|
|
18213
19132
|
offIter();
|
|
18214
19133
|
offProgress();
|
|
18215
|
-
|
|
19134
|
+
resolve6(value);
|
|
18216
19135
|
};
|
|
18217
19136
|
const arm = () => {
|
|
18218
19137
|
if (timer) clearTimeout(timer);
|
|
@@ -18419,13 +19338,13 @@ async function readSubagentPartial(opts, subagentId) {
|
|
|
18419
19338
|
if (!opts.sessionsRoot) return void 0;
|
|
18420
19339
|
const candidates = [];
|
|
18421
19340
|
if (opts.directorRunId) {
|
|
18422
|
-
candidates.push(
|
|
19341
|
+
candidates.push(path4.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
|
|
18423
19342
|
} else {
|
|
18424
19343
|
try {
|
|
18425
19344
|
const entries = await fsp2.readdir(opts.sessionsRoot, { withFileTypes: true });
|
|
18426
19345
|
for (const entry of entries) {
|
|
18427
19346
|
if (entry.isDirectory()) {
|
|
18428
|
-
candidates.push(
|
|
19347
|
+
candidates.push(path4.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
|
|
18429
19348
|
}
|
|
18430
19349
|
}
|
|
18431
19350
|
} catch {
|
|
@@ -18475,9 +19394,9 @@ function makeDirectorSessionFactory(opts) {
|
|
|
18475
19394
|
let dir;
|
|
18476
19395
|
if (opts.store) {
|
|
18477
19396
|
store = opts.store;
|
|
18478
|
-
dir = opts.sessionsRoot ?
|
|
19397
|
+
dir = opts.sessionsRoot ? path4.join(opts.sessionsRoot, runId) : "(caller-managed)";
|
|
18479
19398
|
} else if (opts.sessionsRoot) {
|
|
18480
|
-
dir =
|
|
19399
|
+
dir = path4.join(opts.sessionsRoot, runId);
|
|
18481
19400
|
store = new DefaultSessionStore({ dir });
|
|
18482
19401
|
} else {
|
|
18483
19402
|
throw new Error("makeDirectorSessionFactory requires either `store` or `sessionsRoot`");
|
|
@@ -18627,7 +19546,7 @@ var DefaultModelsRegistry = class {
|
|
|
18627
19546
|
this.overlay = opts.overlay;
|
|
18628
19547
|
this.overlayUrl = opts.overlayUrl;
|
|
18629
19548
|
this.overlayFile = opts.overlayFile;
|
|
18630
|
-
this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ?
|
|
19549
|
+
this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path4.join(path4.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
|
|
18631
19550
|
}
|
|
18632
19551
|
async load(opts = {}) {
|
|
18633
19552
|
if (this.payload && !opts.force) return this.payload;
|
|
@@ -18840,7 +19759,7 @@ var DefaultModelsRegistry = class {
|
|
|
18840
19759
|
}
|
|
18841
19760
|
/** Used by `wstack models refresh` to expose where the cache lives. */
|
|
18842
19761
|
cacheLocation() {
|
|
18843
|
-
return
|
|
19762
|
+
return path4.resolve(this.cacheFile);
|
|
18844
19763
|
}
|
|
18845
19764
|
};
|
|
18846
19765
|
function hasEntries(payload) {
|
|
@@ -19207,7 +20126,7 @@ var DefaultModeStore = class {
|
|
|
19207
20126
|
}
|
|
19208
20127
|
async loadActiveMode() {
|
|
19209
20128
|
try {
|
|
19210
|
-
const configPath =
|
|
20129
|
+
const configPath = path4.join(this.configDir, "mode.json");
|
|
19211
20130
|
const content = await fsp2.readFile(configPath, "utf8");
|
|
19212
20131
|
const data = JSON.parse(content);
|
|
19213
20132
|
this.activeModeId = data.activeMode ?? null;
|
|
@@ -19218,7 +20137,7 @@ var DefaultModeStore = class {
|
|
|
19218
20137
|
async saveActiveMode() {
|
|
19219
20138
|
try {
|
|
19220
20139
|
await fsp2.mkdir(this.configDir, { recursive: true });
|
|
19221
|
-
const configPath =
|
|
20140
|
+
const configPath = path4.join(this.configDir, "mode.json");
|
|
19222
20141
|
await atomicWrite(
|
|
19223
20142
|
configPath,
|
|
19224
20143
|
JSON.stringify({ activeMode: this.activeModeId }, null, 2)
|
|
@@ -19233,11 +20152,11 @@ async function loadProjectModes(modesDir) {
|
|
|
19233
20152
|
const entries = await fsp2.readdir(modesDir);
|
|
19234
20153
|
for (const entry of entries) {
|
|
19235
20154
|
if (!entry.endsWith(".md") && !entry.endsWith(".txt")) continue;
|
|
19236
|
-
const filePath =
|
|
20155
|
+
const filePath = path4.join(modesDir, entry);
|
|
19237
20156
|
const stat6 = await fsp2.stat(filePath);
|
|
19238
20157
|
if (!stat6.isFile()) continue;
|
|
19239
20158
|
const content = await fsp2.readFile(filePath, "utf8");
|
|
19240
|
-
const id =
|
|
20159
|
+
const id = path4.basename(entry, path4.extname(entry));
|
|
19241
20160
|
modes.push({
|
|
19242
20161
|
id,
|
|
19243
20162
|
name: id.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
@@ -19253,7 +20172,7 @@ async function loadProjectModes(modesDir) {
|
|
|
19253
20172
|
async function loadUserModes(modesDir) {
|
|
19254
20173
|
const modes = [];
|
|
19255
20174
|
try {
|
|
19256
|
-
const manifestPath =
|
|
20175
|
+
const manifestPath = path4.join(modesDir, "modes.json");
|
|
19257
20176
|
const content = await fsp2.readFile(manifestPath, "utf8");
|
|
19258
20177
|
const manifest = JSON.parse(content);
|
|
19259
20178
|
for (const mode of manifest.modes) {
|
|
@@ -19264,6 +20183,35 @@ async function loadUserModes(modesDir) {
|
|
|
19264
20183
|
return modes;
|
|
19265
20184
|
}
|
|
19266
20185
|
|
|
20186
|
+
// src/models/provider-model-resolve.ts
|
|
20187
|
+
function describeCatalogModel(m) {
|
|
20188
|
+
return {
|
|
20189
|
+
id: m.id,
|
|
20190
|
+
name: m.name,
|
|
20191
|
+
releaseDate: m.release_date,
|
|
20192
|
+
contextWindow: m.limit?.context,
|
|
20193
|
+
inputCost: m.cost?.input,
|
|
20194
|
+
outputCost: m.cost?.output,
|
|
20195
|
+
capabilities: [
|
|
20196
|
+
...m.tool_call ? ["tools"] : [],
|
|
20197
|
+
...m.reasoning ? ["reasoning"] : [],
|
|
20198
|
+
...m.modalities?.input?.includes("image") ? ["vision"] : [],
|
|
20199
|
+
...m.open_weights ? ["open_weights"] : []
|
|
20200
|
+
]
|
|
20201
|
+
};
|
|
20202
|
+
}
|
|
20203
|
+
function resolveProviderModelList(savedModels, catalog) {
|
|
20204
|
+
if (savedModels && savedModels.length > 0) {
|
|
20205
|
+
const byId = new Map((catalog?.models ?? []).map((m) => [m.id, m]));
|
|
20206
|
+
return savedModels.map((id) => {
|
|
20207
|
+
const hit = byId.get(id);
|
|
20208
|
+
return hit ? describeCatalogModel(hit) : { id, name: id, capabilities: [] };
|
|
20209
|
+
});
|
|
20210
|
+
}
|
|
20211
|
+
if (catalog) return catalog.models.map(describeCatalogModel);
|
|
20212
|
+
return [];
|
|
20213
|
+
}
|
|
20214
|
+
|
|
19267
20215
|
// src/sdd/spec-parser.ts
|
|
19268
20216
|
var SpecParser = class {
|
|
19269
20217
|
parse(content) {
|
|
@@ -20205,7 +21153,7 @@ var SpecStore = class {
|
|
|
20205
21153
|
indexPath;
|
|
20206
21154
|
constructor(opts) {
|
|
20207
21155
|
this.baseDir = opts.baseDir;
|
|
20208
|
-
this.indexPath =
|
|
21156
|
+
this.indexPath = path4.join(this.baseDir, "_index.json");
|
|
20209
21157
|
}
|
|
20210
21158
|
async save(spec) {
|
|
20211
21159
|
await ensureDir(this.baseDir);
|
|
@@ -20274,7 +21222,7 @@ var SpecStore = class {
|
|
|
20274
21222
|
return updated;
|
|
20275
21223
|
}
|
|
20276
21224
|
filePath(id) {
|
|
20277
|
-
return
|
|
21225
|
+
return path4.join(this.baseDir, `${id}.json`);
|
|
20278
21226
|
}
|
|
20279
21227
|
async readIndex() {
|
|
20280
21228
|
try {
|
|
@@ -20331,7 +21279,7 @@ var TaskGraphStore = class {
|
|
|
20331
21279
|
indexPath;
|
|
20332
21280
|
constructor(opts) {
|
|
20333
21281
|
this.baseDir = opts.baseDir;
|
|
20334
|
-
this.indexPath =
|
|
21282
|
+
this.indexPath = path4.join(this.baseDir, "_index.json");
|
|
20335
21283
|
}
|
|
20336
21284
|
async save(graph) {
|
|
20337
21285
|
await ensureDir(this.baseDir);
|
|
@@ -20369,7 +21317,7 @@ var TaskGraphStore = class {
|
|
|
20369
21317
|
}
|
|
20370
21318
|
}
|
|
20371
21319
|
filePath(id) {
|
|
20372
|
-
return
|
|
21320
|
+
return path4.join(this.baseDir, `${id}.json`);
|
|
20373
21321
|
}
|
|
20374
21322
|
async readIndex() {
|
|
20375
21323
|
try {
|
|
@@ -20614,9 +21562,9 @@ var AISpecBuilder = class {
|
|
|
20614
21562
|
if (!this.sessionPath) return;
|
|
20615
21563
|
try {
|
|
20616
21564
|
const fsp16 = await import('fs/promises');
|
|
20617
|
-
const
|
|
21565
|
+
const path21 = await import('path');
|
|
20618
21566
|
const { atomicWrite: atomicWrite2 } = await Promise.resolve().then(() => (init_atomic_write(), atomic_write_exports));
|
|
20619
|
-
await fsp16.mkdir(
|
|
21567
|
+
await fsp16.mkdir(path21.dirname(this.sessionPath), { recursive: true });
|
|
20620
21568
|
await atomicWrite2(this.sessionPath, JSON.stringify(this.session, null, 2));
|
|
20621
21569
|
} catch {
|
|
20622
21570
|
}
|
|
@@ -21343,15 +22291,15 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
|
|
|
21343
22291
|
maxId = id;
|
|
21344
22292
|
}
|
|
21345
22293
|
}
|
|
21346
|
-
const
|
|
22294
|
+
const path21 = [];
|
|
21347
22295
|
let current = maxId;
|
|
21348
22296
|
const visited = /* @__PURE__ */ new Set();
|
|
21349
22297
|
while (current && !visited.has(current)) {
|
|
21350
22298
|
visited.add(current);
|
|
21351
|
-
|
|
22299
|
+
path21.unshift(current);
|
|
21352
22300
|
current = prev.get(current) ?? null;
|
|
21353
22301
|
}
|
|
21354
|
-
return
|
|
22302
|
+
return path21;
|
|
21355
22303
|
}
|
|
21356
22304
|
function computeParallelGroups(graph, blockedByMap) {
|
|
21357
22305
|
const groups = [];
|
|
@@ -22174,9 +23122,9 @@ var DefaultHealthRegistry = class {
|
|
|
22174
23122
|
}
|
|
22175
23123
|
async runOne(check) {
|
|
22176
23124
|
let timer = null;
|
|
22177
|
-
const timeout = new Promise((
|
|
23125
|
+
const timeout = new Promise((resolve6) => {
|
|
22178
23126
|
timer = setTimeout(
|
|
22179
|
-
() =>
|
|
23127
|
+
() => resolve6({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
|
|
22180
23128
|
this.timeoutMs
|
|
22181
23129
|
);
|
|
22182
23130
|
});
|
|
@@ -22359,7 +23307,7 @@ async function startMetricsServer(opts) {
|
|
|
22359
23307
|
const tls = opts.tls;
|
|
22360
23308
|
const useHttps = !!(tls?.cert && tls?.key);
|
|
22361
23309
|
const host = opts.host ?? "127.0.0.1";
|
|
22362
|
-
const
|
|
23310
|
+
const path21 = opts.path ?? "/metrics";
|
|
22363
23311
|
const healthPath = opts.healthPath ?? "/healthz";
|
|
22364
23312
|
const healthRegistry = opts.healthRegistry;
|
|
22365
23313
|
const listener = (req, res) => {
|
|
@@ -22369,7 +23317,7 @@ async function startMetricsServer(opts) {
|
|
|
22369
23317
|
return;
|
|
22370
23318
|
}
|
|
22371
23319
|
const url = req.url.split("?")[0];
|
|
22372
|
-
if (url ===
|
|
23320
|
+
if (url === path21) {
|
|
22373
23321
|
let body;
|
|
22374
23322
|
try {
|
|
22375
23323
|
body = renderPrometheus(opts.sink.snapshot());
|
|
@@ -22415,14 +23363,14 @@ async function startMetricsServer(opts) {
|
|
|
22415
23363
|
const { createServer } = await import('http');
|
|
22416
23364
|
server = createServer(listener);
|
|
22417
23365
|
}
|
|
22418
|
-
await new Promise((
|
|
23366
|
+
await new Promise((resolve6, reject) => {
|
|
22419
23367
|
const onError = (err) => {
|
|
22420
23368
|
server.off("listening", onListening);
|
|
22421
23369
|
reject(err);
|
|
22422
23370
|
};
|
|
22423
23371
|
const onListening = () => {
|
|
22424
23372
|
server.off("error", onError);
|
|
22425
|
-
|
|
23373
|
+
resolve6();
|
|
22426
23374
|
};
|
|
22427
23375
|
server.once("error", onError);
|
|
22428
23376
|
server.once("listening", onListening);
|
|
@@ -22433,9 +23381,9 @@ async function startMetricsServer(opts) {
|
|
|
22433
23381
|
const protocol = useHttps ? "https" : "http";
|
|
22434
23382
|
return {
|
|
22435
23383
|
port: boundPort,
|
|
22436
|
-
url: `${protocol}://${host}:${boundPort}${
|
|
22437
|
-
close: () => new Promise((
|
|
22438
|
-
server.close((err) => err ? reject(err) :
|
|
23384
|
+
url: `${protocol}://${host}:${boundPort}${path21}`,
|
|
23385
|
+
close: () => new Promise((resolve6, reject) => {
|
|
23386
|
+
server.close((err) => err ? reject(err) : resolve6());
|
|
22439
23387
|
})
|
|
22440
23388
|
};
|
|
22441
23389
|
}
|
|
@@ -23093,6 +24041,6 @@ var allServers = () => ({
|
|
|
23093
24041
|
playwright: { ...playwrightServer(), enabled: false }
|
|
23094
24042
|
});
|
|
23095
24043
|
|
|
23096
|
-
export { AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, ConfigMigrationError, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPermissionPolicy, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultTaskStore, Director, DirectorStateCheckpoint, DoneConditionChecker, EternalAutonomyEngine, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetSpawnBudgetError, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, IntelligentCompactor, LLMSelector, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, SddParallelRun, SddTaskDecomposer, SelectiveCompactor, SessionAnalyzer, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, ToolExecutor, addPlanItem, allServers, analyzeCriticalPath, applyRosterBudget, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, classifyFamily, clearPlan, composeDirectorPrompt, composeSubagentPrompt, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDelegateTool, createMessage, createSessionEventBridge, createStrategyCompactor, decryptConfigSecrets, deriveTodosFromPlanItem, dispatchAgent, emptyPlan, encryptConfigSecrets, everArtServer, filesystemServer, formatPlan, formatPlanTemplates, getAgentDefinition, getPlanTemplate, getTemplate, githubServer, googleMapsServer, listPlanTemplates, listTemplates, loadDirectorState, loadPlan, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, migratePlaintextSecrets, miniMaxVisionServer, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, resolveAuditLevel, resolveSessionLoggingConfig, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, savePlan, saveTodosCheckpoint, scoreAgents, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, templateToMarkdown, wireMetricsToEvents, zaiVisionServer };
|
|
24044
|
+
export { AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, ConfigMigrationError, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPermissionPolicy, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultTaskStore, Director, DirectorStateCheckpoint, DoneConditionChecker, EternalAutonomyEngine, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetSpawnBudgetError, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, IntelligentCompactor, LLMSelector, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, SddParallelRun, SddTaskDecomposer, SelectiveCompactor, SessionAnalyzer, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, ToolExecutor, addPlanItem, allServers, analyzeCriticalPath, applyRosterBudget, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, classifyFamily, clearPlan, composeDirectorPrompt, composeSubagentPrompt, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDelegateTool, createMessage, createSessionEventBridge, createStrategyCompactor, decryptConfigSecrets, deriveTodosFromPlanItem, describeCatalogModel, dispatchAgent, emptyPlan, encryptConfigSecrets, everArtServer, filesystemServer, formatPlan, formatPlanTemplates, getAgentDefinition, getPlanTemplate, getTemplate, githubServer, googleMapsServer, listPlanTemplates, listTemplates, loadDirectorState, loadPlan, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, migratePlaintextSecrets, miniMaxVisionServer, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, resolveAuditLevel, resolveProviderModelList, resolveSessionLoggingConfig, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, savePlan, saveTodosCheckpoint, scoreAgents, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, templateToMarkdown, wireMetricsToEvents, zaiVisionServer };
|
|
23097
24045
|
//# sourceMappingURL=index.js.map
|
|
23098
24046
|
//# sourceMappingURL=index.js.map
|