@wrongstack/core 0.260.0 → 0.264.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-BbskZ7HH.d.ts → agent-bridge-D8sa1vtv.d.ts} +1 -1
- package/dist/{agent-subagent-runner-BNIGZx18.d.ts → agent-subagent-runner-c9DLkaas.d.ts} +11 -9
- package/dist/{brain-C2yDd7Lw.d.ts → brain-O1IdKPaK.d.ts} +2 -2
- package/dist/{compactor-t0R_AIt_.d.ts → compactor-BBy0rCtB.d.ts} +1 -1
- package/dist/{config-FG6As4H5.d.ts → config-Dz2F3H2K.d.ts} +7 -1
- package/dist/{context-JFOVvu6z.d.ts → context-BGSpZNSE.d.ts} +11 -0
- package/dist/coordination/index.d.ts +1681 -15
- package/dist/coordination/index.js +2648 -388
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +25 -25
- package/dist/defaults/index.js +1414 -1387
- package/dist/defaults/index.js.map +1 -1
- package/dist/dispatcher-types.d-BBeXBQgS.d.ts +66 -0
- package/dist/execution/index.d.ts +15 -15
- package/dist/execution/index.js +410 -388
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +2 -2
- package/dist/execution/prompt-enhancer.js +7 -1
- package/dist/execution/prompt-enhancer.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/extension/index.js.map +1 -1
- package/dist/{goal-preamble-B1IXJtLX.d.ts → goal-preamble-DzjFuN3p.d.ts} +21 -9
- package/dist/{goal-store-CPXz6Mml.d.ts → goal-store-CxWmCGbH.d.ts} +1 -1
- package/dist/{index-CebbJB94.d.ts → index-CYIQrXVF.d.ts} +8 -8
- package/dist/{index-BPcg4N3M.d.ts → index-CbLSI66_.d.ts} +5 -5
- package/dist/index.d.ts +45 -91
- package/dist/index.js +14976 -12551
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/kernel/index.d.ts +9 -9
- package/dist/kernel/index.js +6 -1
- package/dist/kernel/index.js.map +1 -1
- package/dist/{llm-selector-DXxI2tlu.d.ts → llm-selector-DzxuZnNz.d.ts} +2 -2
- package/dist/{mcp-servers-OwNHo43-.d.ts → mcp-servers-DC4QRPUI.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +6 -1
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-Djlmq4uB.d.ts → models-registry-B_siPxqN.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-CEmrSCMJ.d.ts → multi-agent-coordinator-CK5Jdj9K.d.ts} +2 -2
- package/dist/{null-fleet-bus-DT92xqgJ.d.ts → null-fleet-bus-DgvD4SCO.d.ts} +6 -6
- package/dist/observability/index.d.ts +2 -2
- package/dist/observability/index.js +8 -3
- package/dist/observability/index.js.map +1 -1
- package/dist/{parallel-eternal-engine-0SItuq5r.d.ts → parallel-eternal-engine-bK0JQBR_.d.ts} +9 -9
- package/dist/{path-resolver-DKBh6Jlo.d.ts → path-resolver-BPEDlN38.d.ts} +3 -3
- package/dist/{permission-BJ7eO9Vl.d.ts → permission-4yvGmMRB.d.ts} +1 -1
- package/dist/{permission-policy-DEXOfnpm.d.ts → permission-policy-C6XpsBOy.d.ts} +2 -2
- package/dist/{pipeline-zflkI2dp.d.ts → pipeline-CXCeMz8J.d.ts} +58 -3
- package/dist/{plan-templates-BFXyRkEK.d.ts → plan-templates-BvzRBkJc.d.ts} +5 -5
- package/dist/{provider-runner-BC-uywtT.d.ts → provider-runner-C5aQpDWE.d.ts} +3 -3
- package/dist/{retry-policy-Cavrzmtk.d.ts → retry-policy-CFhdtRzz.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +39 -29
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-CDvDYXWX.d.ts → secret-vault-CxiVLbt1.d.ts} +1 -1
- package/dist/security/index.d.ts +4 -4
- package/dist/security/index.js +208 -203
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-B7AivHsu.d.ts → selector-gIuhRTkN.d.ts} +1 -1
- package/dist/{session-event-bridge-BmIDxdJd.d.ts → session-event-bridge-DkvvrpDt.d.ts} +8 -2
- package/dist/{session-reader-DtofsB-2.d.ts → session-reader-KdfVwkKP.d.ts} +1 -1
- package/dist/skills/index.js +67 -64
- package/dist/skills/index.js.map +1 -1
- package/dist/storage/index.d.ts +31 -12
- package/dist/storage/index.js +441 -360
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/index.d.ts +57 -0
- package/dist/tools/index.js +411 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types/index.d.ts +19 -19
- package/dist/types/index.js +703 -694
- package/dist/types/index.js.map +1 -1
- package/dist/utils/error.d.ts +7 -0
- package/dist/utils/error.js +8 -0
- package/dist/utils/error.js.map +1 -0
- package/dist/utils/index.d.ts +7 -67
- package/dist/utils/index.js +17 -5
- package/dist/utils/index.js.map +1 -1
- package/package.json +5 -1
- package/dist/package-outdated-watcher-C70ag2G9.d.ts +0 -581
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
|
-
import * as
|
|
4
|
-
import * as
|
|
3
|
+
import * as fsp2 from 'fs/promises';
|
|
4
|
+
import * as path3 from 'path';
|
|
5
5
|
import { isAbsolute, resolve } from 'path';
|
|
6
6
|
import * as fs from 'fs';
|
|
7
7
|
import * as os from 'os';
|
|
@@ -33,17 +33,17 @@ __export(atomic_write_exports, {
|
|
|
33
33
|
withFileLock: () => withFileLock
|
|
34
34
|
});
|
|
35
35
|
async function atomicWrite(targetPath, content, opts = {}) {
|
|
36
|
-
const dir =
|
|
37
|
-
await
|
|
38
|
-
const tmp =
|
|
36
|
+
const dir = path3.dirname(targetPath);
|
|
37
|
+
await fsp2.mkdir(dir, { recursive: true });
|
|
38
|
+
const tmp = path3.join(dir, `.${path3.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
39
39
|
try {
|
|
40
40
|
if (typeof content === "string") {
|
|
41
|
-
await
|
|
41
|
+
await fsp2.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
42
42
|
} else {
|
|
43
|
-
await
|
|
43
|
+
await fsp2.writeFile(tmp, content, { flag: "wx" });
|
|
44
44
|
}
|
|
45
45
|
try {
|
|
46
|
-
const fh = await
|
|
46
|
+
const fh = await fsp2.open(tmp, "r+");
|
|
47
47
|
try {
|
|
48
48
|
await fh.sync();
|
|
49
49
|
} finally {
|
|
@@ -53,45 +53,50 @@ async function atomicWrite(targetPath, content, opts = {}) {
|
|
|
53
53
|
}
|
|
54
54
|
let mode;
|
|
55
55
|
try {
|
|
56
|
-
const stat6 = await
|
|
56
|
+
const stat6 = await fsp2.stat(targetPath);
|
|
57
57
|
mode = stat6.mode & 511;
|
|
58
58
|
} catch {
|
|
59
59
|
mode = opts.mode;
|
|
60
60
|
}
|
|
61
61
|
if (mode !== void 0) {
|
|
62
|
-
await
|
|
62
|
+
await fsp2.chmod(tmp, mode);
|
|
63
63
|
}
|
|
64
64
|
await renameWithRetry(tmp, targetPath);
|
|
65
65
|
} catch (err) {
|
|
66
66
|
try {
|
|
67
|
-
await
|
|
67
|
+
await fsp2.unlink(tmp);
|
|
68
68
|
} catch {
|
|
69
69
|
}
|
|
70
70
|
throw err;
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
async function ensureDir(dir) {
|
|
74
|
-
await
|
|
74
|
+
await fsp2.mkdir(dir, { recursive: true });
|
|
75
75
|
}
|
|
76
76
|
async function withFileLock(targetPath, fn, opts = {}) {
|
|
77
|
-
const dir =
|
|
78
|
-
await
|
|
79
|
-
const lockPath =
|
|
77
|
+
const dir = path3.dirname(targetPath);
|
|
78
|
+
await fsp2.mkdir(dir, { recursive: true });
|
|
79
|
+
const lockPath = path3.join(dir, `.${path3.basename(targetPath)}.lock`);
|
|
80
80
|
const timeoutMs = opts.timeoutMs ?? 5e3;
|
|
81
81
|
const staleMs = opts.staleMs ?? 3e4;
|
|
82
82
|
const started = Date.now();
|
|
83
83
|
let handle;
|
|
84
84
|
for (; ; ) {
|
|
85
85
|
try {
|
|
86
|
-
handle = await
|
|
86
|
+
handle = await fsp2.open(lockPath, "wx");
|
|
87
87
|
await handle.writeFile(`${process.pid}:${Date.now()}`);
|
|
88
88
|
break;
|
|
89
89
|
} catch (err) {
|
|
90
|
-
|
|
90
|
+
const code = err.code;
|
|
91
|
+
if (code === "ENOENT") {
|
|
92
|
+
await fsp2.mkdir(dir, { recursive: true });
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (code !== "EEXIST") throw err;
|
|
91
96
|
try {
|
|
92
|
-
const stat6 = await
|
|
97
|
+
const stat6 = await fsp2.stat(lockPath);
|
|
93
98
|
if (Date.now() - stat6.mtimeMs > staleMs) {
|
|
94
|
-
await
|
|
99
|
+
await fsp2.unlink(lockPath);
|
|
95
100
|
continue;
|
|
96
101
|
}
|
|
97
102
|
} catch {
|
|
@@ -111,21 +116,21 @@ async function withFileLock(targetPath, fn, opts = {}) {
|
|
|
111
116
|
} catch {
|
|
112
117
|
}
|
|
113
118
|
try {
|
|
114
|
-
await
|
|
119
|
+
await fsp2.unlink(lockPath);
|
|
115
120
|
} catch {
|
|
116
121
|
}
|
|
117
122
|
}
|
|
118
123
|
}
|
|
119
124
|
async function renameWithRetry(from, to) {
|
|
120
125
|
if (process.platform !== "win32") {
|
|
121
|
-
await
|
|
126
|
+
await fsp2.rename(from, to);
|
|
122
127
|
return;
|
|
123
128
|
}
|
|
124
129
|
const delays = [10, 25, 60, 120, 250];
|
|
125
130
|
let lastErr;
|
|
126
131
|
for (let i = 0; i <= delays.length; i++) {
|
|
127
132
|
try {
|
|
128
|
-
await
|
|
133
|
+
await fsp2.rename(from, to);
|
|
129
134
|
return;
|
|
130
135
|
} catch (err) {
|
|
131
136
|
lastErr = err;
|
|
@@ -229,7 +234,7 @@ var DefaultLogger = class _DefaultLogger {
|
|
|
229
234
|
this.maxFileBytes = opts.maxFileBytes ?? 10 * 1024 * 1024;
|
|
230
235
|
if (this.file) {
|
|
231
236
|
try {
|
|
232
|
-
fs.mkdirSync(
|
|
237
|
+
fs.mkdirSync(path3.dirname(this.file), { recursive: true });
|
|
233
238
|
} catch {
|
|
234
239
|
}
|
|
235
240
|
}
|
|
@@ -431,203 +436,1029 @@ function isEmptyMessage(msg) {
|
|
|
431
436
|
return msg.content.length === 0;
|
|
432
437
|
}
|
|
433
438
|
|
|
434
|
-
// src/
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
function
|
|
439
|
-
|
|
440
|
-
const time = startedAt.slice(11, 19).replace(/:/g, "-");
|
|
441
|
-
const suffix = randomBytes(2).toString("hex");
|
|
442
|
-
const modelPart = model ? `_${sanitizeModel(model)}` : "";
|
|
443
|
-
return `${date}/${time}Z${modelPart}_${suffix}`;
|
|
439
|
+
// src/utils/index.ts
|
|
440
|
+
init_atomic_write();
|
|
441
|
+
|
|
442
|
+
// src/utils/error.ts
|
|
443
|
+
function toErrorMessage(err) {
|
|
444
|
+
return err instanceof Error ? err.message : String(err);
|
|
444
445
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
this.dir = opts.dir;
|
|
451
|
-
this.events = opts.events;
|
|
452
|
-
this.secretScrubber = opts.secretScrubber;
|
|
453
|
-
}
|
|
454
|
-
// ── Storage event helpers ───────────────────────────────────────────────────
|
|
455
|
-
emitRead(sessionId, filePath, operation, outcome, durationMs, error) {
|
|
456
|
-
this.events?.emit("storage.read", {
|
|
457
|
-
sessionId,
|
|
458
|
-
store: "session",
|
|
459
|
-
filePath,
|
|
460
|
-
operation,
|
|
461
|
-
outcome,
|
|
462
|
-
durationMs,
|
|
463
|
-
...error !== void 0 ? { error } : {}
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
emitWrite(sessionId, filePath, operation, outcome, durationMs, eventCount, error) {
|
|
467
|
-
this.events?.emit("storage.write", {
|
|
468
|
-
sessionId,
|
|
469
|
-
store: "session",
|
|
470
|
-
filePath,
|
|
471
|
-
operation,
|
|
472
|
-
outcome,
|
|
473
|
-
durationMs,
|
|
474
|
-
...eventCount !== void 0 ? { eventCount } : {},
|
|
475
|
-
...error !== void 0 ? { error } : {}
|
|
476
|
-
});
|
|
477
|
-
}
|
|
478
|
-
emitError(sessionId, filePath, operation, error, recoverable) {
|
|
479
|
-
this.events?.emit("storage.error", {
|
|
480
|
-
sessionId,
|
|
481
|
-
store: "session",
|
|
482
|
-
filePath,
|
|
483
|
-
operation,
|
|
484
|
-
error,
|
|
485
|
-
recoverable
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
/** Absolute path to the session index file. */
|
|
489
|
-
get indexFile() {
|
|
490
|
-
return path11.join(this.dir, "_index.jsonl");
|
|
491
|
-
}
|
|
492
|
-
/** Join session ID to its absolute path within the store directory. */
|
|
493
|
-
sessionPath(id, ext) {
|
|
494
|
-
return path11.join(this.dir, `${id}${ext}`);
|
|
446
|
+
|
|
447
|
+
// src/utils/safe-json.ts
|
|
448
|
+
function safeParse(input, maxBytes = 5e6) {
|
|
449
|
+
if (input.length > maxBytes) {
|
|
450
|
+
return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
|
|
495
451
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
await ensureDir(dirPath);
|
|
504
|
-
return dirPath;
|
|
452
|
+
try {
|
|
453
|
+
return { ok: true, value: JSON.parse(input) };
|
|
454
|
+
} catch (err) {
|
|
455
|
+
return {
|
|
456
|
+
ok: false,
|
|
457
|
+
error: toErrorMessage(err)
|
|
458
|
+
};
|
|
505
459
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
secretScrubber: this.secretScrubber,
|
|
527
|
-
onClose: (s) => this.appendToIndex(s)
|
|
528
|
-
});
|
|
529
|
-
this.emitWrite(id, file, "create", "success", Date.now() - t0);
|
|
530
|
-
return writer;
|
|
531
|
-
} catch (err) {
|
|
532
|
-
await handle.close().catch((e) => console.warn(JSON.stringify({
|
|
533
|
-
level: "warn",
|
|
534
|
-
event: "session_store.handle_close_failed",
|
|
535
|
-
message: e instanceof Error ? e.message : String(e),
|
|
536
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
537
|
-
})));
|
|
538
|
-
this.emitError(id, file, "create", err instanceof Error ? err.message : String(err), true);
|
|
539
|
-
throw err;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// src/utils/string.ts
|
|
463
|
+
function truncate(s, max) {
|
|
464
|
+
return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// src/utils/glob-match.ts
|
|
468
|
+
function escapeRegex(s) {
|
|
469
|
+
return s.replace(/[.+^${}()|\\]/g, "\\$&");
|
|
470
|
+
}
|
|
471
|
+
var COMPILED_GLOB_CACHE = /* @__PURE__ */ new Map();
|
|
472
|
+
var CACHE_MAX_SIZE = 2e3;
|
|
473
|
+
function getCachedGlob(pattern) {
|
|
474
|
+
const cached = COMPILED_GLOB_CACHE.get(pattern);
|
|
475
|
+
if (cached) return cached;
|
|
476
|
+
if (COMPILED_GLOB_CACHE.size >= CACHE_MAX_SIZE) {
|
|
477
|
+
const keys = [...COMPILED_GLOB_CACHE.keys()];
|
|
478
|
+
for (let i = 0; i < Math.floor(CACHE_MAX_SIZE / 4); i++) {
|
|
479
|
+
COMPILED_GLOB_CACHE.delete(expectDefined(keys[i]));
|
|
540
480
|
}
|
|
541
481
|
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
this.emitError(id, file, "resume", err instanceof Error ? err.message : String(err), false);
|
|
551
|
-
throw new Error(
|
|
552
|
-
`Failed to open session "${id}" for append: ${err instanceof Error ? err.message : String(err)}`,
|
|
553
|
-
{ cause: err }
|
|
554
|
-
);
|
|
555
|
-
}
|
|
556
|
-
try {
|
|
557
|
-
const writer = new FileSessionWriter(
|
|
558
|
-
id,
|
|
559
|
-
handle,
|
|
560
|
-
(/* @__PURE__ */ new Date()).toISOString(),
|
|
561
|
-
{
|
|
562
|
-
id,
|
|
563
|
-
model: data.metadata.model,
|
|
564
|
-
provider: data.metadata.provider
|
|
565
|
-
},
|
|
566
|
-
this.events,
|
|
567
|
-
{
|
|
568
|
-
resumed: true,
|
|
569
|
-
// Shard directory (sessions/<date>/) — must match create() so the
|
|
570
|
-
// .summary.json sidecar lands next to the JSONL instead of the
|
|
571
|
-
// sessions root (where summaryFor() would never find it).
|
|
572
|
-
dir: path11.dirname(file),
|
|
573
|
-
filePath: file,
|
|
574
|
-
secretScrubber: this.secretScrubber,
|
|
575
|
-
onClose: (s) => this.appendToIndex(s)
|
|
576
|
-
}
|
|
577
|
-
);
|
|
578
|
-
this.emitWrite(id, file, "resume", "success", Date.now() - t0);
|
|
579
|
-
return { writer, data };
|
|
580
|
-
} catch (err) {
|
|
581
|
-
await handle.close().catch((e) => console.warn(JSON.stringify({
|
|
582
|
-
level: "warn",
|
|
583
|
-
event: "session_store.handle_close_failed",
|
|
584
|
-
message: e instanceof Error ? e.message : String(e),
|
|
585
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
586
|
-
})));
|
|
587
|
-
this.emitError(id, file, "resume", err instanceof Error ? err.message : String(err), true);
|
|
588
|
-
throw err;
|
|
589
|
-
}
|
|
482
|
+
const re = compileGlob(pattern);
|
|
483
|
+
COMPILED_GLOB_CACHE.set(pattern, re);
|
|
484
|
+
return re;
|
|
485
|
+
}
|
|
486
|
+
var MAX_GLOB_PATTERN_LEN = 1024;
|
|
487
|
+
function compileGlob(pattern) {
|
|
488
|
+
if (pattern.length > MAX_GLOB_PATTERN_LEN) {
|
|
489
|
+
throw new Error(`Glob pattern exceeds ${MAX_GLOB_PATTERN_LEN} characters`);
|
|
590
490
|
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
491
|
+
let i = 0;
|
|
492
|
+
let re = "^";
|
|
493
|
+
while (i < pattern.length) {
|
|
494
|
+
const c = pattern[i];
|
|
495
|
+
if (c === "*") {
|
|
496
|
+
if (pattern[i + 1] === "*") {
|
|
497
|
+
re += ".*";
|
|
498
|
+
i += 2;
|
|
499
|
+
if (pattern[i] === "/") i++;
|
|
500
|
+
} else {
|
|
501
|
+
re += "[^/]*";
|
|
502
|
+
i++;
|
|
503
|
+
}
|
|
504
|
+
} else if (c === "?") {
|
|
505
|
+
re += "[^/]";
|
|
506
|
+
i++;
|
|
507
|
+
} else if (c === "[") {
|
|
508
|
+
let cls = "[";
|
|
509
|
+
i++;
|
|
510
|
+
if (pattern[i] === "!" || pattern[i] === "^") {
|
|
511
|
+
cls += "^";
|
|
512
|
+
i++;
|
|
513
|
+
}
|
|
514
|
+
while (i < pattern.length && pattern[i] !== "]") {
|
|
515
|
+
const ch = pattern[i] ?? "";
|
|
516
|
+
if (ch === "\\") {
|
|
517
|
+
cls += "\\\\";
|
|
518
|
+
} else if (ch === "]" || ch === "^") {
|
|
519
|
+
cls += `\\${ch}`;
|
|
520
|
+
} else {
|
|
521
|
+
cls += ch;
|
|
607
522
|
}
|
|
523
|
+
i++;
|
|
608
524
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
errorMsg = err instanceof Error ? err.message : String(err);
|
|
616
|
-
throw err;
|
|
617
|
-
} finally {
|
|
618
|
-
this.emitRead(id, file, "load", outcome, Date.now() - t0, errorMsg);
|
|
525
|
+
cls += "]";
|
|
526
|
+
re += cls;
|
|
527
|
+
i++;
|
|
528
|
+
} else {
|
|
529
|
+
re += escapeRegex(c ?? "");
|
|
530
|
+
i++;
|
|
619
531
|
}
|
|
620
532
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
533
|
+
re += "$";
|
|
534
|
+
return new RegExp(re);
|
|
535
|
+
}
|
|
536
|
+
function matchGlob(pattern, input) {
|
|
537
|
+
return getCachedGlob(pattern).test(input);
|
|
538
|
+
}
|
|
539
|
+
function matchAny(patterns, input) {
|
|
540
|
+
return patterns.some((p) => matchGlob(p, input));
|
|
541
|
+
}
|
|
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
|
+
};
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// src/utils/sleep.ts
|
|
600
|
+
function sleep(ms) {
|
|
601
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// src/utils/assert-never.ts
|
|
605
|
+
function assertNever(x, message) {
|
|
606
|
+
const err = new Error(
|
|
607
|
+
`Unhandled case: ${JSON.stringify(x)}`
|
|
608
|
+
);
|
|
609
|
+
err.name = "AssertNeverError";
|
|
610
|
+
throw err;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// src/utils/deep-merge.ts
|
|
614
|
+
var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
|
|
615
|
+
"__proto__",
|
|
616
|
+
"constructor",
|
|
617
|
+
"prototype",
|
|
618
|
+
"__defineGetter__",
|
|
619
|
+
"__defineSetter__",
|
|
620
|
+
"__lookupGetter__",
|
|
621
|
+
"__lookupSetter__"
|
|
622
|
+
]);
|
|
623
|
+
function isPrimitiveArray(a) {
|
|
624
|
+
return a.every((v) => v === null || typeof v !== "object" && typeof v !== "function");
|
|
625
|
+
}
|
|
626
|
+
function deepMerge(base, patch, options = {}) {
|
|
627
|
+
const {
|
|
628
|
+
conflictResolution = "prefer-patch",
|
|
629
|
+
arrayMode = "replace",
|
|
630
|
+
protectProto = true,
|
|
631
|
+
onNonPrimitiveArrayReplace
|
|
632
|
+
} = options;
|
|
633
|
+
if (typeof base !== "object" || base === null) {
|
|
634
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
635
|
+
}
|
|
636
|
+
if (typeof patch !== "object" || patch === null) {
|
|
637
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
638
|
+
}
|
|
639
|
+
if (Array.isArray(base) && Array.isArray(patch)) {
|
|
640
|
+
if (arrayMode === "concat-primitives" && isPrimitiveArray(base) && isPrimitiveArray(patch)) {
|
|
641
|
+
return [.../* @__PURE__ */ new Set([...base, ...patch])];
|
|
642
|
+
}
|
|
643
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
644
|
+
}
|
|
645
|
+
if (Array.isArray(base) || Array.isArray(patch)) {
|
|
646
|
+
return conflictResolution === "prefer-patch" ? patch : base;
|
|
647
|
+
}
|
|
648
|
+
const baseObj = base;
|
|
649
|
+
const patchObj = patch;
|
|
650
|
+
const out = { ...baseObj };
|
|
651
|
+
for (const [k, v] of Object.entries(patchObj)) {
|
|
652
|
+
if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;
|
|
653
|
+
const existing = out[k];
|
|
654
|
+
if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
|
|
655
|
+
out[k] = deepMerge(existing, v, options);
|
|
656
|
+
} else if (Array.isArray(v) && Array.isArray(existing)) {
|
|
657
|
+
if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {
|
|
658
|
+
onNonPrimitiveArrayReplace(k, existing.length, v.length);
|
|
659
|
+
}
|
|
660
|
+
out[k] = deepMerge(existing, v, options);
|
|
661
|
+
} else if (v !== void 0) {
|
|
662
|
+
if (onNonPrimitiveArrayReplace && Array.isArray(v) && !isPrimitiveArray(v)) {
|
|
663
|
+
const existingLen = Array.isArray(existing) ? existing.length : 0;
|
|
664
|
+
onNonPrimitiveArrayReplace(k, existingLen, v.length);
|
|
665
|
+
}
|
|
666
|
+
out[k] = v;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return out;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// src/utils/tool-output-serializer.ts
|
|
673
|
+
function createToolOutputSerializer(opts = {}) {
|
|
674
|
+
const capBytes = opts.perIterationOutputCapBytes ?? 1e5;
|
|
675
|
+
function serialize(value) {
|
|
676
|
+
if (typeof value === "string") return value;
|
|
677
|
+
if (value === null || value === void 0) return "";
|
|
678
|
+
if (typeof value === "object") {
|
|
679
|
+
if (Array.isArray(value)) return value.map(serialize).join("\n");
|
|
680
|
+
if ("text" in value) {
|
|
681
|
+
const t = value.text;
|
|
682
|
+
return typeof t === "string" ? t : JSON.stringify(value, null, 2);
|
|
683
|
+
}
|
|
684
|
+
try {
|
|
685
|
+
return JSON.stringify(value, null, 2);
|
|
686
|
+
} catch {
|
|
687
|
+
return String(value);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
return String(value);
|
|
691
|
+
}
|
|
692
|
+
function enforceCap(text, remainingBudget) {
|
|
693
|
+
if (remainingBudget <= 0) {
|
|
694
|
+
return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
|
|
695
|
+
}
|
|
696
|
+
const textBytes = Buffer.byteLength(text, "utf8");
|
|
697
|
+
if (textBytes <= remainingBudget) {
|
|
698
|
+
return { text, newBudget: remainingBudget - textBytes };
|
|
699
|
+
}
|
|
700
|
+
const marker = `
|
|
701
|
+
\u2026[truncated ${textBytes - remainingBudget} bytes]\u2026
|
|
702
|
+
`;
|
|
703
|
+
const markerBytes = Buffer.byteLength(marker, "utf8");
|
|
704
|
+
const available = remainingBudget - markerBytes;
|
|
705
|
+
if (available <= 0) {
|
|
706
|
+
return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
|
|
707
|
+
}
|
|
708
|
+
const half = Math.floor(available / 2);
|
|
709
|
+
const first = text.slice(0, half);
|
|
710
|
+
const second = text.slice(text.length - half);
|
|
711
|
+
return { text: `${first}${marker}${second}`, newBudget: 0 };
|
|
712
|
+
}
|
|
713
|
+
return { serialize, enforceCap, capBytes };
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// src/utils/token-estimate.ts
|
|
717
|
+
var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
|
|
718
|
+
var CALIBRATION_GLOBAL_KEY = "__global__";
|
|
719
|
+
var _cals = /* @__PURE__ */ new Map();
|
|
720
|
+
function calState(key) {
|
|
721
|
+
let state = _cals.get(key);
|
|
722
|
+
if (!state) {
|
|
723
|
+
state = { ratio: 1, count: 0, prevEst: 0 };
|
|
724
|
+
_cals.set(key, state);
|
|
725
|
+
}
|
|
726
|
+
return state;
|
|
727
|
+
}
|
|
728
|
+
var MIN_SAMPLES_FOR_CALIBRATION = 3;
|
|
729
|
+
var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
|
|
730
|
+
var ESTIMATE_CACHE_MAX_SIZE = 1e4;
|
|
731
|
+
function getCachedEstimate(key, compute) {
|
|
732
|
+
const existing = ESTIMATE_CACHE.get(key);
|
|
733
|
+
if (existing !== void 0) return existing;
|
|
734
|
+
if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {
|
|
735
|
+
let evicted = 0;
|
|
736
|
+
const maxEvict = Math.floor(ESTIMATE_CACHE_MAX_SIZE / 4);
|
|
737
|
+
for (const k of ESTIMATE_CACHE.keys()) {
|
|
738
|
+
if (evicted >= maxEvict) break;
|
|
739
|
+
ESTIMATE_CACHE.delete(k);
|
|
740
|
+
evicted++;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
const estimate = compute(key);
|
|
744
|
+
ESTIMATE_CACHE.set(key, estimate);
|
|
745
|
+
return estimate;
|
|
746
|
+
}
|
|
747
|
+
function estimateToolInputTokens(input) {
|
|
748
|
+
if (typeof input === "string") return RoughTokenEstimate(input);
|
|
749
|
+
if (input === null || typeof input !== "object") {
|
|
750
|
+
return RoughTokenEstimate(String(input));
|
|
751
|
+
}
|
|
752
|
+
return getCachedEstimate(JSON.stringify(input), (key) => RoughTokenEstimate(key));
|
|
753
|
+
}
|
|
754
|
+
function estimateToolResultTokens(content) {
|
|
755
|
+
if (typeof content === "string") return RoughTokenEstimate(content);
|
|
756
|
+
return getCachedEstimate(JSON.stringify(content), (key) => RoughTokenEstimate(key));
|
|
757
|
+
}
|
|
758
|
+
function estimateTextTokens(text) {
|
|
759
|
+
return RoughTokenEstimate(text);
|
|
760
|
+
}
|
|
761
|
+
function computeMessageTokens(msg) {
|
|
762
|
+
if (typeof msg.content === "string") return estimateTextTokens(msg.content);
|
|
763
|
+
let total = 0;
|
|
764
|
+
for (const b of msg.content) {
|
|
765
|
+
if (b.type === "text") total += estimateTextTokens(b.text);
|
|
766
|
+
else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
|
|
767
|
+
else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
|
|
768
|
+
else total += RoughTokenEstimate(JSON.stringify(b));
|
|
769
|
+
}
|
|
770
|
+
return total;
|
|
771
|
+
}
|
|
772
|
+
function estimateMessageTokens(messages) {
|
|
773
|
+
let total = 0;
|
|
774
|
+
for (const m of messages) {
|
|
775
|
+
if (typeof m._estTokens === "number" && m._estTokens > 0) {
|
|
776
|
+
total += m._estTokens;
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
total += computeMessageTokens(m);
|
|
780
|
+
}
|
|
781
|
+
return total;
|
|
782
|
+
}
|
|
783
|
+
function estimateToolDefTokens(tool) {
|
|
784
|
+
const cached = tool._estDefTokens;
|
|
785
|
+
if (typeof cached === "number" && cached > 0) return cached;
|
|
786
|
+
return RoughTokenEstimate(tool.name) + RoughTokenEstimate(tool.description ?? "") + RoughTokenEstimate(JSON.stringify(tool.inputSchema));
|
|
787
|
+
}
|
|
788
|
+
function estimateRequestTokens(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
|
|
789
|
+
let messagesTokens = 0;
|
|
790
|
+
if (typeof messages === "string") {
|
|
791
|
+
messagesTokens = RoughTokenEstimate(messages);
|
|
792
|
+
} else if (Array.isArray(messages)) {
|
|
793
|
+
for (const m of messages) {
|
|
794
|
+
if (typeof m === "object" && m !== null && "content" in m) {
|
|
795
|
+
const cached = m._estTokens;
|
|
796
|
+
if (typeof cached === "number" && cached > 0) {
|
|
797
|
+
messagesTokens += cached;
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
800
|
+
const content = m.content;
|
|
801
|
+
if (typeof content === "string") {
|
|
802
|
+
messagesTokens += RoughTokenEstimate(content);
|
|
803
|
+
} else if (Array.isArray(content)) {
|
|
804
|
+
for (const b of content) {
|
|
805
|
+
if (typeof b === "object" && b !== null) {
|
|
806
|
+
if (b.type === "text") {
|
|
807
|
+
messagesTokens += RoughTokenEstimate(b.text);
|
|
808
|
+
} else {
|
|
809
|
+
messagesTokens += RoughTokenEstimate(JSON.stringify(b));
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
let systemTokens = 0;
|
|
818
|
+
if (typeof systemPrompt === "string") {
|
|
819
|
+
systemTokens = RoughTokenEstimate(systemPrompt);
|
|
820
|
+
} else if (Array.isArray(systemPrompt)) {
|
|
821
|
+
for (const b of systemPrompt) {
|
|
822
|
+
if (typeof b === "object" && b !== null && b.type === "text") {
|
|
823
|
+
systemTokens += RoughTokenEstimate(b.text);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
let toolsTokens = 0;
|
|
828
|
+
for (const t of tools) {
|
|
829
|
+
toolsTokens += estimateToolDefTokens(t);
|
|
830
|
+
}
|
|
831
|
+
const total = messagesTokens + systemTokens + toolsTokens;
|
|
832
|
+
calState(calibrationKey).prevEst = total;
|
|
833
|
+
return {
|
|
834
|
+
messages: messagesTokens,
|
|
835
|
+
systemPrompt: systemTokens,
|
|
836
|
+
tools: toolsTokens,
|
|
837
|
+
total
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
function getCalibrationState(calibrationKey = CALIBRATION_GLOBAL_KEY) {
|
|
841
|
+
const cal = calState(calibrationKey);
|
|
842
|
+
return {
|
|
843
|
+
ratio: cal.ratio,
|
|
844
|
+
count: cal.count,
|
|
845
|
+
calibrated: cal.count >= MIN_SAMPLES_FOR_CALIBRATION
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
function estimateRequestTokensCalibrated(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
|
|
849
|
+
const result = estimateRequestTokens(messages, systemPrompt, tools, calibrationKey);
|
|
850
|
+
const cal = calState(calibrationKey);
|
|
851
|
+
if (cal.count >= MIN_SAMPLES_FOR_CALIBRATION) {
|
|
852
|
+
const safeRatio = Math.min(1.5, Math.max(0.5, cal.ratio));
|
|
853
|
+
return {
|
|
854
|
+
messages: Math.round(result.messages * safeRatio),
|
|
855
|
+
systemPrompt: Math.round(result.systemPrompt * safeRatio),
|
|
856
|
+
tools: Math.round(result.tools * safeRatio),
|
|
857
|
+
total: Math.round(result.total * safeRatio)
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
return result;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// src/utils/json-schema-validate.ts
|
|
864
|
+
function validateAgainstSchema(value, schema) {
|
|
865
|
+
const errors = [];
|
|
866
|
+
walk(value, schema, "", errors);
|
|
867
|
+
return { ok: errors.length === 0, errors };
|
|
868
|
+
}
|
|
869
|
+
function walk(value, schema, path19, errors) {
|
|
870
|
+
if (schema.enum !== void 0) {
|
|
871
|
+
if (!schema.enum.some((e) => deepEqual(e, value))) {
|
|
872
|
+
errors.push({
|
|
873
|
+
path: path19 || "<root>",
|
|
874
|
+
message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
|
|
875
|
+
});
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
if (typeof schema.type === "string") {
|
|
880
|
+
if (!checkType(value, schema.type)) {
|
|
881
|
+
errors.push({
|
|
882
|
+
path: path19 || "<root>",
|
|
883
|
+
message: `expected ${schema.type}, got ${describeType(value)}`
|
|
884
|
+
});
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
if (schema.type === "object" && isPlainObject(value)) {
|
|
889
|
+
const obj = value;
|
|
890
|
+
for (const req of schema.required ?? []) {
|
|
891
|
+
if (!(req in obj)) {
|
|
892
|
+
errors.push({ path: joinPath(path19, req), message: "required property missing" });
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
if (schema.properties) {
|
|
896
|
+
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
897
|
+
if (key in obj) {
|
|
898
|
+
walk(obj[key], subSchema, joinPath(path19, key), errors);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
if (schema.type === "array" && Array.isArray(value) && schema.items) {
|
|
904
|
+
for (let i = 0; i < value.length; i++) {
|
|
905
|
+
walk(value[i], schema.items, `${path19}[${i}]`, errors);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
function checkType(value, type) {
|
|
910
|
+
switch (type) {
|
|
911
|
+
case "string":
|
|
912
|
+
return typeof value === "string";
|
|
913
|
+
case "number":
|
|
914
|
+
return typeof value === "number" && !Number.isNaN(value);
|
|
915
|
+
case "integer":
|
|
916
|
+
return typeof value === "number" && Number.isInteger(value);
|
|
917
|
+
case "boolean":
|
|
918
|
+
return typeof value === "boolean";
|
|
919
|
+
case "null":
|
|
920
|
+
return value === null;
|
|
921
|
+
case "array":
|
|
922
|
+
return Array.isArray(value);
|
|
923
|
+
case "object":
|
|
924
|
+
return isPlainObject(value);
|
|
925
|
+
default:
|
|
926
|
+
return true;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
function isPlainObject(v) {
|
|
930
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
931
|
+
}
|
|
932
|
+
function describeType(v) {
|
|
933
|
+
if (v === null) return "null";
|
|
934
|
+
if (Array.isArray(v)) return "array";
|
|
935
|
+
return typeof v;
|
|
936
|
+
}
|
|
937
|
+
function joinPath(parent, key) {
|
|
938
|
+
if (!parent) return key;
|
|
939
|
+
return `${parent}.${key}`;
|
|
940
|
+
}
|
|
941
|
+
function deepEqual(a, b) {
|
|
942
|
+
if (a === b) return true;
|
|
943
|
+
if (typeof a !== typeof b) return false;
|
|
944
|
+
if (a === null || b === null) return a === b;
|
|
945
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
946
|
+
return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
|
|
947
|
+
}
|
|
948
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
949
|
+
const ak = Object.keys(a);
|
|
950
|
+
const bk = Object.keys(b);
|
|
951
|
+
if (ak.length !== bk.length) return false;
|
|
952
|
+
return ak.every(
|
|
953
|
+
(k) => deepEqual(a[k], b[k])
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
return false;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// src/utils/regex-guard.ts
|
|
960
|
+
var MAX_PATTERN_LEN = 512;
|
|
961
|
+
var DANGEROUS_PATTERNS = [
|
|
962
|
+
/(\([^)]*[+*][^)]*\))[+*]/,
|
|
963
|
+
// (a+)+, (.*)+, etc
|
|
964
|
+
/(\(\?:[^)]*[+*][^)]*\))[+*]/
|
|
965
|
+
// same, with non-capturing group
|
|
966
|
+
];
|
|
967
|
+
function compileUserRegex(pattern, flags) {
|
|
968
|
+
if (typeof pattern !== "string") {
|
|
969
|
+
return { ok: false, reason: "pattern must be a string" };
|
|
970
|
+
}
|
|
971
|
+
if (pattern.length === 0) {
|
|
972
|
+
return { ok: false, reason: "pattern is empty" };
|
|
973
|
+
}
|
|
974
|
+
if (pattern.length > MAX_PATTERN_LEN) {
|
|
975
|
+
return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };
|
|
976
|
+
}
|
|
977
|
+
for (const rx of DANGEROUS_PATTERNS) {
|
|
978
|
+
if (rx.test(pattern)) {
|
|
979
|
+
return {
|
|
980
|
+
ok: false,
|
|
981
|
+
reason: "pattern looks vulnerable to catastrophic backtracking \u2014 rewrite without nested quantifiers"
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
try {
|
|
986
|
+
return { ok: true, regex: new RegExp(pattern, flags) };
|
|
987
|
+
} catch (err) {
|
|
988
|
+
return {
|
|
989
|
+
ok: false,
|
|
990
|
+
reason: err instanceof Error ? err.message : "invalid regex"
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
|
|
995
|
+
var IS_WINDOWS = process.platform === "win32";
|
|
996
|
+
var SEP = IS_WINDOWS ? "\\" : "/";
|
|
997
|
+
function isGlob(p) {
|
|
998
|
+
for (const c of p) {
|
|
999
|
+
if (GLOB_CHARS.has(c)) return true;
|
|
1000
|
+
}
|
|
1001
|
+
return false;
|
|
1002
|
+
}
|
|
1003
|
+
function globToRegex(pat) {
|
|
1004
|
+
let i = 0;
|
|
1005
|
+
let re = "^";
|
|
1006
|
+
while (i < pat.length) {
|
|
1007
|
+
const c = expectDefined(pat[i]);
|
|
1008
|
+
if (c === "*") {
|
|
1009
|
+
if (pat[i + 1] === "*") {
|
|
1010
|
+
re += ".*";
|
|
1011
|
+
i += 2;
|
|
1012
|
+
if (pat[i] === "/") i++;
|
|
1013
|
+
} else {
|
|
1014
|
+
re += "[^/\\\\]*";
|
|
1015
|
+
i++;
|
|
1016
|
+
}
|
|
1017
|
+
} else if (c === "?") {
|
|
1018
|
+
re += "[^/\\\\]";
|
|
1019
|
+
i++;
|
|
1020
|
+
} else if (c === "[") {
|
|
1021
|
+
let cls = "[";
|
|
1022
|
+
i++;
|
|
1023
|
+
if (pat[i] === "!" || pat[i] === "^") {
|
|
1024
|
+
cls += "^";
|
|
1025
|
+
i++;
|
|
1026
|
+
}
|
|
1027
|
+
while (i < pat.length && pat[i] !== "]") {
|
|
1028
|
+
const ch = pat[i] ?? "";
|
|
1029
|
+
if (ch === "\\") cls += "\\\\";
|
|
1030
|
+
else if (ch === "]" || ch === "^") cls += `\\${ch}`;
|
|
1031
|
+
else cls += ch;
|
|
1032
|
+
i++;
|
|
1033
|
+
}
|
|
1034
|
+
cls += "]";
|
|
1035
|
+
re += cls;
|
|
1036
|
+
i++;
|
|
1037
|
+
} else {
|
|
1038
|
+
re += c.replace(/[.+^${}()|\\]/g, "\\$&");
|
|
1039
|
+
i++;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
return new RegExp(re + "$");
|
|
1043
|
+
}
|
|
1044
|
+
function baseDir(pat) {
|
|
1045
|
+
let i = pat.length - 1;
|
|
1046
|
+
while (i >= 0 && !GLOB_CHARS.has(expectDefined(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
|
|
1047
|
+
const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
|
|
1048
|
+
return cut < 0 ? "." : pat.slice(0, cut);
|
|
1049
|
+
}
|
|
1050
|
+
async function expandGlob(pattern) {
|
|
1051
|
+
if (!isGlob(pattern)) return [pattern];
|
|
1052
|
+
const results = /* @__PURE__ */ new Set();
|
|
1053
|
+
const abs = isAbsolute(pattern);
|
|
1054
|
+
const base = abs ? baseDir(pattern) : baseDir(pattern);
|
|
1055
|
+
const relPat = base === "." ? pattern : pattern.slice(base.length + 1);
|
|
1056
|
+
async function walk3(dir, pat) {
|
|
1057
|
+
let entries;
|
|
1058
|
+
try {
|
|
1059
|
+
entries = await fsp2.readdir(dir);
|
|
1060
|
+
} catch {
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
const firstGlob = pat.search(/[*?[[]/);
|
|
1064
|
+
if (firstGlob < 0) {
|
|
1065
|
+
const re = globToRegex(pat);
|
|
1066
|
+
for (const e of entries) {
|
|
1067
|
+
if (re.test(e)) {
|
|
1068
|
+
const full = `${dir}${SEP}${e}`;
|
|
1069
|
+
results.add(abs ? resolve(full) : full);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
const before = pat.slice(0, firstGlob);
|
|
1075
|
+
const rest = pat.slice(firstGlob);
|
|
1076
|
+
if (before.endsWith("**")) {
|
|
1077
|
+
await walk3(dir, rest);
|
|
1078
|
+
for (const e of entries) {
|
|
1079
|
+
const full = `${dir}${SEP}${e}`;
|
|
1080
|
+
try {
|
|
1081
|
+
const stat6 = await fsp2.stat(full);
|
|
1082
|
+
if (stat6.isDirectory()) await walk3(full, rest);
|
|
1083
|
+
} catch {
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
} else if (before === "") {
|
|
1087
|
+
const re = globToRegex(rest);
|
|
1088
|
+
for (const e of entries) {
|
|
1089
|
+
if (re.test(e)) {
|
|
1090
|
+
const full = `${dir}${SEP}${e}`;
|
|
1091
|
+
results.add(abs ? resolve(full) : full);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
} else {
|
|
1095
|
+
const seg = before.replace(/[*?[\]]/g, "").replace(/\/$/, "");
|
|
1096
|
+
if (entries.includes(seg)) {
|
|
1097
|
+
const full = `${dir}${SEP}${seg}`;
|
|
1098
|
+
try {
|
|
1099
|
+
const stat6 = await fsp2.stat(full);
|
|
1100
|
+
if (stat6.isDirectory()) await walk3(full, rest);
|
|
1101
|
+
} catch {
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
await walk3(base === "." ? "." : base, relPat);
|
|
1107
|
+
return [...results];
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// src/utils/json-repair.ts
|
|
1111
|
+
function completePartialObject(s) {
|
|
1112
|
+
if (!s.trim().startsWith("{")) return s;
|
|
1113
|
+
if (tryParse(s).ok) return s;
|
|
1114
|
+
return repairTruncated(s);
|
|
1115
|
+
}
|
|
1116
|
+
function repairTruncated(s) {
|
|
1117
|
+
const stack = [];
|
|
1118
|
+
let inString = false;
|
|
1119
|
+
let escaped = false;
|
|
1120
|
+
let sawKey = false;
|
|
1121
|
+
let prevSig = "";
|
|
1122
|
+
let contentEnd = 0;
|
|
1123
|
+
let stringBraceDepth = 0;
|
|
1124
|
+
for (let i = 0; i < s.length; i++) {
|
|
1125
|
+
const ch = expectDefined(s[i]);
|
|
1126
|
+
if (inString) {
|
|
1127
|
+
contentEnd = i + 1;
|
|
1128
|
+
if (escaped) {
|
|
1129
|
+
escaped = false;
|
|
1130
|
+
continue;
|
|
1131
|
+
}
|
|
1132
|
+
if (ch === "\\") {
|
|
1133
|
+
escaped = true;
|
|
1134
|
+
continue;
|
|
1135
|
+
}
|
|
1136
|
+
if (ch === '"') {
|
|
1137
|
+
inString = false;
|
|
1138
|
+
prevSig = '"';
|
|
1139
|
+
stringBraceDepth = 0;
|
|
1140
|
+
continue;
|
|
1141
|
+
}
|
|
1142
|
+
if (ch === "{") stringBraceDepth++;
|
|
1143
|
+
else if (ch === "}" && stringBraceDepth > 0) stringBraceDepth--;
|
|
1144
|
+
continue;
|
|
1145
|
+
}
|
|
1146
|
+
if (ch === " " || ch === " " || ch === "\n" || ch === "\r") continue;
|
|
1147
|
+
contentEnd = i + 1;
|
|
1148
|
+
if (ch === '"') {
|
|
1149
|
+
inString = true;
|
|
1150
|
+
sawKey = true;
|
|
1151
|
+
stringBraceDepth = 0;
|
|
1152
|
+
prevSig = '"';
|
|
1153
|
+
} else if (ch === "{" || ch === "[") {
|
|
1154
|
+
stack.push(ch);
|
|
1155
|
+
prevSig = ch;
|
|
1156
|
+
} else if (ch === "}" || ch === "]") {
|
|
1157
|
+
stack.pop();
|
|
1158
|
+
prevSig = ch;
|
|
1159
|
+
} else {
|
|
1160
|
+
prevSig = ch;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
if (!sawKey && !inString) return s;
|
|
1164
|
+
let result = s.slice(0, contentEnd);
|
|
1165
|
+
if (inString) {
|
|
1166
|
+
if (escaped) {
|
|
1167
|
+
result = result.slice(0, -1);
|
|
1168
|
+
} else if (endsWithInvalidEscape(result)) {
|
|
1169
|
+
result = result.slice(0, -2);
|
|
1170
|
+
}
|
|
1171
|
+
if (stringBraceDepth > 0) result += "}".repeat(stringBraceDepth);
|
|
1172
|
+
result += '"';
|
|
1173
|
+
} else if (prevSig === ":") {
|
|
1174
|
+
result += "null";
|
|
1175
|
+
}
|
|
1176
|
+
for (let k = stack.length - 1; k >= 0; k--) {
|
|
1177
|
+
result += stack[k] === "{" ? "}" : "]";
|
|
1178
|
+
}
|
|
1179
|
+
if (!tryParse(result).ok) {
|
|
1180
|
+
const patched = result.replace(/:(\s*)([}\]])/g, ":null$2");
|
|
1181
|
+
if (tryParse(patched).ok) result = patched;
|
|
1182
|
+
}
|
|
1183
|
+
return result;
|
|
1184
|
+
}
|
|
1185
|
+
var VALID_ESCAPE = /* @__PURE__ */ new Set(['"', "\\", "/", "b", "f", "n", "r", "t", "u"]);
|
|
1186
|
+
function endsWithInvalidEscape(str) {
|
|
1187
|
+
const last = str[str.length - 1];
|
|
1188
|
+
if (str[str.length - 2] !== "\\" || last === void 0) return false;
|
|
1189
|
+
if (VALID_ESCAPE.has(last)) return false;
|
|
1190
|
+
let backslashes = 0;
|
|
1191
|
+
for (let k = str.length - 2; k >= 0 && str[k] === "\\"; k--) backslashes++;
|
|
1192
|
+
return backslashes % 2 === 1;
|
|
1193
|
+
}
|
|
1194
|
+
function tryParse(s) {
|
|
1195
|
+
try {
|
|
1196
|
+
return { ok: true, value: JSON.parse(s) };
|
|
1197
|
+
} catch {
|
|
1198
|
+
return { ok: false };
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
// src/utils/merge-models-payload.ts
|
|
1203
|
+
function mergeModelsPayload(base, overlay) {
|
|
1204
|
+
const out = {};
|
|
1205
|
+
for (const [id, provider] of Object.entries(base)) {
|
|
1206
|
+
out[id] = cloneProvider(provider);
|
|
1207
|
+
}
|
|
1208
|
+
for (const [id, ovProvider] of Object.entries(overlay)) {
|
|
1209
|
+
const existing = out[id];
|
|
1210
|
+
out[id] = existing ? mergeProvider(existing, ovProvider) : cloneProvider(ovProvider);
|
|
1211
|
+
}
|
|
1212
|
+
return out;
|
|
1213
|
+
}
|
|
1214
|
+
function mergeProvider(base, overlay) {
|
|
1215
|
+
const models = {};
|
|
1216
|
+
for (const [mid, m] of Object.entries(base.models ?? {})) {
|
|
1217
|
+
models[mid] = { ...m };
|
|
1218
|
+
}
|
|
1219
|
+
for (const [mid, ovModel] of Object.entries(overlay.models ?? {})) {
|
|
1220
|
+
const existing = models[mid];
|
|
1221
|
+
models[mid] = existing ? mergeModel(existing, ovModel) : { ...ovModel };
|
|
1222
|
+
}
|
|
1223
|
+
return {
|
|
1224
|
+
...base,
|
|
1225
|
+
// Overlay scalar fields win when explicitly provided; otherwise keep base.
|
|
1226
|
+
...stripUndefined({
|
|
1227
|
+
id: overlay.id,
|
|
1228
|
+
name: overlay.name,
|
|
1229
|
+
npm: overlay.npm,
|
|
1230
|
+
api: overlay.api,
|
|
1231
|
+
env: overlay.env,
|
|
1232
|
+
doc: overlay.doc
|
|
1233
|
+
}),
|
|
1234
|
+
models
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
function mergeModel(base, overlay) {
|
|
1238
|
+
const merged = { ...base, ...overlay };
|
|
1239
|
+
if (base.limit || overlay.limit) {
|
|
1240
|
+
merged.limit = { ...base.limit, ...overlay.limit };
|
|
1241
|
+
}
|
|
1242
|
+
if (base.cost || overlay.cost) {
|
|
1243
|
+
merged.cost = { ...base.cost, ...overlay.cost };
|
|
1244
|
+
}
|
|
1245
|
+
if (base.modalities || overlay.modalities) {
|
|
1246
|
+
merged.modalities = { ...base.modalities, ...overlay.modalities };
|
|
1247
|
+
}
|
|
1248
|
+
return merged;
|
|
1249
|
+
}
|
|
1250
|
+
function cloneProvider(p) {
|
|
1251
|
+
const models = {};
|
|
1252
|
+
for (const [mid, m] of Object.entries(p.models ?? {})) {
|
|
1253
|
+
models[mid] = { ...m };
|
|
1254
|
+
}
|
|
1255
|
+
return { ...p, models };
|
|
1256
|
+
}
|
|
1257
|
+
function stripUndefined(obj) {
|
|
1258
|
+
const out = {};
|
|
1259
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
1260
|
+
if (v !== void 0) out[k] = v;
|
|
1261
|
+
}
|
|
1262
|
+
return out;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
// src/storage/session-store.ts
|
|
1266
|
+
function sanitizeModel(model) {
|
|
1267
|
+
return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
1268
|
+
}
|
|
1269
|
+
function generateSessionId(startedAt, model) {
|
|
1270
|
+
const date = startedAt.slice(0, 10);
|
|
1271
|
+
const time = startedAt.slice(11, 19).replace(/:/g, "-");
|
|
1272
|
+
const suffix = randomBytes(2).toString("hex");
|
|
1273
|
+
const modelPart = model ? `_${sanitizeModel(model)}` : "";
|
|
1274
|
+
return `${date}/${time}Z${modelPart}_${suffix}`;
|
|
1275
|
+
}
|
|
1276
|
+
var DefaultSessionStore = class _DefaultSessionStore {
|
|
1277
|
+
dir;
|
|
1278
|
+
events;
|
|
1279
|
+
secretScrubber;
|
|
1280
|
+
constructor(opts) {
|
|
1281
|
+
this.dir = opts.dir;
|
|
1282
|
+
this.events = opts.events;
|
|
1283
|
+
this.secretScrubber = opts.secretScrubber;
|
|
1284
|
+
}
|
|
1285
|
+
// ── Storage event helpers ───────────────────────────────────────────────────
|
|
1286
|
+
emitRead(sessionId, filePath, operation, outcome, durationMs, error) {
|
|
1287
|
+
this.events?.emit("storage.read", {
|
|
1288
|
+
sessionId,
|
|
1289
|
+
store: "session",
|
|
1290
|
+
filePath,
|
|
1291
|
+
operation,
|
|
1292
|
+
outcome,
|
|
1293
|
+
durationMs,
|
|
1294
|
+
...error !== void 0 ? { error } : {}
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
emitWrite(sessionId, filePath, operation, outcome, durationMs, eventCount, error) {
|
|
1298
|
+
this.events?.emit("storage.write", {
|
|
1299
|
+
sessionId,
|
|
1300
|
+
store: "session",
|
|
1301
|
+
filePath,
|
|
1302
|
+
operation,
|
|
1303
|
+
outcome,
|
|
1304
|
+
durationMs,
|
|
1305
|
+
...eventCount !== void 0 ? { eventCount } : {},
|
|
1306
|
+
...error !== void 0 ? { error } : {}
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
emitError(sessionId, filePath, operation, error, recoverable) {
|
|
1310
|
+
this.events?.emit("storage.error", {
|
|
1311
|
+
sessionId,
|
|
1312
|
+
store: "session",
|
|
1313
|
+
filePath,
|
|
1314
|
+
operation,
|
|
1315
|
+
error,
|
|
1316
|
+
recoverable
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
/** Absolute path to the session index file. */
|
|
1320
|
+
get indexFile() {
|
|
1321
|
+
return path3.join(this.dir, "_index.jsonl");
|
|
1322
|
+
}
|
|
1323
|
+
/** Join session ID to its absolute path within the store directory. */
|
|
1324
|
+
sessionPath(id, ext) {
|
|
1325
|
+
return path3.join(this.dir, `${id}${ext}`);
|
|
1326
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* Ensure the directory implied by the session ID exists. When the ID
|
|
1329
|
+
* contains a date prefix like `2026-06-06/...`, this creates the date
|
|
1330
|
+
* subdirectory so sessions group naturally by day.
|
|
1331
|
+
*/
|
|
1332
|
+
async ensureShardDir(id) {
|
|
1333
|
+
const dirPath = path3.dirname(path3.join(this.dir, id));
|
|
1334
|
+
await ensureDir(dirPath);
|
|
1335
|
+
return dirPath;
|
|
1336
|
+
}
|
|
1337
|
+
async create(meta) {
|
|
1338
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1339
|
+
const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt, meta.model ?? meta.provider);
|
|
1340
|
+
const shardDir = await this.ensureShardDir(id);
|
|
1341
|
+
const file = path3.join(shardDir, `${path3.basename(id)}.jsonl`);
|
|
1342
|
+
const t0 = Date.now();
|
|
1343
|
+
let handle;
|
|
1344
|
+
try {
|
|
1345
|
+
handle = await fsp2.open(file, "a", 384);
|
|
1346
|
+
} catch (err) {
|
|
1347
|
+
this.emitError(id, file, "create", toErrorMessage(err), false);
|
|
1348
|
+
throw new Error(
|
|
1349
|
+
`Failed to open session file: ${toErrorMessage(err)}`,
|
|
1350
|
+
{ cause: err }
|
|
1351
|
+
);
|
|
1352
|
+
}
|
|
1353
|
+
try {
|
|
1354
|
+
const writer = new FileSessionWriter(id, handle, startedAt, meta, this.events, {
|
|
1355
|
+
dir: shardDir,
|
|
1356
|
+
filePath: file,
|
|
1357
|
+
secretScrubber: this.secretScrubber,
|
|
1358
|
+
onClose: (s) => this.appendToIndex(s)
|
|
1359
|
+
});
|
|
1360
|
+
this.emitWrite(id, file, "create", "success", Date.now() - t0);
|
|
1361
|
+
return writer;
|
|
1362
|
+
} catch (err) {
|
|
1363
|
+
await handle.close().catch((e) => console.warn(JSON.stringify({
|
|
1364
|
+
level: "warn",
|
|
1365
|
+
event: "session_store.handle_close_failed",
|
|
1366
|
+
message: e instanceof Error ? e.message : String(e),
|
|
1367
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1368
|
+
})));
|
|
1369
|
+
this.emitError(id, file, "create", toErrorMessage(err), true);
|
|
1370
|
+
throw err;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
async resume(id) {
|
|
1374
|
+
const file = this.sessionPath(id, ".jsonl");
|
|
1375
|
+
const t0 = Date.now();
|
|
1376
|
+
const data = await this.load(id);
|
|
1377
|
+
let handle;
|
|
1378
|
+
try {
|
|
1379
|
+
handle = await fsp2.open(file, "a", 384);
|
|
1380
|
+
} catch (err) {
|
|
1381
|
+
this.emitError(id, file, "resume", toErrorMessage(err), false);
|
|
1382
|
+
throw new Error(
|
|
1383
|
+
`Failed to open session "${id}" for append: ${toErrorMessage(err)}`,
|
|
1384
|
+
{ cause: err }
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
try {
|
|
1388
|
+
const writer = new FileSessionWriter(
|
|
1389
|
+
id,
|
|
1390
|
+
handle,
|
|
1391
|
+
(/* @__PURE__ */ new Date()).toISOString(),
|
|
1392
|
+
{
|
|
1393
|
+
id,
|
|
1394
|
+
model: data.metadata.model,
|
|
1395
|
+
provider: data.metadata.provider
|
|
1396
|
+
},
|
|
1397
|
+
this.events,
|
|
1398
|
+
{
|
|
1399
|
+
resumed: true,
|
|
1400
|
+
// Shard directory (sessions/<date>/) — must match create() so the
|
|
1401
|
+
// .summary.json sidecar lands next to the JSONL instead of the
|
|
1402
|
+
// sessions root (where summaryFor() would never find it).
|
|
1403
|
+
dir: path3.dirname(file),
|
|
1404
|
+
filePath: file,
|
|
1405
|
+
secretScrubber: this.secretScrubber,
|
|
1406
|
+
onClose: (s) => this.appendToIndex(s)
|
|
1407
|
+
}
|
|
1408
|
+
);
|
|
1409
|
+
this.emitWrite(id, file, "resume", "success", Date.now() - t0);
|
|
1410
|
+
return { writer, data };
|
|
1411
|
+
} catch (err) {
|
|
1412
|
+
await handle.close().catch((e) => console.warn(JSON.stringify({
|
|
1413
|
+
level: "warn",
|
|
1414
|
+
event: "session_store.handle_close_failed",
|
|
1415
|
+
message: e instanceof Error ? e.message : String(e),
|
|
1416
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1417
|
+
})));
|
|
1418
|
+
this.emitError(id, file, "resume", toErrorMessage(err), true);
|
|
1419
|
+
throw err;
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
async load(id) {
|
|
1423
|
+
const file = this.sessionPath(id, ".jsonl");
|
|
1424
|
+
const t0 = Date.now();
|
|
1425
|
+
let outcome = "success";
|
|
1426
|
+
let errorMsg;
|
|
1427
|
+
try {
|
|
1428
|
+
const raw = await fsp2.readFile(file, "utf8");
|
|
1429
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
1430
|
+
const events = [];
|
|
1431
|
+
for (const line of lines) {
|
|
1432
|
+
try {
|
|
1433
|
+
const parsed = JSON.parse(line);
|
|
1434
|
+
if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
|
|
1435
|
+
events.push(parsed);
|
|
1436
|
+
}
|
|
1437
|
+
} catch {
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
const meta = this.metaFromEvents(id, events);
|
|
1441
|
+
const { messages, usage } = this.replay(events, id);
|
|
1442
|
+
const toolCallEnds = extractToolCallEnds(events);
|
|
1443
|
+
return { metadata: meta, events, messages, usage, toolCallEnds };
|
|
1444
|
+
} catch (err) {
|
|
1445
|
+
outcome = "failure";
|
|
1446
|
+
errorMsg = toErrorMessage(err);
|
|
1447
|
+
throw err;
|
|
1448
|
+
} finally {
|
|
1449
|
+
this.emitRead(id, file, "load", outcome, Date.now() - t0, errorMsg);
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
async list(limit = 20) {
|
|
1453
|
+
try {
|
|
1454
|
+
await ensureDir(this.dir);
|
|
1455
|
+
const indexed = await this.readIndex();
|
|
1456
|
+
if (indexed.length > 0) {
|
|
1457
|
+
indexed.sort((a, b) => {
|
|
1458
|
+
if (a.startedAt < b.startedAt) return 1;
|
|
1459
|
+
if (a.startedAt > b.startedAt) return -1;
|
|
1460
|
+
return a.id.localeCompare(b.id);
|
|
1461
|
+
});
|
|
631
1462
|
return indexed.slice(0, limit);
|
|
632
1463
|
}
|
|
633
1464
|
const ids = await this.collectSessionIds(this.dir);
|
|
@@ -659,7 +1490,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
659
1490
|
try {
|
|
660
1491
|
await ensureDir(this.dir);
|
|
661
1492
|
const line = JSON.stringify(summary) + "\n";
|
|
662
|
-
await
|
|
1493
|
+
await fsp2.appendFile(this.indexFile, line, "utf8");
|
|
663
1494
|
this.indexAppendCount++;
|
|
664
1495
|
if (this.indexAppendCount >= _DefaultSessionStore.COMPACT_EVERY) {
|
|
665
1496
|
await this.compactIndex();
|
|
@@ -673,7 +1504,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
673
1504
|
try {
|
|
674
1505
|
await ensureDir(this.dir);
|
|
675
1506
|
const line = JSON.stringify({ action: "delete", id }) + "\n";
|
|
676
|
-
await
|
|
1507
|
+
await fsp2.appendFile(this.indexFile, line, "utf8");
|
|
677
1508
|
this.indexAppendCount++;
|
|
678
1509
|
} catch {
|
|
679
1510
|
}
|
|
@@ -691,11 +1522,11 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
691
1522
|
if (entries.length === 0) return;
|
|
692
1523
|
const tmp = `${this.indexFile}.compact.tmp`;
|
|
693
1524
|
const lines = entries.map((s) => JSON.stringify(s)).join("\n") + "\n";
|
|
694
|
-
await
|
|
695
|
-
await
|
|
1525
|
+
await fsp2.writeFile(tmp, lines, "utf8");
|
|
1526
|
+
await fsp2.rename(tmp, this.indexFile);
|
|
696
1527
|
} catch (err) {
|
|
697
1528
|
outcome = "failure";
|
|
698
|
-
errorMsg =
|
|
1529
|
+
errorMsg = toErrorMessage(err);
|
|
699
1530
|
} finally {
|
|
700
1531
|
this.emitWrite("~compact~", this.indexFile, "compact", outcome, Date.now() - t0, void 0, errorMsg);
|
|
701
1532
|
}
|
|
@@ -708,7 +1539,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
708
1539
|
async readIndex() {
|
|
709
1540
|
let raw;
|
|
710
1541
|
try {
|
|
711
|
-
raw = await
|
|
1542
|
+
raw = await fsp2.readFile(this.indexFile, "utf8");
|
|
712
1543
|
} catch {
|
|
713
1544
|
return [];
|
|
714
1545
|
}
|
|
@@ -741,8 +1572,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
741
1572
|
const valid = summaries.filter((s) => s !== null);
|
|
742
1573
|
const tmp = `${this.indexFile}.tmp`;
|
|
743
1574
|
const lines = valid.map((s) => JSON.stringify(s)).join("\n") + "\n";
|
|
744
|
-
await
|
|
745
|
-
await
|
|
1575
|
+
await fsp2.writeFile(tmp, lines, "utf8");
|
|
1576
|
+
await fsp2.rename(tmp, this.indexFile);
|
|
746
1577
|
return valid.length;
|
|
747
1578
|
}
|
|
748
1579
|
/** Recursively collect session IDs from date-shard subdirectories.
|
|
@@ -753,7 +1584,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
753
1584
|
const ids = [];
|
|
754
1585
|
let entries;
|
|
755
1586
|
try {
|
|
756
|
-
entries = await
|
|
1587
|
+
entries = await fsp2.readdir(dir, { withFileTypes: true });
|
|
757
1588
|
} catch {
|
|
758
1589
|
return ids;
|
|
759
1590
|
}
|
|
@@ -763,7 +1594,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
763
1594
|
continue;
|
|
764
1595
|
if (entry.isDirectory()) {
|
|
765
1596
|
const childPrefix = depth === 0 ? entry.name : `${prefix}/${entry.name}`;
|
|
766
|
-
ids.push(...await this.collectSessionIds(
|
|
1597
|
+
ids.push(...await this.collectSessionIds(path3.join(dir, entry.name), childPrefix, depth + 1));
|
|
767
1598
|
} else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
|
|
768
1599
|
if (entry.name === "_index.jsonl") continue;
|
|
769
1600
|
const base = entry.name.replace(/\.jsonl$/, "");
|
|
@@ -778,15 +1609,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
778
1609
|
let outcome = "success";
|
|
779
1610
|
let errorMsg;
|
|
780
1611
|
try {
|
|
781
|
-
const raw = await
|
|
1612
|
+
const raw = await fsp2.readFile(manifest, "utf8");
|
|
782
1613
|
this.emitRead(id, manifest, "summary", "success", Date.now() - t0);
|
|
783
1614
|
return JSON.parse(raw);
|
|
784
1615
|
} catch {
|
|
785
1616
|
const full = this.sessionPath(id, ".jsonl");
|
|
786
|
-
const stat6 = await
|
|
1617
|
+
const stat6 = await fsp2.stat(full);
|
|
787
1618
|
const summary = await this.summarize(id, stat6.mtime.toISOString());
|
|
788
1619
|
await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
|
|
789
|
-
const msg =
|
|
1620
|
+
const msg = toErrorMessage(err);
|
|
790
1621
|
this.emitError(id, manifest, "summary_fallback", msg, true);
|
|
791
1622
|
console.warn(JSON.stringify({
|
|
792
1623
|
level: "warn",
|
|
@@ -814,14 +1645,14 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
814
1645
|
async deleteSession(id) {
|
|
815
1646
|
const jsonlPath = this.sessionPath(id, ".jsonl");
|
|
816
1647
|
const summaryPath = this.sessionPath(id, ".summary.json");
|
|
817
|
-
const shardDir =
|
|
818
|
-
const base =
|
|
819
|
-
const sessDir =
|
|
1648
|
+
const shardDir = path3.dirname(path3.join(this.dir, id));
|
|
1649
|
+
const base = path3.basename(id);
|
|
1650
|
+
const sessDir = path3.join(shardDir, base);
|
|
820
1651
|
const deletions = [
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
1652
|
+
fsp2.unlink(jsonlPath),
|
|
1653
|
+
fsp2.unlink(summaryPath),
|
|
1654
|
+
fsp2.unlink(path3.join(shardDir, `${base}.plan.json`)),
|
|
1655
|
+
fsp2.unlink(path3.join(shardDir, `${base}.todos.json`))
|
|
825
1656
|
];
|
|
826
1657
|
const results = await Promise.allSettled(deletions);
|
|
827
1658
|
for (const r of results) {
|
|
@@ -838,12 +1669,12 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
838
1669
|
}
|
|
839
1670
|
}
|
|
840
1671
|
}
|
|
841
|
-
await
|
|
1672
|
+
await fsp2.rm(sessDir, { recursive: true, force: true }).catch((err) => {
|
|
842
1673
|
console.warn(JSON.stringify({
|
|
843
1674
|
level: "warn",
|
|
844
1675
|
event: "session_store.rmdir_failed",
|
|
845
1676
|
sessionId: id,
|
|
846
|
-
message:
|
|
1677
|
+
message: toErrorMessage(err),
|
|
847
1678
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
848
1679
|
}));
|
|
849
1680
|
});
|
|
@@ -857,16 +1688,16 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
857
1688
|
let deleted = 0;
|
|
858
1689
|
let activeSessionId = null;
|
|
859
1690
|
try {
|
|
860
|
-
const raw = await
|
|
1691
|
+
const raw = await fsp2.readFile(path3.join(this.dir, "active.json"), "utf8");
|
|
861
1692
|
const active = JSON.parse(raw);
|
|
862
1693
|
activeSessionId = active.sessionId ?? null;
|
|
863
1694
|
} catch {
|
|
864
1695
|
}
|
|
865
1696
|
const isPrunableJsonl = (name) => name.endsWith(".jsonl") && name !== "_index.jsonl" && name !== "_mailbox.jsonl" && !name.endsWith(".replay.jsonl") && !name.endsWith(".audit.jsonl");
|
|
866
1697
|
const pruneFile = async (dir, name, prefix) => {
|
|
867
|
-
const jsonlPath =
|
|
1698
|
+
const jsonlPath = path3.join(dir, name);
|
|
868
1699
|
try {
|
|
869
|
-
const stat6 = await
|
|
1700
|
+
const stat6 = await fsp2.stat(jsonlPath);
|
|
870
1701
|
if (stat6.mtimeMs >= cutoff) return;
|
|
871
1702
|
} catch {
|
|
872
1703
|
return;
|
|
@@ -877,15 +1708,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
877
1708
|
await this.deleteSession(id);
|
|
878
1709
|
deleted++;
|
|
879
1710
|
};
|
|
880
|
-
const entries = await
|
|
1711
|
+
const entries = await fsp2.readdir(this.dir, { withFileTypes: true }).catch(() => []);
|
|
881
1712
|
for (const entry of entries) {
|
|
882
1713
|
if (entry.isFile()) {
|
|
883
1714
|
if (isPrunableJsonl(entry.name)) await pruneFile(this.dir, entry.name, "");
|
|
884
1715
|
continue;
|
|
885
1716
|
}
|
|
886
1717
|
if (!entry.isDirectory()) continue;
|
|
887
|
-
const dateDir =
|
|
888
|
-
const files = await
|
|
1718
|
+
const dateDir = path3.join(this.dir, entry.name);
|
|
1719
|
+
const files = await fsp2.readdir(dateDir, { withFileTypes: true }).catch(() => []);
|
|
889
1720
|
for (const file of files) {
|
|
890
1721
|
if (!file.isFile() || !isPrunableJsonl(file.name)) continue;
|
|
891
1722
|
await pruneFile(dateDir, file.name, entry.name);
|
|
@@ -896,11 +1727,11 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
896
1727
|
}
|
|
897
1728
|
for (const entry of entries) {
|
|
898
1729
|
if (!entry.isDirectory()) continue;
|
|
899
|
-
const dateDir =
|
|
1730
|
+
const dateDir = path3.join(this.dir, entry.name);
|
|
900
1731
|
try {
|
|
901
|
-
const remaining = await
|
|
1732
|
+
const remaining = await fsp2.readdir(dateDir);
|
|
902
1733
|
if (remaining.length === 0) {
|
|
903
|
-
await
|
|
1734
|
+
await fsp2.rmdir(dateDir).catch(() => void 0);
|
|
904
1735
|
}
|
|
905
1736
|
} catch {
|
|
906
1737
|
}
|
|
@@ -919,8 +1750,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
919
1750
|
provider: "unknown"
|
|
920
1751
|
})}
|
|
921
1752
|
`;
|
|
922
|
-
await
|
|
923
|
-
await
|
|
1753
|
+
await fsp2.writeFile(file, record, "utf8");
|
|
1754
|
+
await fsp2.unlink(meta).catch(() => void 0);
|
|
924
1755
|
}
|
|
925
1756
|
async summarize(id, mtime) {
|
|
926
1757
|
try {
|
|
@@ -1071,7 +1902,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1071
1902
|
this.meta = meta;
|
|
1072
1903
|
this.events = events;
|
|
1073
1904
|
this.resumed = opts.resumed ?? false;
|
|
1074
|
-
this.manifestFile = opts.dir ?
|
|
1905
|
+
this.manifestFile = opts.dir ? path3.join(opts.dir, `${path3.basename(id)}.summary.json`) : "";
|
|
1075
1906
|
this.filePath = opts.filePath ?? "";
|
|
1076
1907
|
this.secretScrubber = opts.secretScrubber;
|
|
1077
1908
|
this.onCloseCb = opts.onClose;
|
|
@@ -1278,7 +2109,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1278
2109
|
await this.enqueueWrite(batch);
|
|
1279
2110
|
} catch (err) {
|
|
1280
2111
|
outcome = "failure";
|
|
1281
|
-
errorMsg =
|
|
2112
|
+
errorMsg = toErrorMessage(err);
|
|
1282
2113
|
this.appendFailCount += eventCount;
|
|
1283
2114
|
const now = Date.now();
|
|
1284
2115
|
if (now - this.lastAppendWarnAt > 5e3) {
|
|
@@ -1286,7 +2117,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1286
2117
|
const tail = suppressed > 0 ? ` (+${suppressed} suppressed)` : "";
|
|
1287
2118
|
console.warn(
|
|
1288
2119
|
"[session] flush failed:",
|
|
1289
|
-
|
|
2120
|
+
toErrorMessage(err),
|
|
1290
2121
|
tail
|
|
1291
2122
|
);
|
|
1292
2123
|
this.lastAppendWarnAt = now;
|
|
@@ -1376,7 +2207,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1376
2207
|
await atomicWrite(this.manifestFile, JSON.stringify(this.summary), { mode: 384 });
|
|
1377
2208
|
} catch (err) {
|
|
1378
2209
|
outcome = "failure";
|
|
1379
|
-
errorMsg =
|
|
2210
|
+
errorMsg = toErrorMessage(err);
|
|
1380
2211
|
} finally {
|
|
1381
2212
|
this.events?.emit("storage.write", {
|
|
1382
2213
|
sessionId: this.id,
|
|
@@ -1397,7 +2228,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1397
2228
|
await this.onCloseCb?.(this.summary);
|
|
1398
2229
|
} catch (err) {
|
|
1399
2230
|
idxOutcome = "failure";
|
|
1400
|
-
idxError =
|
|
2231
|
+
idxError = toErrorMessage(err);
|
|
1401
2232
|
} finally {
|
|
1402
2233
|
this.events?.emit("storage.write", {
|
|
1403
2234
|
sessionId: this.summary.id,
|
|
@@ -1450,7 +2281,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1450
2281
|
}
|
|
1451
2282
|
await this.flushBuffer();
|
|
1452
2283
|
await this.writeChain;
|
|
1453
|
-
const raw = await
|
|
2284
|
+
const raw = await fsp2.readFile(this.filePath, "utf8");
|
|
1454
2285
|
const lines = raw.split("\n");
|
|
1455
2286
|
const kept = [];
|
|
1456
2287
|
let removedCount = 0;
|
|
@@ -1488,13 +2319,13 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1488
2319
|
}
|
|
1489
2320
|
const truncated = kept.join("\n");
|
|
1490
2321
|
const tmpPath = `${this.filePath}.rewind.tmp`;
|
|
1491
|
-
await
|
|
2322
|
+
await fsp2.writeFile(tmpPath, truncated + "\n", "utf8");
|
|
1492
2323
|
try {
|
|
1493
2324
|
await this.handle.close();
|
|
1494
|
-
await
|
|
1495
|
-
this.handle = await
|
|
2325
|
+
await fsp2.rename(tmpPath, this.filePath);
|
|
2326
|
+
this.handle = await fsp2.open(this.filePath, "a", 384);
|
|
1496
2327
|
} catch (err) {
|
|
1497
|
-
await
|
|
2328
|
+
await fsp2.unlink(tmpPath).catch(() => void 0);
|
|
1498
2329
|
throw err;
|
|
1499
2330
|
}
|
|
1500
2331
|
await this.append({
|
|
@@ -1526,7 +2357,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1526
2357
|
provider: this.meta.provider ?? "unknown"
|
|
1527
2358
|
})}
|
|
1528
2359
|
`;
|
|
1529
|
-
await
|
|
2360
|
+
await fsp2.writeFile(this.filePath, record, "utf8");
|
|
1530
2361
|
}
|
|
1531
2362
|
/**
|
|
1532
2363
|
* Idea #1 — write an in-flight marker. The agent loop should call
|
|
@@ -1575,7 +2406,7 @@ var QueueStore = class {
|
|
|
1575
2406
|
events;
|
|
1576
2407
|
traceId;
|
|
1577
2408
|
constructor(opts) {
|
|
1578
|
-
this.file =
|
|
2409
|
+
this.file = path3.join(opts.dir, "queue.json");
|
|
1579
2410
|
this.events = opts.events;
|
|
1580
2411
|
this.traceId = opts.traceId;
|
|
1581
2412
|
}
|
|
@@ -1603,7 +2434,7 @@ var QueueStore = class {
|
|
|
1603
2434
|
filePath: this.file,
|
|
1604
2435
|
operation: "write",
|
|
1605
2436
|
outcome: "failure",
|
|
1606
|
-
error:
|
|
2437
|
+
error: toErrorMessage(err),
|
|
1607
2438
|
recoverable: false,
|
|
1608
2439
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
1609
2440
|
});
|
|
@@ -1611,7 +2442,7 @@ var QueueStore = class {
|
|
|
1611
2442
|
level: "warn",
|
|
1612
2443
|
event: "queue_store.write_failed",
|
|
1613
2444
|
path: this.file,
|
|
1614
|
-
message:
|
|
2445
|
+
message: toErrorMessage(err),
|
|
1615
2446
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1616
2447
|
}));
|
|
1617
2448
|
}
|
|
@@ -1620,7 +2451,7 @@ var QueueStore = class {
|
|
|
1620
2451
|
const t0 = Date.now();
|
|
1621
2452
|
let raw;
|
|
1622
2453
|
try {
|
|
1623
|
-
raw = await
|
|
2454
|
+
raw = await fsp2.readFile(this.file, "utf8");
|
|
1624
2455
|
} catch (err) {
|
|
1625
2456
|
const code = err.code;
|
|
1626
2457
|
if (code === "ENOENT") {
|
|
@@ -1641,7 +2472,7 @@ var QueueStore = class {
|
|
|
1641
2472
|
filePath: this.file,
|
|
1642
2473
|
operation: "read",
|
|
1643
2474
|
outcome: "failure",
|
|
1644
|
-
error:
|
|
2475
|
+
error: toErrorMessage(err),
|
|
1645
2476
|
recoverable: true,
|
|
1646
2477
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
1647
2478
|
});
|
|
@@ -1649,7 +2480,7 @@ var QueueStore = class {
|
|
|
1649
2480
|
level: "warn",
|
|
1650
2481
|
event: "queue_store.read_failed",
|
|
1651
2482
|
path: this.file,
|
|
1652
|
-
message:
|
|
2483
|
+
message: toErrorMessage(err),
|
|
1653
2484
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1654
2485
|
}));
|
|
1655
2486
|
return [];
|
|
@@ -1701,7 +2532,7 @@ var QueueStore = class {
|
|
|
1701
2532
|
async clear() {
|
|
1702
2533
|
const t0 = Date.now();
|
|
1703
2534
|
try {
|
|
1704
|
-
await
|
|
2535
|
+
await fsp2.unlink(this.file);
|
|
1705
2536
|
this.events?.emit("storage.write", {
|
|
1706
2537
|
sessionId: this.traceId ?? "~boot~",
|
|
1707
2538
|
store: "queue",
|
|
@@ -1720,7 +2551,7 @@ var QueueStore = class {
|
|
|
1720
2551
|
filePath: this.file,
|
|
1721
2552
|
operation: "clear",
|
|
1722
2553
|
outcome: "failure",
|
|
1723
|
-
error:
|
|
2554
|
+
error: toErrorMessage(err),
|
|
1724
2555
|
recoverable: true,
|
|
1725
2556
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
1726
2557
|
});
|
|
@@ -1761,8 +2592,8 @@ var DefaultAttachmentStore = class {
|
|
|
1761
2592
|
let spooledPath;
|
|
1762
2593
|
let data = input.data;
|
|
1763
2594
|
if (this.spoolDir && bytes >= this.spoolThreshold) {
|
|
1764
|
-
await
|
|
1765
|
-
spooledPath =
|
|
2595
|
+
await fsp2.mkdir(this.spoolDir, { recursive: true });
|
|
2596
|
+
spooledPath = path3.join(this.spoolDir, `${id}.bin`);
|
|
1766
2597
|
await atomicWrite(spooledPath, input.data, {
|
|
1767
2598
|
encoding: input.kind === "image" ? "base64" : "utf8"
|
|
1768
2599
|
});
|
|
@@ -1824,7 +2655,7 @@ var DefaultAttachmentStore = class {
|
|
|
1824
2655
|
for (const att of this.items.values()) {
|
|
1825
2656
|
if (att.path) toDelete.push(att.path);
|
|
1826
2657
|
}
|
|
1827
|
-
await Promise.all(toDelete.map((p) =>
|
|
2658
|
+
await Promise.all(toDelete.map((p) => fsp2.unlink(p).catch(() => void 0)));
|
|
1828
2659
|
}
|
|
1829
2660
|
this.items.clear();
|
|
1830
2661
|
this.refs.length = 0;
|
|
@@ -1832,7 +2663,7 @@ var DefaultAttachmentStore = class {
|
|
|
1832
2663
|
}
|
|
1833
2664
|
async toBlock(att) {
|
|
1834
2665
|
if (att.kind === "image") {
|
|
1835
|
-
const data = att.data ?? (att.path ? await
|
|
2666
|
+
const data = att.data ?? (att.path ? await fsp2.readFile(att.path, { encoding: "base64" }) : "");
|
|
1836
2667
|
return {
|
|
1837
2668
|
type: "image",
|
|
1838
2669
|
source: {
|
|
@@ -1842,7 +2673,7 @@ var DefaultAttachmentStore = class {
|
|
|
1842
2673
|
}
|
|
1843
2674
|
};
|
|
1844
2675
|
}
|
|
1845
|
-
const raw = att.data ?? (att.path ? await
|
|
2676
|
+
const raw = att.data ?? (att.path ? await fsp2.readFile(att.path, "utf8") : "");
|
|
1846
2677
|
const label = att.meta.filename ? `<file path="${att.meta.filename}">` : "<pasted>";
|
|
1847
2678
|
const close = att.meta.filename ? "</file>" : "</pasted>";
|
|
1848
2679
|
return { type: "text", text: `${label}
|
|
@@ -1974,10 +2805,10 @@ var FileMemoryBackend = class {
|
|
|
1974
2805
|
}
|
|
1975
2806
|
async remember(scope, entry, filePath) {
|
|
1976
2807
|
const file = this.resolveFile(filePath, scope);
|
|
1977
|
-
await ensureDir(
|
|
2808
|
+
await ensureDir(path3.dirname(file));
|
|
1978
2809
|
let existing = "";
|
|
1979
2810
|
try {
|
|
1980
|
-
existing = await
|
|
2811
|
+
existing = await fsp2.readFile(file, "utf8");
|
|
1981
2812
|
} catch {
|
|
1982
2813
|
}
|
|
1983
2814
|
const id = `mem_${Date.now()}_${randomUUID().slice(0, 8)}`;
|
|
@@ -1994,7 +2825,7 @@ ${line}`;
|
|
|
1994
2825
|
return withFileLock(file, async () => {
|
|
1995
2826
|
let existing;
|
|
1996
2827
|
try {
|
|
1997
|
-
existing = await
|
|
2828
|
+
existing = await fsp2.readFile(file, "utf8");
|
|
1998
2829
|
} catch {
|
|
1999
2830
|
return 0;
|
|
2000
2831
|
}
|
|
@@ -2030,7 +2861,7 @@ ${line}`;
|
|
|
2030
2861
|
async readAll(scope, filePath) {
|
|
2031
2862
|
const file = this.resolveFile(filePath, scope);
|
|
2032
2863
|
try {
|
|
2033
|
-
return await
|
|
2864
|
+
return await fsp2.readFile(file, "utf8");
|
|
2034
2865
|
} catch {
|
|
2035
2866
|
return "";
|
|
2036
2867
|
}
|
|
@@ -2065,7 +2896,7 @@ ${line}`;
|
|
|
2065
2896
|
const file = this.resolveFile(filePath, scope);
|
|
2066
2897
|
let existing;
|
|
2067
2898
|
try {
|
|
2068
|
-
existing = await
|
|
2899
|
+
existing = await fsp2.readFile(file, "utf8");
|
|
2069
2900
|
} catch {
|
|
2070
2901
|
return 0;
|
|
2071
2902
|
}
|
|
@@ -2085,7 +2916,7 @@ ${line}`;
|
|
|
2085
2916
|
const next = lines.join("\n");
|
|
2086
2917
|
const backup = `${file}.bak.${Date.now()}`;
|
|
2087
2918
|
try {
|
|
2088
|
-
await
|
|
2919
|
+
await fsp2.copyFile(file, backup);
|
|
2089
2920
|
} catch {
|
|
2090
2921
|
}
|
|
2091
2922
|
try {
|
|
@@ -2202,7 +3033,7 @@ ${body.trim()}`);
|
|
|
2202
3033
|
operation: "readAll",
|
|
2203
3034
|
outcome: "failure",
|
|
2204
3035
|
durationMs: dur,
|
|
2205
|
-
error:
|
|
3036
|
+
error: toErrorMessage(err),
|
|
2206
3037
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2207
3038
|
});
|
|
2208
3039
|
throw err;
|
|
@@ -2235,7 +3066,7 @@ ${body.trim()}`);
|
|
|
2235
3066
|
operation: "read",
|
|
2236
3067
|
outcome: "failure",
|
|
2237
3068
|
durationMs: dur,
|
|
2238
|
-
error:
|
|
3069
|
+
error: toErrorMessage(err),
|
|
2239
3070
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2240
3071
|
});
|
|
2241
3072
|
throw err;
|
|
@@ -2288,7 +3119,7 @@ ${body.trim()}`);
|
|
|
2288
3119
|
operation: "remember",
|
|
2289
3120
|
outcome: "failure",
|
|
2290
3121
|
durationMs: dur,
|
|
2291
|
-
error:
|
|
3122
|
+
error: toErrorMessage(err),
|
|
2292
3123
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2293
3124
|
});
|
|
2294
3125
|
throw err;
|
|
@@ -2444,7 +3275,7 @@ ${body.trim()}`);
|
|
|
2444
3275
|
operation: "forget",
|
|
2445
3276
|
outcome: "failure",
|
|
2446
3277
|
durationMs: dur,
|
|
2447
|
-
error:
|
|
3278
|
+
error: toErrorMessage(err),
|
|
2448
3279
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2449
3280
|
});
|
|
2450
3281
|
throw err;
|
|
@@ -2486,7 +3317,7 @@ ${body.trim()}`);
|
|
|
2486
3317
|
operation: "consolidate",
|
|
2487
3318
|
outcome: "failure",
|
|
2488
3319
|
durationMs: dur,
|
|
2489
|
-
error:
|
|
3320
|
+
error: toErrorMessage(err),
|
|
2490
3321
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2491
3322
|
});
|
|
2492
3323
|
throw err;
|
|
@@ -2526,7 +3357,7 @@ ${body.trim()}`);
|
|
|
2526
3357
|
operation: "clear",
|
|
2527
3358
|
outcome: "failure",
|
|
2528
3359
|
durationMs: dur,
|
|
2529
|
-
error:
|
|
3360
|
+
error: toErrorMessage(err),
|
|
2530
3361
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2531
3362
|
});
|
|
2532
3363
|
throw err;
|
|
@@ -2562,7 +3393,7 @@ ${body.trim()}`);
|
|
|
2562
3393
|
operation: "clear",
|
|
2563
3394
|
outcome: "failure",
|
|
2564
3395
|
durationMs: dur,
|
|
2565
|
-
error:
|
|
3396
|
+
error: toErrorMessage(err),
|
|
2566
3397
|
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2567
3398
|
});
|
|
2568
3399
|
throw err;
|
|
@@ -2722,7 +3553,7 @@ var AgentError = class extends WrongStackError {
|
|
|
2722
3553
|
};
|
|
2723
3554
|
function toWrongStackError(err, code = ERROR_CODES.AGENT_RUN_FAILED) {
|
|
2724
3555
|
if (err instanceof WrongStackError) return err;
|
|
2725
|
-
const message =
|
|
3556
|
+
const message = toErrorMessage(err);
|
|
2726
3557
|
return new AgentError({
|
|
2727
3558
|
message,
|
|
2728
3559
|
code: code === "UNKNOWN" ? ERROR_CODES.AGENT_RUN_FAILED : code,
|
|
@@ -2798,105 +3629,44 @@ var DefaultConfigStore = class {
|
|
|
2798
3629
|
});
|
|
2799
3630
|
}
|
|
2800
3631
|
const prev = this.current;
|
|
2801
|
-
this.current = next;
|
|
2802
|
-
for (const w of this.watchers) {
|
|
2803
|
-
try {
|
|
2804
|
-
w(next, prev);
|
|
2805
|
-
} catch (err) {
|
|
2806
|
-
console.error(JSON.stringify({
|
|
2807
|
-
level: "error",
|
|
2808
|
-
event: "config_store.watcher_threw",
|
|
2809
|
-
message:
|
|
2810
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2811
|
-
}));
|
|
2812
|
-
}
|
|
2813
|
-
}
|
|
2814
|
-
return next;
|
|
2815
|
-
}
|
|
2816
|
-
watch(cb) {
|
|
2817
|
-
this.watchers.add(cb);
|
|
2818
|
-
return () => this.watchers.delete(cb);
|
|
2819
|
-
}
|
|
2820
|
-
};
|
|
2821
|
-
var FROZEN_EMPTY = Object.freeze({});
|
|
2822
|
-
function deepFreeze(obj) {
|
|
2823
|
-
if (obj === null || typeof obj !== "object") return obj;
|
|
2824
|
-
if (Object.isFrozen(obj)) return obj;
|
|
2825
|
-
for (const key of Object.keys(obj)) {
|
|
2826
|
-
const v = obj[key];
|
|
2827
|
-
if (v !== null && typeof v === "object" && !Object.isFrozen(v)) {
|
|
2828
|
-
deepFreeze(v);
|
|
2829
|
-
}
|
|
2830
|
-
}
|
|
2831
|
-
return Object.freeze(obj);
|
|
2832
|
-
}
|
|
2833
|
-
|
|
2834
|
-
// src/types/secret-vault.ts
|
|
2835
|
-
var ENCRYPTED_PREFIX = "enc:v1:";
|
|
2836
|
-
|
|
2837
|
-
// src/security/secret-vault.ts
|
|
2838
|
-
init_atomic_write();
|
|
2839
|
-
|
|
2840
|
-
// src/utils/deep-merge.ts
|
|
2841
|
-
var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
|
|
2842
|
-
"__proto__",
|
|
2843
|
-
"constructor",
|
|
2844
|
-
"prototype",
|
|
2845
|
-
"__defineGetter__",
|
|
2846
|
-
"__defineSetter__",
|
|
2847
|
-
"__lookupGetter__",
|
|
2848
|
-
"__lookupSetter__"
|
|
2849
|
-
]);
|
|
2850
|
-
function isPrimitiveArray(a) {
|
|
2851
|
-
return a.every((v) => v === null || typeof v !== "object" && typeof v !== "function");
|
|
2852
|
-
}
|
|
2853
|
-
function deepMerge(base, patch, options = {}) {
|
|
2854
|
-
const {
|
|
2855
|
-
conflictResolution = "prefer-patch",
|
|
2856
|
-
arrayMode = "replace",
|
|
2857
|
-
protectProto = true,
|
|
2858
|
-
onNonPrimitiveArrayReplace
|
|
2859
|
-
} = options;
|
|
2860
|
-
if (typeof base !== "object" || base === null) {
|
|
2861
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
2862
|
-
}
|
|
2863
|
-
if (typeof patch !== "object" || patch === null) {
|
|
2864
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
2865
|
-
}
|
|
2866
|
-
if (Array.isArray(base) && Array.isArray(patch)) {
|
|
2867
|
-
if (arrayMode === "concat-primitives" && isPrimitiveArray(base) && isPrimitiveArray(patch)) {
|
|
2868
|
-
return [.../* @__PURE__ */ new Set([...base, ...patch])];
|
|
2869
|
-
}
|
|
2870
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
2871
|
-
}
|
|
2872
|
-
if (Array.isArray(base) || Array.isArray(patch)) {
|
|
2873
|
-
return conflictResolution === "prefer-patch" ? patch : base;
|
|
2874
|
-
}
|
|
2875
|
-
const baseObj = base;
|
|
2876
|
-
const patchObj = patch;
|
|
2877
|
-
const out = { ...baseObj };
|
|
2878
|
-
for (const [k, v] of Object.entries(patchObj)) {
|
|
2879
|
-
if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;
|
|
2880
|
-
const existing = out[k];
|
|
2881
|
-
if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
|
|
2882
|
-
out[k] = deepMerge(existing, v, options);
|
|
2883
|
-
} else if (Array.isArray(v) && Array.isArray(existing)) {
|
|
2884
|
-
if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {
|
|
2885
|
-
onNonPrimitiveArrayReplace(k, existing.length, v.length);
|
|
2886
|
-
}
|
|
2887
|
-
out[k] = deepMerge(existing, v, options);
|
|
2888
|
-
} else if (v !== void 0) {
|
|
2889
|
-
if (onNonPrimitiveArrayReplace && Array.isArray(v) && !isPrimitiveArray(v)) {
|
|
2890
|
-
const existingLen = Array.isArray(existing) ? existing.length : 0;
|
|
2891
|
-
onNonPrimitiveArrayReplace(k, existingLen, v.length);
|
|
3632
|
+
this.current = next;
|
|
3633
|
+
for (const w of this.watchers) {
|
|
3634
|
+
try {
|
|
3635
|
+
w(next, prev);
|
|
3636
|
+
} catch (err) {
|
|
3637
|
+
console.error(JSON.stringify({
|
|
3638
|
+
level: "error",
|
|
3639
|
+
event: "config_store.watcher_threw",
|
|
3640
|
+
message: toErrorMessage(err),
|
|
3641
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3642
|
+
}));
|
|
2892
3643
|
}
|
|
2893
|
-
out[k] = v;
|
|
2894
3644
|
}
|
|
3645
|
+
return next;
|
|
2895
3646
|
}
|
|
2896
|
-
|
|
3647
|
+
watch(cb) {
|
|
3648
|
+
this.watchers.add(cb);
|
|
3649
|
+
return () => this.watchers.delete(cb);
|
|
3650
|
+
}
|
|
3651
|
+
};
|
|
3652
|
+
var FROZEN_EMPTY = Object.freeze({});
|
|
3653
|
+
function deepFreeze(obj) {
|
|
3654
|
+
if (obj === null || typeof obj !== "object") return obj;
|
|
3655
|
+
if (Object.isFrozen(obj)) return obj;
|
|
3656
|
+
for (const key of Object.keys(obj)) {
|
|
3657
|
+
const v = obj[key];
|
|
3658
|
+
if (v !== null && typeof v === "object" && !Object.isFrozen(v)) {
|
|
3659
|
+
deepFreeze(v);
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3662
|
+
return Object.freeze(obj);
|
|
2897
3663
|
}
|
|
2898
3664
|
|
|
3665
|
+
// src/types/secret-vault.ts
|
|
3666
|
+
var ENCRYPTED_PREFIX = "enc:v1:";
|
|
3667
|
+
|
|
2899
3668
|
// src/security/secret-vault.ts
|
|
3669
|
+
init_atomic_write();
|
|
2900
3670
|
var KEY_BYTES = 32;
|
|
2901
3671
|
var IV_BYTES = 12;
|
|
2902
3672
|
var TAG_BYTES = 16;
|
|
@@ -2987,7 +3757,7 @@ var DefaultSecretVault = class {
|
|
|
2987
3757
|
} catch (err) {
|
|
2988
3758
|
if (err.code !== "ENOENT") throw err;
|
|
2989
3759
|
}
|
|
2990
|
-
fs.mkdirSync(
|
|
3760
|
+
fs.mkdirSync(path3.dirname(this.keyFile), { recursive: true });
|
|
2991
3761
|
const key = randomBytes(KEY_BYTES);
|
|
2992
3762
|
try {
|
|
2993
3763
|
fs.writeFileSync(this.keyFile, key, { mode: 384, flag: "wx" });
|
|
@@ -3011,7 +3781,7 @@ var DefaultSecretVault = class {
|
|
|
3011
3781
|
};
|
|
3012
3782
|
function decryptConfigSecrets(cfg, vault, opts) {
|
|
3013
3783
|
const warn = opts?.warn ?? ((msg) => console.warn(msg));
|
|
3014
|
-
return
|
|
3784
|
+
return walk2(cfg, vault, (v, key) => {
|
|
3015
3785
|
try {
|
|
3016
3786
|
return vault.decrypt(v);
|
|
3017
3787
|
} catch (err) {
|
|
@@ -3023,20 +3793,20 @@ function decryptConfigSecrets(cfg, vault, opts) {
|
|
|
3023
3793
|
});
|
|
3024
3794
|
}
|
|
3025
3795
|
function encryptConfigSecrets(cfg, vault, _opts) {
|
|
3026
|
-
return
|
|
3796
|
+
return walk2(cfg, vault, (v) => vault.encrypt(v));
|
|
3027
3797
|
}
|
|
3028
|
-
function
|
|
3798
|
+
function walk2(node, vault, transform) {
|
|
3029
3799
|
if (node === null || node === void 0) return node;
|
|
3030
3800
|
if (typeof node !== "object") return node;
|
|
3031
3801
|
if (Array.isArray(node)) {
|
|
3032
|
-
return node.map((item) =>
|
|
3802
|
+
return node.map((item) => walk2(item, vault, transform));
|
|
3033
3803
|
}
|
|
3034
3804
|
const out = /* @__PURE__ */ Object.create(null);
|
|
3035
3805
|
for (const [k, v] of Object.entries(node)) {
|
|
3036
3806
|
if (typeof v === "string" && isSecretField(k)) {
|
|
3037
3807
|
out[k] = transform(v, k);
|
|
3038
3808
|
} else if (typeof v === "object" && v !== null) {
|
|
3039
|
-
out[k] =
|
|
3809
|
+
out[k] = walk2(v, vault, transform);
|
|
3040
3810
|
} else {
|
|
3041
3811
|
out[k] = v;
|
|
3042
3812
|
}
|
|
@@ -3053,20 +3823,20 @@ function isSecretField(name) {
|
|
|
3053
3823
|
async function rewriteConfigEncrypted(configPath, vault, patch) {
|
|
3054
3824
|
let current = {};
|
|
3055
3825
|
try {
|
|
3056
|
-
const raw = await
|
|
3826
|
+
const raw = await fsp2.readFile(configPath, "utf8");
|
|
3057
3827
|
current = JSON.parse(raw);
|
|
3058
3828
|
} catch {
|
|
3059
3829
|
}
|
|
3060
3830
|
const merged = deepMerge(current, patch ?? {});
|
|
3061
3831
|
const encrypted = encryptConfigSecrets(merged, vault);
|
|
3062
|
-
await
|
|
3832
|
+
await fsp2.mkdir(path3.dirname(configPath), { recursive: true });
|
|
3063
3833
|
await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
3064
3834
|
await restrictFilePermissions(configPath);
|
|
3065
3835
|
}
|
|
3066
3836
|
async function migratePlaintextSecrets(configPath, vault, logger) {
|
|
3067
3837
|
let raw;
|
|
3068
3838
|
try {
|
|
3069
|
-
raw = await
|
|
3839
|
+
raw = await fsp2.readFile(configPath, "utf8");
|
|
3070
3840
|
} catch {
|
|
3071
3841
|
return { migrated: 0, file: configPath };
|
|
3072
3842
|
}
|
|
@@ -3108,7 +3878,7 @@ async function restrictFilePermissions(filePath, opts) {
|
|
|
3108
3878
|
}
|
|
3109
3879
|
} else {
|
|
3110
3880
|
try {
|
|
3111
|
-
await
|
|
3881
|
+
await fsp2.chmod(filePath, 384);
|
|
3112
3882
|
} catch {
|
|
3113
3883
|
}
|
|
3114
3884
|
}
|
|
@@ -3194,21 +3964,6 @@ function isContextWindowModeId(id) {
|
|
|
3194
3964
|
return CONTEXT_WINDOW_MODES.some((m) => m.id === id);
|
|
3195
3965
|
}
|
|
3196
3966
|
|
|
3197
|
-
// src/utils/safe-json.ts
|
|
3198
|
-
function safeParse(input, maxBytes = 5e6) {
|
|
3199
|
-
if (input.length > maxBytes) {
|
|
3200
|
-
return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
|
|
3201
|
-
}
|
|
3202
|
-
try {
|
|
3203
|
-
return { ok: true, value: JSON.parse(input) };
|
|
3204
|
-
} catch (err) {
|
|
3205
|
-
return {
|
|
3206
|
-
ok: false,
|
|
3207
|
-
error: err instanceof Error ? err.message : String(err)
|
|
3208
|
-
};
|
|
3209
|
-
}
|
|
3210
|
-
}
|
|
3211
|
-
|
|
3212
3967
|
// src/types/default-config.ts
|
|
3213
3968
|
var DEFAULT_TOOLS_CONFIG = Object.freeze({
|
|
3214
3969
|
defaultExecutionStrategy: "smart",
|
|
@@ -3384,7 +4139,7 @@ var DefaultConfigLoader = class {
|
|
|
3384
4139
|
level: "warn",
|
|
3385
4140
|
event: "config.source_load_failed",
|
|
3386
4141
|
source: src.name,
|
|
3387
|
-
message:
|
|
4142
|
+
message: toErrorMessage(err),
|
|
3388
4143
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3389
4144
|
}));
|
|
3390
4145
|
}
|
|
@@ -3467,7 +4222,7 @@ var DefaultConfigLoader = class {
|
|
|
3467
4222
|
const fp = this.paths.syncConfig;
|
|
3468
4223
|
const t0 = Date.now();
|
|
3469
4224
|
try {
|
|
3470
|
-
const raw = await
|
|
4225
|
+
const raw = await fsp2.readFile(fp, "utf8");
|
|
3471
4226
|
const parsed = safeParse(raw);
|
|
3472
4227
|
if (!parsed.ok || !parsed.value) {
|
|
3473
4228
|
this.events?.emit("storage.read", {
|
|
@@ -3521,7 +4276,7 @@ var DefaultConfigLoader = class {
|
|
|
3521
4276
|
console.warn(JSON.stringify({
|
|
3522
4277
|
level: "warn",
|
|
3523
4278
|
event: "config.sync_load_failed",
|
|
3524
|
-
message:
|
|
4279
|
+
message: toErrorMessage(err),
|
|
3525
4280
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3526
4281
|
}));
|
|
3527
4282
|
return null;
|
|
@@ -3531,7 +4286,7 @@ var DefaultConfigLoader = class {
|
|
|
3531
4286
|
let raw;
|
|
3532
4287
|
const t0 = Date.now();
|
|
3533
4288
|
try {
|
|
3534
|
-
raw = await
|
|
4289
|
+
raw = await fsp2.readFile(file, "utf8");
|
|
3535
4290
|
} catch (err) {
|
|
3536
4291
|
if (err.code !== "ENOENT") {
|
|
3537
4292
|
this.events?.emit("storage.read", {
|
|
@@ -3548,7 +4303,7 @@ var DefaultConfigLoader = class {
|
|
|
3548
4303
|
level: "warn",
|
|
3549
4304
|
event: "config.read_failed",
|
|
3550
4305
|
path: file,
|
|
3551
|
-
message:
|
|
4306
|
+
message: toErrorMessage(err),
|
|
3552
4307
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3553
4308
|
}));
|
|
3554
4309
|
}
|
|
@@ -3702,7 +4457,7 @@ var RecoveryLock = class {
|
|
|
3702
4457
|
sessionStore;
|
|
3703
4458
|
probe;
|
|
3704
4459
|
constructor(opts) {
|
|
3705
|
-
this.file =
|
|
4460
|
+
this.file = path3.join(opts.dir, LOCK_FILE);
|
|
3706
4461
|
this.pid = opts.pid ?? process.pid;
|
|
3707
4462
|
this.hostname = opts.hostname ?? os.hostname();
|
|
3708
4463
|
this.maxAgeMs = opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
|
|
@@ -3763,7 +4518,7 @@ var RecoveryLock = class {
|
|
|
3763
4518
|
* null return before calling this.
|
|
3764
4519
|
*/
|
|
3765
4520
|
async write(sessionId) {
|
|
3766
|
-
await ensureDir(
|
|
4521
|
+
await ensureDir(path3.dirname(this.file));
|
|
3767
4522
|
const lock = {
|
|
3768
4523
|
v: 1,
|
|
3769
4524
|
sessionId,
|
|
@@ -3772,7 +4527,7 @@ var RecoveryLock = class {
|
|
|
3772
4527
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3773
4528
|
};
|
|
3774
4529
|
try {
|
|
3775
|
-
await
|
|
4530
|
+
await fsp2.writeFile(this.file, JSON.stringify(lock), { flag: "wx", mode: 384 });
|
|
3776
4531
|
} catch (err) {
|
|
3777
4532
|
const code = err.code;
|
|
3778
4533
|
if (code === "EEXIST") {
|
|
@@ -3788,7 +4543,7 @@ var RecoveryLock = class {
|
|
|
3788
4543
|
*/
|
|
3789
4544
|
async clear() {
|
|
3790
4545
|
try {
|
|
3791
|
-
await
|
|
4546
|
+
await fsp2.unlink(this.file);
|
|
3792
4547
|
} catch (err) {
|
|
3793
4548
|
const code = err.code;
|
|
3794
4549
|
if (code === "ENOENT") return;
|
|
@@ -3798,7 +4553,7 @@ var RecoveryLock = class {
|
|
|
3798
4553
|
async readLock() {
|
|
3799
4554
|
let raw;
|
|
3800
4555
|
try {
|
|
3801
|
-
raw = await
|
|
4556
|
+
raw = await fsp2.readFile(this.file, "utf8");
|
|
3802
4557
|
} catch (err) {
|
|
3803
4558
|
const code = err.code;
|
|
3804
4559
|
if (code === "ENOENT") return null;
|
|
@@ -3830,42 +4585,6 @@ function defaultIsPidAlive(pid) {
|
|
|
3830
4585
|
}
|
|
3831
4586
|
}
|
|
3832
4587
|
|
|
3833
|
-
// src/utils/regex-guard.ts
|
|
3834
|
-
var MAX_PATTERN_LEN = 512;
|
|
3835
|
-
var DANGEROUS_PATTERNS = [
|
|
3836
|
-
/(\([^)]*[+*][^)]*\))[+*]/,
|
|
3837
|
-
// (a+)+, (.*)+, etc
|
|
3838
|
-
/(\(\?:[^)]*[+*][^)]*\))[+*]/
|
|
3839
|
-
// same, with non-capturing group
|
|
3840
|
-
];
|
|
3841
|
-
function compileUserRegex(pattern, flags) {
|
|
3842
|
-
if (typeof pattern !== "string") {
|
|
3843
|
-
return { ok: false, reason: "pattern must be a string" };
|
|
3844
|
-
}
|
|
3845
|
-
if (pattern.length === 0) {
|
|
3846
|
-
return { ok: false, reason: "pattern is empty" };
|
|
3847
|
-
}
|
|
3848
|
-
if (pattern.length > MAX_PATTERN_LEN) {
|
|
3849
|
-
return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };
|
|
3850
|
-
}
|
|
3851
|
-
for (const rx of DANGEROUS_PATTERNS) {
|
|
3852
|
-
if (rx.test(pattern)) {
|
|
3853
|
-
return {
|
|
3854
|
-
ok: false,
|
|
3855
|
-
reason: "pattern looks vulnerable to catastrophic backtracking \u2014 rewrite without nested quantifiers"
|
|
3856
|
-
};
|
|
3857
|
-
}
|
|
3858
|
-
}
|
|
3859
|
-
try {
|
|
3860
|
-
return { ok: true, regex: new RegExp(pattern, flags) };
|
|
3861
|
-
} catch (err) {
|
|
3862
|
-
return {
|
|
3863
|
-
ok: false,
|
|
3864
|
-
reason: err instanceof Error ? err.message : "invalid regex"
|
|
3865
|
-
};
|
|
3866
|
-
}
|
|
3867
|
-
}
|
|
3868
|
-
|
|
3869
4588
|
// src/storage/session-reader.ts
|
|
3870
4589
|
var DefaultSessionReader = class {
|
|
3871
4590
|
store;
|
|
@@ -4281,7 +5000,7 @@ function isAllowed(type, level) {
|
|
|
4281
5000
|
return true;
|
|
4282
5001
|
}
|
|
4283
5002
|
function createSessionEventBridge(writer, level = "standard", options = {}) {
|
|
4284
|
-
|
|
5003
|
+
let currentLevel = level ?? "standard";
|
|
4285
5004
|
const resolveWriter = typeof writer === "function" ? writer : () => writer;
|
|
4286
5005
|
const progressCounters = /* @__PURE__ */ new Map();
|
|
4287
5006
|
const toolProgressConfig = options.sampling?.toolProgress ?? {};
|
|
@@ -4302,14 +5021,19 @@ function createSessionEventBridge(writer, level = "standard", options = {}) {
|
|
|
4302
5021
|
return true;
|
|
4303
5022
|
}
|
|
4304
5023
|
return {
|
|
4305
|
-
level
|
|
5024
|
+
get level() {
|
|
5025
|
+
return currentLevel;
|
|
5026
|
+
},
|
|
5027
|
+
setAuditLevel(next) {
|
|
5028
|
+
currentLevel = next ?? "standard";
|
|
5029
|
+
},
|
|
4306
5030
|
allows(type) {
|
|
4307
|
-
return isAllowed(type,
|
|
5031
|
+
return isAllowed(type, currentLevel);
|
|
4308
5032
|
},
|
|
4309
5033
|
async append(event) {
|
|
4310
5034
|
const target = resolveWriter();
|
|
4311
5035
|
if (!target) return;
|
|
4312
|
-
if (!isAllowed(event.type,
|
|
5036
|
+
if (!isAllowed(event.type, currentLevel)) return;
|
|
4313
5037
|
if (!shouldSample(event)) return;
|
|
4314
5038
|
try {
|
|
4315
5039
|
await target.append(event);
|
|
@@ -4320,7 +5044,7 @@ function createSessionEventBridge(writer, level = "standard", options = {}) {
|
|
|
4320
5044
|
const target = resolveWriter();
|
|
4321
5045
|
if (!target || events.length === 0) return;
|
|
4322
5046
|
const allowed = events.filter(
|
|
4323
|
-
(e) => isAllowed(e.type,
|
|
5047
|
+
(e) => isAllowed(e.type, currentLevel) && shouldSample(e)
|
|
4324
5048
|
);
|
|
4325
5049
|
if (allowed.length === 0) return;
|
|
4326
5050
|
try {
|
|
@@ -4357,7 +5081,7 @@ async function loadTodosCheckpoint(filePath, events, traceId) {
|
|
|
4357
5081
|
const t0 = Date.now();
|
|
4358
5082
|
let raw;
|
|
4359
5083
|
try {
|
|
4360
|
-
raw = await
|
|
5084
|
+
raw = await fsp2.readFile(filePath, "utf8");
|
|
4361
5085
|
} catch (err) {
|
|
4362
5086
|
events?.emit("storage.error", {
|
|
4363
5087
|
sessionId: traceId ?? "~boot~",
|
|
@@ -4365,7 +5089,7 @@ async function loadTodosCheckpoint(filePath, events, traceId) {
|
|
|
4365
5089
|
filePath,
|
|
4366
5090
|
operation: "load",
|
|
4367
5091
|
outcome: "failure",
|
|
4368
|
-
error:
|
|
5092
|
+
error: toErrorMessage(err),
|
|
4369
5093
|
recoverable: true
|
|
4370
5094
|
});
|
|
4371
5095
|
return null;
|
|
@@ -4437,13 +5161,13 @@ async function saveTodosCheckpoint(filePath, sessionId, todos, events, traceId)
|
|
|
4437
5161
|
filePath,
|
|
4438
5162
|
operation: "save",
|
|
4439
5163
|
outcome: "failure",
|
|
4440
|
-
error:
|
|
5164
|
+
error: toErrorMessage(err),
|
|
4441
5165
|
recoverable: false
|
|
4442
5166
|
});
|
|
4443
5167
|
console.warn(JSON.stringify({
|
|
4444
5168
|
level: "warn",
|
|
4445
5169
|
event: "todos_checkpoint.save_failed",
|
|
4446
|
-
message:
|
|
5170
|
+
message: toErrorMessage(err),
|
|
4447
5171
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4448
5172
|
}));
|
|
4449
5173
|
}
|
|
@@ -4454,7 +5178,7 @@ function attachTodosCheckpoint(state, filePath, sessionId, events, traceId) {
|
|
|
4454
5178
|
let writeChain = Promise.resolve();
|
|
4455
5179
|
const enqueueWrite = (todos) => {
|
|
4456
5180
|
writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos, events, traceId)).catch((err) => {
|
|
4457
|
-
const msg =
|
|
5181
|
+
const msg = toErrorMessage(err);
|
|
4458
5182
|
console.error(JSON.stringify({
|
|
4459
5183
|
level: "error",
|
|
4460
5184
|
event: "todos_checkpoint.write_chain_failed",
|
|
@@ -4499,14 +5223,14 @@ async function loadPlan(filePath, events) {
|
|
|
4499
5223
|
const t0 = Date.now();
|
|
4500
5224
|
let raw;
|
|
4501
5225
|
try {
|
|
4502
|
-
raw = await
|
|
5226
|
+
raw = await fsp2.readFile(filePath, "utf8");
|
|
4503
5227
|
} catch (err) {
|
|
4504
5228
|
events?.emit("storage.error", {
|
|
4505
5229
|
sessionId: "~boot~",
|
|
4506
5230
|
store: "plan",
|
|
4507
5231
|
filePath,
|
|
4508
5232
|
operation: "load",
|
|
4509
|
-
error:
|
|
5233
|
+
error: toErrorMessage(err),
|
|
4510
5234
|
recoverable: true
|
|
4511
5235
|
});
|
|
4512
5236
|
return null;
|
|
@@ -4565,12 +5289,12 @@ async function savePlan(filePath, plan, events) {
|
|
|
4565
5289
|
store: "plan",
|
|
4566
5290
|
filePath,
|
|
4567
5291
|
operation: "save",
|
|
4568
|
-
error:
|
|
5292
|
+
error: toErrorMessage(err),
|
|
4569
5293
|
recoverable: false
|
|
4570
5294
|
});
|
|
4571
5295
|
console.warn(
|
|
4572
5296
|
"[plan-store] save failed:",
|
|
4573
|
-
|
|
5297
|
+
toErrorMessage(err)
|
|
4574
5298
|
);
|
|
4575
5299
|
}
|
|
4576
5300
|
}
|
|
@@ -4791,7 +5515,7 @@ init_atomic_write();
|
|
|
4791
5515
|
async function loadDirectorState(filePath) {
|
|
4792
5516
|
let raw;
|
|
4793
5517
|
try {
|
|
4794
|
-
raw = await
|
|
5518
|
+
raw = await fsp2.readFile(filePath, "utf8");
|
|
4795
5519
|
} catch {
|
|
4796
5520
|
return null;
|
|
4797
5521
|
}
|
|
@@ -4806,7 +5530,7 @@ async function loadDirectorState(filePath) {
|
|
|
4806
5530
|
async function acquireDirectorStateLock(lockPath, processId = process.pid) {
|
|
4807
5531
|
let existing;
|
|
4808
5532
|
try {
|
|
4809
|
-
existing = await
|
|
5533
|
+
existing = await fsp2.readFile(lockPath, "utf8");
|
|
4810
5534
|
} catch {
|
|
4811
5535
|
}
|
|
4812
5536
|
if (existing) {
|
|
@@ -4830,7 +5554,7 @@ async function acquireDirectorStateLock(lockPath, processId = process.pid) {
|
|
|
4830
5554
|
}
|
|
4831
5555
|
async function releaseDirectorStateLock(lockPath) {
|
|
4832
5556
|
try {
|
|
4833
|
-
await
|
|
5557
|
+
await fsp2.unlink(lockPath);
|
|
4834
5558
|
} catch {
|
|
4835
5559
|
}
|
|
4836
5560
|
}
|
|
@@ -4955,7 +5679,7 @@ var DirectorStateCheckpoint = class {
|
|
|
4955
5679
|
} catch (err) {
|
|
4956
5680
|
console.warn(
|
|
4957
5681
|
"[director-state] checkpoint write failed:",
|
|
4958
|
-
|
|
5682
|
+
toErrorMessage(err)
|
|
4959
5683
|
);
|
|
4960
5684
|
} finally {
|
|
4961
5685
|
this.writing = false;
|
|
@@ -5165,82 +5889,6 @@ function getDangerousCapabilities(toolOrCaps) {
|
|
|
5165
5889
|
|
|
5166
5890
|
// src/security/permission-policy.ts
|
|
5167
5891
|
init_atomic_write();
|
|
5168
|
-
|
|
5169
|
-
// src/utils/glob-match.ts
|
|
5170
|
-
function escapeRegex(s) {
|
|
5171
|
-
return s.replace(/[.+^${}()|\\]/g, "\\$&");
|
|
5172
|
-
}
|
|
5173
|
-
var COMPILED_GLOB_CACHE = /* @__PURE__ */ new Map();
|
|
5174
|
-
var CACHE_MAX_SIZE = 2e3;
|
|
5175
|
-
function getCachedGlob(pattern) {
|
|
5176
|
-
const cached = COMPILED_GLOB_CACHE.get(pattern);
|
|
5177
|
-
if (cached) return cached;
|
|
5178
|
-
if (COMPILED_GLOB_CACHE.size >= CACHE_MAX_SIZE) {
|
|
5179
|
-
const keys = [...COMPILED_GLOB_CACHE.keys()];
|
|
5180
|
-
for (let i = 0; i < Math.floor(CACHE_MAX_SIZE / 4); i++) {
|
|
5181
|
-
COMPILED_GLOB_CACHE.delete(expectDefined(keys[i]));
|
|
5182
|
-
}
|
|
5183
|
-
}
|
|
5184
|
-
const re = compileGlob(pattern);
|
|
5185
|
-
COMPILED_GLOB_CACHE.set(pattern, re);
|
|
5186
|
-
return re;
|
|
5187
|
-
}
|
|
5188
|
-
var MAX_GLOB_PATTERN_LEN = 1024;
|
|
5189
|
-
function compileGlob(pattern) {
|
|
5190
|
-
if (pattern.length > MAX_GLOB_PATTERN_LEN) {
|
|
5191
|
-
throw new Error(`Glob pattern exceeds ${MAX_GLOB_PATTERN_LEN} characters`);
|
|
5192
|
-
}
|
|
5193
|
-
let i = 0;
|
|
5194
|
-
let re = "^";
|
|
5195
|
-
while (i < pattern.length) {
|
|
5196
|
-
const c = pattern[i];
|
|
5197
|
-
if (c === "*") {
|
|
5198
|
-
if (pattern[i + 1] === "*") {
|
|
5199
|
-
re += ".*";
|
|
5200
|
-
i += 2;
|
|
5201
|
-
if (pattern[i] === "/") i++;
|
|
5202
|
-
} else {
|
|
5203
|
-
re += "[^/]*";
|
|
5204
|
-
i++;
|
|
5205
|
-
}
|
|
5206
|
-
} else if (c === "?") {
|
|
5207
|
-
re += "[^/]";
|
|
5208
|
-
i++;
|
|
5209
|
-
} else if (c === "[") {
|
|
5210
|
-
let cls = "[";
|
|
5211
|
-
i++;
|
|
5212
|
-
if (pattern[i] === "!" || pattern[i] === "^") {
|
|
5213
|
-
cls += "^";
|
|
5214
|
-
i++;
|
|
5215
|
-
}
|
|
5216
|
-
while (i < pattern.length && pattern[i] !== "]") {
|
|
5217
|
-
const ch = pattern[i] ?? "";
|
|
5218
|
-
if (ch === "\\") {
|
|
5219
|
-
cls += "\\\\";
|
|
5220
|
-
} else if (ch === "]" || ch === "^") {
|
|
5221
|
-
cls += `\\${ch}`;
|
|
5222
|
-
} else {
|
|
5223
|
-
cls += ch;
|
|
5224
|
-
}
|
|
5225
|
-
i++;
|
|
5226
|
-
}
|
|
5227
|
-
cls += "]";
|
|
5228
|
-
re += cls;
|
|
5229
|
-
i++;
|
|
5230
|
-
} else {
|
|
5231
|
-
re += escapeRegex(c ?? "");
|
|
5232
|
-
i++;
|
|
5233
|
-
}
|
|
5234
|
-
}
|
|
5235
|
-
re += "$";
|
|
5236
|
-
return new RegExp(re);
|
|
5237
|
-
}
|
|
5238
|
-
function matchGlob(pattern, input) {
|
|
5239
|
-
return getCachedGlob(pattern).test(input);
|
|
5240
|
-
}
|
|
5241
|
-
function matchAny(patterns, input) {
|
|
5242
|
-
return patterns.some((p) => matchGlob(p, input));
|
|
5243
|
-
}
|
|
5244
5892
|
var DESTRUCTIVE_BASH_PATTERNS = [
|
|
5245
5893
|
/\bgit\s+(?:clean\s+-[^\s]*[xdf]|reset\s+--hard)\b/i,
|
|
5246
5894
|
/\b(?:drop|truncate)\s+(?:table|database|schema)\b/i,
|
|
@@ -5263,9 +5911,9 @@ function getInputString(input, key) {
|
|
|
5263
5911
|
function pathLooksInsideProject(rawPath, projectRoot) {
|
|
5264
5912
|
if (!projectRoot) return false;
|
|
5265
5913
|
if (rawPath === "~" || rawPath.startsWith("~/") || rawPath.startsWith("~\\")) return false;
|
|
5266
|
-
const resolved =
|
|
5267
|
-
const relative2 =
|
|
5268
|
-
return !!relative2 && !relative2.startsWith("..") && !
|
|
5914
|
+
const resolved = path3.resolve(projectRoot, rawPath);
|
|
5915
|
+
const relative2 = path3.relative(projectRoot, resolved);
|
|
5916
|
+
return !!relative2 && !relative2.startsWith("..") && !path3.isAbsolute(relative2);
|
|
5269
5917
|
}
|
|
5270
5918
|
function tokenizeShell(command) {
|
|
5271
5919
|
return command.match(/"[^"]*"|'[^']*'|\S+/g)?.map((token) => token.replace(/^['"]|['"]$/g, "")) ?? [];
|
|
@@ -5275,7 +5923,7 @@ function pathTokenIsOutsideProject(token, projectRoot) {
|
|
|
5275
5923
|
if (token === "/" || token === "~" || token === "." || token === "..") return token !== ".";
|
|
5276
5924
|
if (token.includes("*")) return true;
|
|
5277
5925
|
if (token.startsWith("..") || token.includes("../") || token.includes("..\\")) return true;
|
|
5278
|
-
if (
|
|
5926
|
+
if (path3.isAbsolute(token) || token.startsWith("~/")) return !pathLooksInsideProject(token, projectRoot);
|
|
5279
5927
|
return false;
|
|
5280
5928
|
}
|
|
5281
5929
|
function hasDangerousDeleteTarget(tokens, start, projectRoot) {
|
|
@@ -5421,7 +6069,7 @@ var DefaultPermissionPolicy = class {
|
|
|
5421
6069
|
}
|
|
5422
6070
|
async reload() {
|
|
5423
6071
|
try {
|
|
5424
|
-
const raw = await
|
|
6072
|
+
const raw = await fsp2.readFile(this.trustFile, "utf8");
|
|
5425
6073
|
const parsed = safeParse(raw);
|
|
5426
6074
|
if (parsed.ok && parsed.value) this.policy = parsed.value;
|
|
5427
6075
|
} catch {
|
|
@@ -5693,11 +6341,6 @@ var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
|
|
|
5693
6341
|
}
|
|
5694
6342
|
};
|
|
5695
6343
|
|
|
5696
|
-
// src/utils/string.ts
|
|
5697
|
-
function truncate(s, max) {
|
|
5698
|
-
return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
|
|
5699
|
-
}
|
|
5700
|
-
|
|
5701
6344
|
// src/types/provider.ts
|
|
5702
6345
|
var ProviderError = class extends WrongStackError {
|
|
5703
6346
|
status;
|
|
@@ -5934,12 +6577,12 @@ var DefaultSkillLoader = class {
|
|
|
5934
6577
|
const seen = /* @__PURE__ */ new Set();
|
|
5935
6578
|
for (const { dir, source } of this.dirs) {
|
|
5936
6579
|
try {
|
|
5937
|
-
const entries = await
|
|
6580
|
+
const entries = await fsp2.readdir(dir, { withFileTypes: true });
|
|
5938
6581
|
for (const e of entries) {
|
|
5939
6582
|
if (!e.isDirectory()) continue;
|
|
5940
|
-
const skillFile =
|
|
6583
|
+
const skillFile = path3.join(dir, e.name, "SKILL.md");
|
|
5941
6584
|
try {
|
|
5942
|
-
const raw = await
|
|
6585
|
+
const raw = await fsp2.readFile(skillFile, "utf8");
|
|
5943
6586
|
const meta = parseFrontmatter(raw);
|
|
5944
6587
|
if (!meta.name || !meta.description) continue;
|
|
5945
6588
|
if (seen.has(meta.name)) continue;
|
|
@@ -5982,7 +6625,7 @@ var DefaultSkillLoader = class {
|
|
|
5982
6625
|
const entries = [];
|
|
5983
6626
|
for (const s of skills) {
|
|
5984
6627
|
try {
|
|
5985
|
-
const raw = await
|
|
6628
|
+
const raw = await fsp2.readFile(s.path, "utf8");
|
|
5986
6629
|
const { trigger, scope } = parseDescription(raw);
|
|
5987
6630
|
entries.push({ name: s.name, trigger, scope, source: s.source, path: s.path });
|
|
5988
6631
|
} catch {
|
|
@@ -6001,7 +6644,7 @@ var DefaultSkillLoader = class {
|
|
|
6001
6644
|
if (cached !== void 0) return cached;
|
|
6002
6645
|
const m = await this.find(name);
|
|
6003
6646
|
if (!m) throw new Error(`Skill "${name}" not found`);
|
|
6004
|
-
const body = await
|
|
6647
|
+
const body = await fsp2.readFile(m.path, "utf8");
|
|
6005
6648
|
this.bodyCache.set(name, body);
|
|
6006
6649
|
return body;
|
|
6007
6650
|
}
|
|
@@ -6011,12 +6654,12 @@ var DefaultSkillLoader = class {
|
|
|
6011
6654
|
if (cached !== void 0) return cached;
|
|
6012
6655
|
const m = await this.find(name);
|
|
6013
6656
|
if (!m) throw new Error(`Skill "${name}" not found`);
|
|
6014
|
-
const savePath =
|
|
6657
|
+
const savePath = path3.join(path3.dirname(m.path), "SKILL.save.md");
|
|
6015
6658
|
let result;
|
|
6016
6659
|
try {
|
|
6017
|
-
result = await
|
|
6660
|
+
result = await fsp2.readFile(savePath, "utf8");
|
|
6018
6661
|
} catch {
|
|
6019
|
-
const full = await
|
|
6662
|
+
const full = await fsp2.readFile(m.path, "utf8");
|
|
6020
6663
|
const body = stripFrontmatter(full);
|
|
6021
6664
|
const compact = compactSkillBody(body);
|
|
6022
6665
|
if (compact) {
|
|
@@ -6041,139 +6684,45 @@ function parseFrontmatter(raw) {
|
|
|
6041
6684
|
let value = [];
|
|
6042
6685
|
const flush = () => {
|
|
6043
6686
|
if (key) {
|
|
6044
|
-
out[key] = value.join("\n").trim();
|
|
6045
|
-
}
|
|
6046
|
-
key = null;
|
|
6047
|
-
value = [];
|
|
6048
|
-
};
|
|
6049
|
-
for (const line of block.split("\n")) {
|
|
6050
|
-
const m = /^([a-zA-Z_]+):\s*(\|?)\s*(.*)$/.exec(line);
|
|
6051
|
-
if (m) {
|
|
6052
|
-
flush();
|
|
6053
|
-
key = m[1] ?? "";
|
|
6054
|
-
const pipe = m[2];
|
|
6055
|
-
const rest = m[3] ?? "";
|
|
6056
|
-
if (pipe === "|") {
|
|
6057
|
-
value = [];
|
|
6058
|
-
} else if (rest) {
|
|
6059
|
-
value = [rest];
|
|
6060
|
-
} else {
|
|
6061
|
-
value = [];
|
|
6062
|
-
}
|
|
6063
|
-
} else if (key) {
|
|
6064
|
-
value.push(line.replace(/^\s+/, ""));
|
|
6065
|
-
}
|
|
6066
|
-
}
|
|
6067
|
-
flush();
|
|
6068
|
-
return out;
|
|
6069
|
-
}
|
|
6070
|
-
function parseDescription(raw) {
|
|
6071
|
-
const fm = parseFrontmatter(raw);
|
|
6072
|
-
const desc = fm.description ?? "";
|
|
6073
|
-
const firstSentenceEnd = desc.indexOf(". ");
|
|
6074
|
-
const trigger = firstSentenceEnd !== -1 ? desc.slice(0, firstSentenceEnd + 1).trim() : desc.trim().split("\n")[0] ?? "";
|
|
6075
|
-
const scope = [];
|
|
6076
|
-
const coversMatch = /(?:covers|for|including)\s+([^.]+)/i.exec(desc);
|
|
6077
|
-
if (coversMatch) {
|
|
6078
|
-
const items = coversMatch[1] ?? "".replace(/[·•]/g, ",").split(",").map((s) => s.trim()).filter(Boolean);
|
|
6079
|
-
scope.push(...items);
|
|
6080
|
-
}
|
|
6081
|
-
return { trigger, scope };
|
|
6082
|
-
}
|
|
6083
|
-
|
|
6084
|
-
// src/utils/json-repair.ts
|
|
6085
|
-
function completePartialObject(s) {
|
|
6086
|
-
if (!s.trim().startsWith("{")) return s;
|
|
6087
|
-
if (tryParse(s).ok) return s;
|
|
6088
|
-
return repairTruncated(s);
|
|
6089
|
-
}
|
|
6090
|
-
function repairTruncated(s) {
|
|
6091
|
-
const stack = [];
|
|
6092
|
-
let inString = false;
|
|
6093
|
-
let escaped = false;
|
|
6094
|
-
let sawKey = false;
|
|
6095
|
-
let prevSig = "";
|
|
6096
|
-
let contentEnd = 0;
|
|
6097
|
-
let stringBraceDepth = 0;
|
|
6098
|
-
for (let i = 0; i < s.length; i++) {
|
|
6099
|
-
const ch = expectDefined(s[i]);
|
|
6100
|
-
if (inString) {
|
|
6101
|
-
contentEnd = i + 1;
|
|
6102
|
-
if (escaped) {
|
|
6103
|
-
escaped = false;
|
|
6104
|
-
continue;
|
|
6105
|
-
}
|
|
6106
|
-
if (ch === "\\") {
|
|
6107
|
-
escaped = true;
|
|
6108
|
-
continue;
|
|
6109
|
-
}
|
|
6110
|
-
if (ch === '"') {
|
|
6111
|
-
inString = false;
|
|
6112
|
-
prevSig = '"';
|
|
6113
|
-
stringBraceDepth = 0;
|
|
6114
|
-
continue;
|
|
6115
|
-
}
|
|
6116
|
-
if (ch === "{") stringBraceDepth++;
|
|
6117
|
-
else if (ch === "}" && stringBraceDepth > 0) stringBraceDepth--;
|
|
6118
|
-
continue;
|
|
6119
|
-
}
|
|
6120
|
-
if (ch === " " || ch === " " || ch === "\n" || ch === "\r") continue;
|
|
6121
|
-
contentEnd = i + 1;
|
|
6122
|
-
if (ch === '"') {
|
|
6123
|
-
inString = true;
|
|
6124
|
-
sawKey = true;
|
|
6125
|
-
stringBraceDepth = 0;
|
|
6126
|
-
prevSig = '"';
|
|
6127
|
-
} else if (ch === "{" || ch === "[") {
|
|
6128
|
-
stack.push(ch);
|
|
6129
|
-
prevSig = ch;
|
|
6130
|
-
} else if (ch === "}" || ch === "]") {
|
|
6131
|
-
stack.pop();
|
|
6132
|
-
prevSig = ch;
|
|
6133
|
-
} else {
|
|
6134
|
-
prevSig = ch;
|
|
6687
|
+
out[key] = value.join("\n").trim();
|
|
6135
6688
|
}
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6689
|
+
key = null;
|
|
6690
|
+
value = [];
|
|
6691
|
+
};
|
|
6692
|
+
for (const line of block.split("\n")) {
|
|
6693
|
+
const m = /^([a-zA-Z_]+):\s*(\|?)\s*(.*)$/.exec(line);
|
|
6694
|
+
if (m) {
|
|
6695
|
+
flush();
|
|
6696
|
+
key = m[1] ?? "";
|
|
6697
|
+
const pipe = m[2];
|
|
6698
|
+
const rest = m[3] ?? "";
|
|
6699
|
+
if (pipe === "|") {
|
|
6700
|
+
value = [];
|
|
6701
|
+
} else if (rest) {
|
|
6702
|
+
value = [rest];
|
|
6703
|
+
} else {
|
|
6704
|
+
value = [];
|
|
6705
|
+
}
|
|
6706
|
+
} else if (key) {
|
|
6707
|
+
value.push(line.replace(/^\s+/, ""));
|
|
6144
6708
|
}
|
|
6145
|
-
if (stringBraceDepth > 0) result += "}".repeat(stringBraceDepth);
|
|
6146
|
-
result += '"';
|
|
6147
|
-
} else if (prevSig === ":") {
|
|
6148
|
-
result += "null";
|
|
6149
6709
|
}
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
}
|
|
6153
|
-
if (!tryParse(result).ok) {
|
|
6154
|
-
const patched = result.replace(/:(\s*)([}\]])/g, ":null$2");
|
|
6155
|
-
if (tryParse(patched).ok) result = patched;
|
|
6156
|
-
}
|
|
6157
|
-
return result;
|
|
6158
|
-
}
|
|
6159
|
-
var VALID_ESCAPE = /* @__PURE__ */ new Set(['"', "\\", "/", "b", "f", "n", "r", "t", "u"]);
|
|
6160
|
-
function endsWithInvalidEscape(str) {
|
|
6161
|
-
const last = str[str.length - 1];
|
|
6162
|
-
if (str[str.length - 2] !== "\\" || last === void 0) return false;
|
|
6163
|
-
if (VALID_ESCAPE.has(last)) return false;
|
|
6164
|
-
let backslashes = 0;
|
|
6165
|
-
for (let k = str.length - 2; k >= 0 && str[k] === "\\"; k--) backslashes++;
|
|
6166
|
-
return backslashes % 2 === 1;
|
|
6710
|
+
flush();
|
|
6711
|
+
return out;
|
|
6167
6712
|
}
|
|
6168
|
-
function
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6713
|
+
function parseDescription(raw) {
|
|
6714
|
+
const fm = parseFrontmatter(raw);
|
|
6715
|
+
const desc = fm.description ?? "";
|
|
6716
|
+
const firstSentenceEnd = desc.indexOf(". ");
|
|
6717
|
+
const trigger = firstSentenceEnd !== -1 ? desc.slice(0, firstSentenceEnd + 1).trim() : desc.trim().split("\n")[0] ?? "";
|
|
6718
|
+
const scope = [];
|
|
6719
|
+
const coversMatch = /(?:covers|for|including)\s+([^.]+)/i.exec(desc);
|
|
6720
|
+
if (coversMatch) {
|
|
6721
|
+
const items = (coversMatch[1] ?? "").replace(/[·•]/g, ",").split(",").map((s) => s.trim()).filter(Boolean);
|
|
6722
|
+
scope.push(...items);
|
|
6173
6723
|
}
|
|
6724
|
+
return { trigger, scope };
|
|
6174
6725
|
}
|
|
6175
|
-
|
|
6176
|
-
// src/core/streaming-response-builder.ts
|
|
6177
6726
|
var STREAM_DRAIN_TIMEOUT_MS = 500;
|
|
6178
6727
|
function buildResponse(state) {
|
|
6179
6728
|
const content = [];
|
|
@@ -6469,244 +7018,97 @@ async function runProviderWithRetry(opts) {
|
|
|
6469
7018
|
const res = provider.capabilities.streaming ? await streamProviderToResponse(provider, request, signal, ctx, events, logger) : await provider.complete(request, { signal });
|
|
6470
7019
|
span?.setAttribute("provider.stopReason", res.stopReason);
|
|
6471
7020
|
span?.setAttribute("provider.usage_in", res.usage.input);
|
|
6472
|
-
span?.setAttribute("provider.usage_out", res.usage.output);
|
|
6473
|
-
span?.end();
|
|
6474
|
-
logger.debug("Provider call succeeded", {
|
|
6475
|
-
...providerLogCtx(provider, request),
|
|
6476
|
-
stopReason: res.stopReason,
|
|
6477
|
-
usageInput: res.usage.input,
|
|
6478
|
-
usageOutput: res.usage.output,
|
|
6479
|
-
cacheRead: res.usage.cacheRead,
|
|
6480
|
-
cacheWrite: res.usage.cacheWrite,
|
|
6481
|
-
attempts: attempt + 1
|
|
6482
|
-
});
|
|
6483
|
-
return res;
|
|
6484
|
-
} catch (err) {
|
|
6485
|
-
if (err instanceof Error) span?.recordError(err);
|
|
6486
|
-
span?.end();
|
|
6487
|
-
if (signal.aborted) throw err;
|
|
6488
|
-
const isProviderErr = err instanceof ProviderError;
|
|
6489
|
-
const errAsErr = err instanceof Error ? err : new Error(String(err));
|
|
6490
|
-
const canRetry = retry.shouldRetry(isProviderErr ? err : errAsErr, attempt);
|
|
6491
|
-
const description = isProviderErr ? err.describe() : errAsErr.message;
|
|
6492
|
-
if (!canRetry) {
|
|
6493
|
-
if (isProviderErr) {
|
|
6494
|
-
events.emit("provider.error", {
|
|
6495
|
-
providerId: err.providerId,
|
|
6496
|
-
status: err.status,
|
|
6497
|
-
description,
|
|
6498
|
-
retryable: false
|
|
6499
|
-
});
|
|
6500
|
-
}
|
|
6501
|
-
logger.error(`Provider call failed after ${attempt + 1} attempt(s) \u2014 ${description}`, {
|
|
6502
|
-
...providerLogCtx(provider, request),
|
|
6503
|
-
attempts: attempt + 1,
|
|
6504
|
-
errorDescription: description,
|
|
6505
|
-
status: isProviderErr ? err.status : void 0,
|
|
6506
|
-
errorName: err instanceof Error ? err.name : void 0,
|
|
6507
|
-
errorStack: err instanceof Error ? err.stack?.split("\n").slice(0, 3).join("\n") : void 0
|
|
6508
|
-
});
|
|
6509
|
-
throw toWrongStackError(err);
|
|
6510
|
-
}
|
|
6511
|
-
const delay = Math.round(retry.delayMs(attempt));
|
|
6512
|
-
const attemptNum = attempt + 1;
|
|
6513
|
-
const maxAttempts = retry.maxAttempts(isProviderErr ? err : errAsErr);
|
|
6514
|
-
logger.warn(`Provider retry ${attemptNum}/${maxAttempts} in ${delay}ms \u2014 ${description}`, {
|
|
6515
|
-
...providerLogCtx(provider, request),
|
|
6516
|
-
attempt: attemptNum,
|
|
6517
|
-
maxAttempts,
|
|
6518
|
-
delayMs: delay,
|
|
6519
|
-
errorDescription: description,
|
|
6520
|
-
status: isProviderErr ? err.status : void 0
|
|
6521
|
-
});
|
|
6522
|
-
if (isProviderErr) {
|
|
6523
|
-
events.emit("provider.retry", {
|
|
6524
|
-
providerId: err.providerId,
|
|
6525
|
-
attempt: attemptNum,
|
|
6526
|
-
delayMs: delay,
|
|
6527
|
-
status: err.status,
|
|
6528
|
-
description
|
|
6529
|
-
});
|
|
6530
|
-
}
|
|
6531
|
-
await new Promise((resolve5, reject) => {
|
|
6532
|
-
let settled = false;
|
|
6533
|
-
const onAbort = () => {
|
|
6534
|
-
if (settled) return;
|
|
6535
|
-
settled = true;
|
|
6536
|
-
clearTimeout(t);
|
|
6537
|
-
reject(new Error("aborted"));
|
|
6538
|
-
};
|
|
6539
|
-
const t = setTimeout(() => {
|
|
6540
|
-
if (settled) return;
|
|
6541
|
-
settled = true;
|
|
6542
|
-
clearTimeout(t);
|
|
6543
|
-
signal.removeEventListener("abort", onAbort);
|
|
6544
|
-
resolve5();
|
|
6545
|
-
}, delay);
|
|
6546
|
-
if (signal.aborted) {
|
|
6547
|
-
onAbort();
|
|
6548
|
-
return;
|
|
6549
|
-
}
|
|
6550
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
6551
|
-
});
|
|
6552
|
-
attempt++;
|
|
6553
|
-
}
|
|
6554
|
-
}
|
|
6555
|
-
}
|
|
6556
|
-
|
|
6557
|
-
// src/execution/provider-runner-impl.ts
|
|
6558
|
-
var DefaultProviderRunner = class {
|
|
6559
|
-
async run(opts) {
|
|
6560
|
-
return runProviderWithRetry(opts);
|
|
6561
|
-
}
|
|
6562
|
-
};
|
|
6563
|
-
|
|
6564
|
-
// src/utils/token-estimate.ts
|
|
6565
|
-
var RoughTokenEstimate = (text, charsPerToken = 3.5) => Math.max(1, Math.ceil(text.length / charsPerToken));
|
|
6566
|
-
var CALIBRATION_GLOBAL_KEY = "__global__";
|
|
6567
|
-
var _cals = /* @__PURE__ */ new Map();
|
|
6568
|
-
function calState(key) {
|
|
6569
|
-
let state = _cals.get(key);
|
|
6570
|
-
if (!state) {
|
|
6571
|
-
state = { ratio: 1, count: 0, prevEst: 0 };
|
|
6572
|
-
_cals.set(key, state);
|
|
6573
|
-
}
|
|
6574
|
-
return state;
|
|
6575
|
-
}
|
|
6576
|
-
var MIN_SAMPLES_FOR_CALIBRATION = 3;
|
|
6577
|
-
var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
|
|
6578
|
-
var ESTIMATE_CACHE_MAX_SIZE = 1e4;
|
|
6579
|
-
function getCachedEstimate(key, compute) {
|
|
6580
|
-
const existing = ESTIMATE_CACHE.get(key);
|
|
6581
|
-
if (existing !== void 0) return existing;
|
|
6582
|
-
if (ESTIMATE_CACHE.size >= ESTIMATE_CACHE_MAX_SIZE) {
|
|
6583
|
-
let evicted = 0;
|
|
6584
|
-
const maxEvict = Math.floor(ESTIMATE_CACHE_MAX_SIZE / 4);
|
|
6585
|
-
for (const k of ESTIMATE_CACHE.keys()) {
|
|
6586
|
-
if (evicted >= maxEvict) break;
|
|
6587
|
-
ESTIMATE_CACHE.delete(k);
|
|
6588
|
-
evicted++;
|
|
6589
|
-
}
|
|
6590
|
-
}
|
|
6591
|
-
const estimate = compute(key);
|
|
6592
|
-
ESTIMATE_CACHE.set(key, estimate);
|
|
6593
|
-
return estimate;
|
|
6594
|
-
}
|
|
6595
|
-
function estimateToolInputTokens(input) {
|
|
6596
|
-
if (typeof input === "string") return RoughTokenEstimate(input);
|
|
6597
|
-
if (input === null || typeof input !== "object") {
|
|
6598
|
-
return RoughTokenEstimate(String(input));
|
|
6599
|
-
}
|
|
6600
|
-
return getCachedEstimate(JSON.stringify(input), (key) => RoughTokenEstimate(key));
|
|
6601
|
-
}
|
|
6602
|
-
function estimateToolResultTokens(content) {
|
|
6603
|
-
if (typeof content === "string") return RoughTokenEstimate(content);
|
|
6604
|
-
return getCachedEstimate(JSON.stringify(content), (key) => RoughTokenEstimate(key));
|
|
6605
|
-
}
|
|
6606
|
-
function estimateTextTokens(text) {
|
|
6607
|
-
return RoughTokenEstimate(text);
|
|
6608
|
-
}
|
|
6609
|
-
function computeMessageTokens(msg) {
|
|
6610
|
-
if (typeof msg.content === "string") return estimateTextTokens(msg.content);
|
|
6611
|
-
let total = 0;
|
|
6612
|
-
for (const b of msg.content) {
|
|
6613
|
-
if (b.type === "text") total += estimateTextTokens(b.text);
|
|
6614
|
-
else if (b.type === "tool_use") total += estimateToolInputTokens(b.input);
|
|
6615
|
-
else if (b.type === "tool_result") total += estimateToolResultTokens(b.content);
|
|
6616
|
-
else total += RoughTokenEstimate(JSON.stringify(b));
|
|
6617
|
-
}
|
|
6618
|
-
return total;
|
|
6619
|
-
}
|
|
6620
|
-
function estimateMessageTokens(messages) {
|
|
6621
|
-
let total = 0;
|
|
6622
|
-
for (const m of messages) {
|
|
6623
|
-
if (typeof m._estTokens === "number" && m._estTokens > 0) {
|
|
6624
|
-
total += m._estTokens;
|
|
6625
|
-
continue;
|
|
6626
|
-
}
|
|
6627
|
-
total += computeMessageTokens(m);
|
|
6628
|
-
}
|
|
6629
|
-
return total;
|
|
6630
|
-
}
|
|
6631
|
-
function estimateToolDefTokens(tool) {
|
|
6632
|
-
const cached = tool._estDefTokens;
|
|
6633
|
-
if (typeof cached === "number" && cached > 0) return cached;
|
|
6634
|
-
return RoughTokenEstimate(tool.name) + RoughTokenEstimate(tool.description ?? "") + RoughTokenEstimate(JSON.stringify(tool.inputSchema));
|
|
6635
|
-
}
|
|
6636
|
-
function estimateRequestTokens(messages, systemPrompt, tools, calibrationKey = CALIBRATION_GLOBAL_KEY) {
|
|
6637
|
-
let messagesTokens = 0;
|
|
6638
|
-
if (typeof messages === "string") {
|
|
6639
|
-
messagesTokens = RoughTokenEstimate(messages);
|
|
6640
|
-
} else if (Array.isArray(messages)) {
|
|
6641
|
-
for (const m of messages) {
|
|
6642
|
-
if (typeof m === "object" && m !== null && "content" in m) {
|
|
6643
|
-
const cached = m._estTokens;
|
|
6644
|
-
if (typeof cached === "number" && cached > 0) {
|
|
6645
|
-
messagesTokens += cached;
|
|
6646
|
-
continue;
|
|
6647
|
-
}
|
|
6648
|
-
const content = m.content;
|
|
6649
|
-
if (typeof content === "string") {
|
|
6650
|
-
messagesTokens += RoughTokenEstimate(content);
|
|
6651
|
-
} else if (Array.isArray(content)) {
|
|
6652
|
-
for (const b of content) {
|
|
6653
|
-
if (typeof b === "object" && b !== null) {
|
|
6654
|
-
if (b.type === "text") {
|
|
6655
|
-
messagesTokens += RoughTokenEstimate(b.text);
|
|
6656
|
-
} else {
|
|
6657
|
-
messagesTokens += RoughTokenEstimate(JSON.stringify(b));
|
|
6658
|
-
}
|
|
6659
|
-
}
|
|
6660
|
-
}
|
|
7021
|
+
span?.setAttribute("provider.usage_out", res.usage.output);
|
|
7022
|
+
span?.end();
|
|
7023
|
+
logger.debug("Provider call succeeded", {
|
|
7024
|
+
...providerLogCtx(provider, request),
|
|
7025
|
+
stopReason: res.stopReason,
|
|
7026
|
+
usageInput: res.usage.input,
|
|
7027
|
+
usageOutput: res.usage.output,
|
|
7028
|
+
cacheRead: res.usage.cacheRead,
|
|
7029
|
+
cacheWrite: res.usage.cacheWrite,
|
|
7030
|
+
attempts: attempt + 1
|
|
7031
|
+
});
|
|
7032
|
+
return res;
|
|
7033
|
+
} catch (err) {
|
|
7034
|
+
if (err instanceof Error) span?.recordError(err);
|
|
7035
|
+
span?.end();
|
|
7036
|
+
if (signal.aborted) throw err;
|
|
7037
|
+
const isProviderErr = err instanceof ProviderError;
|
|
7038
|
+
const errAsErr = err instanceof Error ? err : new Error(String(err));
|
|
7039
|
+
const canRetry = retry.shouldRetry(isProviderErr ? err : errAsErr, attempt);
|
|
7040
|
+
const description = isProviderErr ? err.describe() : errAsErr.message;
|
|
7041
|
+
if (!canRetry) {
|
|
7042
|
+
if (isProviderErr) {
|
|
7043
|
+
events.emit("provider.error", {
|
|
7044
|
+
providerId: err.providerId,
|
|
7045
|
+
status: err.status,
|
|
7046
|
+
description,
|
|
7047
|
+
retryable: false
|
|
7048
|
+
});
|
|
6661
7049
|
}
|
|
7050
|
+
logger.error(`Provider call failed after ${attempt + 1} attempt(s) \u2014 ${description}`, {
|
|
7051
|
+
...providerLogCtx(provider, request),
|
|
7052
|
+
attempts: attempt + 1,
|
|
7053
|
+
errorDescription: description,
|
|
7054
|
+
status: isProviderErr ? err.status : void 0,
|
|
7055
|
+
errorName: err instanceof Error ? err.name : void 0,
|
|
7056
|
+
errorStack: err instanceof Error ? err.stack?.split("\n").slice(0, 3).join("\n") : void 0
|
|
7057
|
+
});
|
|
7058
|
+
throw toWrongStackError(err);
|
|
6662
7059
|
}
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
7060
|
+
const delay = Math.round(retry.delayMs(attempt));
|
|
7061
|
+
const attemptNum = attempt + 1;
|
|
7062
|
+
const maxAttempts = retry.maxAttempts(isProviderErr ? err : errAsErr);
|
|
7063
|
+
logger.warn(`Provider retry ${attemptNum}/${maxAttempts} in ${delay}ms \u2014 ${description}`, {
|
|
7064
|
+
...providerLogCtx(provider, request),
|
|
7065
|
+
attempt: attemptNum,
|
|
7066
|
+
maxAttempts,
|
|
7067
|
+
delayMs: delay,
|
|
7068
|
+
errorDescription: description,
|
|
7069
|
+
status: isProviderErr ? err.status : void 0
|
|
7070
|
+
});
|
|
7071
|
+
if (isProviderErr) {
|
|
7072
|
+
events.emit("provider.retry", {
|
|
7073
|
+
providerId: err.providerId,
|
|
7074
|
+
attempt: attemptNum,
|
|
7075
|
+
delayMs: delay,
|
|
7076
|
+
status: err.status,
|
|
7077
|
+
description
|
|
7078
|
+
});
|
|
6672
7079
|
}
|
|
7080
|
+
await new Promise((resolve5, reject) => {
|
|
7081
|
+
let settled = false;
|
|
7082
|
+
const onAbort = () => {
|
|
7083
|
+
if (settled) return;
|
|
7084
|
+
settled = true;
|
|
7085
|
+
clearTimeout(t);
|
|
7086
|
+
reject(new Error("aborted"));
|
|
7087
|
+
};
|
|
7088
|
+
const t = setTimeout(() => {
|
|
7089
|
+
if (settled) return;
|
|
7090
|
+
settled = true;
|
|
7091
|
+
clearTimeout(t);
|
|
7092
|
+
signal.removeEventListener("abort", onAbort);
|
|
7093
|
+
resolve5();
|
|
7094
|
+
}, delay);
|
|
7095
|
+
if (signal.aborted) {
|
|
7096
|
+
onAbort();
|
|
7097
|
+
return;
|
|
7098
|
+
}
|
|
7099
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
7100
|
+
});
|
|
7101
|
+
attempt++;
|
|
6673
7102
|
}
|
|
6674
7103
|
}
|
|
6675
|
-
let toolsTokens = 0;
|
|
6676
|
-
for (const t of tools) {
|
|
6677
|
-
toolsTokens += estimateToolDefTokens(t);
|
|
6678
|
-
}
|
|
6679
|
-
const total = messagesTokens + systemTokens + toolsTokens;
|
|
6680
|
-
calState(calibrationKey).prevEst = total;
|
|
6681
|
-
return {
|
|
6682
|
-
messages: messagesTokens,
|
|
6683
|
-
systemPrompt: systemTokens,
|
|
6684
|
-
tools: toolsTokens,
|
|
6685
|
-
total
|
|
6686
|
-
};
|
|
6687
|
-
}
|
|
6688
|
-
function getCalibrationState(calibrationKey = CALIBRATION_GLOBAL_KEY) {
|
|
6689
|
-
const cal = calState(calibrationKey);
|
|
6690
|
-
return {
|
|
6691
|
-
ratio: cal.ratio,
|
|
6692
|
-
count: cal.count,
|
|
6693
|
-
calibrated: cal.count >= MIN_SAMPLES_FOR_CALIBRATION
|
|
6694
|
-
};
|
|
6695
7104
|
}
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
return {
|
|
6702
|
-
messages: Math.round(result.messages * safeRatio),
|
|
6703
|
-
systemPrompt: Math.round(result.systemPrompt * safeRatio),
|
|
6704
|
-
tools: Math.round(result.tools * safeRatio),
|
|
6705
|
-
total: Math.round(result.total * safeRatio)
|
|
6706
|
-
};
|
|
7105
|
+
|
|
7106
|
+
// src/execution/provider-runner-impl.ts
|
|
7107
|
+
var DefaultProviderRunner = class {
|
|
7108
|
+
async run(opts) {
|
|
7109
|
+
return runProviderWithRetry(opts);
|
|
6707
7110
|
}
|
|
6708
|
-
|
|
6709
|
-
}
|
|
7111
|
+
};
|
|
6710
7112
|
|
|
6711
7113
|
// src/types/blocks.ts
|
|
6712
7114
|
function isTextBlock(b) {
|
|
@@ -7721,6 +8123,12 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
7721
8123
|
hardThreshold;
|
|
7722
8124
|
/** Writable so model-switch can update the denominator without re-registering the middleware. */
|
|
7723
8125
|
_maxContext;
|
|
8126
|
+
/**
|
|
8127
|
+
* Runtime on/off gate. The middleware is always installed in the pipeline so
|
|
8128
|
+
* auto-compaction can be toggled live from the TUI `/settings` picker; when
|
|
8129
|
+
* disabled the handler is a pass-through. Defaults to enabled.
|
|
8130
|
+
*/
|
|
8131
|
+
_enabled = true;
|
|
7724
8132
|
aggressiveOn;
|
|
7725
8133
|
events;
|
|
7726
8134
|
failureMode;
|
|
@@ -7778,8 +8186,19 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
7778
8186
|
setMaxContext(maxContext) {
|
|
7779
8187
|
this._maxContext = maxContext;
|
|
7780
8188
|
}
|
|
8189
|
+
/** Whether auto-compaction is currently active. */
|
|
8190
|
+
get enabled() {
|
|
8191
|
+
return this._enabled;
|
|
8192
|
+
}
|
|
8193
|
+
/** Toggle auto-compaction on a live session (TUI `/settings`). When disabled
|
|
8194
|
+
* the middleware passes every iteration straight through without estimating
|
|
8195
|
+
* tokens or compacting. */
|
|
8196
|
+
setEnabled(enabled) {
|
|
8197
|
+
this._enabled = enabled;
|
|
8198
|
+
}
|
|
7781
8199
|
handler() {
|
|
7782
8200
|
return async (ctx, next) => {
|
|
8201
|
+
if (!this._enabled) return next(ctx);
|
|
7783
8202
|
const msgCount = ctx.messages.length;
|
|
7784
8203
|
const toolCount = (ctx.tools ?? []).length;
|
|
7785
8204
|
let tokens;
|
|
@@ -7901,163 +8320,25 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
|
|
|
7901
8320
|
level: pressure.level,
|
|
7902
8321
|
tokens: pressure.tokens,
|
|
7903
8322
|
maxContext: this._maxContext,
|
|
7904
|
-
load: pressure.load,
|
|
7905
|
-
fatal
|
|
7906
|
-
});
|
|
7907
|
-
if (fatal) {
|
|
7908
|
-
throw new AgentError({
|
|
7909
|
-
message: `Auto-compaction failed at ${pressure.level} threshold`,
|
|
7910
|
-
code: ERROR_CODES.AGENT_CONTEXT_OVERFLOW,
|
|
7911
|
-
recoverable: true,
|
|
7912
|
-
context: {
|
|
7913
|
-
level: pressure.level,
|
|
7914
|
-
tokens: pressure.tokens,
|
|
7915
|
-
maxContext: this._maxContext
|
|
7916
|
-
},
|
|
7917
|
-
cause: err
|
|
7918
|
-
});
|
|
7919
|
-
}
|
|
7920
|
-
}
|
|
7921
|
-
}
|
|
7922
|
-
};
|
|
7923
|
-
|
|
7924
|
-
// src/utils/json-schema-validate.ts
|
|
7925
|
-
function validateAgainstSchema(value, schema) {
|
|
7926
|
-
const errors = [];
|
|
7927
|
-
walk2(value, schema, "", errors);
|
|
7928
|
-
return { ok: errors.length === 0, errors };
|
|
7929
|
-
}
|
|
7930
|
-
function walk2(value, schema, path19, errors) {
|
|
7931
|
-
if (schema.enum !== void 0) {
|
|
7932
|
-
if (!schema.enum.some((e) => deepEqual(e, value))) {
|
|
7933
|
-
errors.push({
|
|
7934
|
-
path: path19 || "<root>",
|
|
7935
|
-
message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
|
|
7936
|
-
});
|
|
7937
|
-
return;
|
|
7938
|
-
}
|
|
7939
|
-
}
|
|
7940
|
-
if (typeof schema.type === "string") {
|
|
7941
|
-
if (!checkType(value, schema.type)) {
|
|
7942
|
-
errors.push({
|
|
7943
|
-
path: path19 || "<root>",
|
|
7944
|
-
message: `expected ${schema.type}, got ${describeType(value)}`
|
|
7945
|
-
});
|
|
7946
|
-
return;
|
|
7947
|
-
}
|
|
7948
|
-
}
|
|
7949
|
-
if (schema.type === "object" && isPlainObject(value)) {
|
|
7950
|
-
const obj = value;
|
|
7951
|
-
for (const req of schema.required ?? []) {
|
|
7952
|
-
if (!(req in obj)) {
|
|
7953
|
-
errors.push({ path: joinPath(path19, req), message: "required property missing" });
|
|
7954
|
-
}
|
|
7955
|
-
}
|
|
7956
|
-
if (schema.properties) {
|
|
7957
|
-
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
7958
|
-
if (key in obj) {
|
|
7959
|
-
walk2(obj[key], subSchema, joinPath(path19, key), errors);
|
|
7960
|
-
}
|
|
7961
|
-
}
|
|
7962
|
-
}
|
|
7963
|
-
}
|
|
7964
|
-
if (schema.type === "array" && Array.isArray(value) && schema.items) {
|
|
7965
|
-
value.forEach((item, i) => walk2(item, schema.items, `${path19}[${i}]`, errors));
|
|
7966
|
-
}
|
|
7967
|
-
}
|
|
7968
|
-
function checkType(value, type) {
|
|
7969
|
-
switch (type) {
|
|
7970
|
-
case "string":
|
|
7971
|
-
return typeof value === "string";
|
|
7972
|
-
case "number":
|
|
7973
|
-
return typeof value === "number" && !Number.isNaN(value);
|
|
7974
|
-
case "integer":
|
|
7975
|
-
return typeof value === "number" && Number.isInteger(value);
|
|
7976
|
-
case "boolean":
|
|
7977
|
-
return typeof value === "boolean";
|
|
7978
|
-
case "null":
|
|
7979
|
-
return value === null;
|
|
7980
|
-
case "array":
|
|
7981
|
-
return Array.isArray(value);
|
|
7982
|
-
case "object":
|
|
7983
|
-
return isPlainObject(value);
|
|
7984
|
-
default:
|
|
7985
|
-
return true;
|
|
7986
|
-
}
|
|
7987
|
-
}
|
|
7988
|
-
function isPlainObject(v) {
|
|
7989
|
-
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
7990
|
-
}
|
|
7991
|
-
function describeType(v) {
|
|
7992
|
-
if (v === null) return "null";
|
|
7993
|
-
if (Array.isArray(v)) return "array";
|
|
7994
|
-
return typeof v;
|
|
7995
|
-
}
|
|
7996
|
-
function joinPath(parent, key) {
|
|
7997
|
-
if (!parent) return key;
|
|
7998
|
-
return `${parent}.${key}`;
|
|
7999
|
-
}
|
|
8000
|
-
function deepEqual(a, b) {
|
|
8001
|
-
if (a === b) return true;
|
|
8002
|
-
if (typeof a !== typeof b) return false;
|
|
8003
|
-
if (a === null || b === null) return a === b;
|
|
8004
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
8005
|
-
return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
|
|
8006
|
-
}
|
|
8007
|
-
if (typeof a === "object" && typeof b === "object") {
|
|
8008
|
-
const ak = Object.keys(a);
|
|
8009
|
-
const bk = Object.keys(b);
|
|
8010
|
-
if (ak.length !== bk.length) return false;
|
|
8011
|
-
return ak.every(
|
|
8012
|
-
(k) => deepEqual(a[k], b[k])
|
|
8013
|
-
);
|
|
8014
|
-
}
|
|
8015
|
-
return false;
|
|
8016
|
-
}
|
|
8017
|
-
|
|
8018
|
-
// src/utils/tool-output-serializer.ts
|
|
8019
|
-
function createToolOutputSerializer(opts = {}) {
|
|
8020
|
-
const capBytes = opts.perIterationOutputCapBytes ?? 1e5;
|
|
8021
|
-
function serialize(value) {
|
|
8022
|
-
if (typeof value === "string") return value;
|
|
8023
|
-
if (value === null || value === void 0) return "";
|
|
8024
|
-
if (typeof value === "object") {
|
|
8025
|
-
if (Array.isArray(value)) return value.map(serialize).join("\n");
|
|
8026
|
-
if ("text" in value) {
|
|
8027
|
-
const t = value.text;
|
|
8028
|
-
return typeof t === "string" ? t : JSON.stringify(value, null, 2);
|
|
8029
|
-
}
|
|
8030
|
-
try {
|
|
8031
|
-
return JSON.stringify(value, null, 2);
|
|
8032
|
-
} catch {
|
|
8033
|
-
return String(value);
|
|
8323
|
+
load: pressure.load,
|
|
8324
|
+
fatal
|
|
8325
|
+
});
|
|
8326
|
+
if (fatal) {
|
|
8327
|
+
throw new AgentError({
|
|
8328
|
+
message: `Auto-compaction failed at ${pressure.level} threshold`,
|
|
8329
|
+
code: ERROR_CODES.AGENT_CONTEXT_OVERFLOW,
|
|
8330
|
+
recoverable: true,
|
|
8331
|
+
context: {
|
|
8332
|
+
level: pressure.level,
|
|
8333
|
+
tokens: pressure.tokens,
|
|
8334
|
+
maxContext: this._maxContext
|
|
8335
|
+
},
|
|
8336
|
+
cause: err
|
|
8337
|
+
});
|
|
8034
8338
|
}
|
|
8035
8339
|
}
|
|
8036
|
-
return String(value);
|
|
8037
|
-
}
|
|
8038
|
-
function enforceCap(text, remainingBudget) {
|
|
8039
|
-
if (remainingBudget <= 0) {
|
|
8040
|
-
return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
|
|
8041
|
-
}
|
|
8042
|
-
const textBytes = Buffer.byteLength(text, "utf8");
|
|
8043
|
-
if (textBytes <= remainingBudget) {
|
|
8044
|
-
return { text, newBudget: remainingBudget - textBytes };
|
|
8045
|
-
}
|
|
8046
|
-
const marker = `
|
|
8047
|
-
\u2026[truncated ${textBytes - remainingBudget} bytes]\u2026
|
|
8048
|
-
`;
|
|
8049
|
-
const markerBytes = Buffer.byteLength(marker, "utf8");
|
|
8050
|
-
const available = remainingBudget - markerBytes;
|
|
8051
|
-
if (available <= 0) {
|
|
8052
|
-
return { text: "[truncated: iteration output cap exceeded]", newBudget: 0 };
|
|
8053
|
-
}
|
|
8054
|
-
const half = Math.floor(available / 2);
|
|
8055
|
-
const first = text.slice(0, half);
|
|
8056
|
-
const second = text.slice(text.length - half);
|
|
8057
|
-
return { text: `${first}${marker}${second}`, newBudget: 0 };
|
|
8058
8340
|
}
|
|
8059
|
-
|
|
8060
|
-
}
|
|
8341
|
+
};
|
|
8061
8342
|
|
|
8062
8343
|
// src/execution/tool-executor.ts
|
|
8063
8344
|
var ToolExecutor = class _ToolExecutor {
|
|
@@ -8222,7 +8503,7 @@ ${post.additionalContext}`;
|
|
|
8222
8503
|
);
|
|
8223
8504
|
return { result, tool, durationMs: Date.now() - start };
|
|
8224
8505
|
} catch (err) {
|
|
8225
|
-
const msg =
|
|
8506
|
+
const msg = toErrorMessage(err);
|
|
8226
8507
|
const scrubbed = this.opts.secretScrubber.scrub(msg);
|
|
8227
8508
|
this.opts.renderer?.writeToolResult(tool.name, scrubbed, true);
|
|
8228
8509
|
const result = {
|
|
@@ -8243,7 +8524,7 @@ ${post.additionalContext}`;
|
|
|
8243
8524
|
try {
|
|
8244
8525
|
return await runOne(use);
|
|
8245
8526
|
} catch (err) {
|
|
8246
|
-
const msg =
|
|
8527
|
+
const msg = toErrorMessage(err);
|
|
8247
8528
|
const scrubbed = this.opts.secretScrubber.scrub(msg);
|
|
8248
8529
|
const result = {
|
|
8249
8530
|
type: "tool_result",
|
|
@@ -8526,15 +8807,6 @@ function extractMalformedRaw(input) {
|
|
|
8526
8807
|
}
|
|
8527
8808
|
}
|
|
8528
8809
|
|
|
8529
|
-
// src/utils/assert-never.ts
|
|
8530
|
-
function assertNever(x, message) {
|
|
8531
|
-
const err = new Error(
|
|
8532
|
-
`Unhandled case: ${JSON.stringify(x)}`
|
|
8533
|
-
);
|
|
8534
|
-
err.name = "AssertNeverError";
|
|
8535
|
-
throw err;
|
|
8536
|
-
}
|
|
8537
|
-
|
|
8538
8810
|
// src/execution/autonomous-runner.ts
|
|
8539
8811
|
var DoneConditionChecker = class {
|
|
8540
8812
|
constructor(condition) {
|
|
@@ -8714,64 +8986,6 @@ var AutonomousRunner = class {
|
|
|
8714
8986
|
|
|
8715
8987
|
// src/storage/goal-store.ts
|
|
8716
8988
|
init_atomic_write();
|
|
8717
|
-
function projectHash(absRoot) {
|
|
8718
|
-
return createHash("sha256").update(path11.resolve(absRoot)).digest("hex").slice(0, 12);
|
|
8719
|
-
}
|
|
8720
|
-
function projectSlug(absRoot) {
|
|
8721
|
-
const base = slugify(path11.basename(absRoot));
|
|
8722
|
-
const hash = createHash("sha256").update(path11.resolve(absRoot)).digest("hex").slice(0, 6);
|
|
8723
|
-
return `${base}-${hash}`;
|
|
8724
|
-
}
|
|
8725
|
-
function slugify(name) {
|
|
8726
|
-
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
|
|
8727
|
-
}
|
|
8728
|
-
function wstackGlobalRoot() {
|
|
8729
|
-
const fromEnv = process.env["WRONGSTACK_HOME"];
|
|
8730
|
-
if (fromEnv && fromEnv.trim().length > 0) return path11.resolve(fromEnv);
|
|
8731
|
-
return path11.join(os.homedir(), ".wrongstack");
|
|
8732
|
-
}
|
|
8733
|
-
function resolveWstackPaths(opts) {
|
|
8734
|
-
const globalRoot = opts.globalRoot ?? (opts.userHome ? path11.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
|
|
8735
|
-
const hash = projectHash(opts.projectRoot);
|
|
8736
|
-
const slug = projectSlug(opts.projectRoot);
|
|
8737
|
-
const projectDir = path11.join(globalRoot, "projects", slug);
|
|
8738
|
-
return {
|
|
8739
|
-
globalRoot,
|
|
8740
|
-
configDir: globalRoot,
|
|
8741
|
-
globalConfig: path11.join(globalRoot, "config.json"),
|
|
8742
|
-
secretsKey: path11.join(globalRoot, ".key"),
|
|
8743
|
-
globalMemory: path11.join(globalRoot, "memory.md"),
|
|
8744
|
-
globalSkills: path11.join(globalRoot, "skills"),
|
|
8745
|
-
globalPrompts: path11.join(globalRoot, "prompts"),
|
|
8746
|
-
cacheDir: path11.join(globalRoot, "cache"),
|
|
8747
|
-
modelsCache: path11.join(globalRoot, "cache", "models.dev.json"),
|
|
8748
|
-
modelsOverlayCache: path11.join(globalRoot, "cache", "models-overlay.json"),
|
|
8749
|
-
historyFile: path11.join(globalRoot, "history"),
|
|
8750
|
-
logFile: path11.join(globalRoot, "logs", "wrongstack.log"),
|
|
8751
|
-
projectDir,
|
|
8752
|
-
projectCodebaseIndex: path11.join(projectDir, "codebase-index"),
|
|
8753
|
-
projectMemory: path11.join(projectDir, "memory.md"),
|
|
8754
|
-
projectSessions: path11.join(projectDir, "sessions"),
|
|
8755
|
-
projectTrust: path11.join(projectDir, "trust.json"),
|
|
8756
|
-
projectMeta: path11.join(projectDir, "meta.json"),
|
|
8757
|
-
projectLocalConfig: path11.join(projectDir, "config.local.json"),
|
|
8758
|
-
inProjectConfig: path11.join(opts.projectRoot, ".wrongstack", "config.json"),
|
|
8759
|
-
inProjectAgentsFile: path11.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
|
|
8760
|
-
inProjectSkills: path11.join(opts.projectRoot, ".wrongstack", "skills"),
|
|
8761
|
-
inProjectWorktrees: path11.join(opts.projectRoot, ".wrongstack", "worktrees"),
|
|
8762
|
-
projectHash: hash,
|
|
8763
|
-
projectSlug: slug,
|
|
8764
|
-
projectGoal: path11.join(projectDir, "goal.json"),
|
|
8765
|
-
projectSpecs: path11.join(projectDir, "specs"),
|
|
8766
|
-
projectTaskGraphs: path11.join(projectDir, "task-graphs"),
|
|
8767
|
-
projectSddSession: path11.join(projectDir, "sdd-session.json"),
|
|
8768
|
-
projectPlan: path11.join(projectDir, "plan.json"),
|
|
8769
|
-
projectAutophase: path11.join(projectDir, "autophase"),
|
|
8770
|
-
syncConfig: path11.join(globalRoot, "sync.json")
|
|
8771
|
-
};
|
|
8772
|
-
}
|
|
8773
|
-
|
|
8774
|
-
// src/storage/goal-store.ts
|
|
8775
8989
|
var MAX_JOURNAL_ENTRIES = 500;
|
|
8776
8990
|
function goalFilePath(projectRoot) {
|
|
8777
8991
|
return resolveWstackPaths({ projectRoot }).projectGoal;
|
|
@@ -8780,7 +8994,7 @@ async function loadGoal(filePath, events) {
|
|
|
8780
8994
|
const t0 = Date.now();
|
|
8781
8995
|
let raw;
|
|
8782
8996
|
try {
|
|
8783
|
-
raw = await
|
|
8997
|
+
raw = await fsp2.readFile(filePath, "utf8");
|
|
8784
8998
|
} catch (err) {
|
|
8785
8999
|
const code = err.code;
|
|
8786
9000
|
if (code === "ENOENT") {
|
|
@@ -8799,7 +9013,7 @@ async function loadGoal(filePath, events) {
|
|
|
8799
9013
|
store: "goal",
|
|
8800
9014
|
filePath,
|
|
8801
9015
|
operation: "load",
|
|
8802
|
-
error:
|
|
9016
|
+
error: toErrorMessage(err),
|
|
8803
9017
|
recoverable: false
|
|
8804
9018
|
});
|
|
8805
9019
|
throw err;
|
|
@@ -8872,11 +9086,11 @@ async function saveGoal(filePath, goal, events) {
|
|
|
8872
9086
|
store: "goal",
|
|
8873
9087
|
filePath,
|
|
8874
9088
|
operation: "save",
|
|
8875
|
-
error:
|
|
9089
|
+
error: toErrorMessage(err),
|
|
8876
9090
|
recoverable: false
|
|
8877
9091
|
});
|
|
8878
9092
|
throw new FsError({
|
|
8879
|
-
message:
|
|
9093
|
+
message: toErrorMessage(err),
|
|
8880
9094
|
code: ERROR_CODES.FS_ATOMIC_WRITE_FAILED,
|
|
8881
9095
|
path: filePath,
|
|
8882
9096
|
cause: err
|
|
@@ -8930,11 +9144,6 @@ function computeTrend(history) {
|
|
|
8930
9144
|
return "steady";
|
|
8931
9145
|
}
|
|
8932
9146
|
|
|
8933
|
-
// src/utils/sleep.ts
|
|
8934
|
-
function sleep(ms) {
|
|
8935
|
-
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
8936
|
-
}
|
|
8937
|
-
|
|
8938
9147
|
// src/execution/autonomy-brain.ts
|
|
8939
9148
|
function formatDecisionSummary(decision, request) {
|
|
8940
9149
|
const question = request.question.length > 80 ? request.question.slice(0, 77) + "\u2026" : request.question;
|
|
@@ -8985,7 +9194,7 @@ var EternalAutonomyEngine = class {
|
|
|
8985
9194
|
console.error(JSON.stringify({
|
|
8986
9195
|
level: "error",
|
|
8987
9196
|
event: "engine.persist_state_failed",
|
|
8988
|
-
message:
|
|
9197
|
+
message: toErrorMessage(err),
|
|
8989
9198
|
context: { expectedState: "stopped" },
|
|
8990
9199
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
8991
9200
|
}));
|
|
@@ -9018,7 +9227,7 @@ var EternalAutonomyEngine = class {
|
|
|
9018
9227
|
} catch (err) {
|
|
9019
9228
|
this.consecutiveFailures++;
|
|
9020
9229
|
this.opts.onError?.(err instanceof Error ? err : new Error(String(err)), this.consecutiveFailures);
|
|
9021
|
-
await this.appendFailure("engine error",
|
|
9230
|
+
await this.appendFailure("engine error", toErrorMessage(err));
|
|
9022
9231
|
}
|
|
9023
9232
|
if (iterationOk) {
|
|
9024
9233
|
this.consecutiveFailures = 0;
|
|
@@ -9119,7 +9328,7 @@ var EternalAutonomyEngine = class {
|
|
|
9119
9328
|
} catch (err) {
|
|
9120
9329
|
const isAbort = err instanceof Error && (err.name === "AbortError" || err.message.includes("abort"));
|
|
9121
9330
|
status = isAbort ? "aborted" : "failure";
|
|
9122
|
-
note =
|
|
9331
|
+
note = toErrorMessage(err);
|
|
9123
9332
|
if (!isAbort && typeof err?.recoverable === "boolean") {
|
|
9124
9333
|
isTransientFailure = err.recoverable;
|
|
9125
9334
|
}
|
|
@@ -12916,7 +13125,7 @@ function classifySubagentError(err, hints = {}) {
|
|
|
12916
13125
|
const baseMessage2 = err.describe();
|
|
12917
13126
|
return providerErrorToSubagentError(err, baseMessage2, cause);
|
|
12918
13127
|
}
|
|
12919
|
-
const baseMessage =
|
|
13128
|
+
const baseMessage = toErrorMessage(err);
|
|
12920
13129
|
if (err instanceof BudgetExceededError) {
|
|
12921
13130
|
const map = {
|
|
12922
13131
|
iterations: "budget_iterations",
|
|
@@ -14111,7 +14320,7 @@ var ParallelEternalEngine = class {
|
|
|
14111
14320
|
console.error(JSON.stringify({
|
|
14112
14321
|
level: "error",
|
|
14113
14322
|
event: "engine.persist_state_failed",
|
|
14114
|
-
message:
|
|
14323
|
+
message: toErrorMessage(err),
|
|
14115
14324
|
context: { expectedState: "stopped" },
|
|
14116
14325
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
14117
14326
|
}));
|
|
@@ -14147,7 +14356,7 @@ var ParallelEternalEngine = class {
|
|
|
14147
14356
|
);
|
|
14148
14357
|
await this.appendFailure(
|
|
14149
14358
|
"engine error",
|
|
14150
|
-
|
|
14359
|
+
toErrorMessage(err)
|
|
14151
14360
|
);
|
|
14152
14361
|
}
|
|
14153
14362
|
if (this.stopRequested) break;
|
|
@@ -14794,123 +15003,6 @@ function createMessage(type, from, payload, to) {
|
|
|
14794
15003
|
priority: "normal"
|
|
14795
15004
|
};
|
|
14796
15005
|
}
|
|
14797
|
-
var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
|
|
14798
|
-
var IS_WINDOWS = process.platform === "win32";
|
|
14799
|
-
var SEP = IS_WINDOWS ? "\\" : "/";
|
|
14800
|
-
function isGlob(p) {
|
|
14801
|
-
for (const c of p) {
|
|
14802
|
-
if (GLOB_CHARS.has(c)) return true;
|
|
14803
|
-
}
|
|
14804
|
-
return false;
|
|
14805
|
-
}
|
|
14806
|
-
function globToRegex(pat) {
|
|
14807
|
-
let i = 0;
|
|
14808
|
-
let re = "^";
|
|
14809
|
-
while (i < pat.length) {
|
|
14810
|
-
const c = expectDefined(pat[i]);
|
|
14811
|
-
if (c === "*") {
|
|
14812
|
-
if (pat[i + 1] === "*") {
|
|
14813
|
-
re += ".*";
|
|
14814
|
-
i += 2;
|
|
14815
|
-
if (pat[i] === "/") i++;
|
|
14816
|
-
} else {
|
|
14817
|
-
re += "[^/\\\\]*";
|
|
14818
|
-
i++;
|
|
14819
|
-
}
|
|
14820
|
-
} else if (c === "?") {
|
|
14821
|
-
re += "[^/\\\\]";
|
|
14822
|
-
i++;
|
|
14823
|
-
} else if (c === "[") {
|
|
14824
|
-
let cls = "[";
|
|
14825
|
-
i++;
|
|
14826
|
-
if (pat[i] === "!" || pat[i] === "^") {
|
|
14827
|
-
cls += "^";
|
|
14828
|
-
i++;
|
|
14829
|
-
}
|
|
14830
|
-
while (i < pat.length && pat[i] !== "]") {
|
|
14831
|
-
const ch = pat[i] ?? "";
|
|
14832
|
-
if (ch === "\\") cls += "\\\\";
|
|
14833
|
-
else if (ch === "]" || ch === "^") cls += `\\${ch}`;
|
|
14834
|
-
else cls += ch;
|
|
14835
|
-
i++;
|
|
14836
|
-
}
|
|
14837
|
-
cls += "]";
|
|
14838
|
-
re += cls;
|
|
14839
|
-
i++;
|
|
14840
|
-
} else {
|
|
14841
|
-
re += c.replace(/[.+^${}()|\\]/g, "\\$&");
|
|
14842
|
-
i++;
|
|
14843
|
-
}
|
|
14844
|
-
}
|
|
14845
|
-
return new RegExp(re + "$");
|
|
14846
|
-
}
|
|
14847
|
-
function baseDir(pat) {
|
|
14848
|
-
let i = pat.length - 1;
|
|
14849
|
-
while (i >= 0 && !GLOB_CHARS.has(expectDefined(pat[i])) && pat[i] !== SEP && pat[i] !== "/") i--;
|
|
14850
|
-
const cut = i >= 0 ? pat.lastIndexOf(SEP, i) : pat.lastIndexOf("/", i);
|
|
14851
|
-
return cut < 0 ? "." : pat.slice(0, cut);
|
|
14852
|
-
}
|
|
14853
|
-
async function expandGlob(pattern) {
|
|
14854
|
-
if (!isGlob(pattern)) return [pattern];
|
|
14855
|
-
const results = /* @__PURE__ */ new Set();
|
|
14856
|
-
const abs = isAbsolute(pattern);
|
|
14857
|
-
const base = abs ? baseDir(pattern) : baseDir(pattern);
|
|
14858
|
-
const relPat = base === "." ? pattern : pattern.slice(base.length + 1);
|
|
14859
|
-
async function walk3(dir, pat) {
|
|
14860
|
-
let entries;
|
|
14861
|
-
try {
|
|
14862
|
-
entries = await fsp.readdir(dir);
|
|
14863
|
-
} catch {
|
|
14864
|
-
return;
|
|
14865
|
-
}
|
|
14866
|
-
const firstGlob = pat.search(/[*?[[]/);
|
|
14867
|
-
if (firstGlob < 0) {
|
|
14868
|
-
const re = globToRegex(pat);
|
|
14869
|
-
for (const e of entries) {
|
|
14870
|
-
if (re.test(e)) {
|
|
14871
|
-
const full = `${dir}${SEP}${e}`;
|
|
14872
|
-
results.add(abs ? resolve(full) : full);
|
|
14873
|
-
}
|
|
14874
|
-
}
|
|
14875
|
-
return;
|
|
14876
|
-
}
|
|
14877
|
-
const before = pat.slice(0, firstGlob);
|
|
14878
|
-
const rest = pat.slice(firstGlob);
|
|
14879
|
-
if (before.endsWith("**")) {
|
|
14880
|
-
await walk3(dir, rest);
|
|
14881
|
-
for (const e of entries) {
|
|
14882
|
-
const full = `${dir}${SEP}${e}`;
|
|
14883
|
-
try {
|
|
14884
|
-
const stat6 = await fsp.stat(full);
|
|
14885
|
-
if (stat6.isDirectory()) await walk3(full, rest);
|
|
14886
|
-
} catch {
|
|
14887
|
-
}
|
|
14888
|
-
}
|
|
14889
|
-
} else if (before === "") {
|
|
14890
|
-
const re = globToRegex(rest);
|
|
14891
|
-
for (const e of entries) {
|
|
14892
|
-
if (re.test(e)) {
|
|
14893
|
-
const full = `${dir}${SEP}${e}`;
|
|
14894
|
-
results.add(abs ? resolve(full) : full);
|
|
14895
|
-
}
|
|
14896
|
-
}
|
|
14897
|
-
} else {
|
|
14898
|
-
const seg = before.replace(/[*?[\]]/g, "").replace(/\/$/, "");
|
|
14899
|
-
if (entries.includes(seg)) {
|
|
14900
|
-
const full = `${dir}${SEP}${seg}`;
|
|
14901
|
-
try {
|
|
14902
|
-
const stat6 = await fsp.stat(full);
|
|
14903
|
-
if (stat6.isDirectory()) await walk3(full, rest);
|
|
14904
|
-
} catch {
|
|
14905
|
-
}
|
|
14906
|
-
}
|
|
14907
|
-
}
|
|
14908
|
-
}
|
|
14909
|
-
await walk3(base === "." ? "." : base, relPat);
|
|
14910
|
-
return [...results];
|
|
14911
|
-
}
|
|
14912
|
-
|
|
14913
|
-
// src/coordination/collab-debug.ts
|
|
14914
15006
|
var DEFAULT_MAX_TARGET_FILES = 30;
|
|
14915
15007
|
var CollabSession = class extends EventEmitter {
|
|
14916
15008
|
sessionId;
|
|
@@ -15000,8 +15092,8 @@ var CollabSession = class extends EventEmitter {
|
|
|
15000
15092
|
for (const filePath of allFiles) {
|
|
15001
15093
|
try {
|
|
15002
15094
|
const [content, stat6] = await Promise.all([
|
|
15003
|
-
|
|
15004
|
-
|
|
15095
|
+
fsp2.readFile(filePath, "utf8"),
|
|
15096
|
+
fsp2.stat(filePath)
|
|
15005
15097
|
]);
|
|
15006
15098
|
const ext = filePath.split(".").pop() ?? "";
|
|
15007
15099
|
const language = ext === "ts" || ext === "tsx" ? "typescript" : ext === "js" || ext === "jsx" ? "javascript" : ext === "md" ? "markdown" : ext === "json" ? "json" : void 0;
|
|
@@ -15423,7 +15515,7 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
|
|
|
15423
15515
|
for (const file of this.snapshot.files) {
|
|
15424
15516
|
if (file.snapshotMtimeMs === void 0 && file.snapshotSizeBytes === void 0) continue;
|
|
15425
15517
|
try {
|
|
15426
|
-
const stat6 = await
|
|
15518
|
+
const stat6 = await fsp2.stat(file.path);
|
|
15427
15519
|
const mtimeChanged = file.snapshotMtimeMs !== void 0 && stat6.mtimeMs > file.snapshotMtimeMs + 1;
|
|
15428
15520
|
const sizeChanged = file.snapshotSizeBytes !== void 0 && stat6.size !== file.snapshotSizeBytes;
|
|
15429
15521
|
if (mtimeChanged || sizeChanged) {
|
|
@@ -15706,7 +15798,7 @@ function makeSpawnTool(director, roster) {
|
|
|
15706
15798
|
if (err instanceof FleetCostCapError) {
|
|
15707
15799
|
return { error: err.message, kind: err.kind, limit: err.limit, observed: err.observed };
|
|
15708
15800
|
}
|
|
15709
|
-
return { error:
|
|
15801
|
+
return { error: toErrorMessage(err) };
|
|
15710
15802
|
}
|
|
15711
15803
|
}
|
|
15712
15804
|
};
|
|
@@ -15788,7 +15880,7 @@ function makeAskTool(director) {
|
|
|
15788
15880
|
_hint: "Response was large and stored. Use ask_result with the key to retrieve it."
|
|
15789
15881
|
};
|
|
15790
15882
|
} catch (err) {
|
|
15791
|
-
return { ok: false, error:
|
|
15883
|
+
return { ok: false, error: toErrorMessage(err) };
|
|
15792
15884
|
}
|
|
15793
15885
|
}
|
|
15794
15886
|
};
|
|
@@ -16014,7 +16106,7 @@ function makeCollabDebugTool(director) {
|
|
|
16014
16106
|
evaluations: report.evaluations
|
|
16015
16107
|
};
|
|
16016
16108
|
} catch (err) {
|
|
16017
|
-
const msg =
|
|
16109
|
+
const msg = toErrorMessage(err);
|
|
16018
16110
|
return { error: "collab_debug failed: " + msg };
|
|
16019
16111
|
}
|
|
16020
16112
|
}
|
|
@@ -16588,7 +16680,7 @@ var Director = class _Director {
|
|
|
16588
16680
|
) : null;
|
|
16589
16681
|
this.fleetManager = opts.fleetManager;
|
|
16590
16682
|
if (this.sharedScratchpadPath) {
|
|
16591
|
-
void
|
|
16683
|
+
void fsp2.mkdir(this.sharedScratchpadPath, { recursive: true }).catch((err) => this.logShutdownError("shared_scratchpad_mkdir", err));
|
|
16592
16684
|
}
|
|
16593
16685
|
this.transport = new InMemoryBridgeTransport();
|
|
16594
16686
|
this.bridge = new InMemoryAgentBridge(
|
|
@@ -17180,7 +17272,7 @@ var Director = class _Director {
|
|
|
17180
17272
|
})),
|
|
17181
17273
|
usage: this.usage.snapshot()
|
|
17182
17274
|
};
|
|
17183
|
-
await
|
|
17275
|
+
await fsp2.mkdir(path3.dirname(this.manifestPath), { recursive: true });
|
|
17184
17276
|
await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
17185
17277
|
return this.manifestPath;
|
|
17186
17278
|
}
|
|
@@ -17230,7 +17322,7 @@ var Director = class _Director {
|
|
|
17230
17322
|
* listener for structured collection, and never affects exit code.
|
|
17231
17323
|
*/
|
|
17232
17324
|
logShutdownError(phase, err) {
|
|
17233
|
-
const detail =
|
|
17325
|
+
const detail = toErrorMessage(err);
|
|
17234
17326
|
process.emitWarning(
|
|
17235
17327
|
`Director shutdown phase "${phase}" failed: ${detail}`,
|
|
17236
17328
|
"DirectorShutdownWarning"
|
|
@@ -17394,10 +17486,10 @@ var Director = class _Director {
|
|
|
17394
17486
|
*/
|
|
17395
17487
|
async readSession(subagentId, tail) {
|
|
17396
17488
|
if (!this.sessionsRoot) return null;
|
|
17397
|
-
const filePath =
|
|
17489
|
+
const filePath = path3.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
|
|
17398
17490
|
let raw;
|
|
17399
17491
|
try {
|
|
17400
|
-
raw = await
|
|
17492
|
+
raw = await fsp2.readFile(filePath, "utf8");
|
|
17401
17493
|
} catch {
|
|
17402
17494
|
return null;
|
|
17403
17495
|
}
|
|
@@ -17823,7 +17915,7 @@ function createDelegateTool(opts) {
|
|
|
17823
17915
|
summary
|
|
17824
17916
|
};
|
|
17825
17917
|
} catch (err) {
|
|
17826
|
-
const message =
|
|
17918
|
+
const message = toErrorMessage(err);
|
|
17827
17919
|
opts.events?.emit("delegate.completed", {
|
|
17828
17920
|
target,
|
|
17829
17921
|
task: i.task,
|
|
@@ -17924,13 +18016,13 @@ async function readSubagentPartial(opts, subagentId) {
|
|
|
17924
18016
|
if (!opts.sessionsRoot) return void 0;
|
|
17925
18017
|
const candidates = [];
|
|
17926
18018
|
if (opts.directorRunId) {
|
|
17927
|
-
candidates.push(
|
|
18019
|
+
candidates.push(path3.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
|
|
17928
18020
|
} else {
|
|
17929
18021
|
try {
|
|
17930
|
-
const entries = await
|
|
18022
|
+
const entries = await fsp2.readdir(opts.sessionsRoot, { withFileTypes: true });
|
|
17931
18023
|
for (const entry of entries) {
|
|
17932
18024
|
if (entry.isDirectory()) {
|
|
17933
|
-
candidates.push(
|
|
18025
|
+
candidates.push(path3.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
|
|
17934
18026
|
}
|
|
17935
18027
|
}
|
|
17936
18028
|
} catch {
|
|
@@ -17940,7 +18032,7 @@ async function readSubagentPartial(opts, subagentId) {
|
|
|
17940
18032
|
for (const file of candidates) {
|
|
17941
18033
|
let raw;
|
|
17942
18034
|
try {
|
|
17943
|
-
raw = await
|
|
18035
|
+
raw = await fsp2.readFile(file, "utf8");
|
|
17944
18036
|
} catch {
|
|
17945
18037
|
continue;
|
|
17946
18038
|
}
|
|
@@ -17980,9 +18072,9 @@ function makeDirectorSessionFactory(opts) {
|
|
|
17980
18072
|
let dir;
|
|
17981
18073
|
if (opts.store) {
|
|
17982
18074
|
store = opts.store;
|
|
17983
|
-
dir = opts.sessionsRoot ?
|
|
18075
|
+
dir = opts.sessionsRoot ? path3.join(opts.sessionsRoot, runId) : "(caller-managed)";
|
|
17984
18076
|
} else if (opts.sessionsRoot) {
|
|
17985
|
-
dir =
|
|
18077
|
+
dir = path3.join(opts.sessionsRoot, runId);
|
|
17986
18078
|
store = new DefaultSessionStore({ dir });
|
|
17987
18079
|
} else {
|
|
17988
18080
|
throw new Error("makeDirectorSessionFactory requires either `store` or `sessionsRoot`");
|
|
@@ -18072,71 +18164,6 @@ var NULL_FLEET_BUS = new FleetBus();
|
|
|
18072
18164
|
|
|
18073
18165
|
// src/models/models-registry.ts
|
|
18074
18166
|
init_atomic_write();
|
|
18075
|
-
|
|
18076
|
-
// src/utils/merge-models-payload.ts
|
|
18077
|
-
function mergeModelsPayload(base, overlay) {
|
|
18078
|
-
const out = {};
|
|
18079
|
-
for (const [id, provider] of Object.entries(base)) {
|
|
18080
|
-
out[id] = cloneProvider(provider);
|
|
18081
|
-
}
|
|
18082
|
-
for (const [id, ovProvider] of Object.entries(overlay)) {
|
|
18083
|
-
const existing = out[id];
|
|
18084
|
-
out[id] = existing ? mergeProvider(existing, ovProvider) : cloneProvider(ovProvider);
|
|
18085
|
-
}
|
|
18086
|
-
return out;
|
|
18087
|
-
}
|
|
18088
|
-
function mergeProvider(base, overlay) {
|
|
18089
|
-
const models = {};
|
|
18090
|
-
for (const [mid, m] of Object.entries(base.models ?? {})) {
|
|
18091
|
-
models[mid] = { ...m };
|
|
18092
|
-
}
|
|
18093
|
-
for (const [mid, ovModel] of Object.entries(overlay.models ?? {})) {
|
|
18094
|
-
const existing = models[mid];
|
|
18095
|
-
models[mid] = existing ? mergeModel(existing, ovModel) : { ...ovModel };
|
|
18096
|
-
}
|
|
18097
|
-
return {
|
|
18098
|
-
...base,
|
|
18099
|
-
// Overlay scalar fields win when explicitly provided; otherwise keep base.
|
|
18100
|
-
...stripUndefined({
|
|
18101
|
-
id: overlay.id,
|
|
18102
|
-
name: overlay.name,
|
|
18103
|
-
npm: overlay.npm,
|
|
18104
|
-
api: overlay.api,
|
|
18105
|
-
env: overlay.env,
|
|
18106
|
-
doc: overlay.doc
|
|
18107
|
-
}),
|
|
18108
|
-
models
|
|
18109
|
-
};
|
|
18110
|
-
}
|
|
18111
|
-
function mergeModel(base, overlay) {
|
|
18112
|
-
const merged = { ...base, ...overlay };
|
|
18113
|
-
if (base.limit || overlay.limit) {
|
|
18114
|
-
merged.limit = { ...base.limit, ...overlay.limit };
|
|
18115
|
-
}
|
|
18116
|
-
if (base.cost || overlay.cost) {
|
|
18117
|
-
merged.cost = { ...base.cost, ...overlay.cost };
|
|
18118
|
-
}
|
|
18119
|
-
if (base.modalities || overlay.modalities) {
|
|
18120
|
-
merged.modalities = { ...base.modalities, ...overlay.modalities };
|
|
18121
|
-
}
|
|
18122
|
-
return merged;
|
|
18123
|
-
}
|
|
18124
|
-
function cloneProvider(p) {
|
|
18125
|
-
const models = {};
|
|
18126
|
-
for (const [mid, m] of Object.entries(p.models ?? {})) {
|
|
18127
|
-
models[mid] = { ...m };
|
|
18128
|
-
}
|
|
18129
|
-
return { ...p, models };
|
|
18130
|
-
}
|
|
18131
|
-
function stripUndefined(obj) {
|
|
18132
|
-
const out = {};
|
|
18133
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
18134
|
-
if (v !== void 0) out[k] = v;
|
|
18135
|
-
}
|
|
18136
|
-
return out;
|
|
18137
|
-
}
|
|
18138
|
-
|
|
18139
|
-
// src/models/models-registry.ts
|
|
18140
18167
|
var DEFAULT_URL = "https://models.dev/api.json";
|
|
18141
18168
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
18142
18169
|
var DEFAULT_REFRESH_TIMEOUT_MS = 15e3;
|
|
@@ -18193,7 +18220,7 @@ var DefaultModelsRegistry = class {
|
|
|
18193
18220
|
this.overlay = opts.overlay;
|
|
18194
18221
|
this.overlayUrl = opts.overlayUrl;
|
|
18195
18222
|
this.overlayFile = opts.overlayFile;
|
|
18196
|
-
this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ?
|
|
18223
|
+
this.overlayCacheFile = opts.overlayCacheFile ?? (opts.overlayUrl ? path3.join(path3.dirname(opts.cacheFile), "models-overlay-cache.json") : void 0);
|
|
18197
18224
|
}
|
|
18198
18225
|
async load(opts = {}) {
|
|
18199
18226
|
if (this.payload && !opts.force) return this.payload;
|
|
@@ -18232,7 +18259,7 @@ var DefaultModelsRegistry = class {
|
|
|
18232
18259
|
}
|
|
18233
18260
|
if (overlayAvailable) {
|
|
18234
18261
|
console.warn(
|
|
18235
|
-
`ModelsRegistry: models.dev unavailable (${
|
|
18262
|
+
`ModelsRegistry: models.dev unavailable (${toErrorMessage(err)}); serving curated overlay only.`
|
|
18236
18263
|
);
|
|
18237
18264
|
return {};
|
|
18238
18265
|
}
|
|
@@ -18320,7 +18347,7 @@ var DefaultModelsRegistry = class {
|
|
|
18320
18347
|
async readOverlayFile() {
|
|
18321
18348
|
if (!this.overlayFile) return void 0;
|
|
18322
18349
|
try {
|
|
18323
|
-
const raw = await
|
|
18350
|
+
const raw = await fsp2.readFile(this.overlayFile, "utf8");
|
|
18324
18351
|
return JSON.parse(raw);
|
|
18325
18352
|
} catch {
|
|
18326
18353
|
return void 0;
|
|
@@ -18398,7 +18425,7 @@ var DefaultModelsRegistry = class {
|
|
|
18398
18425
|
}
|
|
18399
18426
|
async readCacheAt(file) {
|
|
18400
18427
|
try {
|
|
18401
|
-
const raw = await
|
|
18428
|
+
const raw = await fsp2.readFile(file, "utf8");
|
|
18402
18429
|
return JSON.parse(raw);
|
|
18403
18430
|
} catch {
|
|
18404
18431
|
return void 0;
|
|
@@ -18406,7 +18433,7 @@ var DefaultModelsRegistry = class {
|
|
|
18406
18433
|
}
|
|
18407
18434
|
/** Used by `wstack models refresh` to expose where the cache lives. */
|
|
18408
18435
|
cacheLocation() {
|
|
18409
|
-
return
|
|
18436
|
+
return path3.resolve(this.cacheFile);
|
|
18410
18437
|
}
|
|
18411
18438
|
};
|
|
18412
18439
|
function hasEntries(payload) {
|
|
@@ -18773,8 +18800,8 @@ var DefaultModeStore = class {
|
|
|
18773
18800
|
}
|
|
18774
18801
|
async loadActiveMode() {
|
|
18775
18802
|
try {
|
|
18776
|
-
const configPath =
|
|
18777
|
-
const content = await
|
|
18803
|
+
const configPath = path3.join(this.configDir, "mode.json");
|
|
18804
|
+
const content = await fsp2.readFile(configPath, "utf8");
|
|
18778
18805
|
const data = JSON.parse(content);
|
|
18779
18806
|
this.activeModeId = data.activeMode ?? null;
|
|
18780
18807
|
} catch {
|
|
@@ -18783,8 +18810,8 @@ var DefaultModeStore = class {
|
|
|
18783
18810
|
}
|
|
18784
18811
|
async saveActiveMode() {
|
|
18785
18812
|
try {
|
|
18786
|
-
await
|
|
18787
|
-
const configPath =
|
|
18813
|
+
await fsp2.mkdir(this.configDir, { recursive: true });
|
|
18814
|
+
const configPath = path3.join(this.configDir, "mode.json");
|
|
18788
18815
|
await atomicWrite(
|
|
18789
18816
|
configPath,
|
|
18790
18817
|
JSON.stringify({ activeMode: this.activeModeId }, null, 2)
|
|
@@ -18796,14 +18823,14 @@ var DefaultModeStore = class {
|
|
|
18796
18823
|
async function loadProjectModes(modesDir) {
|
|
18797
18824
|
const modes = [];
|
|
18798
18825
|
try {
|
|
18799
|
-
const entries = await
|
|
18826
|
+
const entries = await fsp2.readdir(modesDir);
|
|
18800
18827
|
for (const entry of entries) {
|
|
18801
18828
|
if (!entry.endsWith(".md") && !entry.endsWith(".txt")) continue;
|
|
18802
|
-
const filePath =
|
|
18803
|
-
const stat6 = await
|
|
18829
|
+
const filePath = path3.join(modesDir, entry);
|
|
18830
|
+
const stat6 = await fsp2.stat(filePath);
|
|
18804
18831
|
if (!stat6.isFile()) continue;
|
|
18805
|
-
const content = await
|
|
18806
|
-
const id =
|
|
18832
|
+
const content = await fsp2.readFile(filePath, "utf8");
|
|
18833
|
+
const id = path3.basename(entry, path3.extname(entry));
|
|
18807
18834
|
modes.push({
|
|
18808
18835
|
id,
|
|
18809
18836
|
name: id.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
@@ -18819,8 +18846,8 @@ async function loadProjectModes(modesDir) {
|
|
|
18819
18846
|
async function loadUserModes(modesDir) {
|
|
18820
18847
|
const modes = [];
|
|
18821
18848
|
try {
|
|
18822
|
-
const manifestPath =
|
|
18823
|
-
const content = await
|
|
18849
|
+
const manifestPath = path3.join(modesDir, "modes.json");
|
|
18850
|
+
const content = await fsp2.readFile(manifestPath, "utf8");
|
|
18824
18851
|
const manifest = JSON.parse(content);
|
|
18825
18852
|
for (const mode of manifest.modes) {
|
|
18826
18853
|
modes.push(mode);
|
|
@@ -19546,7 +19573,7 @@ var TaskTracker = class {
|
|
|
19546
19573
|
this.opts.onPersistError ? this.opts.onPersistError(err) : console.warn(JSON.stringify({
|
|
19547
19574
|
level: "warn",
|
|
19548
19575
|
event: "task_tracker.save_graph_failed",
|
|
19549
|
-
message:
|
|
19576
|
+
message: toErrorMessage(err),
|
|
19550
19577
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
19551
19578
|
}));
|
|
19552
19579
|
});
|
|
@@ -19771,7 +19798,7 @@ var SpecStore = class {
|
|
|
19771
19798
|
indexPath;
|
|
19772
19799
|
constructor(opts) {
|
|
19773
19800
|
this.baseDir = opts.baseDir;
|
|
19774
|
-
this.indexPath =
|
|
19801
|
+
this.indexPath = path3.join(this.baseDir, "_index.json");
|
|
19775
19802
|
}
|
|
19776
19803
|
async save(spec) {
|
|
19777
19804
|
await ensureDir(this.baseDir);
|
|
@@ -19781,7 +19808,7 @@ var SpecStore = class {
|
|
|
19781
19808
|
}
|
|
19782
19809
|
async load(id) {
|
|
19783
19810
|
try {
|
|
19784
|
-
const raw = await
|
|
19811
|
+
const raw = await fsp2.readFile(this.filePath(id), "utf8");
|
|
19785
19812
|
return JSON.parse(raw);
|
|
19786
19813
|
} catch {
|
|
19787
19814
|
return null;
|
|
@@ -19793,7 +19820,7 @@ var SpecStore = class {
|
|
|
19793
19820
|
}
|
|
19794
19821
|
async delete(id) {
|
|
19795
19822
|
try {
|
|
19796
|
-
await
|
|
19823
|
+
await fsp2.unlink(this.filePath(id));
|
|
19797
19824
|
await this.removeFromIndex(id);
|
|
19798
19825
|
return true;
|
|
19799
19826
|
} catch {
|
|
@@ -19802,7 +19829,7 @@ var SpecStore = class {
|
|
|
19802
19829
|
}
|
|
19803
19830
|
async exists(id) {
|
|
19804
19831
|
try {
|
|
19805
|
-
await
|
|
19832
|
+
await fsp2.access(this.filePath(id));
|
|
19806
19833
|
return true;
|
|
19807
19834
|
} catch {
|
|
19808
19835
|
return false;
|
|
@@ -19840,11 +19867,11 @@ var SpecStore = class {
|
|
|
19840
19867
|
return updated;
|
|
19841
19868
|
}
|
|
19842
19869
|
filePath(id) {
|
|
19843
|
-
return
|
|
19870
|
+
return path3.join(this.baseDir, `${id}.json`);
|
|
19844
19871
|
}
|
|
19845
19872
|
async readIndex() {
|
|
19846
19873
|
try {
|
|
19847
|
-
const raw = await
|
|
19874
|
+
const raw = await fsp2.readFile(this.indexPath, "utf8");
|
|
19848
19875
|
const parsed = JSON.parse(raw);
|
|
19849
19876
|
if (parsed?.version === 1) return parsed;
|
|
19850
19877
|
} catch {
|
|
@@ -19897,7 +19924,7 @@ var TaskGraphStore = class {
|
|
|
19897
19924
|
indexPath;
|
|
19898
19925
|
constructor(opts) {
|
|
19899
19926
|
this.baseDir = opts.baseDir;
|
|
19900
|
-
this.indexPath =
|
|
19927
|
+
this.indexPath = path3.join(this.baseDir, "_index.json");
|
|
19901
19928
|
}
|
|
19902
19929
|
async save(graph) {
|
|
19903
19930
|
await ensureDir(this.baseDir);
|
|
@@ -19907,7 +19934,7 @@ var TaskGraphStore = class {
|
|
|
19907
19934
|
}
|
|
19908
19935
|
async load(id) {
|
|
19909
19936
|
try {
|
|
19910
|
-
const raw = await
|
|
19937
|
+
const raw = await fsp2.readFile(this.filePath(id), "utf8");
|
|
19911
19938
|
return graphFromJSON(raw);
|
|
19912
19939
|
} catch {
|
|
19913
19940
|
return null;
|
|
@@ -19919,7 +19946,7 @@ var TaskGraphStore = class {
|
|
|
19919
19946
|
}
|
|
19920
19947
|
async delete(id) {
|
|
19921
19948
|
try {
|
|
19922
|
-
await
|
|
19949
|
+
await fsp2.unlink(this.filePath(id));
|
|
19923
19950
|
await this.removeFromIndex(id);
|
|
19924
19951
|
return true;
|
|
19925
19952
|
} catch {
|
|
@@ -19928,18 +19955,18 @@ var TaskGraphStore = class {
|
|
|
19928
19955
|
}
|
|
19929
19956
|
async exists(id) {
|
|
19930
19957
|
try {
|
|
19931
|
-
await
|
|
19958
|
+
await fsp2.access(this.filePath(id));
|
|
19932
19959
|
return true;
|
|
19933
19960
|
} catch {
|
|
19934
19961
|
return false;
|
|
19935
19962
|
}
|
|
19936
19963
|
}
|
|
19937
19964
|
filePath(id) {
|
|
19938
|
-
return
|
|
19965
|
+
return path3.join(this.baseDir, `${id}.json`);
|
|
19939
19966
|
}
|
|
19940
19967
|
async readIndex() {
|
|
19941
19968
|
try {
|
|
19942
|
-
const raw = await
|
|
19969
|
+
const raw = await fsp2.readFile(this.indexPath, "utf8");
|
|
19943
19970
|
const parsed = JSON.parse(raw);
|
|
19944
19971
|
if (parsed?.version === 1) return parsed;
|
|
19945
19972
|
} catch {
|
|
@@ -20216,7 +20243,7 @@ var AISpecBuilder = class {
|
|
|
20216
20243
|
* ENOSPC / EACCES doesn't silently strand session edits in memory. */
|
|
20217
20244
|
autoSave() {
|
|
20218
20245
|
this.saveSession().catch((err) => {
|
|
20219
|
-
const detail =
|
|
20246
|
+
const detail = toErrorMessage(err);
|
|
20220
20247
|
process.emitWarning(
|
|
20221
20248
|
`SpecBuilder autoSave failed: ${detail}`,
|
|
20222
20249
|
"SpecBuilderWarning"
|
|
@@ -21535,7 +21562,7 @@ var SddParallelRun = class {
|
|
|
21535
21562
|
const failCount = results.length - successCount;
|
|
21536
21563
|
for (let i = 0; i < results.length; i++) {
|
|
21537
21564
|
const result = expectDefined(results[i]);
|
|
21538
|
-
const taskId = expectDefined(
|
|
21565
|
+
const taskId = expectDefined(tasks[i]).id;
|
|
21539
21566
|
if (result.status === "success") {
|
|
21540
21567
|
this.opts.tracker.updateNodeStatus(taskId, "completed");
|
|
21541
21568
|
this.retryMap.delete(taskId);
|
|
@@ -21749,7 +21776,7 @@ var DefaultHealthRegistry = class {
|
|
|
21749
21776
|
try {
|
|
21750
21777
|
return await Promise.race([check.check(), timeout]);
|
|
21751
21778
|
} catch (err) {
|
|
21752
|
-
return { status: "unhealthy", detail:
|
|
21779
|
+
return { status: "unhealthy", detail: toErrorMessage(err) };
|
|
21753
21780
|
} finally {
|
|
21754
21781
|
if (timer) clearTimeout(timer);
|
|
21755
21782
|
}
|
|
@@ -21942,7 +21969,7 @@ async function startMetricsServer(opts) {
|
|
|
21942
21969
|
} catch (err) {
|
|
21943
21970
|
res.statusCode = 500;
|
|
21944
21971
|
res.setHeader("content-type", "text/plain; charset=utf-8");
|
|
21945
|
-
res.end(`metrics render failed: ${
|
|
21972
|
+
res.end(`metrics render failed: ${toErrorMessage(err)}`);
|
|
21946
21973
|
return;
|
|
21947
21974
|
}
|
|
21948
21975
|
res.statusCode = 200;
|
|
@@ -21960,7 +21987,7 @@ async function startMetricsServer(opts) {
|
|
|
21960
21987
|
(err) => {
|
|
21961
21988
|
res.statusCode = 500;
|
|
21962
21989
|
res.setHeader("content-type", "text/plain; charset=utf-8");
|
|
21963
|
-
res.end(`health run failed: ${
|
|
21990
|
+
res.end(`health run failed: ${toErrorMessage(err)}`);
|
|
21964
21991
|
}
|
|
21965
21992
|
);
|
|
21966
21993
|
return;
|