@wrongstack/core 0.257.0 → 0.260.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-BrxWHEOm.d.ts → agent-bridge-BbskZ7HH.d.ts} +1 -1
- package/dist/{agent-subagent-runner-US741uBH.d.ts → agent-subagent-runner-BNIGZx18.d.ts} +28 -8
- package/dist/{brain-TjEEwSpw.d.ts → brain-C2yDd7Lw.d.ts} +58 -1
- package/dist/{compactor-C5sT4U7I.d.ts → compactor-t0R_AIt_.d.ts} +1 -1
- package/dist/{config-DuAu23zm.d.ts → config-FG6As4H5.d.ts} +1 -1
- package/dist/{context-CGdgA0q6.d.ts → context-JFOVvu6z.d.ts} +22 -0
- package/dist/coordination/index.d.ts +14 -14
- package/dist/coordination/index.js +195 -33
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +25 -25
- package/dist/defaults/index.js +908 -92
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +15 -15
- package/dist/execution/index.js +134 -35
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/{goal-preamble-UiEkbNmW.d.ts → goal-preamble-B1IXJtLX.d.ts} +11 -9
- package/dist/{goal-store-CV9Yz2X_.d.ts → goal-store-CPXz6Mml.d.ts} +4 -2
- package/dist/{index-CitPrI3a.d.ts → index-BPcg4N3M.d.ts} +5 -5
- package/dist/{index-CC0Mcm05.d.ts → index-CebbJB94.d.ts} +8 -8
- package/dist/index.d.ts +47 -43
- package/dist/index.js +1571 -284
- 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.map +1 -1
- package/dist/{llm-selector-CJ4SyAFE.d.ts → llm-selector-DXxI2tlu.d.ts} +2 -2
- package/dist/{mcp-servers-D8YnLaEp.d.ts → mcp-servers-OwNHo43-.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/{models-registry-ByZCdFuQ.d.ts → models-registry-Djlmq4uB.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-DqTUEAeC.d.ts → multi-agent-coordinator-CEmrSCMJ.d.ts} +1 -1
- package/dist/{null-fleet-bus-B5mfTJXT.d.ts → null-fleet-bus-DT92xqgJ.d.ts} +13 -8
- package/dist/observability/index.d.ts +2 -2
- package/dist/{package-outdated-watcher-BSgR_kK-.d.ts → package-outdated-watcher-C70ag2G9.d.ts} +3 -3
- package/dist/{parallel-eternal-engine-C0juOszP.d.ts → parallel-eternal-engine-0SItuq5r.d.ts} +13 -9
- package/dist/{path-resolver-CbkT-RMU.d.ts → path-resolver-DKBh6Jlo.d.ts} +3 -3
- package/dist/{permission-CwBBpCoF.d.ts → permission-BJ7eO9Vl.d.ts} +1 -1
- package/dist/{permission-policy-B8rSu908.d.ts → permission-policy-DEXOfnpm.d.ts} +3 -2
- package/dist/{pipeline-JG8XoudC.d.ts → pipeline-zflkI2dp.d.ts} +2 -2
- package/dist/{plan-templates-DPiQMkBz.d.ts → plan-templates-BFXyRkEK.d.ts} +32 -11
- package/dist/{provider-runner-hM7EXlLI.d.ts → provider-runner-BC-uywtT.d.ts} +3 -3
- package/dist/{retry-policy-Tg7LXkoK.d.ts → retry-policy-Cavrzmtk.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +20 -2
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-gxtFZYBt.d.ts → secret-vault-CDvDYXWX.d.ts} +1 -1
- package/dist/security/index.d.ts +4 -4
- package/dist/security/index.js +30 -1
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-DWsqVjGf.d.ts → selector-B7AivHsu.d.ts} +1 -1
- package/dist/{session-event-bridge-BAFWdgQ3.d.ts → session-event-bridge-BmIDxdJd.d.ts} +1 -1
- package/dist/{session-reader-CqRvaL5v.d.ts → session-reader-DtofsB-2.d.ts} +1 -1
- package/dist/storage/index.d.ts +30 -21
- package/dist/storage/index.js +1264 -216
- package/dist/storage/index.js.map +1 -1
- package/dist/types/index.d.ts +19 -19
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +101 -3
- package/dist/utils/index.js +92 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/output-standards/SKILL.md +14 -9
- package/skills/output-standards/SKILL.save.md +3 -2
package/dist/defaults/index.js
CHANGED
|
@@ -451,6 +451,40 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
451
451
|
this.events = opts.events;
|
|
452
452
|
this.secretScrubber = opts.secretScrubber;
|
|
453
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
|
+
}
|
|
454
488
|
/** Absolute path to the session index file. */
|
|
455
489
|
get indexFile() {
|
|
456
490
|
return path11.join(this.dir, "_index.jsonl");
|
|
@@ -474,22 +508,26 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
474
508
|
const id = meta.id && meta.id.length > 0 ? meta.id : generateSessionId(startedAt, meta.model ?? meta.provider);
|
|
475
509
|
const shardDir = await this.ensureShardDir(id);
|
|
476
510
|
const file = path11.join(shardDir, `${path11.basename(id)}.jsonl`);
|
|
511
|
+
const t0 = Date.now();
|
|
477
512
|
let handle;
|
|
478
513
|
try {
|
|
479
514
|
handle = await fsp.open(file, "a", 384);
|
|
480
515
|
} catch (err) {
|
|
516
|
+
this.emitError(id, file, "create", err instanceof Error ? err.message : String(err), false);
|
|
481
517
|
throw new Error(
|
|
482
518
|
`Failed to open session file: ${err instanceof Error ? err.message : String(err)}`,
|
|
483
519
|
{ cause: err }
|
|
484
520
|
);
|
|
485
521
|
}
|
|
486
522
|
try {
|
|
487
|
-
|
|
523
|
+
const writer = new FileSessionWriter(id, handle, startedAt, meta, this.events, {
|
|
488
524
|
dir: shardDir,
|
|
489
525
|
filePath: file,
|
|
490
526
|
secretScrubber: this.secretScrubber,
|
|
491
527
|
onClose: (s) => this.appendToIndex(s)
|
|
492
528
|
});
|
|
529
|
+
this.emitWrite(id, file, "create", "success", Date.now() - t0);
|
|
530
|
+
return writer;
|
|
493
531
|
} catch (err) {
|
|
494
532
|
await handle.close().catch((e) => console.warn(JSON.stringify({
|
|
495
533
|
level: "warn",
|
|
@@ -497,16 +535,19 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
497
535
|
message: e instanceof Error ? e.message : String(e),
|
|
498
536
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
499
537
|
})));
|
|
538
|
+
this.emitError(id, file, "create", err instanceof Error ? err.message : String(err), true);
|
|
500
539
|
throw err;
|
|
501
540
|
}
|
|
502
541
|
}
|
|
503
542
|
async resume(id) {
|
|
504
543
|
const file = this.sessionPath(id, ".jsonl");
|
|
544
|
+
const t0 = Date.now();
|
|
505
545
|
const data = await this.load(id);
|
|
506
546
|
let handle;
|
|
507
547
|
try {
|
|
508
548
|
handle = await fsp.open(file, "a", 384);
|
|
509
549
|
} catch (err) {
|
|
550
|
+
this.emitError(id, file, "resume", err instanceof Error ? err.message : String(err), false);
|
|
510
551
|
throw new Error(
|
|
511
552
|
`Failed to open session "${id}" for append: ${err instanceof Error ? err.message : String(err)}`,
|
|
512
553
|
{ cause: err }
|
|
@@ -534,6 +575,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
534
575
|
onClose: (s) => this.appendToIndex(s)
|
|
535
576
|
}
|
|
536
577
|
);
|
|
578
|
+
this.emitWrite(id, file, "resume", "success", Date.now() - t0);
|
|
537
579
|
return { writer, data };
|
|
538
580
|
} catch (err) {
|
|
539
581
|
await handle.close().catch((e) => console.warn(JSON.stringify({
|
|
@@ -542,27 +584,39 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
542
584
|
message: e instanceof Error ? e.message : String(e),
|
|
543
585
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
544
586
|
})));
|
|
587
|
+
this.emitError(id, file, "resume", err instanceof Error ? err.message : String(err), true);
|
|
545
588
|
throw err;
|
|
546
589
|
}
|
|
547
590
|
}
|
|
548
591
|
async load(id) {
|
|
549
592
|
const file = this.sessionPath(id, ".jsonl");
|
|
550
|
-
const
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
593
|
+
const t0 = Date.now();
|
|
594
|
+
let outcome = "success";
|
|
595
|
+
let errorMsg;
|
|
596
|
+
try {
|
|
597
|
+
const raw = await fsp.readFile(file, "utf8");
|
|
598
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
599
|
+
const events = [];
|
|
600
|
+
for (const line of lines) {
|
|
601
|
+
try {
|
|
602
|
+
const parsed = JSON.parse(line);
|
|
603
|
+
if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
|
|
604
|
+
events.push(parsed);
|
|
605
|
+
}
|
|
606
|
+
} catch {
|
|
558
607
|
}
|
|
559
|
-
} catch {
|
|
560
608
|
}
|
|
609
|
+
const meta = this.metaFromEvents(id, events);
|
|
610
|
+
const { messages, usage } = this.replay(events, id);
|
|
611
|
+
const toolCallEnds = extractToolCallEnds(events);
|
|
612
|
+
return { metadata: meta, events, messages, usage, toolCallEnds };
|
|
613
|
+
} catch (err) {
|
|
614
|
+
outcome = "failure";
|
|
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);
|
|
561
619
|
}
|
|
562
|
-
const meta = this.metaFromEvents(id, events);
|
|
563
|
-
const { messages, usage } = this.replay(events, id);
|
|
564
|
-
const toolCallEnds = extractToolCallEnds(events);
|
|
565
|
-
return { metadata: meta, events, messages, usage, toolCallEnds };
|
|
566
620
|
}
|
|
567
621
|
async list(limit = 20) {
|
|
568
622
|
try {
|
|
@@ -629,12 +683,22 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
629
683
|
* (keep latest per session), and rewrite. Atomic via temp+rename.
|
|
630
684
|
*/
|
|
631
685
|
async compactIndex() {
|
|
632
|
-
const
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
686
|
+
const t0 = Date.now();
|
|
687
|
+
let outcome = "success";
|
|
688
|
+
let errorMsg;
|
|
689
|
+
try {
|
|
690
|
+
const entries = await this.readIndex();
|
|
691
|
+
if (entries.length === 0) return;
|
|
692
|
+
const tmp = `${this.indexFile}.compact.tmp`;
|
|
693
|
+
const lines = entries.map((s) => JSON.stringify(s)).join("\n") + "\n";
|
|
694
|
+
await fsp.writeFile(tmp, lines, "utf8");
|
|
695
|
+
await fsp.rename(tmp, this.indexFile);
|
|
696
|
+
} catch (err) {
|
|
697
|
+
outcome = "failure";
|
|
698
|
+
errorMsg = err instanceof Error ? err.message : String(err);
|
|
699
|
+
} finally {
|
|
700
|
+
this.emitWrite("~compact~", this.indexFile, "compact", outcome, Date.now() - t0, void 0, errorMsg);
|
|
701
|
+
}
|
|
638
702
|
}
|
|
639
703
|
/**
|
|
640
704
|
* Read the index file and return deduplicated session summaries.
|
|
@@ -710,22 +774,31 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
710
774
|
}
|
|
711
775
|
async summaryFor(id) {
|
|
712
776
|
const manifest = this.sessionPath(id, ".summary.json");
|
|
777
|
+
const t0 = Date.now();
|
|
778
|
+
let outcome = "success";
|
|
779
|
+
let errorMsg;
|
|
713
780
|
try {
|
|
714
781
|
const raw = await fsp.readFile(manifest, "utf8");
|
|
782
|
+
this.emitRead(id, manifest, "summary", "success", Date.now() - t0);
|
|
715
783
|
return JSON.parse(raw);
|
|
716
784
|
} catch {
|
|
717
785
|
const full = this.sessionPath(id, ".jsonl");
|
|
718
786
|
const stat6 = await fsp.stat(full);
|
|
719
787
|
const summary = await this.summarize(id, stat6.mtime.toISOString());
|
|
720
788
|
await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
|
|
789
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
790
|
+
this.emitError(id, manifest, "summary_fallback", msg, true);
|
|
721
791
|
console.warn(JSON.stringify({
|
|
722
792
|
level: "warn",
|
|
723
793
|
event: "session_store.manifest_write_failed",
|
|
724
794
|
sessionId: id,
|
|
725
|
-
message:
|
|
795
|
+
message: msg,
|
|
726
796
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
727
797
|
}));
|
|
728
798
|
});
|
|
799
|
+
outcome = "failure";
|
|
800
|
+
errorMsg = "summary fallback \u2014 manifest rebuilt";
|
|
801
|
+
this.emitRead(id, manifest, "summary", outcome, Date.now() - t0, errorMsg);
|
|
729
802
|
return summary;
|
|
730
803
|
}
|
|
731
804
|
}
|
|
@@ -991,7 +1064,7 @@ function extractToolCallEnds(events) {
|
|
|
991
1064
|
return result;
|
|
992
1065
|
}
|
|
993
1066
|
var FileSessionWriter = class _FileSessionWriter {
|
|
994
|
-
constructor(id, handle, startedAt, meta, events, opts = {}) {
|
|
1067
|
+
constructor(id, handle, startedAt, meta, events, opts = {}, traceId) {
|
|
995
1068
|
this.id = id;
|
|
996
1069
|
this.handle = handle;
|
|
997
1070
|
this.startedAt = startedAt;
|
|
@@ -1010,6 +1083,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1010
1083
|
provider: meta.provider ?? "unknown",
|
|
1011
1084
|
tokenTotal: 0
|
|
1012
1085
|
};
|
|
1086
|
+
this.traceId = traceId;
|
|
1013
1087
|
}
|
|
1014
1088
|
id;
|
|
1015
1089
|
handle;
|
|
@@ -1042,6 +1116,8 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1042
1116
|
lastAppendWarnAt = 0;
|
|
1043
1117
|
secretScrubber;
|
|
1044
1118
|
onCloseCb;
|
|
1119
|
+
/** Implements SessionWriter.traceId — propagated from ContextInit.traceId. */
|
|
1120
|
+
traceId;
|
|
1045
1121
|
// ── Write buffer — batches events to reduce per-event disk I/O ─────────
|
|
1046
1122
|
//
|
|
1047
1123
|
// Every append() pushes the scrubbed event into an in-memory buffer instead
|
|
@@ -1195,9 +1271,14 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1195
1271
|
const eventCount = this.writeBuffer.length;
|
|
1196
1272
|
const batch = this.writeBuffer.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
1197
1273
|
this.writeBuffer = [];
|
|
1274
|
+
const t0 = Date.now();
|
|
1275
|
+
let outcome = "success";
|
|
1276
|
+
let errorMsg;
|
|
1198
1277
|
try {
|
|
1199
1278
|
await this.enqueueWrite(batch);
|
|
1200
1279
|
} catch (err) {
|
|
1280
|
+
outcome = "failure";
|
|
1281
|
+
errorMsg = err instanceof Error ? err.message : String(err);
|
|
1201
1282
|
this.appendFailCount += eventCount;
|
|
1202
1283
|
const now = Date.now();
|
|
1203
1284
|
if (now - this.lastAppendWarnAt > 5e3) {
|
|
@@ -1211,6 +1292,18 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1211
1292
|
this.lastAppendWarnAt = now;
|
|
1212
1293
|
this.appendFailCount = 0;
|
|
1213
1294
|
}
|
|
1295
|
+
} finally {
|
|
1296
|
+
this.events?.emit("storage.write", {
|
|
1297
|
+
sessionId: this.id,
|
|
1298
|
+
store: "session",
|
|
1299
|
+
filePath: this.filePath,
|
|
1300
|
+
operation: "flush",
|
|
1301
|
+
outcome,
|
|
1302
|
+
durationMs: Date.now() - t0,
|
|
1303
|
+
...errorMsg !== void 0 ? { error: errorMsg } : {},
|
|
1304
|
+
...eventCount !== void 0 ? { eventCount } : {},
|
|
1305
|
+
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
1306
|
+
});
|
|
1214
1307
|
}
|
|
1215
1308
|
}
|
|
1216
1309
|
observeForSummary(event) {
|
|
@@ -1276,14 +1369,46 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
1276
1369
|
outcome: this.outcome ?? "completed"
|
|
1277
1370
|
};
|
|
1278
1371
|
if (this.manifestFile) {
|
|
1372
|
+
const t0 = Date.now();
|
|
1373
|
+
let outcome = "success";
|
|
1374
|
+
let errorMsg;
|
|
1279
1375
|
try {
|
|
1280
1376
|
await atomicWrite(this.manifestFile, JSON.stringify(this.summary), { mode: 384 });
|
|
1281
|
-
} catch {
|
|
1377
|
+
} catch (err) {
|
|
1378
|
+
outcome = "failure";
|
|
1379
|
+
errorMsg = err instanceof Error ? err.message : String(err);
|
|
1380
|
+
} finally {
|
|
1381
|
+
this.events?.emit("storage.write", {
|
|
1382
|
+
sessionId: this.id,
|
|
1383
|
+
store: "session",
|
|
1384
|
+
filePath: this.manifestFile,
|
|
1385
|
+
operation: "close",
|
|
1386
|
+
outcome,
|
|
1387
|
+
durationMs: Date.now() - t0,
|
|
1388
|
+
...errorMsg !== void 0 ? { error: errorMsg } : {},
|
|
1389
|
+
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
1390
|
+
});
|
|
1282
1391
|
}
|
|
1283
1392
|
}
|
|
1393
|
+
const idxT0 = Date.now();
|
|
1394
|
+
let idxOutcome = "success";
|
|
1395
|
+
let idxError;
|
|
1284
1396
|
try {
|
|
1285
1397
|
await this.onCloseCb?.(this.summary);
|
|
1286
|
-
} catch {
|
|
1398
|
+
} catch (err) {
|
|
1399
|
+
idxOutcome = "failure";
|
|
1400
|
+
idxError = err instanceof Error ? err.message : String(err);
|
|
1401
|
+
} finally {
|
|
1402
|
+
this.events?.emit("storage.write", {
|
|
1403
|
+
sessionId: this.summary.id,
|
|
1404
|
+
store: "session",
|
|
1405
|
+
filePath: this.filePath,
|
|
1406
|
+
operation: "index_append",
|
|
1407
|
+
outcome: idxOutcome,
|
|
1408
|
+
durationMs: Date.now() - idxT0,
|
|
1409
|
+
...idxError !== void 0 ? { error: idxError } : {},
|
|
1410
|
+
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
1411
|
+
});
|
|
1287
1412
|
}
|
|
1288
1413
|
try {
|
|
1289
1414
|
await this.handle.close();
|
|
@@ -1445,23 +1570,81 @@ function userInputTitle(content) {
|
|
|
1445
1570
|
init_atomic_write();
|
|
1446
1571
|
var QueueStore = class {
|
|
1447
1572
|
file;
|
|
1573
|
+
// Use `| undefined` (not `?`) so exactOptionalPropertyTypes doesn't
|
|
1574
|
+
// reject assigning an optional constructor parameter to these fields.
|
|
1575
|
+
events;
|
|
1576
|
+
traceId;
|
|
1448
1577
|
constructor(opts) {
|
|
1449
1578
|
this.file = path11.join(opts.dir, "queue.json");
|
|
1579
|
+
this.events = opts.events;
|
|
1580
|
+
this.traceId = opts.traceId;
|
|
1450
1581
|
}
|
|
1451
1582
|
async write(items) {
|
|
1583
|
+
const t0 = Date.now();
|
|
1452
1584
|
if (items.length === 0) {
|
|
1453
1585
|
await this.clear();
|
|
1454
1586
|
return;
|
|
1455
1587
|
}
|
|
1456
|
-
|
|
1588
|
+
try {
|
|
1589
|
+
await atomicWrite(this.file, JSON.stringify(items), { mode: 384 });
|
|
1590
|
+
this.events?.emit("storage.write", {
|
|
1591
|
+
sessionId: this.traceId ?? "~boot~",
|
|
1592
|
+
store: "queue",
|
|
1593
|
+
filePath: this.file,
|
|
1594
|
+
operation: "write",
|
|
1595
|
+
outcome: "success",
|
|
1596
|
+
durationMs: Date.now() - t0,
|
|
1597
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
1598
|
+
});
|
|
1599
|
+
} catch (err) {
|
|
1600
|
+
this.events?.emit("storage.error", {
|
|
1601
|
+
sessionId: this.traceId ?? "~boot~",
|
|
1602
|
+
store: "queue",
|
|
1603
|
+
filePath: this.file,
|
|
1604
|
+
operation: "write",
|
|
1605
|
+
outcome: "failure",
|
|
1606
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1607
|
+
recoverable: false,
|
|
1608
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
1609
|
+
});
|
|
1610
|
+
console.warn(JSON.stringify({
|
|
1611
|
+
level: "warn",
|
|
1612
|
+
event: "queue_store.write_failed",
|
|
1613
|
+
path: this.file,
|
|
1614
|
+
message: err instanceof Error ? err.message : String(err),
|
|
1615
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1616
|
+
}));
|
|
1617
|
+
}
|
|
1457
1618
|
}
|
|
1458
1619
|
async read() {
|
|
1620
|
+
const t0 = Date.now();
|
|
1459
1621
|
let raw;
|
|
1460
1622
|
try {
|
|
1461
1623
|
raw = await fsp.readFile(this.file, "utf8");
|
|
1462
1624
|
} catch (err) {
|
|
1463
1625
|
const code = err.code;
|
|
1464
|
-
if (code === "ENOENT")
|
|
1626
|
+
if (code === "ENOENT") {
|
|
1627
|
+
this.events?.emit("storage.read", {
|
|
1628
|
+
sessionId: this.traceId ?? "~boot~",
|
|
1629
|
+
store: "queue",
|
|
1630
|
+
filePath: this.file,
|
|
1631
|
+
operation: "read",
|
|
1632
|
+
outcome: "success",
|
|
1633
|
+
durationMs: Date.now() - t0,
|
|
1634
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
1635
|
+
});
|
|
1636
|
+
return [];
|
|
1637
|
+
}
|
|
1638
|
+
this.events?.emit("storage.error", {
|
|
1639
|
+
sessionId: this.traceId ?? "~boot~",
|
|
1640
|
+
store: "queue",
|
|
1641
|
+
filePath: this.file,
|
|
1642
|
+
operation: "read",
|
|
1643
|
+
outcome: "failure",
|
|
1644
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1645
|
+
recoverable: true,
|
|
1646
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
1647
|
+
});
|
|
1465
1648
|
console.warn(JSON.stringify({
|
|
1466
1649
|
level: "warn",
|
|
1467
1650
|
event: "queue_store.read_failed",
|
|
@@ -1475,9 +1658,40 @@ var QueueStore = class {
|
|
|
1475
1658
|
try {
|
|
1476
1659
|
parsed = JSON.parse(raw);
|
|
1477
1660
|
} catch {
|
|
1661
|
+
this.events?.emit("storage.read", {
|
|
1662
|
+
sessionId: this.traceId ?? "~boot~",
|
|
1663
|
+
store: "queue",
|
|
1664
|
+
filePath: this.file,
|
|
1665
|
+
operation: "read",
|
|
1666
|
+
outcome: "failure",
|
|
1667
|
+
durationMs: Date.now() - t0,
|
|
1668
|
+
error: "parse_failed",
|
|
1669
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
1670
|
+
});
|
|
1478
1671
|
return [];
|
|
1479
1672
|
}
|
|
1480
|
-
if (!Array.isArray(parsed))
|
|
1673
|
+
if (!Array.isArray(parsed)) {
|
|
1674
|
+
this.events?.emit("storage.read", {
|
|
1675
|
+
sessionId: this.traceId ?? "~boot~",
|
|
1676
|
+
store: "queue",
|
|
1677
|
+
filePath: this.file,
|
|
1678
|
+
operation: "read",
|
|
1679
|
+
outcome: "failure",
|
|
1680
|
+
durationMs: Date.now() - t0,
|
|
1681
|
+
error: "invalid_schema",
|
|
1682
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
1683
|
+
});
|
|
1684
|
+
return [];
|
|
1685
|
+
}
|
|
1686
|
+
this.events?.emit("storage.read", {
|
|
1687
|
+
sessionId: this.traceId ?? "~boot~",
|
|
1688
|
+
store: "queue",
|
|
1689
|
+
filePath: this.file,
|
|
1690
|
+
operation: "read",
|
|
1691
|
+
outcome: "success",
|
|
1692
|
+
durationMs: Date.now() - t0,
|
|
1693
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
1694
|
+
});
|
|
1481
1695
|
const out = [];
|
|
1482
1696
|
for (const v of parsed) {
|
|
1483
1697
|
if (isPersistedQueueItem(v)) out.push(v);
|
|
@@ -1485,11 +1699,31 @@ var QueueStore = class {
|
|
|
1485
1699
|
return out;
|
|
1486
1700
|
}
|
|
1487
1701
|
async clear() {
|
|
1702
|
+
const t0 = Date.now();
|
|
1488
1703
|
try {
|
|
1489
1704
|
await fsp.unlink(this.file);
|
|
1705
|
+
this.events?.emit("storage.write", {
|
|
1706
|
+
sessionId: this.traceId ?? "~boot~",
|
|
1707
|
+
store: "queue",
|
|
1708
|
+
filePath: this.file,
|
|
1709
|
+
operation: "clear",
|
|
1710
|
+
outcome: "success",
|
|
1711
|
+
durationMs: Date.now() - t0,
|
|
1712
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
1713
|
+
});
|
|
1490
1714
|
} catch (err) {
|
|
1491
1715
|
const code = err.code;
|
|
1492
1716
|
if (code === "ENOENT") return;
|
|
1717
|
+
this.events?.emit("storage.error", {
|
|
1718
|
+
sessionId: this.traceId ?? "~boot~",
|
|
1719
|
+
store: "queue",
|
|
1720
|
+
filePath: this.file,
|
|
1721
|
+
operation: "clear",
|
|
1722
|
+
outcome: "failure",
|
|
1723
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1724
|
+
recoverable: true,
|
|
1725
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
1726
|
+
});
|
|
1493
1727
|
console.warn(JSON.stringify({
|
|
1494
1728
|
level: "warn",
|
|
1495
1729
|
event: "queue_store.clear_failed",
|
|
@@ -1876,6 +2110,7 @@ var MAX_BYTES_TOTAL = 32e3;
|
|
|
1876
2110
|
var DefaultMemoryStore = class {
|
|
1877
2111
|
files;
|
|
1878
2112
|
events;
|
|
2113
|
+
traceId;
|
|
1879
2114
|
backend;
|
|
1880
2115
|
/**
|
|
1881
2116
|
* Per-scope serialization queue. `remember` / `forget` / `consolidate` /
|
|
@@ -1941,15 +2176,70 @@ var DefaultMemoryStore = class {
|
|
|
1941
2176
|
if (writeErr2) {
|
|
1942
2177
|
parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${writeErr2.message}`);
|
|
1943
2178
|
}
|
|
1944
|
-
const
|
|
1945
|
-
|
|
2179
|
+
const t0 = Date.now();
|
|
2180
|
+
const filePath = this.files[scope];
|
|
2181
|
+
try {
|
|
2182
|
+
const body = await this.backend.readAll(scope, filePath);
|
|
2183
|
+
const dur = Date.now() - t0;
|
|
2184
|
+
this.events?.emit("storage.read", {
|
|
2185
|
+
sessionId: "~memory~",
|
|
2186
|
+
store: "memory",
|
|
2187
|
+
filePath,
|
|
2188
|
+
operation: "readAll",
|
|
2189
|
+
outcome: "success",
|
|
2190
|
+
durationMs: dur,
|
|
2191
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2192
|
+
});
|
|
2193
|
+
if (body.trim()) parts.push(`## ${labelOf(scope)}
|
|
1946
2194
|
|
|
1947
2195
|
${body.trim()}`);
|
|
2196
|
+
} catch (err) {
|
|
2197
|
+
const dur = Date.now() - t0;
|
|
2198
|
+
this.events?.emit("storage.read", {
|
|
2199
|
+
sessionId: "~memory~",
|
|
2200
|
+
store: "memory",
|
|
2201
|
+
filePath,
|
|
2202
|
+
operation: "readAll",
|
|
2203
|
+
outcome: "failure",
|
|
2204
|
+
durationMs: dur,
|
|
2205
|
+
error: err instanceof Error ? err.message : String(err),
|
|
2206
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2207
|
+
});
|
|
2208
|
+
throw err;
|
|
2209
|
+
}
|
|
1948
2210
|
}
|
|
1949
2211
|
return parts.join("\n\n");
|
|
1950
2212
|
}
|
|
1951
2213
|
async read(scope) {
|
|
1952
|
-
|
|
2214
|
+
const t0 = Date.now();
|
|
2215
|
+
const filePath = this.files[scope];
|
|
2216
|
+
try {
|
|
2217
|
+
const body = await this.backend.readAll(scope, filePath);
|
|
2218
|
+
const dur = Date.now() - t0;
|
|
2219
|
+
this.events?.emit("storage.read", {
|
|
2220
|
+
sessionId: "~memory~",
|
|
2221
|
+
store: "memory",
|
|
2222
|
+
filePath,
|
|
2223
|
+
operation: "read",
|
|
2224
|
+
outcome: "success",
|
|
2225
|
+
durationMs: dur,
|
|
2226
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2227
|
+
});
|
|
2228
|
+
return body;
|
|
2229
|
+
} catch (err) {
|
|
2230
|
+
const dur = Date.now() - t0;
|
|
2231
|
+
this.events?.emit("storage.read", {
|
|
2232
|
+
sessionId: "~memory~",
|
|
2233
|
+
store: "memory",
|
|
2234
|
+
filePath,
|
|
2235
|
+
operation: "read",
|
|
2236
|
+
outcome: "failure",
|
|
2237
|
+
durationMs: dur,
|
|
2238
|
+
error: err instanceof Error ? err.message : String(err),
|
|
2239
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2240
|
+
});
|
|
2241
|
+
throw err;
|
|
2242
|
+
}
|
|
1953
2243
|
}
|
|
1954
2244
|
/**
|
|
1955
2245
|
* List entries from a scope, newest first. Delegates to the backend
|
|
@@ -1975,7 +2265,34 @@ ${body.trim()}`);
|
|
|
1975
2265
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
1976
2266
|
return this.runSerialized(scope, async () => {
|
|
1977
2267
|
const entry = { scope, text, ts, ...metadata };
|
|
1978
|
-
|
|
2268
|
+
const filePath = this.files[scope];
|
|
2269
|
+
const t0 = Date.now();
|
|
2270
|
+
try {
|
|
2271
|
+
await this.backend.remember(scope, entry, filePath);
|
|
2272
|
+
const dur = Date.now() - t0;
|
|
2273
|
+
this.events?.emit("storage.write", {
|
|
2274
|
+
sessionId: "~memory~",
|
|
2275
|
+
store: "memory",
|
|
2276
|
+
filePath,
|
|
2277
|
+
operation: "remember",
|
|
2278
|
+
outcome: "success",
|
|
2279
|
+
durationMs: dur,
|
|
2280
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2281
|
+
});
|
|
2282
|
+
} catch (err) {
|
|
2283
|
+
const dur = Date.now() - t0;
|
|
2284
|
+
this.events?.emit("storage.write", {
|
|
2285
|
+
sessionId: "~memory~",
|
|
2286
|
+
store: "memory",
|
|
2287
|
+
filePath,
|
|
2288
|
+
operation: "remember",
|
|
2289
|
+
outcome: "failure",
|
|
2290
|
+
durationMs: dur,
|
|
2291
|
+
error: err instanceof Error ? err.message : String(err),
|
|
2292
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2293
|
+
});
|
|
2294
|
+
throw err;
|
|
2295
|
+
}
|
|
1979
2296
|
const raw = await this.backend.readAll(scope, this.files[scope]);
|
|
1980
2297
|
if (Buffer.byteLength(raw, "utf8") > MAX_BYTES_TOTAL) {
|
|
1981
2298
|
const removed = await this.backend.consolidate(scope, this.files[scope]);
|
|
@@ -2103,7 +2420,35 @@ ${body.trim()}`);
|
|
|
2103
2420
|
}
|
|
2104
2421
|
async forget(query, scope = "project-memory") {
|
|
2105
2422
|
return this.runSerialized(scope, async () => {
|
|
2106
|
-
const
|
|
2423
|
+
const filePath = this.files[scope];
|
|
2424
|
+
const t0 = Date.now();
|
|
2425
|
+
let removed = 0;
|
|
2426
|
+
try {
|
|
2427
|
+
removed = await this.backend.forget(scope, query, filePath);
|
|
2428
|
+
const dur = Date.now() - t0;
|
|
2429
|
+
this.events?.emit("storage.write", {
|
|
2430
|
+
sessionId: "~memory~",
|
|
2431
|
+
store: "memory",
|
|
2432
|
+
filePath,
|
|
2433
|
+
operation: "forget",
|
|
2434
|
+
outcome: "success",
|
|
2435
|
+
durationMs: dur,
|
|
2436
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2437
|
+
});
|
|
2438
|
+
} catch (err) {
|
|
2439
|
+
const dur = Date.now() - t0;
|
|
2440
|
+
this.events?.emit("storage.write", {
|
|
2441
|
+
sessionId: "~memory~",
|
|
2442
|
+
store: "memory",
|
|
2443
|
+
filePath,
|
|
2444
|
+
operation: "forget",
|
|
2445
|
+
outcome: "failure",
|
|
2446
|
+
durationMs: dur,
|
|
2447
|
+
error: err instanceof Error ? err.message : String(err),
|
|
2448
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2449
|
+
});
|
|
2450
|
+
throw err;
|
|
2451
|
+
}
|
|
2107
2452
|
if (removed > 0) {
|
|
2108
2453
|
this.events?.emit("memory.forgotten", {
|
|
2109
2454
|
scope,
|
|
@@ -2117,7 +2462,35 @@ ${body.trim()}`);
|
|
|
2117
2462
|
}
|
|
2118
2463
|
async consolidate(scope) {
|
|
2119
2464
|
return this.runSerialized(scope, async () => {
|
|
2120
|
-
const
|
|
2465
|
+
const filePath = this.files[scope];
|
|
2466
|
+
const t0 = Date.now();
|
|
2467
|
+
let removed = 0;
|
|
2468
|
+
try {
|
|
2469
|
+
removed = await this.backend.consolidate(scope, filePath);
|
|
2470
|
+
const dur = Date.now() - t0;
|
|
2471
|
+
this.events?.emit("storage.write", {
|
|
2472
|
+
sessionId: "~memory~",
|
|
2473
|
+
store: "memory",
|
|
2474
|
+
filePath,
|
|
2475
|
+
operation: "consolidate",
|
|
2476
|
+
outcome: "success",
|
|
2477
|
+
durationMs: dur,
|
|
2478
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2479
|
+
});
|
|
2480
|
+
} catch (err) {
|
|
2481
|
+
const dur = Date.now() - t0;
|
|
2482
|
+
this.events?.emit("storage.write", {
|
|
2483
|
+
sessionId: "~memory~",
|
|
2484
|
+
store: "memory",
|
|
2485
|
+
filePath,
|
|
2486
|
+
operation: "consolidate",
|
|
2487
|
+
outcome: "failure",
|
|
2488
|
+
durationMs: dur,
|
|
2489
|
+
error: err instanceof Error ? err.message : String(err),
|
|
2490
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2491
|
+
});
|
|
2492
|
+
throw err;
|
|
2493
|
+
}
|
|
2121
2494
|
if (removed > 0) {
|
|
2122
2495
|
this.events?.emit("memory.consolidated", {
|
|
2123
2496
|
scope,
|
|
@@ -2130,7 +2503,34 @@ ${body.trim()}`);
|
|
|
2130
2503
|
async clear(scope) {
|
|
2131
2504
|
if (scope) {
|
|
2132
2505
|
await this.runSerialized(scope, async () => {
|
|
2133
|
-
|
|
2506
|
+
const filePath = this.files[scope];
|
|
2507
|
+
const t0 = Date.now();
|
|
2508
|
+
try {
|
|
2509
|
+
await this.backend.clear(scope, filePath);
|
|
2510
|
+
const dur = Date.now() - t0;
|
|
2511
|
+
this.events?.emit("storage.write", {
|
|
2512
|
+
sessionId: "~memory~",
|
|
2513
|
+
store: "memory",
|
|
2514
|
+
filePath,
|
|
2515
|
+
operation: "clear",
|
|
2516
|
+
outcome: "success",
|
|
2517
|
+
durationMs: dur,
|
|
2518
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2519
|
+
});
|
|
2520
|
+
} catch (err) {
|
|
2521
|
+
const dur = Date.now() - t0;
|
|
2522
|
+
this.events?.emit("storage.write", {
|
|
2523
|
+
sessionId: "~memory~",
|
|
2524
|
+
store: "memory",
|
|
2525
|
+
filePath,
|
|
2526
|
+
operation: "clear",
|
|
2527
|
+
outcome: "failure",
|
|
2528
|
+
durationMs: dur,
|
|
2529
|
+
error: err instanceof Error ? err.message : String(err),
|
|
2530
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2531
|
+
});
|
|
2532
|
+
throw err;
|
|
2533
|
+
}
|
|
2134
2534
|
this.events?.emit("memory.cleared", { scope });
|
|
2135
2535
|
await this.mirrorBackup(scope);
|
|
2136
2536
|
});
|
|
@@ -2138,15 +2538,54 @@ ${body.trim()}`);
|
|
|
2138
2538
|
}
|
|
2139
2539
|
await Promise.all(
|
|
2140
2540
|
["project-agents", "project-memory", "user-memory"].map(
|
|
2141
|
-
(s) => this.runSerialized(s, async () => {
|
|
2142
|
-
|
|
2541
|
+
async (s) => this.runSerialized(s, async () => {
|
|
2542
|
+
const filePath = this.files[s];
|
|
2543
|
+
const t0 = Date.now();
|
|
2544
|
+
try {
|
|
2545
|
+
await this.backend.clear(s, filePath);
|
|
2546
|
+
const dur = Date.now() - t0;
|
|
2547
|
+
this.events?.emit("storage.write", {
|
|
2548
|
+
sessionId: "~memory~",
|
|
2549
|
+
store: "memory",
|
|
2550
|
+
filePath,
|
|
2551
|
+
operation: "clear",
|
|
2552
|
+
outcome: "success",
|
|
2553
|
+
durationMs: dur,
|
|
2554
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2555
|
+
});
|
|
2556
|
+
} catch (err) {
|
|
2557
|
+
const dur = Date.now() - t0;
|
|
2558
|
+
this.events?.emit("storage.write", {
|
|
2559
|
+
sessionId: "~memory~",
|
|
2560
|
+
store: "memory",
|
|
2561
|
+
filePath,
|
|
2562
|
+
operation: "clear",
|
|
2563
|
+
outcome: "failure",
|
|
2564
|
+
durationMs: dur,
|
|
2565
|
+
error: err instanceof Error ? err.message : String(err),
|
|
2566
|
+
...this.traceId !== void 0 && { traceId: this.traceId }
|
|
2567
|
+
});
|
|
2568
|
+
throw err;
|
|
2569
|
+
}
|
|
2143
2570
|
this.events?.emit("memory.cleared", { scope: s });
|
|
2144
2571
|
await this.mirrorBackup(s);
|
|
2145
2572
|
})
|
|
2146
2573
|
)
|
|
2147
2574
|
);
|
|
2148
2575
|
}
|
|
2149
|
-
/**
|
|
2576
|
+
/**
|
|
2577
|
+
* Return a new MemoryStore proxy that carries `traceId` on every storage
|
|
2578
|
+
* event. The original store is left unchanged — callers that need a
|
|
2579
|
+
* trace-decorated view (e.g. session-run tools) receive the proxy while
|
|
2580
|
+
* the singleton remains trace-free for boot-time use.
|
|
2581
|
+
*
|
|
2582
|
+
* The proxy implements the full `MemoryStore` interface; all other
|
|
2583
|
+
* properties (backend, etc.) are delegated to the original store.
|
|
2584
|
+
*/
|
|
2585
|
+
withTraceId(traceId) {
|
|
2586
|
+
this.traceId = traceId;
|
|
2587
|
+
return this;
|
|
2588
|
+
}
|
|
2150
2589
|
async mirrorBackup(scope) {
|
|
2151
2590
|
if (!this.persistBackup || scope === "project-agents") return;
|
|
2152
2591
|
try {
|
|
@@ -2796,6 +3235,13 @@ var DEFAULT_SESSION_LOGGING_CONFIG = Object.freeze({
|
|
|
2796
3235
|
});
|
|
2797
3236
|
|
|
2798
3237
|
// src/storage/config-loader.ts
|
|
3238
|
+
function storageErrorString(err) {
|
|
3239
|
+
if (err instanceof Error) {
|
|
3240
|
+
const code = err.code;
|
|
3241
|
+
return code ? `${code}: ${err.message}` : err.message;
|
|
3242
|
+
}
|
|
3243
|
+
return String(err);
|
|
3244
|
+
}
|
|
2799
3245
|
var BEHAVIOR_DEFAULTS = {
|
|
2800
3246
|
version: 1,
|
|
2801
3247
|
context: {
|
|
@@ -2898,11 +3344,15 @@ var DefaultConfigLoader = class {
|
|
|
2898
3344
|
strict;
|
|
2899
3345
|
vault;
|
|
2900
3346
|
extraSources;
|
|
3347
|
+
events;
|
|
3348
|
+
traceId;
|
|
2901
3349
|
constructor(opts) {
|
|
2902
3350
|
this.paths = opts.paths;
|
|
2903
3351
|
this.strict = opts.strict ?? false;
|
|
2904
3352
|
this.vault = opts.vault;
|
|
2905
3353
|
this.extraSources = opts.sources ?? [];
|
|
3354
|
+
this.events = opts.events;
|
|
3355
|
+
this.traceId = opts.traceId;
|
|
2906
3356
|
}
|
|
2907
3357
|
async load(opts = {}) {
|
|
2908
3358
|
let cfg = { ...BEHAVIOR_DEFAULTS };
|
|
@@ -2979,7 +3429,33 @@ var DefaultConfigLoader = class {
|
|
|
2979
3429
|
if (this.vault && toWrite.githubToken && !toWrite.githubToken.startsWith("enc:")) {
|
|
2980
3430
|
toWrite = { ...toWrite, githubToken: this.vault.encrypt(toWrite.githubToken) };
|
|
2981
3431
|
}
|
|
2982
|
-
|
|
3432
|
+
const fp = this.paths.syncConfig;
|
|
3433
|
+
const t0 = Date.now();
|
|
3434
|
+
try {
|
|
3435
|
+
await atomicWrite(fp, JSON.stringify(toWrite, null, 2), { mode: 384 });
|
|
3436
|
+
this.events?.emit("storage.write", {
|
|
3437
|
+
sessionId: "~config~",
|
|
3438
|
+
store: "config",
|
|
3439
|
+
filePath: fp,
|
|
3440
|
+
operation: "persist_sync",
|
|
3441
|
+
outcome: "success",
|
|
3442
|
+
durationMs: Date.now() - t0,
|
|
3443
|
+
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
3444
|
+
});
|
|
3445
|
+
} catch (err) {
|
|
3446
|
+
this.events?.emit("storage.error", {
|
|
3447
|
+
sessionId: "~config~",
|
|
3448
|
+
store: "config",
|
|
3449
|
+
filePath: fp,
|
|
3450
|
+
operation: "persist_sync",
|
|
3451
|
+
outcome: "failure",
|
|
3452
|
+
error: storageErrorString(err),
|
|
3453
|
+
recoverable: false,
|
|
3454
|
+
durationMs: Date.now() - t0,
|
|
3455
|
+
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
3456
|
+
});
|
|
3457
|
+
throw err;
|
|
3458
|
+
}
|
|
2983
3459
|
}
|
|
2984
3460
|
/**
|
|
2985
3461
|
* Read ~/.wrongstack/sync.json (encrypted GitHub token storage) and decrypt
|
|
@@ -2988,17 +3464,60 @@ var DefaultConfigLoader = class {
|
|
|
2988
3464
|
* isolated — it should never be part of project-local or env-driven config.
|
|
2989
3465
|
*/
|
|
2990
3466
|
async loadSyncConfig() {
|
|
3467
|
+
const fp = this.paths.syncConfig;
|
|
3468
|
+
const t0 = Date.now();
|
|
2991
3469
|
try {
|
|
2992
|
-
const raw = await fsp.readFile(
|
|
3470
|
+
const raw = await fsp.readFile(fp, "utf8");
|
|
2993
3471
|
const parsed = safeParse(raw);
|
|
2994
|
-
if (!parsed.ok || !parsed.value)
|
|
3472
|
+
if (!parsed.ok || !parsed.value) {
|
|
3473
|
+
this.events?.emit("storage.read", {
|
|
3474
|
+
sessionId: "~config~",
|
|
3475
|
+
store: "config",
|
|
3476
|
+
filePath: fp,
|
|
3477
|
+
operation: "load_sync",
|
|
3478
|
+
outcome: "failure",
|
|
3479
|
+
durationMs: Date.now() - t0,
|
|
3480
|
+
error: "parse error or empty file",
|
|
3481
|
+
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
3482
|
+
});
|
|
3483
|
+
return null;
|
|
3484
|
+
}
|
|
2995
3485
|
if (this.vault) {
|
|
2996
3486
|
const decrypted = decryptConfigSecrets({ sync: parsed.value }, this.vault);
|
|
2997
|
-
|
|
3487
|
+
const result = decrypted.sync ?? null;
|
|
3488
|
+
this.events?.emit("storage.read", {
|
|
3489
|
+
sessionId: "~config~",
|
|
3490
|
+
store: "config",
|
|
3491
|
+
filePath: fp,
|
|
3492
|
+
operation: "load_sync",
|
|
3493
|
+
outcome: "success",
|
|
3494
|
+
durationMs: Date.now() - t0,
|
|
3495
|
+
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
3496
|
+
});
|
|
3497
|
+
return result;
|
|
2998
3498
|
}
|
|
3499
|
+
this.events?.emit("storage.read", {
|
|
3500
|
+
sessionId: "~config~",
|
|
3501
|
+
store: "config",
|
|
3502
|
+
filePath: fp,
|
|
3503
|
+
operation: "load_sync",
|
|
3504
|
+
outcome: "success",
|
|
3505
|
+
durationMs: Date.now() - t0,
|
|
3506
|
+
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
3507
|
+
});
|
|
2999
3508
|
return parsed.value;
|
|
3000
3509
|
} catch (err) {
|
|
3001
3510
|
if (err.code === "ENOENT") return null;
|
|
3511
|
+
this.events?.emit("storage.read", {
|
|
3512
|
+
sessionId: "~config~",
|
|
3513
|
+
store: "config",
|
|
3514
|
+
filePath: fp,
|
|
3515
|
+
operation: "load_sync",
|
|
3516
|
+
outcome: "failure",
|
|
3517
|
+
durationMs: Date.now() - t0,
|
|
3518
|
+
error: storageErrorString(err),
|
|
3519
|
+
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
3520
|
+
});
|
|
3002
3521
|
console.warn(JSON.stringify({
|
|
3003
3522
|
level: "warn",
|
|
3004
3523
|
event: "config.sync_load_failed",
|
|
@@ -3010,10 +3529,21 @@ var DefaultConfigLoader = class {
|
|
|
3010
3529
|
}
|
|
3011
3530
|
async readJson(file) {
|
|
3012
3531
|
let raw;
|
|
3532
|
+
const t0 = Date.now();
|
|
3013
3533
|
try {
|
|
3014
3534
|
raw = await fsp.readFile(file, "utf8");
|
|
3015
3535
|
} catch (err) {
|
|
3016
3536
|
if (err.code !== "ENOENT") {
|
|
3537
|
+
this.events?.emit("storage.read", {
|
|
3538
|
+
sessionId: "~config~",
|
|
3539
|
+
store: "config",
|
|
3540
|
+
filePath: file,
|
|
3541
|
+
operation: "read_json",
|
|
3542
|
+
outcome: "failure",
|
|
3543
|
+
durationMs: Date.now() - t0,
|
|
3544
|
+
error: storageErrorString(err),
|
|
3545
|
+
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
3546
|
+
});
|
|
3017
3547
|
console.warn(JSON.stringify({
|
|
3018
3548
|
level: "warn",
|
|
3019
3549
|
event: "config.read_failed",
|
|
@@ -3026,6 +3556,16 @@ var DefaultConfigLoader = class {
|
|
|
3026
3556
|
}
|
|
3027
3557
|
const parsed = safeParse(raw);
|
|
3028
3558
|
if (!parsed.ok || !parsed.value) {
|
|
3559
|
+
this.events?.emit("storage.read", {
|
|
3560
|
+
sessionId: "~config~",
|
|
3561
|
+
store: "config",
|
|
3562
|
+
filePath: file,
|
|
3563
|
+
operation: "read_json",
|
|
3564
|
+
outcome: "failure",
|
|
3565
|
+
durationMs: Date.now() - t0,
|
|
3566
|
+
error: "parse error or empty file",
|
|
3567
|
+
...this.traceId !== void 0 ? { traceId: this.traceId } : {}
|
|
3568
|
+
});
|
|
3029
3569
|
console.warn(JSON.stringify({
|
|
3030
3570
|
level: "warn",
|
|
3031
3571
|
event: "config.parse_failed",
|
|
@@ -3813,24 +4353,66 @@ function resolveSessionLoggingConfig(cfg) {
|
|
|
3813
4353
|
|
|
3814
4354
|
// src/storage/todos-checkpoint.ts
|
|
3815
4355
|
init_atomic_write();
|
|
3816
|
-
async function loadTodosCheckpoint(filePath) {
|
|
4356
|
+
async function loadTodosCheckpoint(filePath, events, traceId) {
|
|
4357
|
+
const t0 = Date.now();
|
|
3817
4358
|
let raw;
|
|
3818
4359
|
try {
|
|
3819
4360
|
raw = await fsp.readFile(filePath, "utf8");
|
|
3820
|
-
} catch {
|
|
4361
|
+
} catch (err) {
|
|
4362
|
+
events?.emit("storage.error", {
|
|
4363
|
+
sessionId: traceId ?? "~boot~",
|
|
4364
|
+
store: "todos",
|
|
4365
|
+
filePath,
|
|
4366
|
+
operation: "load",
|
|
4367
|
+
outcome: "failure",
|
|
4368
|
+
error: err instanceof Error ? err.message : String(err),
|
|
4369
|
+
recoverable: true
|
|
4370
|
+
});
|
|
3821
4371
|
return null;
|
|
3822
4372
|
}
|
|
3823
4373
|
try {
|
|
3824
4374
|
const parsed = JSON.parse(raw);
|
|
3825
|
-
if (parsed?.version !== 1 || !Array.isArray(parsed.todos))
|
|
4375
|
+
if (parsed?.version !== 1 || !Array.isArray(parsed.todos)) {
|
|
4376
|
+
events?.emit("storage.read", {
|
|
4377
|
+
sessionId: traceId ?? "~boot~",
|
|
4378
|
+
store: "todos",
|
|
4379
|
+
filePath,
|
|
4380
|
+
operation: "load",
|
|
4381
|
+
outcome: "failure",
|
|
4382
|
+
durationMs: Date.now() - t0,
|
|
4383
|
+
error: "invalid_schema",
|
|
4384
|
+
...traceId !== void 0 && { traceId }
|
|
4385
|
+
});
|
|
4386
|
+
return null;
|
|
4387
|
+
}
|
|
4388
|
+
events?.emit("storage.read", {
|
|
4389
|
+
sessionId: traceId ?? "~boot~",
|
|
4390
|
+
store: "todos",
|
|
4391
|
+
filePath,
|
|
4392
|
+
operation: "load",
|
|
4393
|
+
outcome: "success",
|
|
4394
|
+
durationMs: Date.now() - t0,
|
|
4395
|
+
...traceId !== void 0 && { traceId }
|
|
4396
|
+
});
|
|
3826
4397
|
return parsed.todos.filter(
|
|
3827
4398
|
(t) => !!t && typeof t.id === "string" && typeof t.content === "string" && typeof t.status === "string" && (t.activeForm === void 0 || typeof t.activeForm === "string")
|
|
3828
4399
|
);
|
|
3829
4400
|
} catch {
|
|
4401
|
+
events?.emit("storage.read", {
|
|
4402
|
+
sessionId: traceId ?? "~boot~",
|
|
4403
|
+
store: "todos",
|
|
4404
|
+
filePath,
|
|
4405
|
+
operation: "load",
|
|
4406
|
+
outcome: "failure",
|
|
4407
|
+
durationMs: Date.now() - t0,
|
|
4408
|
+
error: "parse_failed",
|
|
4409
|
+
...traceId !== void 0 && { traceId }
|
|
4410
|
+
});
|
|
3830
4411
|
return null;
|
|
3831
4412
|
}
|
|
3832
4413
|
}
|
|
3833
|
-
async function saveTodosCheckpoint(filePath, sessionId, todos) {
|
|
4414
|
+
async function saveTodosCheckpoint(filePath, sessionId, todos, events, traceId) {
|
|
4415
|
+
const t0 = Date.now();
|
|
3834
4416
|
const payload = {
|
|
3835
4417
|
version: 1,
|
|
3836
4418
|
sessionId,
|
|
@@ -3839,7 +4421,25 @@ async function saveTodosCheckpoint(filePath, sessionId, todos) {
|
|
|
3839
4421
|
};
|
|
3840
4422
|
try {
|
|
3841
4423
|
await atomicWrite(filePath, JSON.stringify(payload, null, 2), { mode: 384 });
|
|
4424
|
+
events?.emit("storage.write", {
|
|
4425
|
+
sessionId: traceId ?? sessionId,
|
|
4426
|
+
store: "todos",
|
|
4427
|
+
filePath,
|
|
4428
|
+
operation: "save",
|
|
4429
|
+
outcome: "success",
|
|
4430
|
+
durationMs: Date.now() - t0,
|
|
4431
|
+
...traceId !== void 0 && { traceId }
|
|
4432
|
+
});
|
|
3842
4433
|
} catch (err) {
|
|
4434
|
+
events?.emit("storage.error", {
|
|
4435
|
+
sessionId: traceId ?? sessionId,
|
|
4436
|
+
store: "todos",
|
|
4437
|
+
filePath,
|
|
4438
|
+
operation: "save",
|
|
4439
|
+
outcome: "failure",
|
|
4440
|
+
error: err instanceof Error ? err.message : String(err),
|
|
4441
|
+
recoverable: false
|
|
4442
|
+
});
|
|
3843
4443
|
console.warn(JSON.stringify({
|
|
3844
4444
|
level: "warn",
|
|
3845
4445
|
event: "todos_checkpoint.save_failed",
|
|
@@ -3848,12 +4448,12 @@ async function saveTodosCheckpoint(filePath, sessionId, todos) {
|
|
|
3848
4448
|
}));
|
|
3849
4449
|
}
|
|
3850
4450
|
}
|
|
3851
|
-
function attachTodosCheckpoint(state, filePath, sessionId) {
|
|
4451
|
+
function attachTodosCheckpoint(state, filePath, sessionId, events, traceId) {
|
|
3852
4452
|
let timer = null;
|
|
3853
4453
|
let pending = null;
|
|
3854
4454
|
let writeChain = Promise.resolve();
|
|
3855
4455
|
const enqueueWrite = (todos) => {
|
|
3856
|
-
writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos)).catch((err) => {
|
|
4456
|
+
writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos, events, traceId)).catch((err) => {
|
|
3857
4457
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3858
4458
|
console.error(JSON.stringify({
|
|
3859
4459
|
level: "error",
|
|
@@ -3895,25 +4495,79 @@ function attachTodosCheckpoint(state, filePath, sessionId) {
|
|
|
3895
4495
|
|
|
3896
4496
|
// src/storage/plan-store.ts
|
|
3897
4497
|
init_atomic_write();
|
|
3898
|
-
async function loadPlan(filePath) {
|
|
4498
|
+
async function loadPlan(filePath, events) {
|
|
4499
|
+
const t0 = Date.now();
|
|
3899
4500
|
let raw;
|
|
3900
4501
|
try {
|
|
3901
4502
|
raw = await fsp.readFile(filePath, "utf8");
|
|
3902
|
-
} catch {
|
|
4503
|
+
} catch (err) {
|
|
4504
|
+
events?.emit("storage.error", {
|
|
4505
|
+
sessionId: "~boot~",
|
|
4506
|
+
store: "plan",
|
|
4507
|
+
filePath,
|
|
4508
|
+
operation: "load",
|
|
4509
|
+
error: err instanceof Error ? err.message : String(err),
|
|
4510
|
+
recoverable: true
|
|
4511
|
+
});
|
|
3903
4512
|
return null;
|
|
3904
4513
|
}
|
|
3905
4514
|
try {
|
|
3906
4515
|
const parsed = JSON.parse(raw);
|
|
3907
|
-
if (parsed?.version !== 1 || !Array.isArray(parsed.items))
|
|
4516
|
+
if (parsed?.version !== 1 || !Array.isArray(parsed.items)) {
|
|
4517
|
+
events?.emit("storage.read", {
|
|
4518
|
+
sessionId: "~boot~",
|
|
4519
|
+
store: "plan",
|
|
4520
|
+
filePath,
|
|
4521
|
+
operation: "load",
|
|
4522
|
+
outcome: "failure",
|
|
4523
|
+
durationMs: Date.now() - t0,
|
|
4524
|
+
error: "invalid_schema"
|
|
4525
|
+
});
|
|
4526
|
+
return null;
|
|
4527
|
+
}
|
|
4528
|
+
events?.emit("storage.read", {
|
|
4529
|
+
sessionId: "~boot~",
|
|
4530
|
+
store: "plan",
|
|
4531
|
+
filePath,
|
|
4532
|
+
operation: "load",
|
|
4533
|
+
outcome: "success",
|
|
4534
|
+
durationMs: Date.now() - t0
|
|
4535
|
+
});
|
|
3908
4536
|
return parsed;
|
|
3909
4537
|
} catch {
|
|
4538
|
+
events?.emit("storage.read", {
|
|
4539
|
+
sessionId: "~boot~",
|
|
4540
|
+
store: "plan",
|
|
4541
|
+
filePath,
|
|
4542
|
+
operation: "load",
|
|
4543
|
+
outcome: "failure",
|
|
4544
|
+
durationMs: Date.now() - t0,
|
|
4545
|
+
error: "parse_failed"
|
|
4546
|
+
});
|
|
3910
4547
|
return null;
|
|
3911
4548
|
}
|
|
3912
4549
|
}
|
|
3913
|
-
async function savePlan(filePath, plan) {
|
|
4550
|
+
async function savePlan(filePath, plan, events) {
|
|
4551
|
+
const t0 = Date.now();
|
|
3914
4552
|
try {
|
|
3915
4553
|
await atomicWrite(filePath, JSON.stringify(plan, null, 2), { mode: 384 });
|
|
4554
|
+
events?.emit("storage.write", {
|
|
4555
|
+
sessionId: "~boot~",
|
|
4556
|
+
store: "plan",
|
|
4557
|
+
filePath,
|
|
4558
|
+
operation: "save",
|
|
4559
|
+
outcome: "success",
|
|
4560
|
+
durationMs: Date.now() - t0
|
|
4561
|
+
});
|
|
3916
4562
|
} catch (err) {
|
|
4563
|
+
events?.emit("storage.error", {
|
|
4564
|
+
sessionId: "~boot~",
|
|
4565
|
+
store: "plan",
|
|
4566
|
+
filePath,
|
|
4567
|
+
operation: "save",
|
|
4568
|
+
error: err instanceof Error ? err.message : String(err),
|
|
4569
|
+
recoverable: false
|
|
4570
|
+
});
|
|
3917
4571
|
console.warn(
|
|
3918
4572
|
"[plan-store] save failed:",
|
|
3919
4573
|
err instanceof Error ? err.message : String(err)
|
|
@@ -4493,6 +5147,13 @@ function hasDangerousCapabilityForSubagents(toolOrCaps) {
|
|
|
4493
5147
|
const caps = Array.isArray(toolOrCaps) ? toolOrCaps : input.capabilities ?? [];
|
|
4494
5148
|
return caps.some((c) => DANGEROUS_FOR_SUBAGENTS.includes(c));
|
|
4495
5149
|
}
|
|
5150
|
+
function hasCapability(toolOrCaps, capability) {
|
|
5151
|
+
if (!toolOrCaps) return false;
|
|
5152
|
+
const input = toolOrCaps;
|
|
5153
|
+
const caps = Array.isArray(toolOrCaps) ? toolOrCaps : input.capabilities ?? [];
|
|
5154
|
+
const toCheck = Array.isArray(capability) ? capability : [capability];
|
|
5155
|
+
return toCheck.some((c) => caps.includes(c));
|
|
5156
|
+
}
|
|
4496
5157
|
function getDangerousCapabilities(toolOrCaps) {
|
|
4497
5158
|
if (!toolOrCaps) return [];
|
|
4498
5159
|
const input = toolOrCaps;
|
|
@@ -4856,7 +5517,16 @@ var DefaultPermissionPolicy = class {
|
|
|
4856
5517
|
};
|
|
4857
5518
|
}
|
|
4858
5519
|
}
|
|
4859
|
-
|
|
5520
|
+
const hasWriteCap = hasCapability(tool, ToolCapabilities.FS_WRITE);
|
|
5521
|
+
const hasShellCap = hasCapability(tool, [
|
|
5522
|
+
ToolCapabilities.SHELL_ARBITRARY,
|
|
5523
|
+
ToolCapabilities.SHELL_RESTRICTED
|
|
5524
|
+
]);
|
|
5525
|
+
const hasInstallCap = hasCapability(tool, ToolCapabilities.PACKAGE_INSTALL);
|
|
5526
|
+
const hasConfigCap = hasCapability(tool, ToolCapabilities.CONFIG_MUTATE);
|
|
5527
|
+
const hasSubagentCap = hasCapability(tool, ToolCapabilities.SUBAGENT_SPAWN);
|
|
5528
|
+
const isMutating = tool.mutating || hasWriteCap || hasShellCap || hasInstallCap || hasConfigCap || hasSubagentCap;
|
|
5529
|
+
if (tool.permission === "auto" && !isMutating) {
|
|
4860
5530
|
const decision = { permission: "auto", source: "default" };
|
|
4861
5531
|
this._evalCache.set(cacheKey, decision);
|
|
4862
5532
|
return decision;
|
|
@@ -4875,7 +5545,27 @@ var DefaultPermissionPolicy = class {
|
|
|
4875
5545
|
}
|
|
4876
5546
|
return { permission: "confirm", source: "default" };
|
|
4877
5547
|
}
|
|
5548
|
+
// Capability-based destructive check (preferred over name-based)
|
|
5549
|
+
isDestructiveByCapability(tool) {
|
|
5550
|
+
const caps = tool.capabilities ?? [];
|
|
5551
|
+
if (caps.includes("shell.arbitrary")) return true;
|
|
5552
|
+
if (caps.includes("fs.write")) return true;
|
|
5553
|
+
if (caps.includes("fs.write.outside-project")) return true;
|
|
5554
|
+
return false;
|
|
5555
|
+
}
|
|
4878
5556
|
isDestructiveYoloCall(tool, input, ctx) {
|
|
5557
|
+
if (this.isDestructiveByCapability(tool)) {
|
|
5558
|
+
if (tool.name === "bash") {
|
|
5559
|
+
const command = getInputString(input, "command");
|
|
5560
|
+
return command ? isClearlyDestructiveBashCommand(command, ctx.projectRoot) : true;
|
|
5561
|
+
}
|
|
5562
|
+
if (tool.name === "write" || tool.name === "edit" || tool.name === "replace" || tool.name === "patch") {
|
|
5563
|
+
const targetPath = getInputString(input, "path") ?? getInputString(input, "file");
|
|
5564
|
+
if (!targetPath || !ctx.projectRoot) return false;
|
|
5565
|
+
return !pathLooksInsideProject(targetPath, ctx.projectRoot);
|
|
5566
|
+
}
|
|
5567
|
+
return true;
|
|
5568
|
+
}
|
|
4879
5569
|
if (tool.name === "bash") {
|
|
4880
5570
|
const command = getInputString(input, "command");
|
|
4881
5571
|
return command ? isClearlyDestructiveBashCommand(command, ctx.projectRoot) : true;
|
|
@@ -5227,6 +5917,8 @@ function compactSkillBody(body) {
|
|
|
5227
5917
|
var DefaultSkillLoader = class {
|
|
5228
5918
|
dirs;
|
|
5229
5919
|
cache;
|
|
5920
|
+
entriesCache;
|
|
5921
|
+
bodyCache = /* @__PURE__ */ new Map();
|
|
5230
5922
|
constructor(opts) {
|
|
5231
5923
|
this.dirs = [
|
|
5232
5924
|
{ dir: opts.paths.inProjectSkills, source: "project" },
|
|
@@ -5285,6 +5977,7 @@ var DefaultSkillLoader = class {
|
|
|
5285
5977
|
return lines.join("\n");
|
|
5286
5978
|
}
|
|
5287
5979
|
async listEntries() {
|
|
5980
|
+
if (this.entriesCache) return this.entriesCache;
|
|
5288
5981
|
const skills = await this.list();
|
|
5289
5982
|
const entries = [];
|
|
5290
5983
|
for (const s of skills) {
|
|
@@ -5295,33 +5988,47 @@ var DefaultSkillLoader = class {
|
|
|
5295
5988
|
} catch {
|
|
5296
5989
|
}
|
|
5297
5990
|
}
|
|
5991
|
+
this.entriesCache = entries;
|
|
5298
5992
|
return entries;
|
|
5299
5993
|
}
|
|
5300
5994
|
invalidateCache() {
|
|
5301
5995
|
this.cache = void 0;
|
|
5996
|
+
this.entriesCache = void 0;
|
|
5997
|
+
this.bodyCache.clear();
|
|
5302
5998
|
}
|
|
5303
5999
|
async readBody(name) {
|
|
6000
|
+
const cached = this.bodyCache.get(name);
|
|
6001
|
+
if (cached !== void 0) return cached;
|
|
5304
6002
|
const m = await this.find(name);
|
|
5305
6003
|
if (!m) throw new Error(`Skill "${name}" not found`);
|
|
5306
|
-
|
|
6004
|
+
const body = await fsp.readFile(m.path, "utf8");
|
|
6005
|
+
this.bodyCache.set(name, body);
|
|
6006
|
+
return body;
|
|
5307
6007
|
}
|
|
5308
6008
|
async readSaveBody(name) {
|
|
6009
|
+
const key = `save:${name}`;
|
|
6010
|
+
const cached = this.bodyCache.get(key);
|
|
6011
|
+
if (cached !== void 0) return cached;
|
|
5309
6012
|
const m = await this.find(name);
|
|
5310
6013
|
if (!m) throw new Error(`Skill "${name}" not found`);
|
|
5311
6014
|
const savePath = path11.join(path11.dirname(m.path), "SKILL.save.md");
|
|
6015
|
+
let result;
|
|
5312
6016
|
try {
|
|
5313
|
-
|
|
6017
|
+
result = await fsp.readFile(savePath, "utf8");
|
|
5314
6018
|
} catch {
|
|
5315
6019
|
const full = await fsp.readFile(m.path, "utf8");
|
|
5316
6020
|
const body = stripFrontmatter(full);
|
|
5317
6021
|
const compact = compactSkillBody(body);
|
|
5318
6022
|
if (compact) {
|
|
5319
|
-
|
|
6023
|
+
result = `## Overview
|
|
5320
6024
|
|
|
5321
6025
|
${compact}`;
|
|
6026
|
+
} else {
|
|
6027
|
+
result = body.trim().slice(0, 300);
|
|
5322
6028
|
}
|
|
5323
|
-
return body.trim().slice(0, 300);
|
|
5324
6029
|
}
|
|
6030
|
+
this.bodyCache.set(key, result);
|
|
6031
|
+
return result;
|
|
5325
6032
|
}
|
|
5326
6033
|
};
|
|
5327
6034
|
function parseFrontmatter(raw) {
|
|
@@ -8069,13 +8776,32 @@ var MAX_JOURNAL_ENTRIES = 500;
|
|
|
8069
8776
|
function goalFilePath(projectRoot) {
|
|
8070
8777
|
return resolveWstackPaths({ projectRoot }).projectGoal;
|
|
8071
8778
|
}
|
|
8072
|
-
async function loadGoal(filePath) {
|
|
8779
|
+
async function loadGoal(filePath, events) {
|
|
8780
|
+
const t0 = Date.now();
|
|
8073
8781
|
let raw;
|
|
8074
8782
|
try {
|
|
8075
8783
|
raw = await fsp.readFile(filePath, "utf8");
|
|
8076
8784
|
} catch (err) {
|
|
8077
8785
|
const code = err.code;
|
|
8078
|
-
if (code === "ENOENT")
|
|
8786
|
+
if (code === "ENOENT") {
|
|
8787
|
+
events?.emit("storage.read", {
|
|
8788
|
+
sessionId: "~boot~",
|
|
8789
|
+
store: "goal",
|
|
8790
|
+
filePath,
|
|
8791
|
+
operation: "load",
|
|
8792
|
+
outcome: "success",
|
|
8793
|
+
durationMs: Date.now() - t0
|
|
8794
|
+
});
|
|
8795
|
+
return null;
|
|
8796
|
+
}
|
|
8797
|
+
events?.emit("storage.error", {
|
|
8798
|
+
sessionId: "~boot~",
|
|
8799
|
+
store: "goal",
|
|
8800
|
+
filePath,
|
|
8801
|
+
operation: "load",
|
|
8802
|
+
error: err instanceof Error ? err.message : String(err),
|
|
8803
|
+
recoverable: false
|
|
8804
|
+
});
|
|
8079
8805
|
throw err;
|
|
8080
8806
|
}
|
|
8081
8807
|
try {
|
|
@@ -8088,8 +8814,25 @@ async function loadGoal(filePath) {
|
|
|
8088
8814
|
message: "invalid schema \u2014 consider deleting and re-creating",
|
|
8089
8815
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
8090
8816
|
}));
|
|
8817
|
+
events?.emit("storage.read", {
|
|
8818
|
+
sessionId: "~boot~",
|
|
8819
|
+
store: "goal",
|
|
8820
|
+
filePath,
|
|
8821
|
+
operation: "load",
|
|
8822
|
+
outcome: "failure",
|
|
8823
|
+
durationMs: Date.now() - t0,
|
|
8824
|
+
error: "invalid_schema"
|
|
8825
|
+
});
|
|
8091
8826
|
return null;
|
|
8092
8827
|
}
|
|
8828
|
+
events?.emit("storage.read", {
|
|
8829
|
+
sessionId: "~boot~",
|
|
8830
|
+
store: "goal",
|
|
8831
|
+
filePath,
|
|
8832
|
+
operation: "load",
|
|
8833
|
+
outcome: "success",
|
|
8834
|
+
durationMs: Date.now() - t0
|
|
8835
|
+
});
|
|
8093
8836
|
return parsed;
|
|
8094
8837
|
} catch {
|
|
8095
8838
|
console.warn(JSON.stringify({
|
|
@@ -8099,13 +8842,39 @@ async function loadGoal(filePath) {
|
|
|
8099
8842
|
message: "JSON parse failed \u2014 consider deleting and re-creating",
|
|
8100
8843
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
8101
8844
|
}));
|
|
8845
|
+
events?.emit("storage.read", {
|
|
8846
|
+
sessionId: "~boot~",
|
|
8847
|
+
store: "goal",
|
|
8848
|
+
filePath,
|
|
8849
|
+
operation: "load",
|
|
8850
|
+
outcome: "failure",
|
|
8851
|
+
durationMs: Date.now() - t0,
|
|
8852
|
+
error: "parse_failed"
|
|
8853
|
+
});
|
|
8102
8854
|
return null;
|
|
8103
8855
|
}
|
|
8104
8856
|
}
|
|
8105
|
-
async function saveGoal(filePath, goal) {
|
|
8857
|
+
async function saveGoal(filePath, goal, events) {
|
|
8858
|
+
const t0 = Date.now();
|
|
8106
8859
|
try {
|
|
8107
8860
|
await atomicWrite(filePath, JSON.stringify(goal, null, 2), { mode: 384 });
|
|
8861
|
+
events?.emit("storage.write", {
|
|
8862
|
+
sessionId: "~boot~",
|
|
8863
|
+
store: "goal",
|
|
8864
|
+
filePath,
|
|
8865
|
+
operation: "save",
|
|
8866
|
+
outcome: "success",
|
|
8867
|
+
durationMs: Date.now() - t0
|
|
8868
|
+
});
|
|
8108
8869
|
} catch (err) {
|
|
8870
|
+
events?.emit("storage.error", {
|
|
8871
|
+
sessionId: "~boot~",
|
|
8872
|
+
store: "goal",
|
|
8873
|
+
filePath,
|
|
8874
|
+
operation: "save",
|
|
8875
|
+
error: err instanceof Error ? err.message : String(err),
|
|
8876
|
+
recoverable: false
|
|
8877
|
+
});
|
|
8109
8878
|
throw new FsError({
|
|
8110
8879
|
message: err instanceof Error ? err.message : String(err),
|
|
8111
8880
|
code: ERROR_CODES.FS_ATOMIC_WRITE_FAILED,
|
|
@@ -8275,7 +9044,7 @@ var EternalAutonomyEngine = class {
|
|
|
8275
9044
|
const emit = (stage) => {
|
|
8276
9045
|
this.opts.onStage?.(stage);
|
|
8277
9046
|
};
|
|
8278
|
-
const goal = await loadGoal(this.goalPath);
|
|
9047
|
+
const goal = await loadGoal(this.goalPath, this.opts.events);
|
|
8279
9048
|
if (!goal) {
|
|
8280
9049
|
emit({ phase: "stopped" });
|
|
8281
9050
|
this.stopRequested = true;
|
|
@@ -8379,7 +9148,7 @@ var EternalAutonomyEngine = class {
|
|
|
8379
9148
|
emit({ phase: "reflect", status, note });
|
|
8380
9149
|
let iterationIndex = 0;
|
|
8381
9150
|
try {
|
|
8382
|
-
const reloaded = await loadGoal(this.goalPath);
|
|
9151
|
+
const reloaded = await loadGoal(this.goalPath, this.opts.events);
|
|
8383
9152
|
iterationIndex = reloaded?.iterations ?? 0;
|
|
8384
9153
|
} catch {
|
|
8385
9154
|
}
|
|
@@ -8699,12 +9468,12 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
8699
9468
|
}
|
|
8700
9469
|
}
|
|
8701
9470
|
async appendIterationEntry(entry) {
|
|
8702
|
-
const current = await loadGoal(this.goalPath);
|
|
9471
|
+
const current = await loadGoal(this.goalPath, this.opts.events);
|
|
8703
9472
|
if (!current) {
|
|
8704
9473
|
return;
|
|
8705
9474
|
}
|
|
8706
9475
|
const updated = appendJournal(current, entry);
|
|
8707
|
-
await saveGoal(this.goalPath, updated);
|
|
9476
|
+
await saveGoal(this.goalPath, updated, this.opts.events);
|
|
8708
9477
|
}
|
|
8709
9478
|
/**
|
|
8710
9479
|
* Persistent per-todo failure counter. Skipped silently when the goal
|
|
@@ -8713,11 +9482,11 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
8713
9482
|
* the counter to rotate past stuck todos once they cross `todoMaxAttempts`.
|
|
8714
9483
|
*/
|
|
8715
9484
|
async bumpTodoAttempt(todoId) {
|
|
8716
|
-
const current = await loadGoal(this.goalPath);
|
|
9485
|
+
const current = await loadGoal(this.goalPath, this.opts.events);
|
|
8717
9486
|
if (!current) return;
|
|
8718
9487
|
const attempts = { ...current.todoAttempts ?? {} };
|
|
8719
9488
|
attempts[todoId] = (attempts[todoId] ?? 0) + 1;
|
|
8720
|
-
await saveGoal(this.goalPath, { ...current, todoAttempts: attempts });
|
|
9489
|
+
await saveGoal(this.goalPath, { ...current, todoAttempts: attempts }, this.opts.events);
|
|
8721
9490
|
}
|
|
8722
9491
|
/**
|
|
8723
9492
|
* Flip the mission to `completed` and journal it. Called from two
|
|
@@ -8727,7 +9496,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
8727
9496
|
* goal is already `completed`.
|
|
8728
9497
|
*/
|
|
8729
9498
|
async markGoalCompleted(action, note) {
|
|
8730
|
-
const current = await loadGoal(this.goalPath);
|
|
9499
|
+
const current = await loadGoal(this.goalPath, this.opts.events);
|
|
8731
9500
|
if (!current) return;
|
|
8732
9501
|
if (current.goalState === "completed") return;
|
|
8733
9502
|
const withFlag = { ...current, goalState: "completed" };
|
|
@@ -8737,7 +9506,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
8737
9506
|
status: "success",
|
|
8738
9507
|
note: note.slice(0, 240)
|
|
8739
9508
|
});
|
|
8740
|
-
await saveGoal(this.goalPath, withEntry);
|
|
9509
|
+
await saveGoal(this.goalPath, withEntry, this.opts.events);
|
|
8741
9510
|
this.opts.onEternalStop?.();
|
|
8742
9511
|
}
|
|
8743
9512
|
/**
|
|
@@ -8746,10 +9515,10 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
8746
9515
|
* `onEternalStop` so the REPL returns to normal mode.
|
|
8747
9516
|
*/
|
|
8748
9517
|
async clearGoalManually(note) {
|
|
8749
|
-
const current = await loadGoal(this.goalPath);
|
|
9518
|
+
const current = await loadGoal(this.goalPath, this.opts.events);
|
|
8750
9519
|
if (current) {
|
|
8751
9520
|
const abandoned = { ...current, goalState: "abandoned" };
|
|
8752
|
-
await saveGoal(this.goalPath, abandoned);
|
|
9521
|
+
await saveGoal(this.goalPath, abandoned, this.opts.events);
|
|
8753
9522
|
}
|
|
8754
9523
|
try {
|
|
8755
9524
|
const { unlink: unlink10 } = await import('fs/promises');
|
|
@@ -8825,16 +9594,16 @@ ${recentJournal}` : ""
|
|
|
8825
9594
|
* Persist a progress update from the agent's [PROGRESS: N%] output.
|
|
8826
9595
|
*/
|
|
8827
9596
|
async updateProgress(progress, note) {
|
|
8828
|
-
const current = await loadGoal(this.goalPath);
|
|
9597
|
+
const current = await loadGoal(this.goalPath, this.opts.events);
|
|
8829
9598
|
if (!current) return;
|
|
8830
9599
|
const updated = recordProgress(current, progress, note);
|
|
8831
|
-
await saveGoal(this.goalPath, updated);
|
|
9600
|
+
await saveGoal(this.goalPath, updated, this.opts.events);
|
|
8832
9601
|
}
|
|
8833
9602
|
async persistEngineState(state) {
|
|
8834
|
-
const current = await loadGoal(this.goalPath);
|
|
9603
|
+
const current = await loadGoal(this.goalPath, this.opts.events);
|
|
8835
9604
|
if (!current) return;
|
|
8836
9605
|
if (current.engineState === state) return;
|
|
8837
|
-
await saveGoal(this.goalPath, { ...current, engineState: state });
|
|
9606
|
+
await saveGoal(this.goalPath, { ...current, engineState: state }, this.opts.events);
|
|
8838
9607
|
}
|
|
8839
9608
|
};
|
|
8840
9609
|
|
|
@@ -9183,6 +9952,20 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
9183
9952
|
};
|
|
9184
9953
|
|
|
9185
9954
|
// src/coordination/agent-subagent-runner.ts
|
|
9955
|
+
function withDisabledToolFiltering(factory) {
|
|
9956
|
+
return async (config) => {
|
|
9957
|
+
const result = await factory(config);
|
|
9958
|
+
const disabled = config.disabledTools ?? [];
|
|
9959
|
+
if (disabled.length === 0) return result;
|
|
9960
|
+
const registry = result.agent.tools;
|
|
9961
|
+
if (registry && typeof registry.unregister === "function") {
|
|
9962
|
+
for (const toolName of disabled) {
|
|
9963
|
+
registry.unregister(toolName);
|
|
9964
|
+
}
|
|
9965
|
+
}
|
|
9966
|
+
return result;
|
|
9967
|
+
};
|
|
9968
|
+
}
|
|
9186
9969
|
function makeAgentSubagentRunner(opts) {
|
|
9187
9970
|
const format = opts.formatTaskInput ?? defaultFormatTaskInput;
|
|
9188
9971
|
return async (task, ctx) => {
|
|
@@ -12006,17 +12789,17 @@ function normalize(text) {
|
|
|
12006
12789
|
return ` ${text.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim()} `;
|
|
12007
12790
|
}
|
|
12008
12791
|
function scoreAgents(task, catalog = AGENT_CATALOG) {
|
|
12009
|
-
const
|
|
12792
|
+
const haySet = new Set(normalize(task).split(/\s+/).filter(Boolean));
|
|
12010
12793
|
const out = [];
|
|
12011
12794
|
for (const def of Object.values(catalog)) {
|
|
12012
12795
|
if (!def?.config?.role) continue;
|
|
12013
12796
|
let score = 0;
|
|
12014
12797
|
const matched = [];
|
|
12015
12798
|
for (const kw of def.capability.keywords) {
|
|
12016
|
-
const
|
|
12017
|
-
|
|
12018
|
-
|
|
12019
|
-
score +=
|
|
12799
|
+
const needleWords = normalize(kw).split(/\s+/).filter(Boolean);
|
|
12800
|
+
const allPresent = needleWords.every((w) => haySet.has(w));
|
|
12801
|
+
if (allPresent) {
|
|
12802
|
+
score += needleWords.length;
|
|
12020
12803
|
matched.push(kw);
|
|
12021
12804
|
}
|
|
12022
12805
|
}
|
|
@@ -13349,7 +14132,8 @@ var ParallelEternalEngine = class {
|
|
|
13349
14132
|
doneCondition: { type: "all_tasks_done" }
|
|
13350
14133
|
};
|
|
13351
14134
|
this.coordinator = new DefaultMultiAgentCoordinator(config);
|
|
13352
|
-
const
|
|
14135
|
+
const filteredFactory = withDisabledToolFiltering(this.agentFactory);
|
|
14136
|
+
const runner = makeAgentSubagentRunner({ factory: filteredFactory });
|
|
13353
14137
|
this.coordinator.setRunner?.(runner);
|
|
13354
14138
|
try {
|
|
13355
14139
|
while (!this.stopRequested) {
|
|
@@ -13384,7 +14168,7 @@ var ParallelEternalEngine = class {
|
|
|
13384
14168
|
this.opts.onStage?.(stage);
|
|
13385
14169
|
};
|
|
13386
14170
|
this.iterations++;
|
|
13387
|
-
const goal = await loadGoal(this.goalPath);
|
|
14171
|
+
const goal = await loadGoal(this.goalPath, this.opts.events);
|
|
13388
14172
|
if (!goal) {
|
|
13389
14173
|
this.stopRequested = true;
|
|
13390
14174
|
emit({ phase: "stopped" });
|
|
@@ -13402,7 +14186,8 @@ var ParallelEternalEngine = class {
|
|
|
13402
14186
|
doneCondition: { type: "all_tasks_done" }
|
|
13403
14187
|
};
|
|
13404
14188
|
this.coordinator = new DefaultMultiAgentCoordinator(config);
|
|
13405
|
-
const
|
|
14189
|
+
const filteredFactory = withDisabledToolFiltering(this.agentFactory);
|
|
14190
|
+
const runner = makeAgentSubagentRunner({ factory: filteredFactory });
|
|
13406
14191
|
this.coordinator.setRunner?.(runner);
|
|
13407
14192
|
}
|
|
13408
14193
|
emit({ phase: "decompose" });
|
|
@@ -13511,13 +14296,17 @@ ${personaLine}Task: ${task}
|
|
|
13511
14296
|
role: route.role,
|
|
13512
14297
|
tools: route.definition.config.tools,
|
|
13513
14298
|
systemPromptOverride: route.definition.config.prompt,
|
|
13514
|
-
timeoutMs: this.timeoutMs
|
|
14299
|
+
timeoutMs: this.timeoutMs,
|
|
14300
|
+
// Disable delegation — subagents are leaf workers, not orchestrators
|
|
14301
|
+
disabledTools: ["delegate"]
|
|
13515
14302
|
} : {
|
|
13516
14303
|
id: subagentId,
|
|
13517
14304
|
name: `slot-${subagentId.slice(-6)}`,
|
|
13518
14305
|
// Let the coordinator apply its default budget (roster or generic).
|
|
13519
14306
|
// Hardcoding low limits here defeats the x10 budget improvement.
|
|
13520
|
-
timeoutMs: this.timeoutMs
|
|
14307
|
+
timeoutMs: this.timeoutMs,
|
|
14308
|
+
// Disable delegation — subagents are leaf workers, not orchestrators
|
|
14309
|
+
disabledTools: ["delegate"]
|
|
13521
14310
|
}
|
|
13522
14311
|
);
|
|
13523
14312
|
subagentIds.push(subagentId);
|
|
@@ -13663,10 +14452,10 @@ ${lastFew}` : "No prior iterations.",
|
|
|
13663
14452
|
// Helpers
|
|
13664
14453
|
// -------------------------------------------------------------------------
|
|
13665
14454
|
async appendIterationEntry(entry) {
|
|
13666
|
-
const current = await loadGoal(this.goalPath);
|
|
14455
|
+
const current = await loadGoal(this.goalPath, this.opts.events);
|
|
13667
14456
|
if (!current) return;
|
|
13668
14457
|
const updated = appendJournal(current, entry);
|
|
13669
|
-
await saveGoal(this.goalPath, updated);
|
|
14458
|
+
await saveGoal(this.goalPath, updated, this.opts.events);
|
|
13670
14459
|
const entryWithMeta = {
|
|
13671
14460
|
at: (this.opts.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
13672
14461
|
iteration: updated.iterations,
|
|
@@ -13678,10 +14467,10 @@ ${lastFew}` : "No prior iterations.",
|
|
|
13678
14467
|
await this.appendIterationEntry({ source: "manual", task, status: "failure", note });
|
|
13679
14468
|
}
|
|
13680
14469
|
async persistState(state) {
|
|
13681
|
-
const current = await loadGoal(this.goalPath);
|
|
14470
|
+
const current = await loadGoal(this.goalPath, this.opts.events);
|
|
13682
14471
|
if (!current) return;
|
|
13683
14472
|
if (current.engineState === state) return;
|
|
13684
|
-
await saveGoal(this.goalPath, { ...current, engineState: state });
|
|
14473
|
+
await saveGoal(this.goalPath, { ...current, engineState: state }, this.opts.events);
|
|
13685
14474
|
}
|
|
13686
14475
|
};
|
|
13687
14476
|
|
|
@@ -14707,6 +15496,7 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
|
|
|
14707
15496
|
}
|
|
14708
15497
|
for (const dispose of this.disposers) dispose();
|
|
14709
15498
|
this.disposers.length = 0;
|
|
15499
|
+
this.snapshot.files.length = 0;
|
|
14710
15500
|
}
|
|
14711
15501
|
};
|
|
14712
15502
|
|
|
@@ -14740,7 +15530,12 @@ Working rules:
|
|
|
14740
15530
|
6. Never claim a subagent's work as your own without verifying it. If a
|
|
14741
15531
|
result looks wrong, ask_subagent for clarification before passing it
|
|
14742
15532
|
to the user.
|
|
14743
|
-
7.
|
|
15533
|
+
7. **Act on subagent mail immediately**. Subagent messages (result, ask,
|
|
15534
|
+
assign, note) are injected inline before every step \u2014 even mid-task.
|
|
15535
|
+
When you see one, address it before continuing: reply to asks, factor
|
|
15536
|
+
in results, act on assignments. Use \`mailbox action=ack\` to mark
|
|
15537
|
+
completed messages.
|
|
15538
|
+
8. Wind down when satisfied. When the results are good enough, call
|
|
14744
15539
|
work_complete \u2014 no new subagents will spawn and queued tasks complete
|
|
14745
15540
|
as aborted. Running subagents finish naturally. Call terminate_subagent
|
|
14746
15541
|
only for ones you need to stop immediately.`;
|
|
@@ -14757,6 +15552,13 @@ Bridge contract:
|
|
|
14757
15552
|
structured, and self-contained \u2014 assume the Director will paste your
|
|
14758
15553
|
output into its own context.
|
|
14759
15554
|
|
|
15555
|
+
CRITICAL CONSTRAINT \u2014 NO FURTHER DELEGATION:
|
|
15556
|
+
- You MUST NOT call the \`delegate\` tool or attempt to spawn subagents.
|
|
15557
|
+
- You MUST NOT use \`spawn_subagent\`, \`assign_task\`, or any equivalent.
|
|
15558
|
+
- Your role is to execute the assigned task yourself, not to orchestrate.
|
|
15559
|
+
- If a subtask is too complex, report back to the Director with what you
|
|
15560
|
+
found and let the Director decide how to decompose.
|
|
15561
|
+
|
|
14760
15562
|
Inter-agent mailbox (if you have the \`mail_send\`/\`mail_inbox\`/\`mailbox\` tools):
|
|
14761
15563
|
- You are part of a project-wide fleet that may span other terminals and
|
|
14762
15564
|
WebUIs. Your mailbox identity is \`<your-name>@<session-tag>\` (unique
|
|
@@ -14771,7 +15573,12 @@ Inter-agent mailbox (if you have the \`mail_send\`/\`mail_inbox\`/\`mailbox\` to
|
|
|
14771
15573
|
their exact id instead of doing everything yourself. Discover ids with
|
|
14772
15574
|
\`mailbox action=online\`.
|
|
14773
15575
|
- Answer your mail: reply to the sender's exact \`from\` id. When done with
|
|
14774
|
-
an assigned task, post a \`result\` back to whoever assigned it
|
|
15576
|
+
an assigned task, post a \`result\` back to whoever assigned it.
|
|
15577
|
+
- **Mail to the leader is always seen**: when you send \`ask\`, \`result\`,
|
|
15578
|
+
or \`assign\` to the director/leader, the message is injected inline into
|
|
15579
|
+
the leader's conversation before their next step \u2014 even if the leader is
|
|
15580
|
+
mid-task. Use \`mail_send\` to reliably reach the leader instead of
|
|
15581
|
+
waiting for them to check in.`;
|
|
14775
15582
|
function composeDirectorPrompt(parts = {}) {
|
|
14776
15583
|
const sections = [];
|
|
14777
15584
|
const preamble = parts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
|
|
@@ -17168,6 +17975,7 @@ async function readSubagentPartial(opts, subagentId) {
|
|
|
17168
17975
|
}
|
|
17169
17976
|
function makeDirectorSessionFactory(opts) {
|
|
17170
17977
|
const runId = opts.directorRunId ?? `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-director`;
|
|
17978
|
+
const { traceId } = opts;
|
|
17171
17979
|
let store;
|
|
17172
17980
|
let dir;
|
|
17173
17981
|
if (opts.store) {
|
|
@@ -17183,12 +17991,16 @@ function makeDirectorSessionFactory(opts) {
|
|
|
17183
17991
|
dir,
|
|
17184
17992
|
directorRunId: runId,
|
|
17185
17993
|
async createSubagentSession({ subagentId, provider, model, title }) {
|
|
17186
|
-
|
|
17994
|
+
const writer = await store.create({
|
|
17187
17995
|
id: subagentId,
|
|
17188
17996
|
title: title ?? subagentId,
|
|
17189
17997
|
provider: provider ?? "unknown",
|
|
17190
17998
|
model: model ?? "unknown"
|
|
17191
17999
|
});
|
|
18000
|
+
if (traceId !== void 0) {
|
|
18001
|
+
writer.traceId = traceId;
|
|
18002
|
+
}
|
|
18003
|
+
return writer;
|
|
17192
18004
|
}
|
|
17193
18005
|
};
|
|
17194
18006
|
}
|
|
@@ -20632,7 +21444,9 @@ var SddParallelRun = class {
|
|
|
20632
21444
|
doneCondition: { type: "all_tasks_done" }
|
|
20633
21445
|
};
|
|
20634
21446
|
this.coordinator = new DefaultMultiAgentCoordinator(config);
|
|
20635
|
-
const
|
|
21447
|
+
const baseFactory = this.opts.subagentFactory ?? this.defaultFactory();
|
|
21448
|
+
const filteredFactory = withDisabledToolFiltering(baseFactory);
|
|
21449
|
+
const runner = makeAgentSubagentRunner({ factory: filteredFactory });
|
|
20636
21450
|
this.coordinator.setRunner?.(runner);
|
|
20637
21451
|
}
|
|
20638
21452
|
defaultFactory() {
|
|
@@ -20674,7 +21488,9 @@ var SddParallelRun = class {
|
|
|
20674
21488
|
id: subagentId,
|
|
20675
21489
|
name: subagentId,
|
|
20676
21490
|
role: "executor",
|
|
20677
|
-
timeoutMs: this.timeoutMs
|
|
21491
|
+
timeoutMs: this.timeoutMs,
|
|
21492
|
+
// Disable delegation — subagents are leaf workers, not orchestrators
|
|
21493
|
+
disabledTools: ["delegate"]
|
|
20678
21494
|
})
|
|
20679
21495
|
);
|
|
20680
21496
|
const spawnResults = await Promise.all(spawns);
|