@wrongstack/core 0.265.1 → 0.268.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-bridge-DrkBxszZ.d.ts → agent-bridge-UhojbpWx.d.ts} +1 -1
- package/dist/{agent-subagent-runner-DM2pP-B6.d.ts → agent-subagent-runner-Bvtf1o9K.d.ts} +25 -7
- package/dist/{brain-BXd_61kQ.d.ts → brain-69wzMKp1.d.ts} +73 -1
- package/dist/{compactor-B8pOf45Y.d.ts → compactor-CBQAJoDc.d.ts} +19 -1
- package/dist/{config-BMCj_XDs.d.ts → config-VKfOZ-6X.d.ts} +122 -3
- package/dist/{context-MRk5PhNv.d.ts → context-C0U8B9NF.d.ts} +88 -1
- package/dist/coordination/index.d.ts +57 -161
- package/dist/coordination/index.js +471 -177
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +26 -25
- package/dist/defaults/index.js +1818 -844
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +72 -16
- package/dist/execution/index.js +1270 -265
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +1 -1
- package/dist/extension/index.d.ts +7 -6
- package/dist/global-mailbox-KByEFFBa.d.ts +663 -0
- package/dist/{goal-preamble-DvHDSKSe.d.ts → goal-preamble-CrYjmdw4.d.ts} +28 -11
- package/dist/{goal-store-DtLMySNb.d.ts → goal-store-Y_zdLZ3q.d.ts} +1 -1
- package/dist/hq/index.d.ts +195 -0
- package/dist/hq/index.js +1884 -0
- package/dist/hq/index.js.map +1 -0
- package/dist/index-BfaS-f_m.d.ts +82 -0
- package/dist/{index-B-ch8K9C.d.ts → index-CtQnmkaS.d.ts} +8 -8
- package/dist/{index-CEDeNodM.d.ts → index-gCv830d7.d.ts} +5 -5
- package/dist/index.d.ts +124 -47
- package/dist/index.js +5600 -2662
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +117 -19
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +10 -9
- package/dist/kernel/index.js.map +1 -1
- package/dist/{pipeline-DPDxH_7m.d.ts → mailbox-types-Ct2hJq0P.d.ts} +1 -244
- package/dist/{mcp-servers-2x4w6Jn9.d.ts → mcp-servers-HT3Fi7Bl.d.ts} +10 -4
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +33 -3
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-DmJlKuNp.d.ts → models-registry-Bvcl3Vaa.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-DyCkCZnU.d.ts → multi-agent-coordinator-BACjsmkC.d.ts} +1 -1
- package/dist/{null-fleet-bus-CG9QY2aP.d.ts → null-fleet-bus-DA7fvhUg.d.ts} +14 -9
- package/dist/observability/index.d.ts +2 -2
- package/dist/{parallel-eternal-engine-Jw9uhEoT.d.ts → parallel-eternal-engine-Ci71gYu_.d.ts} +11 -15
- package/dist/{path-resolver-Dy2ej-gE.d.ts → path-resolver-O1IJnmKE.d.ts} +4 -3
- package/dist/{permission-B9SB45lp.d.ts → permission-Bd-57Lbl.d.ts} +1 -1
- package/dist/{permission-policy-CkjSXabK.d.ts → permission-policy-uNXC6Kge.d.ts} +2 -3
- package/dist/pipeline-BDNvENyV.d.ts +245 -0
- package/dist/{plan-templates-CzD9GnAU.d.ts → plan-templates-EMsalEtN.d.ts} +5 -5
- package/dist/{llm-selector-C0tfTCUe.d.ts → provider-model-resolve-CEb9x886.d.ts} +40 -3
- package/dist/{provider-runner-DMa70ODu.d.ts → provider-runner-DWJbpo70.d.ts} +3 -3
- package/dist/{retry-policy-CN0khdlj.d.ts → retry-policy-C3s_lvdK.d.ts} +1 -1
- package/dist/sdd/index.d.ts +9 -8
- package/dist/sdd/index.js +44 -14
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-B2yw84VT.d.ts → secret-vault-Cgduf5xL.d.ts} +2 -2
- package/dist/security/index.d.ts +5 -67
- package/dist/security/index.js +129 -99
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-CzHh_igB.d.ts → selector-47LBnBVk.d.ts} +1 -1
- package/dist/{session-event-bridge-BUI6Jf-4.d.ts → session-event-bridge-Cw7oqmW2.d.ts} +1 -1
- package/dist/{session-reader-CMgdMSRP.d.ts → session-reader-DD4v2Obw.d.ts} +1 -1
- package/dist/storage/index.d.ts +14 -12
- package/dist/storage/index.js +144 -120
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/index.d.ts +4 -2
- package/dist/tools/index.js +166 -31
- package/dist/tools/index.js.map +1 -1
- package/dist/types/index.d.ts +20 -19
- package/dist/types/index.js +1358 -476
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +472 -405
- package/dist/utils/index.js +2321 -1193
- package/dist/utils/index.js.map +1 -1
- package/package.json +5 -1
|
@@ -449,11 +449,6 @@ function safeParse(input, maxBytes = 5e6) {
|
|
|
449
449
|
}
|
|
450
450
|
}
|
|
451
451
|
|
|
452
|
-
// src/utils/string.ts
|
|
453
|
-
function truncate(s, max) {
|
|
454
|
-
return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
452
|
// src/utils/expect-defined.ts
|
|
458
453
|
function expectDefined(value, label) {
|
|
459
454
|
if (value === null || value === void 0) {
|
|
@@ -463,111 +458,6 @@ function expectDefined(value, label) {
|
|
|
463
458
|
}
|
|
464
459
|
return value;
|
|
465
460
|
}
|
|
466
|
-
function projectSlug(absRoot) {
|
|
467
|
-
const base = slugify(path5.basename(absRoot));
|
|
468
|
-
const hash = createHash("sha256").update(path5.resolve(absRoot)).digest("hex").slice(0, 6);
|
|
469
|
-
return `${base}-${hash}`;
|
|
470
|
-
}
|
|
471
|
-
function slugify(name) {
|
|
472
|
-
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
|
|
473
|
-
}
|
|
474
|
-
function wstackGlobalRoot() {
|
|
475
|
-
const fromEnv = process.env["WRONGSTACK_HOME"];
|
|
476
|
-
if (fromEnv && fromEnv.trim().length > 0) return path5.resolve(fromEnv);
|
|
477
|
-
return path5.join(os.homedir(), ".wrongstack");
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
// src/utils/message-invariants.ts
|
|
481
|
-
function repairToolUseAdjacency(messages) {
|
|
482
|
-
const removedToolUses = [];
|
|
483
|
-
const removedToolResults = [];
|
|
484
|
-
let removedMessages = 0;
|
|
485
|
-
let changed = false;
|
|
486
|
-
const out = [];
|
|
487
|
-
for (let i = 0; i < messages.length; i++) {
|
|
488
|
-
const original = expectDefined(messages[i]);
|
|
489
|
-
let msg = original;
|
|
490
|
-
if (hasToolUse(msg)) {
|
|
491
|
-
const nextIds = toolResultIds(messages[i + 1]);
|
|
492
|
-
const filtered = mapContent(msg, (blocks) => {
|
|
493
|
-
const next = [];
|
|
494
|
-
for (const block of blocks) {
|
|
495
|
-
if (block.type === "tool_use" && !nextIds.has(block.id)) {
|
|
496
|
-
removedToolUses.push(block.id);
|
|
497
|
-
changed = true;
|
|
498
|
-
continue;
|
|
499
|
-
}
|
|
500
|
-
next.push(block);
|
|
501
|
-
}
|
|
502
|
-
return next;
|
|
503
|
-
});
|
|
504
|
-
msg = filtered ?? msg;
|
|
505
|
-
}
|
|
506
|
-
if (hasToolResult(msg)) {
|
|
507
|
-
const allowed = toolUseIds(out[out.length - 1]);
|
|
508
|
-
const filtered = mapContent(msg, (blocks) => {
|
|
509
|
-
const next = [];
|
|
510
|
-
for (const block of blocks) {
|
|
511
|
-
if (block.type === "tool_result" && !allowed.has(block.tool_use_id)) {
|
|
512
|
-
removedToolResults.push(block.tool_use_id);
|
|
513
|
-
changed = true;
|
|
514
|
-
continue;
|
|
515
|
-
}
|
|
516
|
-
next.push(block);
|
|
517
|
-
}
|
|
518
|
-
return next;
|
|
519
|
-
});
|
|
520
|
-
msg = filtered ?? msg;
|
|
521
|
-
}
|
|
522
|
-
if (isEmptyMessage(msg)) {
|
|
523
|
-
removedMessages++;
|
|
524
|
-
changed = true;
|
|
525
|
-
continue;
|
|
526
|
-
}
|
|
527
|
-
out.push(msg);
|
|
528
|
-
}
|
|
529
|
-
return {
|
|
530
|
-
messages: changed ? out : messages,
|
|
531
|
-
report: { changed, removedToolUses, removedToolResults, removedMessages }
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
function hasToolUse(msg) {
|
|
535
|
-
return contentBlocks(msg).some((b) => b.type === "tool_use");
|
|
536
|
-
}
|
|
537
|
-
function hasToolResult(msg) {
|
|
538
|
-
return contentBlocks(msg).some((b) => b.type === "tool_result");
|
|
539
|
-
}
|
|
540
|
-
function toolUseIds(msg) {
|
|
541
|
-
const ids = /* @__PURE__ */ new Set();
|
|
542
|
-
if (!msg || msg.role !== "assistant") return ids;
|
|
543
|
-
for (const block of contentBlocks(msg)) {
|
|
544
|
-
if (block.type === "tool_use") ids.add(block.id);
|
|
545
|
-
}
|
|
546
|
-
return ids;
|
|
547
|
-
}
|
|
548
|
-
function toolResultIds(msg) {
|
|
549
|
-
const ids = /* @__PURE__ */ new Set();
|
|
550
|
-
if (!msg || msg.role !== "user") return ids;
|
|
551
|
-
for (const block of contentBlocks(msg)) {
|
|
552
|
-
if (block.type === "tool_result") ids.add(block.tool_use_id);
|
|
553
|
-
}
|
|
554
|
-
return ids;
|
|
555
|
-
}
|
|
556
|
-
function contentBlocks(msg) {
|
|
557
|
-
return msg && Array.isArray(msg.content) ? msg.content : [];
|
|
558
|
-
}
|
|
559
|
-
function mapContent(msg, fn) {
|
|
560
|
-
if (!Array.isArray(msg.content)) return msg;
|
|
561
|
-
const next = fn(msg.content);
|
|
562
|
-
if (next.length === msg.content.length && next.every((b, idx) => b === msg.content[idx])) {
|
|
563
|
-
return msg;
|
|
564
|
-
}
|
|
565
|
-
return { ...msg, content: next };
|
|
566
|
-
}
|
|
567
|
-
function isEmptyMessage(msg) {
|
|
568
|
-
if (typeof msg.content === "string") return msg.content.trim().length === 0;
|
|
569
|
-
return msg.content.length === 0;
|
|
570
|
-
}
|
|
571
461
|
var GLOB_CHARS = /* @__PURE__ */ new Set(["*", "?", "["]);
|
|
572
462
|
var IS_WINDOWS = process.platform === "win32";
|
|
573
463
|
var SEP = IS_WINDOWS ? "\\" : "/";
|
|
@@ -684,6 +574,116 @@ async function expandGlob(pattern) {
|
|
|
684
574
|
return [...results];
|
|
685
575
|
}
|
|
686
576
|
|
|
577
|
+
// src/utils/message-invariants.ts
|
|
578
|
+
function repairToolUseAdjacency(messages) {
|
|
579
|
+
const removedToolUses = [];
|
|
580
|
+
const removedToolResults = [];
|
|
581
|
+
let removedMessages = 0;
|
|
582
|
+
let changed = false;
|
|
583
|
+
const out = [];
|
|
584
|
+
for (let i = 0; i < messages.length; i++) {
|
|
585
|
+
const original = expectDefined(messages[i]);
|
|
586
|
+
let msg = original;
|
|
587
|
+
if (hasToolUse(msg)) {
|
|
588
|
+
const nextIds = toolResultIds(messages[i + 1]);
|
|
589
|
+
const filtered = mapContent(msg, (blocks) => {
|
|
590
|
+
const next = [];
|
|
591
|
+
for (const block of blocks) {
|
|
592
|
+
if (block.type === "tool_use" && !nextIds.has(block.id)) {
|
|
593
|
+
removedToolUses.push(block.id);
|
|
594
|
+
changed = true;
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
next.push(block);
|
|
598
|
+
}
|
|
599
|
+
return next;
|
|
600
|
+
});
|
|
601
|
+
msg = filtered ?? msg;
|
|
602
|
+
}
|
|
603
|
+
if (hasToolResult(msg)) {
|
|
604
|
+
const allowed = toolUseIds(out[out.length - 1]);
|
|
605
|
+
const filtered = mapContent(msg, (blocks) => {
|
|
606
|
+
const next = [];
|
|
607
|
+
for (const block of blocks) {
|
|
608
|
+
if (block.type === "tool_result" && !allowed.has(block.tool_use_id)) {
|
|
609
|
+
removedToolResults.push(block.tool_use_id);
|
|
610
|
+
changed = true;
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
next.push(block);
|
|
614
|
+
}
|
|
615
|
+
return next;
|
|
616
|
+
});
|
|
617
|
+
msg = filtered ?? msg;
|
|
618
|
+
}
|
|
619
|
+
if (isEmptyMessage(msg)) {
|
|
620
|
+
removedMessages++;
|
|
621
|
+
changed = true;
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
624
|
+
out.push(msg);
|
|
625
|
+
}
|
|
626
|
+
return {
|
|
627
|
+
messages: changed ? out : messages,
|
|
628
|
+
report: { changed, removedToolUses, removedToolResults, removedMessages }
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
function hasToolUse(msg) {
|
|
632
|
+
return contentBlocks(msg).some((b) => b.type === "tool_use");
|
|
633
|
+
}
|
|
634
|
+
function hasToolResult(msg) {
|
|
635
|
+
return contentBlocks(msg).some((b) => b.type === "tool_result");
|
|
636
|
+
}
|
|
637
|
+
function toolUseIds(msg) {
|
|
638
|
+
const ids = /* @__PURE__ */ new Set();
|
|
639
|
+
if (!msg || msg.role !== "assistant") return ids;
|
|
640
|
+
for (const block of contentBlocks(msg)) {
|
|
641
|
+
if (block.type === "tool_use") ids.add(block.id);
|
|
642
|
+
}
|
|
643
|
+
return ids;
|
|
644
|
+
}
|
|
645
|
+
function toolResultIds(msg) {
|
|
646
|
+
const ids = /* @__PURE__ */ new Set();
|
|
647
|
+
if (!msg || msg.role !== "user") return ids;
|
|
648
|
+
for (const block of contentBlocks(msg)) {
|
|
649
|
+
if (block.type === "tool_result") ids.add(block.tool_use_id);
|
|
650
|
+
}
|
|
651
|
+
return ids;
|
|
652
|
+
}
|
|
653
|
+
function contentBlocks(msg) {
|
|
654
|
+
return msg && Array.isArray(msg.content) ? msg.content : [];
|
|
655
|
+
}
|
|
656
|
+
function mapContent(msg, fn) {
|
|
657
|
+
if (!Array.isArray(msg.content)) return msg;
|
|
658
|
+
const next = fn(msg.content);
|
|
659
|
+
if (next.length === msg.content.length && next.every((b, idx) => b === msg.content[idx])) {
|
|
660
|
+
return msg;
|
|
661
|
+
}
|
|
662
|
+
return { ...msg, content: next };
|
|
663
|
+
}
|
|
664
|
+
function isEmptyMessage(msg) {
|
|
665
|
+
if (typeof msg.content === "string") return msg.content.trim().length === 0;
|
|
666
|
+
return msg.content.length === 0;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// src/utils/string.ts
|
|
670
|
+
function truncate(s, max) {
|
|
671
|
+
return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
|
|
672
|
+
}
|
|
673
|
+
function projectSlug(absRoot) {
|
|
674
|
+
const base = slugify(path5.basename(absRoot));
|
|
675
|
+
const hash = createHash("sha256").update(path5.resolve(absRoot)).digest("hex").slice(0, 6);
|
|
676
|
+
return `${base}-${hash}`;
|
|
677
|
+
}
|
|
678
|
+
function slugify(name) {
|
|
679
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
|
|
680
|
+
}
|
|
681
|
+
function wstackGlobalRoot() {
|
|
682
|
+
const fromEnv = process.env["WRONGSTACK_HOME"];
|
|
683
|
+
if (fromEnv && fromEnv.trim().length > 0) return path5.resolve(fromEnv);
|
|
684
|
+
return path5.join(os.homedir(), ".wrongstack");
|
|
685
|
+
}
|
|
686
|
+
|
|
687
687
|
// src/types/errors.ts
|
|
688
688
|
var ERROR_CODES = {
|
|
689
689
|
// Provider
|
|
@@ -1539,6 +1539,24 @@ Working rules:
|
|
|
1539
1539
|
var DEFAULT_SUBAGENT_BASELINE = `You are a subagent operating under a Director. You were spawned to handle
|
|
1540
1540
|
a specific slice of a larger plan \u2014 do that slice well and report back.
|
|
1541
1541
|
|
|
1542
|
+
Capabilities & operating rules:
|
|
1543
|
+
- You have full developer tools for your task: read, write/edit, search,
|
|
1544
|
+
shell + build (lint, format, typecheck, test), and dependency install.
|
|
1545
|
+
Use them directly to finish the task end-to-end. You run non-interactively
|
|
1546
|
+
\u2014 there is no human to approve individual tool calls, so routine work is
|
|
1547
|
+
pre-authorized; do not stop to ask for permission to read, edit, or build.
|
|
1548
|
+
- Stay inside the project root. Do not write files outside the repository,
|
|
1549
|
+
and do not touch machine config, credentials, or global state \u2014 those
|
|
1550
|
+
require an explicit grant you do not have.
|
|
1551
|
+
- Prefer the least-destructive path. Do not run irreversible or destructive
|
|
1552
|
+
commands (e.g. \`rm -rf\`, \`git push --force\`, history rewrites, dropping
|
|
1553
|
+
databases, mass deletes) unless the task explicitly requires it and names
|
|
1554
|
+
the target.
|
|
1555
|
+
- When you change code, verify it: run the relevant build / typecheck / tests
|
|
1556
|
+
and fix what you broke before reporting done.
|
|
1557
|
+
- Make only the changes the task calls for \u2014 don't refactor or reformat
|
|
1558
|
+
unrelated code.
|
|
1559
|
+
|
|
1542
1560
|
Bridge contract:
|
|
1543
1561
|
- You have a parent (the Director). You may call \`request\` on the
|
|
1544
1562
|
parent bridge to ask a clarifying question. Use this sparingly; the
|
|
@@ -3747,7 +3765,27 @@ Working rules:
|
|
|
3747
3765
|
id: "devops",
|
|
3748
3766
|
name: "DevOps",
|
|
3749
3767
|
role: "devops",
|
|
3750
|
-
tools: [
|
|
3768
|
+
tools: [
|
|
3769
|
+
...TOOLS.build,
|
|
3770
|
+
"mcp__ssh__ssh_list_servers",
|
|
3771
|
+
"mcp__ssh__ssh_connection_status",
|
|
3772
|
+
"mcp__ssh__ssh_execute",
|
|
3773
|
+
"mcp__ssh__ssh_execute_sudo",
|
|
3774
|
+
"mcp__ssh__ssh_upload",
|
|
3775
|
+
"mcp__ssh__ssh_download",
|
|
3776
|
+
"mcp__ssh__ssh_sync",
|
|
3777
|
+
"mcp__ssh__ssh_deploy",
|
|
3778
|
+
"mcp__ssh__ssh_health_check",
|
|
3779
|
+
"mcp__ssh__ssh_service_status",
|
|
3780
|
+
"mcp__ssh__ssh_process_manager",
|
|
3781
|
+
"mcp__ssh__ssh_tunnel",
|
|
3782
|
+
"mcp__ssh__ssh_backup_create",
|
|
3783
|
+
"mcp__ssh__ssh_backup_list",
|
|
3784
|
+
"mcp__ssh__ssh_backup_restore",
|
|
3785
|
+
"mcp__ssh__ssh_db_list",
|
|
3786
|
+
"mcp__ssh__ssh_db_query",
|
|
3787
|
+
"mcp__ssh__ssh_profile"
|
|
3788
|
+
],
|
|
3751
3789
|
prompt: `You are the DevOps agent. Your job is CI/CD, containerization, and
|
|
3752
3790
|
deployment configuration: make builds reproducible and deploys safe.
|
|
3753
3791
|
|
|
@@ -3756,6 +3794,7 @@ Scope:
|
|
|
3756
3794
|
- Write Dockerfiles/compose and optimize image size and layer caching
|
|
3757
3795
|
- Configure deployment (env, secrets handling, health checks, rollback)
|
|
3758
3796
|
- Diagnose flaky/broken pipelines
|
|
3797
|
+
- Use optional SSH MCP tools for remote hosts when the 'ssh' MCP server is enabled: list servers, run health checks, inspect services, transfer/deploy files, and open tunnels
|
|
3759
3798
|
|
|
3760
3799
|
Input format you accept:
|
|
3761
3800
|
{ "task": "ci | container | deploy | fix-pipeline", "platform": "github-actions | gitlab | docker | k8s", "target": "<what>" }
|
|
@@ -3770,7 +3809,8 @@ Working rules:
|
|
|
3770
3809
|
- Never hardcode secrets in config; reference the secret store
|
|
3771
3810
|
- Pin versions for reproducible builds; avoid floating :latest
|
|
3772
3811
|
- Every deploy path needs a rollback and a health check
|
|
3773
|
-
- Treat CI/CD changes as high-risk \u2014 explain blast radius before applying
|
|
3812
|
+
- Treat CI/CD changes as high-risk \u2014 explain blast radius before applying
|
|
3813
|
+
- For remote SSH work, start with ssh_list_servers / ssh_connection_status, prefer read-only checks first, and do not run destructive commands without explicit user approval`
|
|
3774
3814
|
},
|
|
3775
3815
|
budget: MEDIUM_BUDGET,
|
|
3776
3816
|
capability: {
|
|
@@ -3787,6 +3827,13 @@ Working rules:
|
|
|
3787
3827
|
"kubernetes",
|
|
3788
3828
|
"k8s",
|
|
3789
3829
|
"deploy",
|
|
3830
|
+
"ssh",
|
|
3831
|
+
"remote ssh",
|
|
3832
|
+
"remote server",
|
|
3833
|
+
"sftp",
|
|
3834
|
+
"tunnel",
|
|
3835
|
+
"bastion",
|
|
3836
|
+
"jump host",
|
|
3790
3837
|
"github actions",
|
|
3791
3838
|
"container"
|
|
3792
3839
|
]
|
|
@@ -6782,6 +6829,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6782
6829
|
subagentId: result.subagentId,
|
|
6783
6830
|
taskId: result.taskId,
|
|
6784
6831
|
status: result.status,
|
|
6832
|
+
result: result.result,
|
|
6785
6833
|
iterations: result.iterations,
|
|
6786
6834
|
toolCalls: result.toolCalls,
|
|
6787
6835
|
durationMs: result.durationMs
|
|
@@ -6996,6 +7044,8 @@ var Director = class _Director {
|
|
|
6996
7044
|
sessionsRoot;
|
|
6997
7045
|
/** Director run id for JSONL path resolution. */
|
|
6998
7046
|
directorRunId;
|
|
7047
|
+
/** Optional logger for structured logging. Falls back to noop when omitted. */
|
|
7048
|
+
logger;
|
|
6999
7049
|
/** Resolves task descriptions back from `assign()` so completion events
|
|
7000
7050
|
* can also carry a human-readable title. */
|
|
7001
7051
|
taskDescriptions = /* @__PURE__ */ new Map();
|
|
@@ -7084,6 +7134,7 @@ var Director = class _Director {
|
|
|
7084
7134
|
opts.checkpointDebounceMs ?? 250
|
|
7085
7135
|
) : null;
|
|
7086
7136
|
this.fleetManager = opts.fleetManager;
|
|
7137
|
+
this.logger = opts.logger;
|
|
7087
7138
|
if (this.sharedScratchpadPath) {
|
|
7088
7139
|
void fsp6.mkdir(this.sharedScratchpadPath, { recursive: true }).catch((err) => this.logShutdownError("shared_scratchpad_mkdir", err));
|
|
7089
7140
|
}
|
|
@@ -7383,7 +7434,10 @@ var Director = class _Director {
|
|
|
7383
7434
|
entry.session.cancel(reason);
|
|
7384
7435
|
for (const [_role, subagentId] of entry.session.getSubagentIds()) {
|
|
7385
7436
|
this.coordinator.stop(subagentId).catch((err) => {
|
|
7386
|
-
|
|
7437
|
+
this.logger?.debug(`stop subagent ${subagentId} failed (may have already completed)`, {
|
|
7438
|
+
subagentId,
|
|
7439
|
+
err: err instanceof Error ? err.message : String(err)
|
|
7440
|
+
});
|
|
7387
7441
|
});
|
|
7388
7442
|
}
|
|
7389
7443
|
}
|
|
@@ -7443,7 +7497,7 @@ var Director = class _Director {
|
|
|
7443
7497
|
* Caller-supplied `priceLookup` is optional but recommended — without
|
|
7444
7498
|
* it the `cost` column in `usage.snapshot()` stays at 0.
|
|
7445
7499
|
*/
|
|
7446
|
-
async spawn(
|
|
7500
|
+
async spawn(callerConfig, priceLookup) {
|
|
7447
7501
|
if (this.workCompleteFlag) {
|
|
7448
7502
|
throw new FleetSpawnBudgetError(
|
|
7449
7503
|
"max_spawns",
|
|
@@ -7452,6 +7506,7 @@ var Director = class _Director {
|
|
|
7452
7506
|
"workComplete() has been called \u2014 director closed further spawning"
|
|
7453
7507
|
);
|
|
7454
7508
|
}
|
|
7509
|
+
const config = { ...callerConfig };
|
|
7455
7510
|
if (!config.model && this.modelMatrix) {
|
|
7456
7511
|
const matrix = typeof this.modelMatrix === "function" ? this.modelMatrix() : this.modelMatrix;
|
|
7457
7512
|
const entry = resolveModelMatrix(matrix, config.role);
|
|
@@ -8879,6 +8934,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8879
8934
|
const cached = this._loadCache.get(id);
|
|
8880
8935
|
if (cached && cached.mtimeMs === stat6.mtimeMs && cached.size === stat6.size) {
|
|
8881
8936
|
cacheHit = true;
|
|
8937
|
+
this._loadCache.delete(id);
|
|
8938
|
+
this._loadCache.set(id, cached);
|
|
8882
8939
|
return cached.data;
|
|
8883
8940
|
}
|
|
8884
8941
|
const raw = await fsp6.readFile(file, "utf8");
|
|
@@ -10375,30 +10432,18 @@ var DefaultMailbox = class {
|
|
|
10375
10432
|
async query(q) {
|
|
10376
10433
|
const all = await this._readAll();
|
|
10377
10434
|
const limit = q.limit ?? 50;
|
|
10378
|
-
|
|
10379
|
-
|
|
10380
|
-
|
|
10381
|
-
|
|
10382
|
-
|
|
10383
|
-
|
|
10384
|
-
|
|
10385
|
-
|
|
10386
|
-
|
|
10387
|
-
|
|
10388
|
-
|
|
10389
|
-
filtered
|
|
10390
|
-
}
|
|
10391
|
-
if (q.type !== void 0) {
|
|
10392
|
-
filtered = filtered.filter((m) => m.type === q.type);
|
|
10393
|
-
}
|
|
10394
|
-
if (q.minPriority !== void 0) {
|
|
10395
|
-
const order = { low: 0, normal: 1, high: 2 };
|
|
10396
|
-
const min = order[q.minPriority];
|
|
10397
|
-
filtered = filtered.filter((m) => (order[m.priority] ?? 1) >= min);
|
|
10398
|
-
}
|
|
10399
|
-
if (q.since !== void 0) {
|
|
10400
|
-
const since = q.since;
|
|
10401
|
-
filtered = filtered.filter((m) => m.timestamp > since);
|
|
10435
|
+
const order = q.minPriority !== void 0 ? { low: 0, normal: 1, high: 2 } : null;
|
|
10436
|
+
const minPriorityRank = order && q.minPriority !== void 0 ? order[q.minPriority] : 0;
|
|
10437
|
+
const filtered = [];
|
|
10438
|
+
for (const msg of all) {
|
|
10439
|
+
if (q.to !== void 0 && msg.to !== q.to && msg.to !== "*") continue;
|
|
10440
|
+
if (q.from !== void 0 && msg.from !== q.from) continue;
|
|
10441
|
+
if (q.unreadBy !== void 0 && q.unreadBy in msg.readBy) continue;
|
|
10442
|
+
if (q.incompleteOnly && msg.completed) continue;
|
|
10443
|
+
if (q.type !== void 0 && msg.type !== q.type) continue;
|
|
10444
|
+
if (order !== null && (order[msg.priority] ?? 1) < minPriorityRank) continue;
|
|
10445
|
+
if (q.since !== void 0 && msg.timestamp <= q.since) continue;
|
|
10446
|
+
filtered.push(msg);
|
|
10402
10447
|
}
|
|
10403
10448
|
filtered.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
10404
10449
|
return filtered.slice(0, limit);
|
|
@@ -10719,6 +10764,8 @@ var GlobalMailbox = class {
|
|
|
10719
10764
|
clientRegistryPath;
|
|
10720
10765
|
/** Optional event bus for emitting agent registration/heartbeat events. */
|
|
10721
10766
|
_events;
|
|
10767
|
+
/** Optional HQ publisher for cross-project command-center telemetry. */
|
|
10768
|
+
_hqPublisher;
|
|
10722
10769
|
/**
|
|
10723
10770
|
* Local cache of the agent registry to avoid re-reading on every call.
|
|
10724
10771
|
* Time-bounded: the registry file is shared ACROSS PROCESSES (that's the
|
|
@@ -10756,12 +10803,28 @@ var GlobalMailbox = class {
|
|
|
10756
10803
|
/**
|
|
10757
10804
|
* @param projectDir — `~/.wrongstack/projects/<slug>/`
|
|
10758
10805
|
* @param events — optional EventBus for real-time TUI/WebUI notifications
|
|
10806
|
+
* @param hqPublisher — optional HQ publisher for cross-project telemetry
|
|
10759
10807
|
*/
|
|
10760
|
-
constructor(projectDir, events) {
|
|
10808
|
+
constructor(projectDir, events, hqPublisher) {
|
|
10761
10809
|
this.messagePath = path5.join(projectDir, MAILBOX_FILE2);
|
|
10762
10810
|
this.registryPath = path5.join(projectDir, "_mailbox.registry.json");
|
|
10763
10811
|
this.clientRegistryPath = path5.join(projectDir, CLIENT_REGISTRY_FILE);
|
|
10764
10812
|
this._events = events;
|
|
10813
|
+
this._hqPublisher = hqPublisher;
|
|
10814
|
+
}
|
|
10815
|
+
get hqMailboxId() {
|
|
10816
|
+
return `${path5.basename(path5.dirname(this.messagePath))}:mailbox`;
|
|
10817
|
+
}
|
|
10818
|
+
publishHqMailboxEvent(input) {
|
|
10819
|
+
try {
|
|
10820
|
+
this._hqPublisher?.publishMailboxEvent(input);
|
|
10821
|
+
} catch {
|
|
10822
|
+
}
|
|
10823
|
+
}
|
|
10824
|
+
publishHqMailboxSnapshot() {
|
|
10825
|
+
if (this._hqPublisher === void 0) return;
|
|
10826
|
+
void this._hqPublisher.publishMailboxSnapshot(this, { mailboxId: this.hqMailboxId }).catch(() => {
|
|
10827
|
+
});
|
|
10765
10828
|
}
|
|
10766
10829
|
// ── Messages ────────────────────────────────────────────────────────────
|
|
10767
10830
|
async send(input) {
|
|
@@ -10788,6 +10851,8 @@ var GlobalMailbox = class {
|
|
|
10788
10851
|
await fsp6.appendFile(this.messagePath, line, "utf8");
|
|
10789
10852
|
this._pushToCache(msg);
|
|
10790
10853
|
});
|
|
10854
|
+
this.publishHqMailboxEvent({ mailboxId: this.hqMailboxId, action: "message.sent", message: msg });
|
|
10855
|
+
this.publishHqMailboxSnapshot();
|
|
10791
10856
|
return msg;
|
|
10792
10857
|
}
|
|
10793
10858
|
async query(q) {
|
|
@@ -10854,6 +10919,14 @@ var GlobalMailbox = class {
|
|
|
10854
10919
|
cacheSnapshot = all;
|
|
10855
10920
|
});
|
|
10856
10921
|
if (cacheSnapshot) this._setMessageCache(cacheSnapshot);
|
|
10922
|
+
for (const message of updated) {
|
|
10923
|
+
this.publishHqMailboxEvent({
|
|
10924
|
+
mailboxId: this.hqMailboxId,
|
|
10925
|
+
action: message.completed ? "message.completed" : "message.read",
|
|
10926
|
+
message
|
|
10927
|
+
});
|
|
10928
|
+
}
|
|
10929
|
+
if (updated.length > 0) this.publishHqMailboxSnapshot();
|
|
10857
10930
|
return updated;
|
|
10858
10931
|
}
|
|
10859
10932
|
async unreadCount(forAgentId) {
|
|
@@ -10901,6 +10974,25 @@ var GlobalMailbox = class {
|
|
|
10901
10974
|
role: input.role,
|
|
10902
10975
|
source: input.source
|
|
10903
10976
|
});
|
|
10977
|
+
this.publishHqMailboxEvent({
|
|
10978
|
+
mailboxId: this.hqMailboxId,
|
|
10979
|
+
action: "agent.registered",
|
|
10980
|
+
agent: {
|
|
10981
|
+
agentId: input.agentId,
|
|
10982
|
+
name: input.name,
|
|
10983
|
+
...input.role !== void 0 ? { role: input.role } : {},
|
|
10984
|
+
sessionId: input.sessionId,
|
|
10985
|
+
status: "idle",
|
|
10986
|
+
iterations: 0,
|
|
10987
|
+
toolCalls: 0,
|
|
10988
|
+
lastActivityAt: now,
|
|
10989
|
+
lastSeenAt: now,
|
|
10990
|
+
online: true,
|
|
10991
|
+
pid: input.pid,
|
|
10992
|
+
...input.source !== void 0 ? { source: input.source } : {}
|
|
10993
|
+
}
|
|
10994
|
+
});
|
|
10995
|
+
this.publishHqMailboxSnapshot();
|
|
10904
10996
|
}
|
|
10905
10997
|
async heartbeat(input) {
|
|
10906
10998
|
const last = this._lastHeartbeat.get(input.agentId) ?? 0;
|
|
@@ -10931,6 +11023,12 @@ var GlobalMailbox = class {
|
|
|
10931
11023
|
currentTool: input.currentTool,
|
|
10932
11024
|
currentTask: input.currentTask
|
|
10933
11025
|
});
|
|
11026
|
+
this.publishHqMailboxEvent({
|
|
11027
|
+
mailboxId: this.hqMailboxId,
|
|
11028
|
+
action: "agent.heartbeat",
|
|
11029
|
+
summary: input.agentId
|
|
11030
|
+
});
|
|
11031
|
+
this.publishHqMailboxSnapshot();
|
|
10934
11032
|
}
|
|
10935
11033
|
async getAgentStatuses() {
|
|
10936
11034
|
await this._ensureRegistry();
|
|
@@ -11743,13 +11841,13 @@ function makeDependencyWatcherConfig(opts) {
|
|
|
11743
11841
|
const globPatterns = patterns.filter((p) => p.includes("*"));
|
|
11744
11842
|
const plainPatterns = patterns.filter((p) => !p.includes("*"));
|
|
11745
11843
|
function matchesPattern(filePath) {
|
|
11746
|
-
const
|
|
11747
|
-
if (plainPatterns.includes(
|
|
11844
|
+
const basename6 = filePath.split("/").pop()?.split("\\").pop() ?? "";
|
|
11845
|
+
if (plainPatterns.includes(basename6)) return true;
|
|
11748
11846
|
for (const gp of globPatterns) {
|
|
11749
11847
|
const regex = new RegExp(
|
|
11750
11848
|
"^" + gp.replace(/\./g, "\\.").replace(/\*/g, ".*") + "$"
|
|
11751
11849
|
);
|
|
11752
|
-
if (regex.test(
|
|
11850
|
+
if (regex.test(basename6)) return true;
|
|
11753
11851
|
}
|
|
11754
11852
|
return false;
|
|
11755
11853
|
}
|
|
@@ -12725,12 +12823,12 @@ var ConsensusProtocol = class {
|
|
|
12725
12823
|
* Initiate a vote on a proposed change. Updates the change node's status
|
|
12726
12824
|
* to 'proposed' and notifies eligible voters via FleetBus.
|
|
12727
12825
|
*/
|
|
12728
|
-
initiateVote(changeId) {
|
|
12826
|
+
async initiateVote(changeId) {
|
|
12729
12827
|
const change = this.graph.get(changeId);
|
|
12730
12828
|
if (!change || change.type !== "change") {
|
|
12731
12829
|
throw new Error(`ConsensusProtocol: no change found with id "${changeId}"`);
|
|
12732
12830
|
}
|
|
12733
|
-
this.graph.update(changeId, { status: "proposed", votes: [] });
|
|
12831
|
+
await this.graph.update(changeId, { status: "proposed", votes: [] });
|
|
12734
12832
|
const eligible = this._eligibleVoters(change);
|
|
12735
12833
|
this._notifyVoters(change, eligible, "vote_initiated");
|
|
12736
12834
|
}
|
|
@@ -12738,7 +12836,7 @@ var ConsensusProtocol = class {
|
|
|
12738
12836
|
* Cast a vote. Updates the change node in the graph and re-evaluates
|
|
12739
12837
|
* consensus. If the vote triggers a resolution, updates the change status.
|
|
12740
12838
|
*/
|
|
12741
|
-
castVote(changeId, voterId, value, rationale) {
|
|
12839
|
+
async castVote(changeId, voterId, value, rationale) {
|
|
12742
12840
|
const change = this.graph.get(changeId);
|
|
12743
12841
|
if (!change || change.type !== "change") {
|
|
12744
12842
|
throw new Error(`ConsensusProtocol: no change found for "${changeId}"`);
|
|
@@ -12761,7 +12859,7 @@ var ConsensusProtocol = class {
|
|
|
12761
12859
|
const existingIdx = change.votes.findIndex((v) => v.agentId === voterId);
|
|
12762
12860
|
const newVotes = existingIdx >= 0 ? change.votes.with(existingIdx, vote) : [...change.votes, vote];
|
|
12763
12861
|
const result = this._resolve(changeId, newVotes, eligible);
|
|
12764
|
-
this.graph.update(changeId, {
|
|
12862
|
+
await this.graph.update(changeId, {
|
|
12765
12863
|
votes: newVotes,
|
|
12766
12864
|
...result.outcome !== "pending" ? { status: this._toChangeStatus(result.outcome) } : {}
|
|
12767
12865
|
});
|
|
@@ -12772,13 +12870,13 @@ var ConsensusProtocol = class {
|
|
|
12772
12870
|
* Resolve the current vote without waiting for all eligible voters.
|
|
12773
12871
|
* Useful when a timeout fires or an agent decides to finalize early.
|
|
12774
12872
|
*/
|
|
12775
|
-
resolveNow(changeId) {
|
|
12873
|
+
async resolveNow(changeId) {
|
|
12776
12874
|
const change = this.graph.get(changeId);
|
|
12777
12875
|
if (!change) throw new Error(`ConsensusProtocol: unknown change "${changeId}"`);
|
|
12778
12876
|
const eligible = this._eligibleVoters(change);
|
|
12779
12877
|
const result = this._resolve(changeId, change.votes, eligible);
|
|
12780
12878
|
if (result.outcome !== "pending") {
|
|
12781
|
-
this.graph.update(changeId, { status: this._toChangeStatus(result.outcome) });
|
|
12879
|
+
await this.graph.update(changeId, { status: this._toChangeStatus(result.outcome) });
|
|
12782
12880
|
this._notifyVoters(change, eligible, "vote_resolved", { result });
|
|
12783
12881
|
}
|
|
12784
12882
|
return result;
|
|
@@ -12985,8 +13083,7 @@ var ChangeManager = class {
|
|
|
12985
13083
|
// filled after quality gate
|
|
12986
13084
|
satisfiesGoals: input.satisfiesGoals
|
|
12987
13085
|
});
|
|
12988
|
-
void this._runQualityGate(node.id, input.files).then((gate) => {
|
|
12989
|
-
void this.graph.update(node.id, { qualityGate: gate });
|
|
13086
|
+
void this._runQualityGate(node.id, input.files).then((gate) => this.graph.update(node.id, { qualityGate: gate })).catch(() => {
|
|
12990
13087
|
});
|
|
12991
13088
|
this._emit("change:proposed", { changeId: node.id, title: node.title });
|
|
12992
13089
|
return node;
|
|
@@ -13702,7 +13799,8 @@ Priority: ${goal.priority}`,
|
|
|
13702
13799
|
this.agentTaskCount(agentId, -1);
|
|
13703
13800
|
await this.graph.update(taskId, {
|
|
13704
13801
|
status: "done",
|
|
13705
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13802
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13803
|
+
..._result !== void 0 ? { result: _result } : {}
|
|
13706
13804
|
});
|
|
13707
13805
|
this.bidRetryCounts.delete(taskId);
|
|
13708
13806
|
this.pendingBids.delete(taskId);
|
|
@@ -13947,7 +14045,7 @@ ${goal.description}`,
|
|
|
13947
14045
|
this.agentTaskCounts.set(agentId, next);
|
|
13948
14046
|
}
|
|
13949
14047
|
};
|
|
13950
|
-
var AutonomousCoordinator = class {
|
|
14048
|
+
var AutonomousCoordinator = class _AutonomousCoordinator {
|
|
13951
14049
|
graph;
|
|
13952
14050
|
dag;
|
|
13953
14051
|
auction;
|
|
@@ -13963,6 +14061,8 @@ var AutonomousCoordinator = class {
|
|
|
13963
14061
|
onCoordinatorEvent;
|
|
13964
14062
|
running = false;
|
|
13965
14063
|
iterationCount = 0;
|
|
14064
|
+
lastSyncAt = 0;
|
|
14065
|
+
static SYNC_INTERVAL_MS = 5e3;
|
|
13966
14066
|
/** Tasks already handled by _onSubagentTerminated (to avoid double goal:failed on fleet event). */
|
|
13967
14067
|
_handledBySubagent = /* @__PURE__ */ new Set();
|
|
13968
14068
|
/** FleetBus subscription disposers, detached in dispose(). */
|
|
@@ -14019,7 +14119,7 @@ var AutonomousCoordinator = class {
|
|
|
14019
14119
|
const taskId = payload?.taskId;
|
|
14020
14120
|
if (!taskId || this._handledBySubagent.has(taskId)) return;
|
|
14021
14121
|
this._handledBySubagent.add(taskId);
|
|
14022
|
-
this.
|
|
14122
|
+
this._recordTaskFailed(taskId, payload?.error ?? "Task failed");
|
|
14023
14123
|
});
|
|
14024
14124
|
if (offFailed) this.unsubs.push(offFailed);
|
|
14025
14125
|
this._emit({ type: "coordinator:mode", mode: this.fleet ? "fleet" : "standalone" });
|
|
@@ -14038,6 +14138,7 @@ var AutonomousCoordinator = class {
|
|
|
14038
14138
|
const maxCost = opts.maxCostUsd;
|
|
14039
14139
|
try {
|
|
14040
14140
|
await this.graph.load();
|
|
14141
|
+
this._rebuildDagFromGraph();
|
|
14041
14142
|
const goalConfigs = await this._decomposeGoal(goal);
|
|
14042
14143
|
for (const g of goalConfigs) {
|
|
14043
14144
|
const goalId = await this.auction.publishTask(g);
|
|
@@ -14045,23 +14146,49 @@ var AutonomousCoordinator = class {
|
|
|
14045
14146
|
this._emit({ type: "goal:added", goalId, title: g.title, text: g.description });
|
|
14046
14147
|
}
|
|
14047
14148
|
while (this.running) {
|
|
14149
|
+
if (this.dag.getRunning().length > 0 && this.auction.getPendingTasks().length === 0) {
|
|
14150
|
+
await this._waitForDagProgress(1e3);
|
|
14151
|
+
continue;
|
|
14152
|
+
}
|
|
14048
14153
|
this.iterationCount++;
|
|
14154
|
+
await this._maybeSyncFromGraph();
|
|
14049
14155
|
if (this.iterationCount >= maxIterations) break;
|
|
14050
14156
|
if (maxCost !== void 0) {
|
|
14051
14157
|
const cost = this.fleetManager?.snapshot()?.total?.cost ?? 0;
|
|
14052
14158
|
if (cost >= maxCost) break;
|
|
14053
14159
|
}
|
|
14054
14160
|
if (opts.runUntilComplete && this.dag.isDone()) break;
|
|
14161
|
+
const pendingTasks = this.auction.getPendingTasks();
|
|
14162
|
+
const dispatchable = pendingTasks.filter((task) => {
|
|
14163
|
+
const dagNode = this.dag.getNode(task.id);
|
|
14164
|
+
return !dagNode || dagNode.status === "ready";
|
|
14165
|
+
});
|
|
14166
|
+
if (dispatchable.length === 0) {
|
|
14167
|
+
if (this.dag.getRunning().length > 0 || this.dag.getReady().length > 0) {
|
|
14168
|
+
await this._waitForDagProgress(1e3);
|
|
14169
|
+
continue;
|
|
14170
|
+
}
|
|
14171
|
+
if (pendingTasks.length > 0) {
|
|
14172
|
+
await this._waitForDagProgress(2e3);
|
|
14173
|
+
continue;
|
|
14174
|
+
}
|
|
14175
|
+
if (this.dag.hasDeadlock()) {
|
|
14176
|
+
const blocked = this.dag.getBlocked();
|
|
14177
|
+
(this.events?.emit)("autonomous:deadlock", { blocked });
|
|
14178
|
+
this._emit({ type: "deadlock:detected", goalId: blocked[0]?.id ?? "", text: `Deadlock detected: ${blocked.map((n) => n.id).join(", ")}` });
|
|
14179
|
+
}
|
|
14180
|
+
break;
|
|
14181
|
+
}
|
|
14055
14182
|
const decision = await this.brain.decideAuto({
|
|
14056
14183
|
id: randomUUID(),
|
|
14057
14184
|
source: "system",
|
|
14058
14185
|
decisionType: "prioritize_goals",
|
|
14059
|
-
question: `What should we work on next? Open goals: ${
|
|
14186
|
+
question: `What should we work on next? Open goals: ${dispatchable.map((g) => g.title).join(", ")}`,
|
|
14060
14187
|
context: {
|
|
14061
|
-
goals:
|
|
14188
|
+
goals: dispatchable,
|
|
14062
14189
|
fleetStatus: this._fleetStatus()
|
|
14063
14190
|
},
|
|
14064
|
-
options: this._goalToOptions(
|
|
14191
|
+
options: this._goalToOptions(dispatchable),
|
|
14065
14192
|
risk: "medium",
|
|
14066
14193
|
requiresConsensus: false
|
|
14067
14194
|
});
|
|
@@ -14108,6 +14235,55 @@ var AutonomousCoordinator = class {
|
|
|
14108
14235
|
this.running = false;
|
|
14109
14236
|
console.error(`[AutonomousCoordinator] stop signal received \u2014 shutting down (iteration ${this.iterationCount})`);
|
|
14110
14237
|
}
|
|
14238
|
+
/**
|
|
14239
|
+
* Report that a terminal worker (not a Director subagent) completed a claimed
|
|
14240
|
+
* task. This updates the auction, DAG, publishes a task-result fact, and
|
|
14241
|
+
* extracts follow-up goals — the same path as subagent completion.
|
|
14242
|
+
*/
|
|
14243
|
+
async reportTaskCompletion(taskId, result) {
|
|
14244
|
+
this._handledBySubagent.add(taskId);
|
|
14245
|
+
await this._completeTask(taskId, result);
|
|
14246
|
+
}
|
|
14247
|
+
/**
|
|
14248
|
+
* Report that a terminal worker failed a claimed task.
|
|
14249
|
+
*/
|
|
14250
|
+
async reportTaskFailure(taskId, error) {
|
|
14251
|
+
this._handledBySubagent.add(taskId);
|
|
14252
|
+
await this._failTask(taskId, error);
|
|
14253
|
+
}
|
|
14254
|
+
/**
|
|
14255
|
+
* Reload the KnowledgeGraph from disk and sync the in-memory DAG with any
|
|
14256
|
+
* changes published by other terminal sessions. New goals are added to the
|
|
14257
|
+
* DAG; existing goals whose status changed (e.g. completed by another
|
|
14258
|
+
* terminal) are transitioned accordingly.
|
|
14259
|
+
*
|
|
14260
|
+
* Safe to call at any time — also used internally by the run loop.
|
|
14261
|
+
*/
|
|
14262
|
+
async syncFromGraph() {
|
|
14263
|
+
await this.graph.load();
|
|
14264
|
+
this._rebuildDagFromGraph();
|
|
14265
|
+
this._syncDagStatuses();
|
|
14266
|
+
}
|
|
14267
|
+
_syncDagStatuses() {
|
|
14268
|
+
const goals = this.graph.getGoals({});
|
|
14269
|
+
for (const goal of goals) {
|
|
14270
|
+
const dagNode = this.dag.getNode(goal.id);
|
|
14271
|
+
if (!dagNode) continue;
|
|
14272
|
+
if (goal.status === "done" && dagNode.status !== "done" && dagNode.status !== "failed") {
|
|
14273
|
+
this.dag.complete(goal.id, goal.result ?? "Completed by another session");
|
|
14274
|
+
} else if (goal.status === "failed" && dagNode.status !== "failed" && dagNode.status !== "done") {
|
|
14275
|
+
this.dag.fail(goal.id, goal.result ?? "Failed by another session");
|
|
14276
|
+
} else if (goal.status === "in_progress" && (dagNode.status === "ready" || dagNode.status === "pending")) {
|
|
14277
|
+
this.dag.start(goal.id, goal.assignee ?? "another-session");
|
|
14278
|
+
}
|
|
14279
|
+
}
|
|
14280
|
+
}
|
|
14281
|
+
async _maybeSyncFromGraph() {
|
|
14282
|
+
const now = Date.now();
|
|
14283
|
+
if (now - this.lastSyncAt < _AutonomousCoordinator.SYNC_INTERVAL_MS) return;
|
|
14284
|
+
this.lastSyncAt = now;
|
|
14285
|
+
await this.syncFromGraph();
|
|
14286
|
+
}
|
|
14111
14287
|
/**
|
|
14112
14288
|
* Tear down the coordinator for good: stop the loop and detach all FleetBus
|
|
14113
14289
|
* subscriptions (this coordinator's + the auctioneer's) plus any open bid
|
|
@@ -14204,6 +14380,65 @@ ${input.detail}`
|
|
|
14204
14380
|
return goal;
|
|
14205
14381
|
}
|
|
14206
14382
|
// ── Private ───────────────────────────────────────────────────────────
|
|
14383
|
+
_waitForDagProgress(timeoutMs) {
|
|
14384
|
+
const before = this._dagProgressKey();
|
|
14385
|
+
if (this.dag.isDone()) return Promise.resolve();
|
|
14386
|
+
return new Promise((resolve3) => {
|
|
14387
|
+
let off;
|
|
14388
|
+
const timer = setTimeout(() => {
|
|
14389
|
+
off?.();
|
|
14390
|
+
resolve3();
|
|
14391
|
+
}, timeoutMs);
|
|
14392
|
+
off = this.dag.onEvent(() => {
|
|
14393
|
+
if (this._dagProgressKey() === before) return;
|
|
14394
|
+
clearTimeout(timer);
|
|
14395
|
+
off?.();
|
|
14396
|
+
resolve3();
|
|
14397
|
+
});
|
|
14398
|
+
});
|
|
14399
|
+
}
|
|
14400
|
+
_dagProgressKey() {
|
|
14401
|
+
const s = this.dag.stats();
|
|
14402
|
+
return `${s.pending}:${s.ready}:${s.running}:${s.done}:${s.failed}:${s.skipped}`;
|
|
14403
|
+
}
|
|
14404
|
+
_rebuildDagFromGraph() {
|
|
14405
|
+
const goals = this.graph.getGoals({});
|
|
14406
|
+
const knownGoalIds = new Set(goals.map((goal) => goal.id));
|
|
14407
|
+
const added = /* @__PURE__ */ new Set();
|
|
14408
|
+
const remaining = new Map(goals.map((goal) => [goal.id, goal]));
|
|
14409
|
+
while (remaining.size > 0) {
|
|
14410
|
+
let progressed = false;
|
|
14411
|
+
for (const [id, goal] of Array.from(remaining.entries())) {
|
|
14412
|
+
const deps = goal.blockedBy.filter((depId) => knownGoalIds.has(depId));
|
|
14413
|
+
if (!deps.every((depId) => added.has(depId))) continue;
|
|
14414
|
+
this._rebuildDagNode(goal, deps);
|
|
14415
|
+
added.add(id);
|
|
14416
|
+
remaining.delete(id);
|
|
14417
|
+
progressed = true;
|
|
14418
|
+
}
|
|
14419
|
+
if (!progressed) {
|
|
14420
|
+
for (const [id, goal] of Array.from(remaining.entries())) {
|
|
14421
|
+
this._rebuildDagNode(goal, []);
|
|
14422
|
+
added.add(id);
|
|
14423
|
+
remaining.delete(id);
|
|
14424
|
+
}
|
|
14425
|
+
}
|
|
14426
|
+
}
|
|
14427
|
+
}
|
|
14428
|
+
_rebuildDagNode(goal, deps) {
|
|
14429
|
+
this.dag.addNode(goal.id, goal.description, deps, { tags: goal.tags });
|
|
14430
|
+
if (goal.status === "in_progress") {
|
|
14431
|
+
this.dag.start(goal.id, goal.assignee ?? "unknown");
|
|
14432
|
+
return;
|
|
14433
|
+
}
|
|
14434
|
+
if (goal.status === "done") {
|
|
14435
|
+
this.dag.complete(goal.id, goal.result ?? "Persisted completion");
|
|
14436
|
+
return;
|
|
14437
|
+
}
|
|
14438
|
+
if (goal.status === "failed") {
|
|
14439
|
+
this.dag.fail(goal.id, goal.result ?? "Persisted failure");
|
|
14440
|
+
}
|
|
14441
|
+
}
|
|
14207
14442
|
async _decomposeGoal(goalText) {
|
|
14208
14443
|
const category = this._inferCategory(goalText);
|
|
14209
14444
|
const subGoals = [];
|
|
@@ -14240,13 +14475,7 @@ ${input.detail}`
|
|
|
14240
14475
|
const goalNode = this.graph.get(goalId);
|
|
14241
14476
|
if (!goalNode) return;
|
|
14242
14477
|
const title = goalNode.title || dagNode.description;
|
|
14243
|
-
|
|
14244
|
-
title,
|
|
14245
|
-
description: goalNode.description,
|
|
14246
|
-
priority: this._dagPriorityToGoal(dagNode.priority),
|
|
14247
|
-
tags: dagNode.tags
|
|
14248
|
-
});
|
|
14249
|
-
this._emit({ type: "task:ready", goalId, taskId, title });
|
|
14478
|
+
this._emit({ type: "task:ready", goalId, taskId: goalId, title });
|
|
14250
14479
|
if (this.director) {
|
|
14251
14480
|
const config = {
|
|
14252
14481
|
name: `worker-${goalId.slice(0, 8)}`,
|
|
@@ -14256,7 +14485,7 @@ ${input.detail}`
|
|
|
14256
14485
|
// 10 minutes per goal
|
|
14257
14486
|
};
|
|
14258
14487
|
const subagentId = await this.director.spawn(config);
|
|
14259
|
-
await this.auction.claim(
|
|
14488
|
+
await this.auction.claim(goalId, subagentId, config.name);
|
|
14260
14489
|
await this.director.assign({
|
|
14261
14490
|
id: goalId,
|
|
14262
14491
|
subagentId,
|
|
@@ -14264,6 +14493,78 @@ ${input.detail}`
|
|
|
14264
14493
|
});
|
|
14265
14494
|
}
|
|
14266
14495
|
}
|
|
14496
|
+
_stringifyTaskResult(result) {
|
|
14497
|
+
if (typeof result === "string" && result.trim()) return result.trim();
|
|
14498
|
+
if (result === void 0 || result === null) return "Subagent completed successfully";
|
|
14499
|
+
try {
|
|
14500
|
+
return JSON.stringify(result);
|
|
14501
|
+
} catch {
|
|
14502
|
+
return String(result);
|
|
14503
|
+
}
|
|
14504
|
+
}
|
|
14505
|
+
async _completeTask(taskId, result) {
|
|
14506
|
+
await this.auction.complete(taskId, result);
|
|
14507
|
+
if (this.dag.getNode(taskId)) {
|
|
14508
|
+
this.dag.complete(taskId, result);
|
|
14509
|
+
}
|
|
14510
|
+
await this._publishTaskResultFact(taskId, result);
|
|
14511
|
+
await this._createFollowUpGoalsFromResult(taskId, result);
|
|
14512
|
+
this._emit({ type: "task:completed", goalId: taskId, taskId, text: result });
|
|
14513
|
+
}
|
|
14514
|
+
async _publishTaskResultFact(taskId, result) {
|
|
14515
|
+
const key = `task-result:${taskId}`;
|
|
14516
|
+
if (this.graph.getFacts({ category: "quality" }).some((fact2) => fact2.key === key)) return;
|
|
14517
|
+
const goal = this.graph.get(taskId);
|
|
14518
|
+
const subject = goal?.type === "goal" ? `Task completed: ${goal.title}` : `Task completed: ${taskId}`;
|
|
14519
|
+
const fact = await this.graph.add({
|
|
14520
|
+
type: "fact",
|
|
14521
|
+
category: "quality",
|
|
14522
|
+
subject,
|
|
14523
|
+
detail: result,
|
|
14524
|
+
discoveredBy: this.selfAgentId,
|
|
14525
|
+
discoveredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14526
|
+
tags: ["task-result", "autonomous-coordinator"],
|
|
14527
|
+
key,
|
|
14528
|
+
related: [taskId]
|
|
14529
|
+
});
|
|
14530
|
+
this._emit({ type: "knowledge:added", knowledgeId: fact.id, title: subject, text: result });
|
|
14531
|
+
}
|
|
14532
|
+
async _createFollowUpGoalsFromResult(taskId, result) {
|
|
14533
|
+
const followUps = this._extractFollowUps(result);
|
|
14534
|
+
if (followUps.length === 0) return;
|
|
14535
|
+
const existing = this.graph.getGoals({});
|
|
14536
|
+
for (const title of followUps) {
|
|
14537
|
+
if (existing.some((goal2) => goal2.title === title && goal2.tags.includes("follow-up"))) continue;
|
|
14538
|
+
const goal = await this.createGoal({
|
|
14539
|
+
title,
|
|
14540
|
+
description: title,
|
|
14541
|
+
priority: "medium",
|
|
14542
|
+
tags: ["follow-up", "task-result", taskId]
|
|
14543
|
+
});
|
|
14544
|
+
this._emit({ type: "goal:added", goalId: goal.id, title: goal.title, text: goal.description });
|
|
14545
|
+
}
|
|
14546
|
+
}
|
|
14547
|
+
_extractFollowUps(result) {
|
|
14548
|
+
const found = [];
|
|
14549
|
+
for (const line of result.split(/\r?\n/)) {
|
|
14550
|
+
const match = /^\s*(?:[-*]\s*)?(?:NEXT|TODO|FOLLOW-?UP):\s*(.+)$/i.exec(line);
|
|
14551
|
+
const text = match?.[1]?.trim();
|
|
14552
|
+
if (!text || found.includes(text)) continue;
|
|
14553
|
+
found.push(text);
|
|
14554
|
+
if (found.length >= 5) break;
|
|
14555
|
+
}
|
|
14556
|
+
return found;
|
|
14557
|
+
}
|
|
14558
|
+
async _failTask(taskId, error) {
|
|
14559
|
+
await this.auction.fail(taskId, error);
|
|
14560
|
+
this._recordTaskFailed(taskId, error);
|
|
14561
|
+
}
|
|
14562
|
+
_recordTaskFailed(taskId, error) {
|
|
14563
|
+
if (this.dag.getNode(taskId)) {
|
|
14564
|
+
this.dag.fail(taskId, error);
|
|
14565
|
+
}
|
|
14566
|
+
this._emit({ type: "goal:failed", goalId: taskId, text: error });
|
|
14567
|
+
}
|
|
14267
14568
|
async _handlePendingChange(change) {
|
|
14268
14569
|
const result = this.consensus.getStatus(change.id);
|
|
14269
14570
|
if (result?.outcome !== "pending") return;
|
|
@@ -14304,16 +14605,15 @@ ${input.detail}`
|
|
|
14304
14605
|
_onSubagentTerminated(e) {
|
|
14305
14606
|
const payload = e.payload;
|
|
14306
14607
|
const subagentId = payload?.subagentId ?? e.subagentId;
|
|
14307
|
-
const
|
|
14308
|
-
const
|
|
14608
|
+
const rawStatus = payload?.stopReason ?? payload?.status ?? "unknown";
|
|
14609
|
+
const succeeded = rawStatus === "end_turn" || rawStatus === "ok" || rawStatus === "success";
|
|
14610
|
+
const tasks = payload?.taskId ? this.auction.getTasksForAgent(subagentId).filter((task) => task.id === payload.taskId) : this.auction.getTasksForAgent(subagentId);
|
|
14309
14611
|
for (const task of tasks) {
|
|
14310
14612
|
this._handledBySubagent.add(task.id);
|
|
14311
|
-
if (
|
|
14312
|
-
void this.
|
|
14313
|
-
this._emit({ type: "task:completed", goalId: task.id, taskId: task.id, text: "Subagent completed successfully" });
|
|
14613
|
+
if (succeeded) {
|
|
14614
|
+
void this._completeTask(task.id, this._stringifyTaskResult(payload?.result));
|
|
14314
14615
|
} else {
|
|
14315
|
-
void this.
|
|
14316
|
-
this._emit({ type: "goal:failed", goalId: task.id, text: `Subagent terminated: ${stopReason}` });
|
|
14616
|
+
void this._failTask(task.id, `Subagent terminated: ${rawStatus}`);
|
|
14317
14617
|
}
|
|
14318
14618
|
}
|
|
14319
14619
|
}
|
|
@@ -14348,12 +14648,6 @@ ${input.detail}`
|
|
|
14348
14648
|
_optionToGoal(optionId) {
|
|
14349
14649
|
return this.graph.get(optionId);
|
|
14350
14650
|
}
|
|
14351
|
-
_dagPriorityToGoal(p) {
|
|
14352
|
-
if (p <= 1) return "critical";
|
|
14353
|
-
if (p <= 2) return "high";
|
|
14354
|
-
if (p <= 4) return "medium";
|
|
14355
|
-
return "low";
|
|
14356
|
-
}
|
|
14357
14651
|
async _mailboxBroadcast(msg) {
|
|
14358
14652
|
if (!this.mailbox) return;
|
|
14359
14653
|
try {
|