@wrongstack/core 0.264.0 → 0.267.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-bridge-D8sa1vtv.d.ts → agent-bridge-STJ3JwwK.d.ts} +1 -1
- package/dist/{agent-subagent-runner-c9DLkaas.d.ts → agent-subagent-runner-CzPGP3jA.d.ts} +131 -11
- package/dist/{brain-O1IdKPaK.d.ts → brain-Cdg77tVN.d.ts} +103 -2
- package/dist/{compactor-BBy0rCtB.d.ts → compactor-iMZ84CXq.d.ts} +19 -1
- package/dist/{config-Dz2F3H2K.d.ts → config-Du3pYYln.d.ts} +132 -13
- package/dist/{context-BGSpZNSE.d.ts → context-dT5Ueund.d.ts} +90 -12
- package/dist/coordination/index.d.ts +78 -22
- package/dist/coordination/index.js +695 -273
- package/dist/coordination/index.js.map +1 -1
- package/dist/{default-config-CXsDvOmP.d.ts → default-config-B0cj-Hry.d.ts} +11 -1
- package/dist/defaults/index.d.ts +28 -28
- package/dist/defaults/index.js +2327 -965
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +16 -16
- package/dist/execution/index.js +1500 -371
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +2 -2
- package/dist/execution/prompt-enhancer.js +1 -1
- package/dist/execution/prompt-enhancer.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/{goal-preamble-DzjFuN3p.d.ts → goal-preamble-SulMTowG.d.ts} +33 -12
- package/dist/{goal-store-CxWmCGbH.d.ts → goal-store-CABDwdFE.d.ts} +1 -1
- package/dist/{index-CbLSI66_.d.ts → index-Bms0m4oy.d.ts} +5 -5
- package/dist/{index-CYIQrXVF.d.ts → index-DtCVWel4.d.ts} +8 -8
- package/dist/index-IEuxQd-E.d.ts +82 -0
- package/dist/index.d.ts +261 -57
- package/dist/index.js +4799 -2212
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +84 -9
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +9 -9
- package/dist/kernel/index.js +1 -1
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mcp-servers-DC4QRPUI.d.ts → mcp-servers-C2cBTxUR.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +104 -31
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-B_siPxqN.d.ts → models-registry-BqGZNJQ-.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-CK5Jdj9K.d.ts → multi-agent-coordinator-B8R43uPz.d.ts} +1 -1
- package/dist/{null-fleet-bus-DgvD4SCO.d.ts → null-fleet-bus-CnXa5oTH.d.ts} +14 -9
- package/dist/observability/index.d.ts +2 -2
- package/dist/{parallel-eternal-engine-bK0JQBR_.d.ts → parallel-eternal-engine-DdNnw9BQ.d.ts} +11 -9
- package/dist/{path-resolver-BPEDlN38.d.ts → path-resolver-COIMLCQL.d.ts} +3 -3
- package/dist/{permission-4yvGmMRB.d.ts → permission-B75JAi3-.d.ts} +1 -1
- package/dist/{permission-policy-C6XpsBOy.d.ts → permission-policy-DlR9eJAM.d.ts} +2 -2
- package/dist/{pipeline-CXCeMz8J.d.ts → pipeline-BfD2k1rT.d.ts} +3 -3
- package/dist/{plan-templates-BvzRBkJc.d.ts → plan-templates-DSIKCXZN.d.ts} +32 -8
- package/dist/provider-model-resolve-BNRsNuJx.d.ts +107 -0
- package/dist/{provider-runner-C5aQpDWE.d.ts → provider-runner-CX7iIvox.d.ts} +3 -3
- package/dist/{retry-policy-CFhdtRzz.d.ts → retry-policy-BilV1ujH.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +286 -105
- package/dist/sdd/index.js.map +1 -1
- package/dist/secret-vault-BAKpgFw_.d.ts +57 -0
- package/dist/{secret-vault-CxiVLbt1.d.ts → secret-vault-gkvEZZfE.d.ts} +43 -4
- package/dist/security/index.d.ts +6 -68
- package/dist/security/index.js +296 -95
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-gIuhRTkN.d.ts → selector-Bc7eWtT3.d.ts} +1 -1
- package/dist/{session-event-bridge-DkvvrpDt.d.ts → session-event-bridge-D-araDEz.d.ts} +1 -1
- package/dist/{session-reader-KdfVwkKP.d.ts → session-reader-D7Dapswh.d.ts} +1 -1
- package/dist/storage/index.d.ts +112 -15
- package/dist/storage/index.js +491 -156
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/index.d.ts +4 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/types/index.d.ts +21 -21
- package/dist/types/index.js +1523 -450
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +455 -407
- package/dist/utils/index.js +2191 -1203
- package/dist/utils/index.js.map +1 -1
- package/dist/{wstack-paths-CJjEwPXn.d.ts → wstack-paths-hOpNLmvf.d.ts} +2 -0
- package/package.json +1 -1
- package/skills/api-design/SKILL.md +1 -1
- package/skills/audit-log/SKILL.md +6 -6
- package/skills/bug-hunter/SKILL.md +5 -5
- package/skills/chimera/SKILL.md +4 -4
- package/skills/docker-deploy/SKILL.md +1 -1
- package/skills/git-flow/SKILL.md +3 -3
- package/skills/multi-agent/SKILL.md +3 -3
- package/skills/node-modern/SKILL.md +1 -0
- package/skills/observability/SKILL.md +2 -2
- package/skills/output-standards/SKILL.md +51 -28
- package/skills/refactor-planner/SKILL.md +3 -3
- package/skills/security-scanner/SKILL.md +4 -3
- package/skills/tech-stack/SKILL.md +1 -2
- package/dist/llm-selector-DzxuZnNz.d.ts +0 -58
- package/dist/secret-vault-BJDY28ev.d.ts +0 -25
|
@@ -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
|
|
@@ -5089,6 +5107,7 @@ function resolveModelMatrix(matrix, role) {
|
|
|
5089
5107
|
|
|
5090
5108
|
// src/coordination/subagent-budget.ts
|
|
5091
5109
|
var TIMEOUT_PREEMPT_FRACTION = 0.85;
|
|
5110
|
+
var DECISION_TIMEOUT_MS = 6e4;
|
|
5092
5111
|
var BudgetExceededError = class extends Error {
|
|
5093
5112
|
kind;
|
|
5094
5113
|
limit;
|
|
@@ -5118,6 +5137,31 @@ var BudgetThresholdSignal = class extends Error {
|
|
|
5118
5137
|
};
|
|
5119
5138
|
var SubagentBudget = class _SubagentBudget {
|
|
5120
5139
|
limits;
|
|
5140
|
+
/** Patch one or more budget limits in-place after construction.
|
|
5141
|
+
* Used by the coordinator watchdog when granting an extension.
|
|
5142
|
+
* All fields are optional — only provided fields are updated.
|
|
5143
|
+
* This is the single write path for limit mutations so that future
|
|
5144
|
+
* validation or side-effects live in one place (M1). */
|
|
5145
|
+
patchLimits(ext) {
|
|
5146
|
+
if (ext.maxIterations !== void 0) {
|
|
5147
|
+
this.limits.maxIterations = ext.maxIterations;
|
|
5148
|
+
}
|
|
5149
|
+
if (ext.maxToolCalls !== void 0) {
|
|
5150
|
+
this.limits.maxToolCalls = ext.maxToolCalls;
|
|
5151
|
+
}
|
|
5152
|
+
if (ext.maxTokens !== void 0) {
|
|
5153
|
+
this.limits.maxTokens = ext.maxTokens;
|
|
5154
|
+
}
|
|
5155
|
+
if (ext.maxCostUsd !== void 0) {
|
|
5156
|
+
this.limits.maxCostUsd = ext.maxCostUsd;
|
|
5157
|
+
}
|
|
5158
|
+
if (ext.timeoutMs !== void 0) {
|
|
5159
|
+
this.limits.timeoutMs = ext.timeoutMs;
|
|
5160
|
+
}
|
|
5161
|
+
if (ext.idleTimeoutMs !== void 0) {
|
|
5162
|
+
this.limits.idleTimeoutMs = ext.idleTimeoutMs;
|
|
5163
|
+
}
|
|
5164
|
+
}
|
|
5121
5165
|
iterations = 0;
|
|
5122
5166
|
toolCalls = 0;
|
|
5123
5167
|
tokenInput = 0;
|
|
@@ -5138,12 +5182,44 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
5138
5182
|
* or hung listener (Director not built / event filter detached mid-run)
|
|
5139
5183
|
* leaves the budget over-limit and never enforces anything.
|
|
5140
5184
|
*/
|
|
5141
|
-
static DECISION_TIMEOUT_MS =
|
|
5185
|
+
static DECISION_TIMEOUT_MS = DECISION_TIMEOUT_MS;
|
|
5142
5186
|
/**
|
|
5143
5187
|
* Injected by the runner when wiring the budget to its EventBus.
|
|
5144
5188
|
* Used to emit `budget.threshold_reached` events in `'auto'` mode.
|
|
5145
5189
|
*/
|
|
5146
5190
|
_events;
|
|
5191
|
+
/**
|
|
5192
|
+
* Guard against dual-path races between the coordinator watchdog
|
|
5193
|
+
* (`executeWithTimeout`) and the budget's own `checkTimeout()`.
|
|
5194
|
+
* Both paths detect `elapsed >= timeoutMs` and can emit
|
|
5195
|
+
* `budget.threshold_reached` for kind `'timeout'` simultaneously.
|
|
5196
|
+
* Set to the current `timeoutMs` ceiling by the coordinator BEFORE
|
|
5197
|
+
* calling `onThreshold`, and cleared after the negotiation resolves.
|
|
5198
|
+
* `checkTimeout()` skips its wall-clock check while this is set so
|
|
5199
|
+
* the coordinator's watchdog is the sole source of wall-clock timeout
|
|
5200
|
+
* events — `checkTimeout()` focuses exclusively on `idle_timeout`.
|
|
5201
|
+
*/
|
|
5202
|
+
_watchdogActive;
|
|
5203
|
+
/** Returns the timeout ceiling currently being negotiated by the watchdog,
|
|
5204
|
+
* or `undefined` when no wall-clock negotiation is in flight.
|
|
5205
|
+
* Used by `executeWithTimeout` to detect a stale lock (M3). */
|
|
5206
|
+
get watchdogActive() {
|
|
5207
|
+
return this._watchdogActive;
|
|
5208
|
+
}
|
|
5209
|
+
/** Called by the coordinator watchdog BEFORE calling `onThreshold` so that
|
|
5210
|
+
* `checkTimeout()` skips its wall-clock check for this ceiling. Prevents
|
|
5211
|
+
* the budget's own `checkTimeout()` from emitting a second
|
|
5212
|
+
* `budget.threshold_reached` event while the watchdog is already
|
|
5213
|
+
* negotiating the same wall-clock deadline (C1). */
|
|
5214
|
+
setWatchdogNegotiation(timeoutMs) {
|
|
5215
|
+
this._watchdogActive = timeoutMs;
|
|
5216
|
+
}
|
|
5217
|
+
/** Clears the watchdog guard after negotiation resolves. Called in the
|
|
5218
|
+
* `finally` block of both the pre-empt and deadline branches so it fires
|
|
5219
|
+
* on every exit path: grant, deny, throw, or error. */
|
|
5220
|
+
clearWatchdogNegotiation() {
|
|
5221
|
+
this._watchdogActive = void 0;
|
|
5222
|
+
}
|
|
5147
5223
|
/**
|
|
5148
5224
|
* Negotiation mode — controls whether a threshold hit tries to emit
|
|
5149
5225
|
* `budget.threshold_reached` and wait for a coordinator decision, or
|
|
@@ -5244,7 +5320,8 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
5244
5320
|
if (this.limits.idleTimeoutMs !== void 0 && idle > this.limits.idleTimeoutMs) {
|
|
5245
5321
|
exceeded.push({ kind: "idle_timeout", used: idle, limit: this.limits.idleTimeoutMs });
|
|
5246
5322
|
}
|
|
5247
|
-
|
|
5323
|
+
const wallOwnedByWatchdog = this._onThreshold !== void 0 && this._watchdogActive === this.limits.timeoutMs;
|
|
5324
|
+
if (this.limits.timeoutMs !== void 0 && elapsedMs > this.limits.timeoutMs && !wallOwnedByWatchdog) {
|
|
5248
5325
|
exceeded.push({ kind: "timeout", used: elapsedMs, limit: this.limits.timeoutMs });
|
|
5249
5326
|
}
|
|
5250
5327
|
}
|
|
@@ -5258,19 +5335,99 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
5258
5335
|
throw new BudgetExceededError(first2.kind, first2.limit, first2.used);
|
|
5259
5336
|
}
|
|
5260
5337
|
const bus = this._events;
|
|
5261
|
-
if (!bus
|
|
5338
|
+
if (!bus) {
|
|
5262
5339
|
const first2 = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
5263
5340
|
throw new BudgetExceededError(first2.kind, first2.limit, first2.used);
|
|
5264
5341
|
}
|
|
5342
|
+
const first = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
5343
|
+
if (bus.hasListenerFor("budget.threshold_reached")) {
|
|
5344
|
+
for (const entry of exceeded) {
|
|
5345
|
+
if (this._pendingNegotiations.has(entry.kind)) continue;
|
|
5346
|
+
this._pendingNegotiations.set(entry.kind, this._negotiateExtension(entry));
|
|
5347
|
+
}
|
|
5348
|
+
const decision = this._pendingNegotiations.get(first.kind);
|
|
5349
|
+
if (!decision) throw new Error(`No pending negotiation for ${first.kind}`);
|
|
5350
|
+
throw new BudgetThresholdSignal(first.kind, first.limit, first.used, decision);
|
|
5351
|
+
}
|
|
5352
|
+
let hardStop = null;
|
|
5265
5353
|
for (const entry of exceeded) {
|
|
5266
5354
|
if (this._pendingNegotiations.has(entry.kind)) continue;
|
|
5267
|
-
const
|
|
5268
|
-
this._pendingNegotiations.set(entry.kind,
|
|
5355
|
+
const marker = Promise.resolve("stop");
|
|
5356
|
+
this._pendingNegotiations.set(entry.kind, marker);
|
|
5357
|
+
void marker.finally(() => this._pendingNegotiations.delete(entry.kind));
|
|
5358
|
+
const sync = this._invokeHandlerSync(entry);
|
|
5359
|
+
if (!sync) hardStop ??= new BudgetExceededError(entry.kind, entry.limit, entry.used);
|
|
5360
|
+
}
|
|
5361
|
+
if (hardStop) throw hardStop;
|
|
5362
|
+
return exceeded;
|
|
5363
|
+
}
|
|
5364
|
+
/**
|
|
5365
|
+
* Invoke `onThreshold` once for `entry` on the NO-LISTENER path and report
|
|
5366
|
+
* whether it decided synchronously. Returns `true` when the handler returned
|
|
5367
|
+
* a synchronous decision (already honored — an `extend` patched the limits),
|
|
5368
|
+
* or `false` when it returned a Promise (async; the caller hard-stops, since
|
|
5369
|
+
* there is no listener to resolve the negotiation). The handler is given the
|
|
5370
|
+
* full info shape (`requestDecision` plus direct `extend`/`deny`) so both
|
|
5371
|
+
* recording handlers and policy handlers work without a wired listener.
|
|
5372
|
+
*/
|
|
5373
|
+
_invokeHandlerSync(entry) {
|
|
5374
|
+
const handler = this._onThreshold;
|
|
5375
|
+
if (!handler) return false;
|
|
5376
|
+
let extendArg;
|
|
5377
|
+
const result = handler({
|
|
5378
|
+
kind: entry.kind,
|
|
5379
|
+
used: entry.used,
|
|
5380
|
+
limit: entry.limit,
|
|
5381
|
+
requestDecision: () => this._busRequestDecision(entry),
|
|
5382
|
+
// Direct hooks for synchronous policy/recording handlers.
|
|
5383
|
+
extend: (extra) => {
|
|
5384
|
+
extendArg = extra;
|
|
5385
|
+
},
|
|
5386
|
+
deny: () => {
|
|
5387
|
+
}
|
|
5388
|
+
});
|
|
5389
|
+
if (result && typeof result.then === "function") return false;
|
|
5390
|
+
if (result === "throw") return false;
|
|
5391
|
+
if (result && typeof result === "object" && "extend" in result) {
|
|
5392
|
+
extendArg = result.extend;
|
|
5269
5393
|
}
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5394
|
+
if (extendArg) this.patchLimits(extendArg);
|
|
5395
|
+
return true;
|
|
5396
|
+
}
|
|
5397
|
+
/**
|
|
5398
|
+
* Emit `budget.threshold_reached` and resolve to the listener's verdict.
|
|
5399
|
+
* Resolves to `'stop'` immediately when there is no listener (or no bus) so
|
|
5400
|
+
* no negotiation can hang and no fallback timer leaks. Mirrors the
|
|
5401
|
+
* coordinator watchdog's own request path so both agree on the no-listener
|
|
5402
|
+
* default.
|
|
5403
|
+
*/
|
|
5404
|
+
_busRequestDecision(entry) {
|
|
5405
|
+
const bus = this._events;
|
|
5406
|
+
if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
|
|
5407
|
+
return Promise.resolve("stop");
|
|
5408
|
+
}
|
|
5409
|
+
return new Promise((resolve3) => {
|
|
5410
|
+
let resolved = false;
|
|
5411
|
+
const respond = (d) => {
|
|
5412
|
+
if (resolved) return;
|
|
5413
|
+
resolved = true;
|
|
5414
|
+
clearTimeout(fallback);
|
|
5415
|
+
resolve3(d);
|
|
5416
|
+
};
|
|
5417
|
+
const fallback = setTimeout(() => respond("stop"), _SubagentBudget.DECISION_TIMEOUT_MS);
|
|
5418
|
+
bus.emit("budget.threshold_reached", {
|
|
5419
|
+
kind: entry.kind,
|
|
5420
|
+
used: entry.used,
|
|
5421
|
+
limit: entry.limit,
|
|
5422
|
+
timeoutMs: _SubagentBudget.DECISION_TIMEOUT_MS,
|
|
5423
|
+
// deny() wins over a same-dispatch extend(): a listener that both grants
|
|
5424
|
+
// and denies (or two listeners disagreeing) is resolved as a stop. The
|
|
5425
|
+
// grant is deferred a microtask so a synchronous deny in the same emit
|
|
5426
|
+
// pre-empts it; async grants still resolve normally.
|
|
5427
|
+
extend: (extra) => queueMicrotask(() => respond({ extend: extra })),
|
|
5428
|
+
deny: () => respond("stop")
|
|
5429
|
+
});
|
|
5430
|
+
});
|
|
5274
5431
|
}
|
|
5275
5432
|
/**
|
|
5276
5433
|
* Per-kind in-flight negotiation Promises. Each budget kind can have its
|
|
@@ -5290,77 +5447,33 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
5290
5447
|
* `{ extend: {} }` — keep going without patching; next overrun fires
|
|
5291
5448
|
* a fresh signal.
|
|
5292
5449
|
*/
|
|
5293
|
-
async _negotiateExtension(
|
|
5450
|
+
async _negotiateExtension(entry) {
|
|
5294
5451
|
if (!this._onThreshold) {
|
|
5295
5452
|
return "stop";
|
|
5296
5453
|
}
|
|
5297
5454
|
try {
|
|
5298
|
-
const first = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
5299
5455
|
const result = this._onThreshold({
|
|
5300
|
-
kind:
|
|
5301
|
-
used:
|
|
5302
|
-
limit:
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
if (resolved) return;
|
|
5312
|
-
resolved = true;
|
|
5313
|
-
resolve3(d);
|
|
5314
|
-
};
|
|
5315
|
-
const fallback = setTimeout(
|
|
5316
|
-
() => respond("stop"),
|
|
5317
|
-
_SubagentBudget.DECISION_TIMEOUT_MS
|
|
5318
|
-
);
|
|
5319
|
-
for (const { kind: kind2, used, limit } of exceeded) {
|
|
5320
|
-
bus.emit("budget.threshold_reached", {
|
|
5321
|
-
kind: kind2,
|
|
5322
|
-
used,
|
|
5323
|
-
limit,
|
|
5324
|
-
timeoutMs: _SubagentBudget.DECISION_TIMEOUT_MS,
|
|
5325
|
-
extend: (extra) => {
|
|
5326
|
-
clearTimeout(fallback);
|
|
5327
|
-
respond({ extend: extra });
|
|
5328
|
-
},
|
|
5329
|
-
deny: () => {
|
|
5330
|
-
clearTimeout(fallback);
|
|
5331
|
-
respond("stop");
|
|
5332
|
-
}
|
|
5333
|
-
});
|
|
5334
|
-
}
|
|
5335
|
-
});
|
|
5456
|
+
kind: entry.kind,
|
|
5457
|
+
used: entry.used,
|
|
5458
|
+
limit: entry.limit,
|
|
5459
|
+
// One event for THIS kind only — each exceeded kind has its own
|
|
5460
|
+
// negotiation (and its own resolve), so there is no cross-kind
|
|
5461
|
+
// first-wins drop and no O(N^2) re-emission.
|
|
5462
|
+
requestDecision: () => this._busRequestDecision(entry),
|
|
5463
|
+
extend: (extra) => {
|
|
5464
|
+
this.patchLimits(extra);
|
|
5465
|
+
},
|
|
5466
|
+
deny: () => {
|
|
5336
5467
|
}
|
|
5337
5468
|
});
|
|
5338
5469
|
if (result === "throw") return "stop";
|
|
5339
5470
|
if (result === "continue") return { extend: {} };
|
|
5340
5471
|
const decision = await result;
|
|
5341
5472
|
if (decision === "stop") return "stop";
|
|
5342
|
-
|
|
5343
|
-
if (ext.maxIterations !== void 0) {
|
|
5344
|
-
this.limits.maxIterations = ext.maxIterations;
|
|
5345
|
-
}
|
|
5346
|
-
if (ext.maxToolCalls !== void 0) {
|
|
5347
|
-
this.limits.maxToolCalls = ext.maxToolCalls;
|
|
5348
|
-
}
|
|
5349
|
-
if (ext.maxTokens !== void 0) {
|
|
5350
|
-
this.limits.maxTokens = ext.maxTokens;
|
|
5351
|
-
}
|
|
5352
|
-
if (ext.maxCostUsd !== void 0) {
|
|
5353
|
-
this.limits.maxCostUsd = ext.maxCostUsd;
|
|
5354
|
-
}
|
|
5355
|
-
if (ext.timeoutMs !== void 0) {
|
|
5356
|
-
this.limits.timeoutMs = ext.timeoutMs;
|
|
5357
|
-
}
|
|
5358
|
-
if (ext.idleTimeoutMs !== void 0) {
|
|
5359
|
-
this.limits.idleTimeoutMs = ext.idleTimeoutMs;
|
|
5360
|
-
}
|
|
5473
|
+
this.patchLimits(decision.extend);
|
|
5361
5474
|
return decision;
|
|
5362
5475
|
} finally {
|
|
5363
|
-
this._pendingNegotiations.delete(kind);
|
|
5476
|
+
this._pendingNegotiations.delete(entry.kind);
|
|
5364
5477
|
}
|
|
5365
5478
|
}
|
|
5366
5479
|
recordIteration() {
|
|
@@ -5403,7 +5516,8 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
5403
5516
|
const { timeoutMs, idleTimeoutMs } = this.limits;
|
|
5404
5517
|
if (timeoutMs === void 0 && idleTimeoutMs === void 0) return;
|
|
5405
5518
|
const elapsed = Date.now() - this.startTime;
|
|
5406
|
-
const
|
|
5519
|
+
const wallSkipped = this._onThreshold !== void 0 && this._watchdogActive !== void 0 && timeoutMs !== void 0 && this._watchdogActive === timeoutMs;
|
|
5520
|
+
const wallTripped = wallSkipped ? false : timeoutMs !== void 0 && elapsed > timeoutMs;
|
|
5407
5521
|
const idleTripped = idleTimeoutMs !== void 0 && this.idleMs() > idleTimeoutMs;
|
|
5408
5522
|
if (!wallTripped && !idleTripped) return;
|
|
5409
5523
|
void this.checkLimits(elapsed);
|
|
@@ -6025,6 +6139,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6025
6139
|
terminating = /* @__PURE__ */ new Set();
|
|
6026
6140
|
constructor(config, options = {}) {
|
|
6027
6141
|
super();
|
|
6142
|
+
this.setMaxListeners(0);
|
|
6028
6143
|
this.coordinatorId = config.coordinatorId;
|
|
6029
6144
|
this.config = config;
|
|
6030
6145
|
this.runner = options.runner;
|
|
@@ -6419,7 +6534,13 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6419
6534
|
let result;
|
|
6420
6535
|
budget.start();
|
|
6421
6536
|
try {
|
|
6422
|
-
const outcome = await this.executeWithTimeout(
|
|
6537
|
+
const outcome = await this.executeWithTimeout(
|
|
6538
|
+
this.runner,
|
|
6539
|
+
task,
|
|
6540
|
+
runCtx,
|
|
6541
|
+
budget,
|
|
6542
|
+
subagent.config.preemptFraction
|
|
6543
|
+
);
|
|
6423
6544
|
result = {
|
|
6424
6545
|
subagentId,
|
|
6425
6546
|
taskId: task.id,
|
|
@@ -6446,7 +6567,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6446
6567
|
}
|
|
6447
6568
|
this.recordCompletion(result);
|
|
6448
6569
|
}
|
|
6449
|
-
async executeWithTimeout(runner, task, ctx, budget) {
|
|
6570
|
+
async executeWithTimeout(runner, task, ctx, budget, preemptFraction = TIMEOUT_PREEMPT_FRACTION) {
|
|
6450
6571
|
const initialTimeoutMs = budget.limits.timeoutMs;
|
|
6451
6572
|
const idleLimitMs = budget.limits.idleTimeoutMs;
|
|
6452
6573
|
if (initialTimeoutMs === void 0 && idleLimitMs === void 0) {
|
|
@@ -6454,8 +6575,21 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6454
6575
|
}
|
|
6455
6576
|
const start = Date.now();
|
|
6456
6577
|
let timer = null;
|
|
6457
|
-
let
|
|
6578
|
+
let PreemptState;
|
|
6579
|
+
((PreemptState2) => {
|
|
6580
|
+
PreemptState2["ACTIVE"] = "active";
|
|
6581
|
+
PreemptState2["LOCKED"] = "locked";
|
|
6582
|
+
})(PreemptState || (PreemptState = {}));
|
|
6583
|
+
let preemptedCeiling = null;
|
|
6584
|
+
let preemptState = "active" /* ACTIVE */;
|
|
6585
|
+
let lastGrantActivityTs = -1;
|
|
6458
6586
|
const timeoutPromise = new Promise((_, reject) => {
|
|
6587
|
+
const terminate = (kind, limit, used) => {
|
|
6588
|
+
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
6589
|
+
reject(
|
|
6590
|
+
budget._events?.hasListenerFor("budget.threshold_reached") ? new Error(`subagent stopped: budget ${kind} (limit=${limit}, used=${used})`) : new BudgetExceededError(kind, limit, used)
|
|
6591
|
+
);
|
|
6592
|
+
};
|
|
6459
6593
|
const armFor = (ms) => {
|
|
6460
6594
|
if (timer) clearTimeout(timer);
|
|
6461
6595
|
timer = setTimeout(onTick, Math.max(0, ms));
|
|
@@ -6464,7 +6598,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6464
6598
|
const wallLimit = budget.limits.timeoutMs ?? initialTimeoutMs;
|
|
6465
6599
|
const wallRemaining = initialTimeoutMs === void 0 ? Number.POSITIVE_INFINITY : wallLimit - (Date.now() - start);
|
|
6466
6600
|
const idleRemaining = idleLimitMs === void 0 ? Number.POSITIVE_INFINITY : (budget.limits.idleTimeoutMs ?? idleLimitMs) - budget.idleMs();
|
|
6467
|
-
const preemptRemaining = initialTimeoutMs === void 0 ||
|
|
6601
|
+
const preemptRemaining = initialTimeoutMs === void 0 || preemptedCeiling === wallLimit ? Number.POSITIVE_INFINITY : wallLimit * preemptFraction - (Date.now() - start);
|
|
6468
6602
|
armFor(Math.max(25, Math.min(wallRemaining, idleRemaining, preemptRemaining)));
|
|
6469
6603
|
};
|
|
6470
6604
|
const negotiateTimeout = async (used, limit) => {
|
|
@@ -6474,16 +6608,42 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6474
6608
|
kind: "timeout",
|
|
6475
6609
|
used,
|
|
6476
6610
|
limit,
|
|
6477
|
-
requestDecision: () =>
|
|
6478
|
-
budget._events?.
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6611
|
+
requestDecision: () => {
|
|
6612
|
+
if (!budget._events?.hasListenerFor("budget.threshold_reached")) {
|
|
6613
|
+
return Promise.resolve("stop");
|
|
6614
|
+
}
|
|
6615
|
+
return new Promise((resolveDecision) => {
|
|
6616
|
+
let settled = false;
|
|
6617
|
+
const resolve3 = (d) => {
|
|
6618
|
+
if (settled) return;
|
|
6619
|
+
settled = true;
|
|
6620
|
+
resolveDecision(d);
|
|
6621
|
+
};
|
|
6622
|
+
const fallback = setTimeout(() => resolve3("stop"), DECISION_TIMEOUT_MS);
|
|
6623
|
+
budget._events?.emit("budget.threshold_reached", {
|
|
6624
|
+
kind: "timeout",
|
|
6625
|
+
used,
|
|
6626
|
+
limit,
|
|
6627
|
+
// Informational: the budget's own decision deadline. Listeners may use
|
|
6628
|
+
// this to display a countdown. The coordinator does NOT enforce it —
|
|
6629
|
+
// it is the budget's own `setTimeout(fallback)` that races against
|
|
6630
|
+
// the listener's `extend()`/`deny()` call to guarantee progress.
|
|
6631
|
+
timeoutMs: DECISION_TIMEOUT_MS,
|
|
6632
|
+
// deny() wins over a same-dispatch extend(): defer the grant a
|
|
6633
|
+
// microtask so a synchronous deny in the same emit pre-empts it
|
|
6634
|
+
// (a listener that both grants and denies, or two listeners
|
|
6635
|
+
// disagreeing, resolves as a stop). Async grants still resolve.
|
|
6636
|
+
extend: (extra) => {
|
|
6637
|
+
clearTimeout(fallback);
|
|
6638
|
+
queueMicrotask(() => resolve3({ extend: extra }));
|
|
6639
|
+
},
|
|
6640
|
+
deny: () => {
|
|
6641
|
+
clearTimeout(fallback);
|
|
6642
|
+
resolve3("stop");
|
|
6643
|
+
}
|
|
6644
|
+
});
|
|
6485
6645
|
});
|
|
6486
|
-
}
|
|
6646
|
+
}
|
|
6487
6647
|
});
|
|
6488
6648
|
return typeof result === "string" ? result : await result;
|
|
6489
6649
|
};
|
|
@@ -6494,21 +6654,45 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6494
6654
|
const wallExceeded = wallLimit !== void 0 && elapsed >= wallLimit;
|
|
6495
6655
|
const idleExceeded = idleLimit !== void 0 && budget.idleMs() >= idleLimit;
|
|
6496
6656
|
if (idleExceeded && !wallExceeded) {
|
|
6657
|
+
budget._events?.emit("budget.threshold_reached", {
|
|
6658
|
+
kind: "idle_timeout",
|
|
6659
|
+
used: budget.idleMs(),
|
|
6660
|
+
limit: idleLimit ?? 0,
|
|
6661
|
+
timeoutMs: DECISION_TIMEOUT_MS,
|
|
6662
|
+
extend: () => {
|
|
6663
|
+
},
|
|
6664
|
+
deny: () => {
|
|
6665
|
+
}
|
|
6666
|
+
});
|
|
6497
6667
|
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
6498
|
-
reject(new BudgetExceededError("
|
|
6668
|
+
reject(new BudgetExceededError("idle_timeout", idleLimit ?? 0, budget.idleMs()));
|
|
6499
6669
|
return;
|
|
6500
6670
|
}
|
|
6501
|
-
if (wallLimit !== void 0 && !wallExceeded && budget.onThreshold &&
|
|
6671
|
+
if (wallLimit !== void 0 && !wallExceeded && budget.onThreshold && preemptState === "active" /* ACTIVE */ && elapsed >= wallLimit * preemptFraction) {
|
|
6672
|
+
const activityTs = Date.now() - budget.idleMs();
|
|
6673
|
+
if (activityTs <= lastGrantActivityTs) {
|
|
6674
|
+
preemptState = "locked" /* LOCKED */;
|
|
6675
|
+
preemptedCeiling = wallLimit;
|
|
6676
|
+
scheduleNext();
|
|
6677
|
+
return;
|
|
6678
|
+
}
|
|
6679
|
+
budget.setWatchdogNegotiation(wallLimit);
|
|
6502
6680
|
try {
|
|
6503
6681
|
const decision = await negotiateTimeout(elapsed, wallLimit);
|
|
6504
6682
|
if (typeof decision !== "string" && decision.extend.timeoutMs !== void 0) {
|
|
6505
|
-
budget.
|
|
6506
|
-
|
|
6683
|
+
budget.patchLimits({ timeoutMs: decision.extend.timeoutMs });
|
|
6684
|
+
lastGrantActivityTs = Date.now() - budget.idleMs();
|
|
6685
|
+
preemptState = "active" /* ACTIVE */;
|
|
6686
|
+
preemptedCeiling = null;
|
|
6507
6687
|
} else {
|
|
6508
|
-
|
|
6688
|
+
preemptState = "locked" /* LOCKED */;
|
|
6689
|
+
preemptedCeiling = wallLimit;
|
|
6509
6690
|
}
|
|
6510
6691
|
} catch {
|
|
6511
|
-
|
|
6692
|
+
preemptState = "locked" /* LOCKED */;
|
|
6693
|
+
preemptedCeiling = wallLimit;
|
|
6694
|
+
} finally {
|
|
6695
|
+
budget.clearWatchdogNegotiation();
|
|
6512
6696
|
}
|
|
6513
6697
|
scheduleNext();
|
|
6514
6698
|
return;
|
|
@@ -6523,26 +6707,41 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6523
6707
|
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
6524
6708
|
return;
|
|
6525
6709
|
}
|
|
6710
|
+
budget.setWatchdogNegotiation(limit);
|
|
6526
6711
|
try {
|
|
6527
6712
|
const decision = await negotiateTimeout(elapsed, limit);
|
|
6528
|
-
if (decision === "
|
|
6529
|
-
|
|
6713
|
+
if (decision === "throw") {
|
|
6714
|
+
terminate("timeout", limit, elapsed);
|
|
6715
|
+
return;
|
|
6716
|
+
}
|
|
6717
|
+
if (decision === "continue") {
|
|
6718
|
+
preemptState = "locked" /* LOCKED */;
|
|
6719
|
+
preemptedCeiling = wallLimit;
|
|
6530
6720
|
armFor(Math.max(1e3, limit));
|
|
6531
6721
|
return;
|
|
6532
6722
|
}
|
|
6723
|
+
if (decision === "stop") {
|
|
6724
|
+
terminate("timeout", limit, elapsed);
|
|
6725
|
+
return;
|
|
6726
|
+
}
|
|
6533
6727
|
if (decision.extend.timeoutMs !== void 0) {
|
|
6534
|
-
budget.
|
|
6535
|
-
|
|
6728
|
+
budget.patchLimits({ timeoutMs: decision.extend.timeoutMs });
|
|
6729
|
+
lastGrantActivityTs = Date.now() - budget.idleMs();
|
|
6730
|
+
preemptState = "active" /* ACTIVE */;
|
|
6731
|
+
preemptedCeiling = null;
|
|
6536
6732
|
scheduleNext();
|
|
6537
6733
|
return;
|
|
6538
6734
|
}
|
|
6539
|
-
|
|
6540
|
-
|
|
6735
|
+
terminate("timeout", limit, elapsed);
|
|
6736
|
+
return;
|
|
6541
6737
|
} catch (err) {
|
|
6542
6738
|
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
6543
6739
|
reject(
|
|
6544
6740
|
err instanceof BudgetExceededError ? err : new BudgetExceededError("timeout", limit, elapsed)
|
|
6545
6741
|
);
|
|
6742
|
+
return;
|
|
6743
|
+
} finally {
|
|
6744
|
+
budget.clearWatchdogNegotiation();
|
|
6546
6745
|
}
|
|
6547
6746
|
};
|
|
6548
6747
|
scheduleNext();
|
|
@@ -6815,6 +7014,8 @@ var Director = class _Director {
|
|
|
6815
7014
|
sessionsRoot;
|
|
6816
7015
|
/** Director run id for JSONL path resolution. */
|
|
6817
7016
|
directorRunId;
|
|
7017
|
+
/** Optional logger for structured logging. Falls back to noop when omitted. */
|
|
7018
|
+
logger;
|
|
6818
7019
|
/** Resolves task descriptions back from `assign()` so completion events
|
|
6819
7020
|
* can also carry a human-readable title. */
|
|
6820
7021
|
taskDescriptions = /* @__PURE__ */ new Map();
|
|
@@ -6903,6 +7104,7 @@ var Director = class _Director {
|
|
|
6903
7104
|
opts.checkpointDebounceMs ?? 250
|
|
6904
7105
|
) : null;
|
|
6905
7106
|
this.fleetManager = opts.fleetManager;
|
|
7107
|
+
this.logger = opts.logger;
|
|
6906
7108
|
if (this.sharedScratchpadPath) {
|
|
6907
7109
|
void fsp6.mkdir(this.sharedScratchpadPath, { recursive: true }).catch((err) => this.logShutdownError("shared_scratchpad_mkdir", err));
|
|
6908
7110
|
}
|
|
@@ -7202,7 +7404,10 @@ var Director = class _Director {
|
|
|
7202
7404
|
entry.session.cancel(reason);
|
|
7203
7405
|
for (const [_role, subagentId] of entry.session.getSubagentIds()) {
|
|
7204
7406
|
this.coordinator.stop(subagentId).catch((err) => {
|
|
7205
|
-
|
|
7407
|
+
this.logger?.debug(`stop subagent ${subagentId} failed (may have already completed)`, {
|
|
7408
|
+
subagentId,
|
|
7409
|
+
err: err instanceof Error ? err.message : String(err)
|
|
7410
|
+
});
|
|
7206
7411
|
});
|
|
7207
7412
|
}
|
|
7208
7413
|
}
|
|
@@ -7262,7 +7467,7 @@ var Director = class _Director {
|
|
|
7262
7467
|
* Caller-supplied `priceLookup` is optional but recommended — without
|
|
7263
7468
|
* it the `cost` column in `usage.snapshot()` stays at 0.
|
|
7264
7469
|
*/
|
|
7265
|
-
async spawn(
|
|
7470
|
+
async spawn(callerConfig, priceLookup) {
|
|
7266
7471
|
if (this.workCompleteFlag) {
|
|
7267
7472
|
throw new FleetSpawnBudgetError(
|
|
7268
7473
|
"max_spawns",
|
|
@@ -7271,6 +7476,7 @@ var Director = class _Director {
|
|
|
7271
7476
|
"workComplete() has been called \u2014 director closed further spawning"
|
|
7272
7477
|
);
|
|
7273
7478
|
}
|
|
7479
|
+
const config = { ...callerConfig };
|
|
7274
7480
|
if (!config.model && this.modelMatrix) {
|
|
7275
7481
|
const matrix = typeof this.modelMatrix === "function" ? this.modelMatrix() : this.modelMatrix;
|
|
7276
7482
|
const entry = resolveModelMatrix(matrix, config.role);
|
|
@@ -8516,11 +8722,34 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8516
8722
|
dir;
|
|
8517
8723
|
events;
|
|
8518
8724
|
secretScrubber;
|
|
8725
|
+
/**
|
|
8726
|
+
* In-memory cache for load() results, keyed by session ID. The cache is
|
|
8727
|
+
* invalidated when the file's mtimeMs or size changes (indicating the
|
|
8728
|
+
* file was written to). This eliminates redundant full-file reads and
|
|
8729
|
+
* JSON parses when the same session is loaded multiple times within the
|
|
8730
|
+
* store's lifetime (e.g., webui session detail views, list() fallbacks).
|
|
8731
|
+
*
|
|
8732
|
+
* Max size is capped to prevent unbounded memory growth in long-running
|
|
8733
|
+
* processes. When the limit is reached, the oldest entry is evicted.
|
|
8734
|
+
*/
|
|
8735
|
+
_loadCache = /* @__PURE__ */ new Map();
|
|
8736
|
+
static LOAD_CACHE_MAX_ENTRIES = 50;
|
|
8519
8737
|
constructor(opts) {
|
|
8520
8738
|
this.dir = opts.dir;
|
|
8521
8739
|
this.events = opts.events;
|
|
8522
8740
|
this.secretScrubber = opts.secretScrubber;
|
|
8523
8741
|
}
|
|
8742
|
+
/**
|
|
8743
|
+
* Clear the load() cache. Useful for testing or when the caller knows
|
|
8744
|
+
* the file has changed externally (e.g., another process wrote to it).
|
|
8745
|
+
*/
|
|
8746
|
+
clearLoadCache(sessionId) {
|
|
8747
|
+
if (sessionId !== void 0) {
|
|
8748
|
+
this._loadCache.delete(sessionId);
|
|
8749
|
+
} else {
|
|
8750
|
+
this._loadCache.clear();
|
|
8751
|
+
}
|
|
8752
|
+
}
|
|
8524
8753
|
// ── Storage event helpers ───────────────────────────────────────────────────
|
|
8525
8754
|
emitRead(sessionId, filePath, operation, outcome, durationMs, error) {
|
|
8526
8755
|
this.events?.emit("storage.read", {
|
|
@@ -8663,7 +8892,20 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8663
8892
|
const t0 = Date.now();
|
|
8664
8893
|
let outcome = "success";
|
|
8665
8894
|
let errorMsg;
|
|
8895
|
+
let cacheHit = false;
|
|
8666
8896
|
try {
|
|
8897
|
+
let stat6;
|
|
8898
|
+
try {
|
|
8899
|
+
const s = await fsp6.stat(file);
|
|
8900
|
+
stat6 = { mtimeMs: s.mtimeMs, size: s.size };
|
|
8901
|
+
} catch (err) {
|
|
8902
|
+
throw err;
|
|
8903
|
+
}
|
|
8904
|
+
const cached = this._loadCache.get(id);
|
|
8905
|
+
if (cached && cached.mtimeMs === stat6.mtimeMs && cached.size === stat6.size) {
|
|
8906
|
+
cacheHit = true;
|
|
8907
|
+
return cached.data;
|
|
8908
|
+
}
|
|
8667
8909
|
const raw = await fsp6.readFile(file, "utf8");
|
|
8668
8910
|
const lines = raw.split("\n").filter((l) => l.trim());
|
|
8669
8911
|
const events = [];
|
|
@@ -8679,13 +8921,30 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8679
8921
|
const meta = this.metaFromEvents(id, events);
|
|
8680
8922
|
const { messages, usage } = this.replay(events, id);
|
|
8681
8923
|
const toolCallEnds = extractToolCallEnds(events);
|
|
8682
|
-
|
|
8924
|
+
const data = { metadata: meta, events, messages, usage, toolCallEnds };
|
|
8925
|
+
if (this._loadCache.size >= _DefaultSessionStore.LOAD_CACHE_MAX_ENTRIES) {
|
|
8926
|
+
const oldest = this._loadCache.keys().next().value;
|
|
8927
|
+
if (oldest !== void 0) {
|
|
8928
|
+
this._loadCache.delete(oldest);
|
|
8929
|
+
}
|
|
8930
|
+
}
|
|
8931
|
+
this._loadCache.set(id, { mtimeMs: stat6.mtimeMs, size: stat6.size, data });
|
|
8932
|
+
return data;
|
|
8683
8933
|
} catch (err) {
|
|
8684
8934
|
outcome = "failure";
|
|
8685
8935
|
errorMsg = toErrorMessage(err);
|
|
8686
8936
|
throw err;
|
|
8687
8937
|
} finally {
|
|
8688
8938
|
this.emitRead(id, file, "load", outcome, Date.now() - t0, errorMsg);
|
|
8939
|
+
if (cacheHit) {
|
|
8940
|
+
this.events?.emit("storage.cache_hit", {
|
|
8941
|
+
sessionId: id,
|
|
8942
|
+
store: "session",
|
|
8943
|
+
filePath: file,
|
|
8944
|
+
operation: "load",
|
|
8945
|
+
durationMs: Date.now() - t0
|
|
8946
|
+
});
|
|
8947
|
+
}
|
|
8689
8948
|
}
|
|
8690
8949
|
}
|
|
8691
8950
|
async list(limit = 20) {
|
|
@@ -9692,6 +9951,7 @@ function attachAutoExtend(events, policy = {}) {
|
|
|
9692
9951
|
const extendCounts = /* @__PURE__ */ new Map();
|
|
9693
9952
|
let progress = 0;
|
|
9694
9953
|
let lastTimeoutProgress = -1;
|
|
9954
|
+
let lastSeenKey = null;
|
|
9695
9955
|
const unsubs = [
|
|
9696
9956
|
events.on("tool.executed", () => {
|
|
9697
9957
|
progress++;
|
|
@@ -9701,6 +9961,9 @@ function attachAutoExtend(events, policy = {}) {
|
|
|
9701
9961
|
}),
|
|
9702
9962
|
events.on("budget.threshold_reached", (e) => {
|
|
9703
9963
|
const { kind, limit, extend, deny } = e;
|
|
9964
|
+
const key = `${kind}:${limit}`;
|
|
9965
|
+
if (key === lastSeenKey) return;
|
|
9966
|
+
lastSeenKey = key;
|
|
9704
9967
|
if (kind === "timeout" || kind === "idle_timeout") {
|
|
9705
9968
|
if (progress > lastTimeoutProgress) {
|
|
9706
9969
|
lastTimeoutProgress = progress;
|
|
@@ -10410,7 +10673,8 @@ var BrainMonitor = class {
|
|
|
10410
10673
|
id: "steer",
|
|
10411
10674
|
label: "Steer the agent with corrective guidance",
|
|
10412
10675
|
consequence: "A steer message is injected before its next step.",
|
|
10413
|
-
risk: "low"
|
|
10676
|
+
risk: "low",
|
|
10677
|
+
recommended: true
|
|
10414
10678
|
},
|
|
10415
10679
|
{
|
|
10416
10680
|
id: "continue",
|
|
@@ -10419,9 +10683,9 @@ var BrainMonitor = class {
|
|
|
10419
10683
|
}
|
|
10420
10684
|
],
|
|
10421
10685
|
risk: "medium",
|
|
10422
|
-
//
|
|
10423
|
-
//
|
|
10424
|
-
fallback: "
|
|
10686
|
+
// 'ask_human' routes to the LLM-backed autonomous layer via
|
|
10687
|
+
// createTieredBrainArbiter before any human escalation.
|
|
10688
|
+
fallback: "ask_human"
|
|
10425
10689
|
};
|
|
10426
10690
|
const decision = await this.opts.brain.decide(request);
|
|
10427
10691
|
const intervened = await this.maybeIntervene(kind, request, decision);
|
|
@@ -11518,6 +11782,10 @@ function makeDependencyWatcherConfig(opts) {
|
|
|
11518
11782
|
return {
|
|
11519
11783
|
watchPaths: unique,
|
|
11520
11784
|
debounceMs,
|
|
11785
|
+
dispose() {
|
|
11786
|
+
for (const t of pending.values()) clearTimeout(t);
|
|
11787
|
+
pending.clear();
|
|
11788
|
+
},
|
|
11521
11789
|
async onChange(entry) {
|
|
11522
11790
|
if (entry.event === "delete") return;
|
|
11523
11791
|
if (!matchesPattern(entry.path)) return;
|
|
@@ -11935,6 +12203,10 @@ var KnowledgeGraph = class {
|
|
|
11935
12203
|
pendingDeliveries = /* @__PURE__ */ new Map();
|
|
11936
12204
|
filePath;
|
|
11937
12205
|
graphFilePath;
|
|
12206
|
+
/** Exposed for unit-testing only: read current index contents. */
|
|
12207
|
+
getIndex() {
|
|
12208
|
+
return this.index;
|
|
12209
|
+
}
|
|
11938
12210
|
constructor(sessionDir) {
|
|
11939
12211
|
this.filePath = path5.join(sessionDir, "_knowledge_graph");
|
|
11940
12212
|
this.graphFilePath = path5.join(this.filePath, "graph.jsonl");
|
|
@@ -11947,7 +12219,7 @@ var KnowledgeGraph = class {
|
|
|
11947
12219
|
async add(node) {
|
|
11948
12220
|
const full = { id: randomUUID(), ...node };
|
|
11949
12221
|
this.nodes.set(full.id, full);
|
|
11950
|
-
this.
|
|
12222
|
+
this._addToIndex(full, this._indexKeys(full));
|
|
11951
12223
|
await this._persist(full);
|
|
11952
12224
|
this._deliver(full);
|
|
11953
12225
|
return full;
|
|
@@ -11956,8 +12228,10 @@ var KnowledgeGraph = class {
|
|
|
11956
12228
|
async update(id, patch) {
|
|
11957
12229
|
const existing = this.nodes.get(id);
|
|
11958
12230
|
if (!existing) return null;
|
|
12231
|
+
this._removeFromIndex(existing, this._indexKeys(existing));
|
|
11959
12232
|
const updated = { ...existing, ...patch };
|
|
11960
12233
|
this.nodes.set(id, updated);
|
|
12234
|
+
this._addToIndex(updated, this._indexKeys(updated));
|
|
11961
12235
|
this._deliver(updated);
|
|
11962
12236
|
await this._append(updated);
|
|
11963
12237
|
return updated;
|
|
@@ -12043,15 +12317,10 @@ var KnowledgeGraph = class {
|
|
|
12043
12317
|
return { passed: checks.every((c) => c.passed), checks };
|
|
12044
12318
|
}
|
|
12045
12319
|
// ── Private ────────────────────────────────────────────────────────────
|
|
12046
|
-
|
|
12047
|
-
|
|
12048
|
-
|
|
12049
|
-
|
|
12050
|
-
set = /* @__PURE__ */ new Set();
|
|
12051
|
-
this.index.set(key, set);
|
|
12052
|
-
}
|
|
12053
|
-
set.add(node.id);
|
|
12054
|
-
};
|
|
12320
|
+
/** Pure: compute the set of index keys a node would belong to. */
|
|
12321
|
+
_indexKeys(node) {
|
|
12322
|
+
const keys = /* @__PURE__ */ new Set();
|
|
12323
|
+
const add = (key) => keys.add(key);
|
|
12055
12324
|
add(`type:${node.type}`);
|
|
12056
12325
|
if (node.type === "fact") {
|
|
12057
12326
|
const f = node;
|
|
@@ -12060,6 +12329,8 @@ var KnowledgeGraph = class {
|
|
|
12060
12329
|
add(`by:${f.discoveredBy}`);
|
|
12061
12330
|
for (const tag of f.tags) add(`tag:${tag}`);
|
|
12062
12331
|
add(`key:${f.key}`);
|
|
12332
|
+
add(`subject:${f.subject}`);
|
|
12333
|
+
if (f.detail) add(`detail:${f.detail}`);
|
|
12063
12334
|
}
|
|
12064
12335
|
if (node.type === "goal") {
|
|
12065
12336
|
const g = node;
|
|
@@ -12074,6 +12345,24 @@ var KnowledgeGraph = class {
|
|
|
12074
12345
|
add(`by:${c.proposedBy}`);
|
|
12075
12346
|
for (const g of c.satisfiesGoals) add(`goal:${g}`);
|
|
12076
12347
|
}
|
|
12348
|
+
return keys;
|
|
12349
|
+
}
|
|
12350
|
+
/** Mutate the index: add a node's id to every set for the given keys. */
|
|
12351
|
+
_addToIndex(node, keys) {
|
|
12352
|
+
for (const key of keys) {
|
|
12353
|
+
let set = this.index.get(key);
|
|
12354
|
+
if (!set) {
|
|
12355
|
+
set = /* @__PURE__ */ new Set();
|
|
12356
|
+
this.index.set(key, set);
|
|
12357
|
+
}
|
|
12358
|
+
set.add(node.id);
|
|
12359
|
+
}
|
|
12360
|
+
}
|
|
12361
|
+
/** Remove a node's id from all index sets for the given keys. */
|
|
12362
|
+
_removeFromIndex(node, keys) {
|
|
12363
|
+
for (const key of keys) {
|
|
12364
|
+
this.index.get(key)?.delete(node.id);
|
|
12365
|
+
}
|
|
12077
12366
|
}
|
|
12078
12367
|
_matches(node, f) {
|
|
12079
12368
|
if (f.type && node.type !== f.type) return false;
|
|
@@ -12123,11 +12412,15 @@ var KnowledgeGraph = class {
|
|
|
12123
12412
|
try {
|
|
12124
12413
|
const parsed = JSON.parse(line);
|
|
12125
12414
|
if (parsed.op === "update") {
|
|
12415
|
+
const oldNode = this.nodes.get(parsed.node.id);
|
|
12416
|
+
if (oldNode) {
|
|
12417
|
+
this._removeFromIndex(oldNode, this._indexKeys(oldNode));
|
|
12418
|
+
}
|
|
12126
12419
|
this.nodes.set(parsed.node.id, parsed.node);
|
|
12127
|
-
this.
|
|
12420
|
+
this._addToIndex(parsed.node, this._indexKeys(parsed.node));
|
|
12128
12421
|
} else {
|
|
12129
12422
|
this.nodes.set(parsed.id, parsed);
|
|
12130
|
-
this.
|
|
12423
|
+
this._addToIndex(parsed, this._indexKeys(parsed));
|
|
12131
12424
|
}
|
|
12132
12425
|
} catch {
|
|
12133
12426
|
}
|
|
@@ -12428,7 +12721,7 @@ var TaskDAG = class {
|
|
|
12428
12721
|
if (visited.has(current)) continue;
|
|
12429
12722
|
visited.add(current);
|
|
12430
12723
|
const node = this.nodes.get(current);
|
|
12431
|
-
if (node) stack.push(...node.
|
|
12724
|
+
if (node) stack.push(...node.deps);
|
|
12432
12725
|
}
|
|
12433
12726
|
return false;
|
|
12434
12727
|
}
|
|
@@ -12531,8 +12824,10 @@ var ConsensusProtocol = class {
|
|
|
12531
12824
|
return this._resolve(changeId, change.votes, eligible);
|
|
12532
12825
|
}
|
|
12533
12826
|
// ── Private ───────────────────────────────────────────────────────────
|
|
12534
|
-
_eligibleVoters(
|
|
12535
|
-
return Array.from(this.voters.keys())
|
|
12827
|
+
_eligibleVoters(change) {
|
|
12828
|
+
return Array.from(this.voters.keys()).filter(
|
|
12829
|
+
(agentId) => agentId !== change.proposedBy
|
|
12830
|
+
);
|
|
12536
12831
|
}
|
|
12537
12832
|
_resolve(changeId, votes, eligible) {
|
|
12538
12833
|
const totalEligible = eligible.length;
|
|
@@ -12577,7 +12872,7 @@ var ConsensusProtocol = class {
|
|
|
12577
12872
|
if (!quorumMet) {
|
|
12578
12873
|
return {
|
|
12579
12874
|
changeId,
|
|
12580
|
-
outcome: "
|
|
12875
|
+
outcome: "quorum_not_met",
|
|
12581
12876
|
votes,
|
|
12582
12877
|
approveCount: approve.length,
|
|
12583
12878
|
rejectCount: reject.length,
|
|
@@ -12715,8 +13010,7 @@ var ChangeManager = class {
|
|
|
12715
13010
|
// filled after quality gate
|
|
12716
13011
|
satisfiesGoals: input.satisfiesGoals
|
|
12717
13012
|
});
|
|
12718
|
-
void this._runQualityGate(node.id, input.files).then((gate) => {
|
|
12719
|
-
void this.graph.update(node.id, { qualityGate: gate });
|
|
13013
|
+
void this._runQualityGate(node.id, input.files).then((gate) => this.graph.update(node.id, { qualityGate: gate })).catch(() => {
|
|
12720
13014
|
});
|
|
12721
13015
|
this._emit("change:proposed", { changeId: node.id, title: node.title });
|
|
12722
13016
|
return node;
|
|
@@ -12954,7 +13248,7 @@ var AutonomousBrain = class {
|
|
|
12954
13248
|
}
|
|
12955
13249
|
return { type: "deny", reason: `Brain LLM failed: ${String(err)}` };
|
|
12956
13250
|
}
|
|
12957
|
-
|
|
13251
|
+
this._recordDecision({
|
|
12958
13252
|
id,
|
|
12959
13253
|
decisionType,
|
|
12960
13254
|
question,
|
|
@@ -12963,6 +13257,7 @@ var AutonomousBrain = class {
|
|
|
12963
13257
|
rationale: result.rationale,
|
|
12964
13258
|
madeBy: "autonomous-brain",
|
|
12965
13259
|
context: JSON.stringify(context)
|
|
13260
|
+
}).catch(() => {
|
|
12966
13261
|
});
|
|
12967
13262
|
if (requiresConsensus) {
|
|
12968
13263
|
this._emit("brain.decision", { id, decisionType, optionId: result.optionId, rationale: result.rationale, consensusRequired: true });
|
|
@@ -13241,10 +13536,16 @@ var TaskAuctioneer = class {
|
|
|
13241
13536
|
maxTasksPerAgent;
|
|
13242
13537
|
minConfidence;
|
|
13243
13538
|
// minimum dispatcher confidence to accept a bid
|
|
13539
|
+
maxBidRetries;
|
|
13540
|
+
// max republished attempts before marking task failed
|
|
13244
13541
|
/** Pending bids keyed by taskId. */
|
|
13245
13542
|
pendingBids = /* @__PURE__ */ new Map();
|
|
13246
13543
|
/** Active bid windows keyed by taskId. */
|
|
13247
13544
|
bidTimers = /* @__PURE__ */ new Map();
|
|
13545
|
+
/** FleetBus subscription disposers, detached in dispose(). */
|
|
13546
|
+
unsubs = [];
|
|
13547
|
+
/** How many times a task has been republished with no bids received. */
|
|
13548
|
+
bidRetryCounts = /* @__PURE__ */ new Map();
|
|
13248
13549
|
/** Agent → current task count (from graph + in-flight). */
|
|
13249
13550
|
agentTaskCounts = /* @__PURE__ */ new Map();
|
|
13250
13551
|
constructor(opts) {
|
|
@@ -13255,8 +13556,26 @@ var TaskAuctioneer = class {
|
|
|
13255
13556
|
this.bidWindowMs = opts.bidWindowMs ?? 3e4;
|
|
13256
13557
|
this.maxTasksPerAgent = opts.maxTasksPerAgent ?? 3;
|
|
13257
13558
|
this.minConfidence = opts.minConfidence ?? 0.3;
|
|
13258
|
-
this.
|
|
13259
|
-
this.fleet?.filter("task:
|
|
13559
|
+
this.maxBidRetries = opts.maxBidRetries ?? 3;
|
|
13560
|
+
const offBid = this.fleet?.filter("task:bid", (e) => this._onBidEvent(e));
|
|
13561
|
+
const offClaimed = this.fleet?.filter("task:claimed", (e) => this._onClaimedEvent(e));
|
|
13562
|
+
if (offBid) this.unsubs.push(offBid);
|
|
13563
|
+
if (offClaimed) this.unsubs.push(offClaimed);
|
|
13564
|
+
}
|
|
13565
|
+
/**
|
|
13566
|
+
* Detach all FleetBus subscriptions and cancel any open bid-window timers.
|
|
13567
|
+
* Call when the owning coordinator stops/restarts so handlers and timers
|
|
13568
|
+
* don't accumulate across cycles.
|
|
13569
|
+
*/
|
|
13570
|
+
dispose() {
|
|
13571
|
+
for (const off of this.unsubs.splice(0)) {
|
|
13572
|
+
try {
|
|
13573
|
+
off();
|
|
13574
|
+
} catch {
|
|
13575
|
+
}
|
|
13576
|
+
}
|
|
13577
|
+
for (const t of this.bidTimers.values()) clearTimeout(t);
|
|
13578
|
+
this.bidTimers.clear();
|
|
13260
13579
|
}
|
|
13261
13580
|
// ── Publish a task ────────────────────────────────────────────────────
|
|
13262
13581
|
/**
|
|
@@ -13266,14 +13585,16 @@ var TaskAuctioneer = class {
|
|
|
13266
13585
|
* If `targetAgent` is specified, the task is assigned directly without auction.
|
|
13267
13586
|
*/
|
|
13268
13587
|
async publishTask(input) {
|
|
13588
|
+
const blockedBy = input.blockedBy ?? [];
|
|
13589
|
+
const hasOpenBlockers = blockedBy.length > 0 && blockedBy.some((id) => this.graph.get(id)?.status !== "done");
|
|
13269
13590
|
const goal = await this.graph.add({
|
|
13270
13591
|
type: "goal",
|
|
13271
13592
|
title: input.title,
|
|
13272
13593
|
description: input.description,
|
|
13273
|
-
status: input.targetAgent ? "in_progress" : "pending",
|
|
13594
|
+
status: input.targetAgent ? "in_progress" : hasOpenBlockers ? "blocked" : "pending",
|
|
13274
13595
|
priority: input.priority ?? "medium",
|
|
13275
13596
|
assignee: input.targetAgent,
|
|
13276
|
-
blockedBy
|
|
13597
|
+
blockedBy,
|
|
13277
13598
|
dependsOn: input.satisfiesGoals ?? [],
|
|
13278
13599
|
createdBy: this.selfAgentId,
|
|
13279
13600
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -13290,6 +13611,12 @@ var TaskAuctioneer = class {
|
|
|
13290
13611
|
});
|
|
13291
13612
|
}
|
|
13292
13613
|
}
|
|
13614
|
+
for (const blockerId of blockedBy) {
|
|
13615
|
+
const blocker = this.graph.get(blockerId);
|
|
13616
|
+
if (blocker && !blocker.children.includes(goal.id)) {
|
|
13617
|
+
await this.graph.update(blockerId, { children: [...blocker.children, goal.id] });
|
|
13618
|
+
}
|
|
13619
|
+
}
|
|
13293
13620
|
if (input.targetAgent) {
|
|
13294
13621
|
await this._assignDirect(goal.id, input.targetAgent);
|
|
13295
13622
|
} else {
|
|
@@ -13401,9 +13728,12 @@ Priority: ${goal.priority}`,
|
|
|
13401
13728
|
status: "done",
|
|
13402
13729
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13403
13730
|
});
|
|
13731
|
+
this.bidRetryCounts.delete(taskId);
|
|
13732
|
+
this.pendingBids.delete(taskId);
|
|
13733
|
+
this._cancelBidWindow(taskId);
|
|
13404
13734
|
for (const childId of goal.children) {
|
|
13405
13735
|
const child = this.graph.get(childId);
|
|
13406
|
-
if (child) {
|
|
13736
|
+
if (child && child.status === "blocked") {
|
|
13407
13737
|
const allUnblocked = child.blockedBy.every((blockedId) => {
|
|
13408
13738
|
const blocked = this.graph.get(blockedId);
|
|
13409
13739
|
return blocked?.status === "done";
|
|
@@ -13412,6 +13742,7 @@ Priority: ${goal.priority}`,
|
|
|
13412
13742
|
await this.graph.update(childId, { status: "pending", updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
13413
13743
|
const unblockedGoal = this.graph.get(childId);
|
|
13414
13744
|
await this._broadcastTask(unblockedGoal);
|
|
13745
|
+
this._startBidWindow(childId);
|
|
13415
13746
|
}
|
|
13416
13747
|
}
|
|
13417
13748
|
}
|
|
@@ -13437,6 +13768,9 @@ ${_result ?? "No result provided."}`
|
|
|
13437
13768
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13438
13769
|
result: error
|
|
13439
13770
|
});
|
|
13771
|
+
this.bidRetryCounts.delete(taskId);
|
|
13772
|
+
this.pendingBids.delete(taskId);
|
|
13773
|
+
this._cancelBidWindow(taskId);
|
|
13440
13774
|
this._emit("task:failed", { taskId, agentId, error });
|
|
13441
13775
|
await this._mailboxPublish({
|
|
13442
13776
|
type: "note",
|
|
@@ -13515,7 +13849,7 @@ Assignee: ${agentId}`
|
|
|
13515
13849
|
priority: goal.priority,
|
|
13516
13850
|
tags: goal.tags
|
|
13517
13851
|
});
|
|
13518
|
-
|
|
13852
|
+
this._mailboxPublish({
|
|
13519
13853
|
type: "broadcast",
|
|
13520
13854
|
subject: `[task] ${goal.title} (${goal.priority})`,
|
|
13521
13855
|
body: `New task available: "${goal.title}"
|
|
@@ -13526,6 +13860,7 @@ Task ID: ${goal.id}
|
|
|
13526
13860
|
Tags: ${goal.tags.join(", ") || "none"}
|
|
13527
13861
|
|
|
13528
13862
|
Bid by calling taskAuctioneer.bid("${goal.id}", ...)`
|
|
13863
|
+
}).catch(() => {
|
|
13529
13864
|
});
|
|
13530
13865
|
}
|
|
13531
13866
|
async _mailboxPublish(msg) {
|
|
@@ -13559,9 +13894,10 @@ Bid by calling taskAuctioneer.bid("${goal.id}", ...)`
|
|
|
13559
13894
|
}
|
|
13560
13895
|
_startBidWindow(taskId) {
|
|
13561
13896
|
this._cancelBidWindow(taskId);
|
|
13562
|
-
const timer = setTimeout(
|
|
13897
|
+
const timer = setTimeout(() => {
|
|
13563
13898
|
this.bidTimers.delete(taskId);
|
|
13564
|
-
|
|
13899
|
+
void this._evaluateBids(taskId).catch(() => {
|
|
13900
|
+
});
|
|
13565
13901
|
}, this.bidWindowMs);
|
|
13566
13902
|
this.bidTimers.set(taskId, timer);
|
|
13567
13903
|
}
|
|
@@ -13575,6 +13911,13 @@ Bid by calling taskAuctioneer.bid("${goal.id}", ...)`
|
|
|
13575
13911
|
async _evaluateBids(taskId) {
|
|
13576
13912
|
const bids = this.pendingBids.get(taskId);
|
|
13577
13913
|
if (!bids || bids.length === 0) {
|
|
13914
|
+
const retryCount = (this.bidRetryCounts.get(taskId) ?? 0) + 1;
|
|
13915
|
+
this.bidRetryCounts.set(taskId, retryCount);
|
|
13916
|
+
if (retryCount >= this.maxBidRetries) {
|
|
13917
|
+
await this.fail(taskId, `No bids received after ${this.maxBidRetries} attempts`);
|
|
13918
|
+
this.bidRetryCounts.delete(taskId);
|
|
13919
|
+
return;
|
|
13920
|
+
}
|
|
13578
13921
|
const goal = this.graph.get(taskId);
|
|
13579
13922
|
if (goal) {
|
|
13580
13923
|
await this._broadcastTask(goal);
|
|
@@ -13583,7 +13926,15 @@ Bid by calling taskAuctioneer.bid("${goal.id}", ...)`
|
|
|
13583
13926
|
return;
|
|
13584
13927
|
}
|
|
13585
13928
|
bids.sort((a, b) => b.score - a.score);
|
|
13586
|
-
const winner = bids
|
|
13929
|
+
const winner = bids.find((b) => this._getAgentTaskCount(b.agentId) < this.maxTasksPerAgent);
|
|
13930
|
+
if (!winner) {
|
|
13931
|
+
const goal = this.graph.get(taskId);
|
|
13932
|
+
if (goal) {
|
|
13933
|
+
await this._broadcastTask(goal);
|
|
13934
|
+
this._startBidWindow(taskId);
|
|
13935
|
+
}
|
|
13936
|
+
return;
|
|
13937
|
+
}
|
|
13587
13938
|
await this.claim(taskId, winner.agentId, winner.agentName);
|
|
13588
13939
|
}
|
|
13589
13940
|
async _assignDirect(taskId, agentId) {
|
|
@@ -13630,16 +13981,24 @@ var AutonomousCoordinator = class {
|
|
|
13630
13981
|
selfAgentId;
|
|
13631
13982
|
fleet;
|
|
13632
13983
|
fleetManager;
|
|
13984
|
+
director;
|
|
13633
13985
|
mailbox;
|
|
13634
13986
|
events;
|
|
13987
|
+
onCoordinatorEvent;
|
|
13635
13988
|
running = false;
|
|
13636
13989
|
iterationCount = 0;
|
|
13990
|
+
/** Tasks already handled by _onSubagentTerminated (to avoid double goal:failed on fleet event). */
|
|
13991
|
+
_handledBySubagent = /* @__PURE__ */ new Set();
|
|
13992
|
+
/** FleetBus subscription disposers, detached in dispose(). */
|
|
13993
|
+
unsubs = [];
|
|
13637
13994
|
constructor(opts) {
|
|
13638
13995
|
this.selfAgentId = opts.selfAgentId;
|
|
13639
13996
|
this.fleet = opts.fleet ?? void 0;
|
|
13640
13997
|
this.fleetManager = opts.fleetManager ?? void 0;
|
|
13998
|
+
this.director = opts.director ?? void 0;
|
|
13641
13999
|
this.mailbox = opts.mailbox ?? void 0;
|
|
13642
14000
|
this.events = opts.events ?? void 0;
|
|
14001
|
+
this.onCoordinatorEvent = opts.onCoordinatorEvent;
|
|
13643
14002
|
this.graph = new KnowledgeGraph(opts.sessionDir);
|
|
13644
14003
|
this.dag = new TaskDAG();
|
|
13645
14004
|
this.auction = new TaskAuctioneer({
|
|
@@ -13675,9 +14034,19 @@ var AutonomousCoordinator = class {
|
|
|
13675
14034
|
this.dag.onEvent((event) => {
|
|
13676
14035
|
this._onDagEvent(event);
|
|
13677
14036
|
});
|
|
13678
|
-
this.fleet?.filter("subagent.
|
|
14037
|
+
const offCompleted = this.fleet?.filter("subagent.completed", (e) => {
|
|
13679
14038
|
this._onSubagentTerminated(e);
|
|
13680
14039
|
});
|
|
14040
|
+
if (offCompleted) this.unsubs.push(offCompleted);
|
|
14041
|
+
const offFailed = this.fleet?.filter("task:failed", (e) => {
|
|
14042
|
+
const payload = e.payload;
|
|
14043
|
+
const taskId = payload?.taskId;
|
|
14044
|
+
if (!taskId || this._handledBySubagent.has(taskId)) return;
|
|
14045
|
+
this._handledBySubagent.add(taskId);
|
|
14046
|
+
this._emit({ type: "goal:failed", goalId: taskId, text: payload?.error ?? "Task failed" });
|
|
14047
|
+
});
|
|
14048
|
+
if (offFailed) this.unsubs.push(offFailed);
|
|
14049
|
+
this._emit({ type: "coordinator:mode", mode: this.fleet ? "fleet" : "standalone" });
|
|
13681
14050
|
}
|
|
13682
14051
|
// ── Public API ───────────────────────────────────────────────────────
|
|
13683
14052
|
/**
|
|
@@ -13691,11 +14060,13 @@ var AutonomousCoordinator = class {
|
|
|
13691
14060
|
const maxIterations = opts.maxIterations ?? 100;
|
|
13692
14061
|
const goal = opts.goal ?? "Improve the codebase";
|
|
13693
14062
|
const maxCost = opts.maxCostUsd;
|
|
13694
|
-
await this.graph.load();
|
|
13695
14063
|
try {
|
|
14064
|
+
await this.graph.load();
|
|
13696
14065
|
const goalConfigs = await this._decomposeGoal(goal);
|
|
13697
14066
|
for (const g of goalConfigs) {
|
|
13698
|
-
await this.auction.publishTask(g);
|
|
14067
|
+
const goalId = await this.auction.publishTask(g);
|
|
14068
|
+
this.dag.addNode(goalId, g.description, []);
|
|
14069
|
+
this._emit({ type: "goal:added", goalId, title: g.title, text: g.description });
|
|
13699
14070
|
}
|
|
13700
14071
|
while (this.running) {
|
|
13701
14072
|
this.iterationCount++;
|
|
@@ -13722,6 +14093,7 @@ var AutonomousCoordinator = class {
|
|
|
13722
14093
|
const blocked = this.dag.getBlocked();
|
|
13723
14094
|
if (blocked.length > 0 && this.dag.hasDeadlock()) {
|
|
13724
14095
|
(this.events?.emit)("autonomous:deadlock", { blocked });
|
|
14096
|
+
this._emit({ type: "deadlock:detected", goalId: blocked[0]?.id ?? "", text: `Deadlock detected: ${blocked.map((n) => n.id).join(", ")}` });
|
|
13725
14097
|
this.running = false;
|
|
13726
14098
|
}
|
|
13727
14099
|
break;
|
|
@@ -13738,7 +14110,15 @@ var AutonomousCoordinator = class {
|
|
|
13738
14110
|
}
|
|
13739
14111
|
const pendingChanges = this.changes.getPendingReviews();
|
|
13740
14112
|
for (const change of pendingChanges) {
|
|
13741
|
-
|
|
14113
|
+
try {
|
|
14114
|
+
await this._handlePendingChange(change);
|
|
14115
|
+
} catch (err) {
|
|
14116
|
+
this._emit({
|
|
14117
|
+
type: "goal:failed",
|
|
14118
|
+
goalId: change.id,
|
|
14119
|
+
text: `Consensus handling failed: ${err instanceof Error ? err.message : String(err)}`
|
|
14120
|
+
});
|
|
14121
|
+
}
|
|
13742
14122
|
}
|
|
13743
14123
|
}
|
|
13744
14124
|
} finally {
|
|
@@ -13748,7 +14128,26 @@ var AutonomousCoordinator = class {
|
|
|
13748
14128
|
}
|
|
13749
14129
|
/** Stop the autonomous loop. */
|
|
13750
14130
|
stop() {
|
|
14131
|
+
if (!this.running) return;
|
|
13751
14132
|
this.running = false;
|
|
14133
|
+
console.error(`[AutonomousCoordinator] stop signal received \u2014 shutting down (iteration ${this.iterationCount})`);
|
|
14134
|
+
}
|
|
14135
|
+
/**
|
|
14136
|
+
* Tear down the coordinator for good: stop the loop and detach all FleetBus
|
|
14137
|
+
* subscriptions (this coordinator's + the auctioneer's) plus any open bid
|
|
14138
|
+
* timers. Call this when discarding the instance (e.g. `/coordinator stop`
|
|
14139
|
+
* that recreates a fresh coordinator on the next start) so handlers and
|
|
14140
|
+
* timers don't accumulate across cycles. `stop()` only pauses the loop.
|
|
14141
|
+
*/
|
|
14142
|
+
dispose() {
|
|
14143
|
+
this.stop();
|
|
14144
|
+
for (const off of this.unsubs.splice(0)) {
|
|
14145
|
+
try {
|
|
14146
|
+
off();
|
|
14147
|
+
} catch {
|
|
14148
|
+
}
|
|
14149
|
+
}
|
|
14150
|
+
this.auction.dispose();
|
|
13752
14151
|
}
|
|
13753
14152
|
/** Get a stats snapshot. */
|
|
13754
14153
|
getStats() {
|
|
@@ -13803,6 +14202,7 @@ var AutonomousCoordinator = class {
|
|
|
13803
14202
|
body: `**${input.category}**${input.file ? ` in ${input.file}${input.line ? `:${input.line}` : ""}` : ""}
|
|
13804
14203
|
${input.detail}`
|
|
13805
14204
|
});
|
|
14205
|
+
this._emit({ type: "knowledge:added", knowledgeId: fact.id, title: input.subject, text: input.detail });
|
|
13806
14206
|
return fact;
|
|
13807
14207
|
}
|
|
13808
14208
|
// ── Goal creation helpers ────────────────────────────────────────────
|
|
@@ -13815,7 +14215,10 @@ ${input.detail}`
|
|
|
13815
14215
|
title: input.title,
|
|
13816
14216
|
description: input.description,
|
|
13817
14217
|
priority: resolvedPriority,
|
|
13818
|
-
...input.tags ? { tags: input.tags } : {}
|
|
14218
|
+
...input.tags ? { tags: input.tags } : {},
|
|
14219
|
+
// Mirror the dependency edges into the auction so blocked goals aren't
|
|
14220
|
+
// biddable until their deps complete (the DAG tracks the same edges).
|
|
14221
|
+
...input.deps && input.deps.length > 0 ? { blockedBy: input.deps } : {}
|
|
13819
14222
|
});
|
|
13820
14223
|
const goal = this.graph.get(goalId);
|
|
13821
14224
|
for (const depId of input.deps ?? []) {
|
|
@@ -13856,30 +14259,34 @@ ${input.detail}`
|
|
|
13856
14259
|
async _processGoal(goalId) {
|
|
13857
14260
|
const ready = this.dag.getReady();
|
|
13858
14261
|
if (ready.length === 0) return;
|
|
13859
|
-
const
|
|
13860
|
-
this.dag.start(
|
|
13861
|
-
const
|
|
13862
|
-
if (!
|
|
13863
|
-
|
|
13864
|
-
|
|
13865
|
-
|
|
13866
|
-
|
|
13867
|
-
|
|
14262
|
+
const dagNode = ready.find((n) => n.id === goalId) ?? ready[0];
|
|
14263
|
+
this.dag.start(dagNode.id, "auctioneer");
|
|
14264
|
+
const goalNode = this.graph.get(goalId);
|
|
14265
|
+
if (!goalNode) return;
|
|
14266
|
+
const title = goalNode.title || dagNode.description;
|
|
14267
|
+
const taskId = await this.auction.publishTask({
|
|
14268
|
+
title,
|
|
14269
|
+
description: goalNode.description,
|
|
14270
|
+
priority: this._dagPriorityToGoal(dagNode.priority),
|
|
14271
|
+
tags: dagNode.tags
|
|
14272
|
+
});
|
|
14273
|
+
this._emit({ type: "task:ready", goalId, taskId, title });
|
|
14274
|
+
if (this.director) {
|
|
14275
|
+
const config = {
|
|
14276
|
+
name: `worker-${goalId.slice(0, 8)}`,
|
|
14277
|
+
role: "general",
|
|
14278
|
+
maxIterations: 100,
|
|
14279
|
+
timeoutMs: 6e5
|
|
14280
|
+
// 10 minutes per goal
|
|
14281
|
+
};
|
|
14282
|
+
const subagentId = await this.director.spawn(config);
|
|
14283
|
+
await this.auction.claim(taskId, subagentId, config.name);
|
|
14284
|
+
await this.director.assign({
|
|
14285
|
+
id: goalId,
|
|
14286
|
+
subagentId,
|
|
14287
|
+
description: goalNode.description
|
|
13868
14288
|
});
|
|
13869
14289
|
}
|
|
13870
|
-
await this._waitForClaim(next.id);
|
|
13871
|
-
}
|
|
13872
|
-
async _waitForClaim(taskId) {
|
|
13873
|
-
const maxWait = 6e4;
|
|
13874
|
-
const pollInterval = 2e3;
|
|
13875
|
-
const start = Date.now();
|
|
13876
|
-
while (Date.now() - start < maxWait) {
|
|
13877
|
-
const goal = this.graph.get(taskId);
|
|
13878
|
-
if (goal?.status === "in_progress" || goal?.status === "done") {
|
|
13879
|
-
return;
|
|
13880
|
-
}
|
|
13881
|
-
await this._sleep(pollInterval);
|
|
13882
|
-
}
|
|
13883
14290
|
}
|
|
13884
14291
|
async _handlePendingChange(change) {
|
|
13885
14292
|
const result = this.consensus.getStatus(change.id);
|
|
@@ -13893,6 +14300,17 @@ ${input.detail}`
|
|
|
13893
14300
|
);
|
|
13894
14301
|
if (voteResult.outcome === "approved") {
|
|
13895
14302
|
await this.changes.markApplied(change.id, (/* @__PURE__ */ new Date()).toISOString());
|
|
14303
|
+
this._emit({ type: "consensus:reached", goalId: change.id, text: "Change approved and applied" });
|
|
14304
|
+
}
|
|
14305
|
+
} else {
|
|
14306
|
+
const voteResult = await this.consensus.castVote(
|
|
14307
|
+
change.id,
|
|
14308
|
+
this.selfAgentId,
|
|
14309
|
+
"reject",
|
|
14310
|
+
`Quality gate failed: ${change.qualityGate.checks.map((c) => `${c.name}=${c.passed}`).join(", ")}`
|
|
14311
|
+
);
|
|
14312
|
+
if (voteResult.outcome === "rejected" || voteResult.outcome === "vetoed") {
|
|
14313
|
+
this._emit({ type: "consensus:reached", goalId: change.id, text: "Change rejected by quality gate" });
|
|
13896
14314
|
}
|
|
13897
14315
|
}
|
|
13898
14316
|
}
|
|
@@ -13903,10 +14321,6 @@ ${input.detail}`
|
|
|
13903
14321
|
(this.events?.emit)("autonomous:task_ready", { taskId: event.nodeId, description: node.description });
|
|
13904
14322
|
}
|
|
13905
14323
|
}
|
|
13906
|
-
if (event.type === "deadlock") {
|
|
13907
|
-
(this.events?.emit)("autonomous:deadlock", { blocked: event.blocked });
|
|
13908
|
-
this.running = false;
|
|
13909
|
-
}
|
|
13910
14324
|
if (event.type === "graph:done") {
|
|
13911
14325
|
(this.events?.emit)("autonomous:all_done", this.getStats());
|
|
13912
14326
|
}
|
|
@@ -13914,13 +14328,16 @@ ${input.detail}`
|
|
|
13914
14328
|
_onSubagentTerminated(e) {
|
|
13915
14329
|
const payload = e.payload;
|
|
13916
14330
|
const subagentId = payload?.subagentId ?? e.subagentId;
|
|
13917
|
-
const stopReason = payload?.stopReason ?? "unknown";
|
|
14331
|
+
const stopReason = payload?.stopReason ?? (payload?.status === "ok" ? "end_turn" : payload?.status ?? "unknown");
|
|
13918
14332
|
const tasks = this.auction.getTasksForAgent(subagentId);
|
|
13919
14333
|
for (const task of tasks) {
|
|
14334
|
+
this._handledBySubagent.add(task.id);
|
|
13920
14335
|
if (stopReason === "end_turn") {
|
|
13921
14336
|
void this.auction.complete(task.id, "Subagent completed successfully");
|
|
14337
|
+
this._emit({ type: "task:completed", goalId: task.id, taskId: task.id, text: "Subagent completed successfully" });
|
|
13922
14338
|
} else {
|
|
13923
14339
|
void this.auction.fail(task.id, `Subagent terminated: ${stopReason}`);
|
|
14340
|
+
this._emit({ type: "goal:failed", goalId: task.id, text: `Subagent terminated: ${stopReason}` });
|
|
13924
14341
|
}
|
|
13925
14342
|
}
|
|
13926
14343
|
}
|
|
@@ -13934,6 +14351,10 @@ ${input.detail}`
|
|
|
13934
14351
|
}
|
|
13935
14352
|
_buildVoters() {
|
|
13936
14353
|
return [
|
|
14354
|
+
// The coordinator itself casts the quality-gate auto-vote in
|
|
14355
|
+
// _handlePendingChange — it MUST be a registered, eligible voter or
|
|
14356
|
+
// castVote throws "unknown voter" and tears down the run() loop.
|
|
14357
|
+
{ agentId: this.selfAgentId, agentName: "Coordinator", role: "coordinator", weight: 1 },
|
|
13937
14358
|
{ agentId: "critic", agentName: "Critic", role: "critic", weight: 2, veto: true },
|
|
13938
14359
|
{ agentId: "bug-hunter", agentName: "Bug Hunter", role: "bug-hunter", weight: 1.5 },
|
|
13939
14360
|
{ agentId: "security-scanner", agentName: "Security Scanner", role: "security-scanner", weight: 1.5 },
|
|
@@ -13971,11 +14392,12 @@ ${input.detail}`
|
|
|
13971
14392
|
} catch {
|
|
13972
14393
|
}
|
|
13973
14394
|
}
|
|
13974
|
-
|
|
13975
|
-
|
|
14395
|
+
/** Emit a CoordinatorEvent to the subscriber (e.g. TUI panel timeline). */
|
|
14396
|
+
_emit(event) {
|
|
14397
|
+
this.onCoordinatorEvent?.(event);
|
|
13976
14398
|
}
|
|
13977
14399
|
};
|
|
13978
14400
|
|
|
13979
|
-
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutonomousBrain, AutonomousCoordinator, BUG_HUNTER_AGENT, BUILD_AGENTS, BrainDecisionQueue, BrainMonitor, BudgetExceededError, BudgetThresholdSignal, ChangeManager, CollabSession, ConsensusProtocol, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_QUALITY_CHECKS, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DEPENDENCY_FILE_PATTERNS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultBrainArbiter, DefaultMailbox, DefaultMultiAgentCoordinator, Director, DirectorAlertLevel, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, GlobalMailbox, HEAVY_BUDGET, HumanEscalatingBrainArbiter, InMemoryAgentBridge, InMemoryBridgeTransport, KNOWLEDGE_AGENTS, KnowledgeGraph, LIGHT_BUDGET, LargeAnswerStore, MEDIUM_BUDGET, META_AGENTS, NULL_FLEET_BUS, ObservableBrainArbiter, PLANNING_AGENTS, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, SECURITY_SCANNER_AGENT, SubagentBudget, TaskAuctioneer, TaskDAG, VERIFY_AGENTS, applyRosterBudget, attachAutoExtend, attachDepWatcherBridge, composeDirectorPrompt, composeSubagentPrompt, createDelegateTool, createMailboxHooks, createMessage, detectEcosystem, dispatchAgent, formatHumanPrompt, getAgentDefinition, getFullPackageLog, getManifestPackages, getPackageAuthor, getPackagesByAgent, mailboxSessionTag, makeAgentSubagentRunner, makeAskResultTool, makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeDependencyWatcherConfig, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeMailInboxTool, makeMailSendTool, makeMailboxTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, makeWorkCompleteTool, normalizeRecipient, recordPackageAction, resolveMailboxIdentity, resolveProjectDir, rosterSummaryFromConfigs, scoreAgents, startPackageOutdatedWatcher, updatePackageOutdatedStatus, withDisabledToolFiltering };
|
|
14401
|
+
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutonomousBrain, AutonomousCoordinator, BUG_HUNTER_AGENT, BUILD_AGENTS, BrainDecisionQueue, BrainMonitor, BudgetExceededError, BudgetThresholdSignal, ChangeManager, CollabSession, ConsensusProtocol, DECISION_TIMEOUT_MS, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_QUALITY_CHECKS, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DEPENDENCY_FILE_PATTERNS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultBrainArbiter, DefaultMailbox, DefaultMultiAgentCoordinator, Director, DirectorAlertLevel, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, GlobalMailbox, HEAVY_BUDGET, HumanEscalatingBrainArbiter, InMemoryAgentBridge, InMemoryBridgeTransport, KNOWLEDGE_AGENTS, KnowledgeGraph, LIGHT_BUDGET, LargeAnswerStore, MEDIUM_BUDGET, META_AGENTS, NULL_FLEET_BUS, ObservableBrainArbiter, PLANNING_AGENTS, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, SECURITY_SCANNER_AGENT, SubagentBudget, TIMEOUT_PREEMPT_FRACTION, TaskAuctioneer, TaskDAG, VERIFY_AGENTS, applyRosterBudget, attachAutoExtend, attachDepWatcherBridge, composeDirectorPrompt, composeSubagentPrompt, createDelegateTool, createMailboxHooks, createMessage, detectEcosystem, dispatchAgent, formatHumanPrompt, getAgentDefinition, getFullPackageLog, getManifestPackages, getPackageAuthor, getPackagesByAgent, mailboxSessionTag, makeAgentSubagentRunner, makeAskResultTool, makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeDependencyWatcherConfig, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeMailInboxTool, makeMailSendTool, makeMailboxTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, makeWorkCompleteTool, normalizeRecipient, recordPackageAction, resolveMailboxIdentity, resolveProjectDir, rosterSummaryFromConfigs, scoreAgents, startPackageOutdatedWatcher, updatePackageOutdatedStatus, withDisabledToolFiltering };
|
|
13980
14402
|
//# sourceMappingURL=index.js.map
|
|
13981
14403
|
//# sourceMappingURL=index.js.map
|