@wrongstack/core 0.9.7 → 0.9.19
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-subagent-runner-DaF_EgRG.d.ts → agent-subagent-runner-C4qt9e5Y.d.ts} +1 -1
- package/dist/{config-SkMIDN9L.d.ts → config-CWva0qoL.d.ts} +4 -0
- package/dist/coordination/index.d.ts +8 -7
- package/dist/coordination/index.js +672 -46
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +12 -11
- package/dist/defaults/index.js +780 -23
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +7 -6
- package/dist/execution/index.js +1 -0
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +4 -3
- package/dist/{index-CP8638Wm.d.ts → index-aizK8olO.d.ts} +3 -2
- package/dist/{index-Bsha5K4D.d.ts → index-p95HQ22A.d.ts} +3 -2
- package/dist/index.d.ts +22 -16
- package/dist/index.js +861 -35
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +2 -2
- package/dist/kernel/index.d.ts +4 -3
- package/dist/{mcp-servers-BouUWYW6.d.ts → mcp-servers-BkVEqkRe.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-DTXF2aAl.d.ts → multi-agent-coordinator-bRaI_aD1.d.ts} +1 -1
- package/dist/{null-fleet-bus-Chrc_3Pp.d.ts → null-fleet-bus-DKM3Iy9d.d.ts} +183 -3
- package/dist/{secret-scrubber-DttNiGYA.d.ts → permission-bPuzAy4x.d.ts} +1 -6
- package/dist/{permission-policy-BpCGYBud.d.ts → permission-policy-BUQSutpl.d.ts} +8 -1
- package/dist/{plan-templates-envSmNlZ.d.ts → plan-templates-fkQTyz3U.d.ts} +25 -1
- package/dist/sdd/index.d.ts +5 -4
- package/dist/sdd/index.js +1 -0
- package/dist/sdd/index.js.map +1 -1
- package/dist/secret-scrubber-3MHDDAtm.d.ts +6 -0
- package/dist/{secret-scrubber-QSeI0ADi.d.ts → secret-scrubber-7rSC_emZ.d.ts} +1 -1
- package/dist/security/index.d.ts +4 -3
- package/dist/security/index.js +37 -6
- package/dist/security/index.js.map +1 -1
- package/dist/storage/index.d.ts +3 -2
- package/dist/storage/index.js +88 -5
- package/dist/storage/index.js.map +1 -1
- package/dist/{tool-executor-CsktM3h9.d.ts → tool-executor-Boo3dekH.d.ts} +1 -1
- package/dist/types/index.d.ts +6 -5
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +12 -1
- package/dist/utils/index.js +85 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID, randomBytes } from 'crypto';
|
|
2
|
-
import * as
|
|
2
|
+
import * as fsp5 from 'fs/promises';
|
|
3
3
|
import * as path4 from 'path';
|
|
4
4
|
import { EventEmitter } from 'events';
|
|
5
5
|
|
|
@@ -11,16 +11,16 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
11
11
|
});
|
|
12
12
|
async function atomicWrite(targetPath, content, opts = {}) {
|
|
13
13
|
const dir = path4.dirname(targetPath);
|
|
14
|
-
await
|
|
14
|
+
await fsp5.mkdir(dir, { recursive: true });
|
|
15
15
|
const tmp = path4.join(dir, `.${path4.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
16
16
|
try {
|
|
17
17
|
if (typeof content === "string") {
|
|
18
|
-
await
|
|
18
|
+
await fsp5.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
19
19
|
} else {
|
|
20
|
-
await
|
|
20
|
+
await fsp5.writeFile(tmp, content, { flag: "wx" });
|
|
21
21
|
}
|
|
22
22
|
try {
|
|
23
|
-
const fh = await
|
|
23
|
+
const fh = await fsp5.open(tmp, "r+");
|
|
24
24
|
try {
|
|
25
25
|
await fh.sync();
|
|
26
26
|
} finally {
|
|
@@ -30,37 +30,37 @@ async function atomicWrite(targetPath, content, opts = {}) {
|
|
|
30
30
|
}
|
|
31
31
|
let mode;
|
|
32
32
|
try {
|
|
33
|
-
const stat3 = await
|
|
33
|
+
const stat3 = await fsp5.stat(targetPath);
|
|
34
34
|
mode = stat3.mode & 511;
|
|
35
35
|
} catch {
|
|
36
36
|
mode = opts.mode;
|
|
37
37
|
}
|
|
38
38
|
if (mode !== void 0) {
|
|
39
|
-
await
|
|
39
|
+
await fsp5.chmod(tmp, mode);
|
|
40
40
|
}
|
|
41
41
|
await renameWithRetry(tmp, targetPath);
|
|
42
42
|
} catch (err) {
|
|
43
43
|
try {
|
|
44
|
-
await
|
|
44
|
+
await fsp5.unlink(tmp);
|
|
45
45
|
} catch {
|
|
46
46
|
}
|
|
47
47
|
throw err;
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
async function ensureDir(dir) {
|
|
51
|
-
await
|
|
51
|
+
await fsp5.mkdir(dir, { recursive: true });
|
|
52
52
|
}
|
|
53
53
|
var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
|
|
54
54
|
async function renameWithRetry(from, to) {
|
|
55
55
|
if (process.platform !== "win32") {
|
|
56
|
-
await
|
|
56
|
+
await fsp5.rename(from, to);
|
|
57
57
|
return;
|
|
58
58
|
}
|
|
59
59
|
const delays = [10, 25, 60, 120, 250];
|
|
60
60
|
let lastErr;
|
|
61
61
|
for (let i = 0; i <= delays.length; i++) {
|
|
62
62
|
try {
|
|
63
|
-
await
|
|
63
|
+
await fsp5.rename(from, to);
|
|
64
64
|
return;
|
|
65
65
|
} catch (err) {
|
|
66
66
|
lastErr = err;
|
|
@@ -76,7 +76,7 @@ async function renameWithRetry(from, to) {
|
|
|
76
76
|
async function acquireDirectorStateLock(lockPath, processId = process.pid) {
|
|
77
77
|
let existing;
|
|
78
78
|
try {
|
|
79
|
-
existing = await
|
|
79
|
+
existing = await fsp5.readFile(lockPath, "utf8");
|
|
80
80
|
} catch {
|
|
81
81
|
}
|
|
82
82
|
if (existing) {
|
|
@@ -100,7 +100,7 @@ async function acquireDirectorStateLock(lockPath, processId = process.pid) {
|
|
|
100
100
|
}
|
|
101
101
|
async function releaseDirectorStateLock(lockPath) {
|
|
102
102
|
try {
|
|
103
|
-
await
|
|
103
|
+
await fsp5.unlink(lockPath);
|
|
104
104
|
} catch {
|
|
105
105
|
}
|
|
106
106
|
}
|
|
@@ -631,6 +631,106 @@ var FleetUsageAggregator = class {
|
|
|
631
631
|
}
|
|
632
632
|
};
|
|
633
633
|
|
|
634
|
+
// src/coordination/subagent-nicknames.ts
|
|
635
|
+
var NICKNAME_POOL = {
|
|
636
|
+
// Physics & fundamental sciences
|
|
637
|
+
"einstein": { name: "Einstein", domain: "physics" },
|
|
638
|
+
"newton": { name: "Newton", domain: "physics" },
|
|
639
|
+
"feynman": { name: "Feynman", domain: "physics" },
|
|
640
|
+
"dirac": { name: "Dirac", domain: "physics" },
|
|
641
|
+
"bohr": { name: "Bohr", domain: "physics" },
|
|
642
|
+
"planck": { name: "Planck", domain: "physics" },
|
|
643
|
+
"curie": { name: "Curie", domain: "physics" },
|
|
644
|
+
"fermi": { name: "Fermi", domain: "physics" },
|
|
645
|
+
"heisenberg": { name: "Heisenberg", domain: "physics" },
|
|
646
|
+
"schrodinger": { name: "Schr\xF6dinger", domain: "physics" },
|
|
647
|
+
// Mathematics
|
|
648
|
+
"euclid": { name: "Euclid", domain: "math" },
|
|
649
|
+
"gauss": { name: "Gauss", domain: "math" },
|
|
650
|
+
"turing": { name: "Turing", domain: "math" },
|
|
651
|
+
"poincare": { name: "Poincar\xE9", domain: "math" },
|
|
652
|
+
"riemann": { name: "Riemann", domain: "math" },
|
|
653
|
+
"hilbert": { name: "Hilbert", domain: "math" },
|
|
654
|
+
"pythagoras": { name: "Pythagoras", domain: "math" },
|
|
655
|
+
// Computing & information theory
|
|
656
|
+
"von-neumann": { name: "Von Neumann", domain: "computing" },
|
|
657
|
+
"shannon": { name: "Shannon", domain: "computing" },
|
|
658
|
+
"hopper": { name: "Hopper", domain: "computing" },
|
|
659
|
+
"backus": { name: "Backus", domain: "computing" },
|
|
660
|
+
"knuth": { name: "Knuth", domain: "computing" },
|
|
661
|
+
"torvalds": { name: "Torvalds", domain: "computing" },
|
|
662
|
+
"stallman": { name: "Stallman", domain: "computing" },
|
|
663
|
+
"berners-lee": { name: "Berners-Lee", domain: "computing" },
|
|
664
|
+
"babbage": { name: "Babbage", domain: "computing" },
|
|
665
|
+
"lovelace": { name: "Lovelace", domain: "computing" },
|
|
666
|
+
"klein": { name: "Klein", domain: "computing" },
|
|
667
|
+
// Electronics & electrical engineering
|
|
668
|
+
"edison": { name: "Edison", domain: "ee" },
|
|
669
|
+
"tesla": { name: "Tesla", domain: "ee" },
|
|
670
|
+
"faraday": { name: "Faraday", domain: "ee" },
|
|
671
|
+
"maxwell": { name: "Maxwell", domain: "ee" },
|
|
672
|
+
"ohm": { name: "Ohm", domain: "ee" },
|
|
673
|
+
"bell": { name: "Bell", domain: "ee" },
|
|
674
|
+
"marconi": { name: "Marconi", domain: "ee" },
|
|
675
|
+
"lamarr": { name: "Lamarr", domain: "ee" },
|
|
676
|
+
// General science / multi-disciplinary
|
|
677
|
+
"darwin": { name: "Darwin", domain: "biology" },
|
|
678
|
+
"mendel": { name: "Mendel", domain: "biology" },
|
|
679
|
+
"pasteur": { name: "Pasteur", domain: "biology" },
|
|
680
|
+
"hawking": { name: "Hawking", domain: "cosmology" },
|
|
681
|
+
"sagan": { name: "Sagan", domain: "cosmology" },
|
|
682
|
+
// Chemistry / materials
|
|
683
|
+
"lavoisier": { name: "Lavoisier", domain: "chemistry" },
|
|
684
|
+
"mendeleev": { name: "Mendeleev", domain: "chemistry" }
|
|
685
|
+
};
|
|
686
|
+
var ALL_NICKNAMES = Object.values(NICKNAME_POOL);
|
|
687
|
+
var DOMAIN_PREFERENCES = {
|
|
688
|
+
"security": ["shannon", "turing", "lamarr", "stallman"],
|
|
689
|
+
"bug-hunter": ["darwin", "curie", "feynman", "fermi"],
|
|
690
|
+
"refactor": ["gauss", "hilbert", "euclid", "planck"],
|
|
691
|
+
"audit-log": ["sagan", "hawking", "poincare", "newton"],
|
|
692
|
+
"planner": ["hilbert", "gauss", "turing", "euclid"],
|
|
693
|
+
"researcher": ["sagan", "hawking", "darwin", "pasteur"],
|
|
694
|
+
"explorer": ["marconi", "bell", "columbus", "polo"],
|
|
695
|
+
"testing": ["pasteur", "curie", "fermi", "bohr"],
|
|
696
|
+
"frontend": ["lovelace", "hopper", "babbage", "backus"],
|
|
697
|
+
"backend": ["torvalds", "stallman", "von-neumann", "backus"],
|
|
698
|
+
"database": ["turing", "shannon", "backus", "knuth"],
|
|
699
|
+
"devops": ["tesla", "edison", "faraday", "bell"],
|
|
700
|
+
"security-scanner": ["shannon", "turing", "lamarr", "stallman"],
|
|
701
|
+
"refactor-planner": ["gauss", "hilbert", "planck", "newton"],
|
|
702
|
+
"architect": ["von-neumann", "turing", "gauss", "hilbert"],
|
|
703
|
+
"critic": ["einstein", "feynman", "dirac", "bohr"],
|
|
704
|
+
"e2e": ["hopper", "bell", "marconi", "tesla"],
|
|
705
|
+
"performance": ["knuth", "gauss", "planck", "feynman"],
|
|
706
|
+
"chaos": ["tesla", "edison", "curie", "fermi"],
|
|
707
|
+
"cost": ["oh", "bell", "marconi", "tesla"],
|
|
708
|
+
// default fallback
|
|
709
|
+
"default": ["einstein", "newton", "curie", "tesla", "edison", "turing", "shannon", "hopper", "knuth", "stallman"]
|
|
710
|
+
};
|
|
711
|
+
function assignNickname(role, used) {
|
|
712
|
+
const preferences = [
|
|
713
|
+
...DOMAIN_PREFERENCES[role] ?? [],
|
|
714
|
+
...DOMAIN_PREFERENCES["default"] ?? []
|
|
715
|
+
];
|
|
716
|
+
for (const key of preferences) {
|
|
717
|
+
if (!used.has(key)) {
|
|
718
|
+
return `${NICKNAME_POOL[key].name} (${formatRole(role)})`;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
for (const entry of ALL_NICKNAMES) {
|
|
722
|
+
const key = Object.entries(NICKNAME_POOL).find(([, v]) => v.name === entry.name)?.[0];
|
|
723
|
+
if (key && !used.has(key)) {
|
|
724
|
+
return `${entry.name} (${formatRole(role)})`;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
const counter = used.size + 1;
|
|
728
|
+
return `Scientist #${counter} (${formatRole(role)})`;
|
|
729
|
+
}
|
|
730
|
+
function formatRole(role) {
|
|
731
|
+
return role.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
732
|
+
}
|
|
733
|
+
|
|
634
734
|
// src/types/errors.ts
|
|
635
735
|
var ERROR_CODES = {
|
|
636
736
|
// Provider
|
|
@@ -3341,11 +3441,46 @@ Working rules:
|
|
|
3341
3441
|
- When in doubt, flag as medium rather than ignoring potential issues`
|
|
3342
3442
|
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
3343
3443
|
};
|
|
3444
|
+
var CRITIC_AGENT = {
|
|
3445
|
+
id: "critic",
|
|
3446
|
+
name: "Critic",
|
|
3447
|
+
role: "critic",
|
|
3448
|
+
prompt: `You are the Critic agent. Your job is to evaluate code quality,
|
|
3449
|
+
architectural decisions, and proposed changes against project conventions,
|
|
3450
|
+
engineering standards, and known quality gates. You do not write code \u2014
|
|
3451
|
+
you judge it.
|
|
3452
|
+
|
|
3453
|
+
Scope:
|
|
3454
|
+
- Evaluate bug severity and fix quality from Bug Hunter reports
|
|
3455
|
+
- Score refactoring plans from Refactor Planner (risk, completeness, trade-offs)
|
|
3456
|
+
- Flag gaps in test coverage, error handling, and edge case coverage
|
|
3457
|
+
- Assess whether a proposed change aligns with existing project patterns
|
|
3458
|
+
- Detect over-engineering or under-engineering relative to the problem scope
|
|
3459
|
+
|
|
3460
|
+
Input format you accept:
|
|
3461
|
+
{ "task": "evaluate | score | review", "subject": "bug_report | refactor_plan | diff", "focus": "correctness | maintainability | risk | all" }
|
|
3462
|
+
|
|
3463
|
+
Output: Markdown critic report:
|
|
3464
|
+
- ## Overall Score (0-10 with rationale)
|
|
3465
|
+
- ## Strengths (what's solid)
|
|
3466
|
+
- ## Weaknesses (what needs work)
|
|
3467
|
+
- ## Specific Concerns (with file:line when applicable)
|
|
3468
|
+
- ## Verdict: **Approve / Needs Revision / Reject**
|
|
3469
|
+
|
|
3470
|
+
Working rules:
|
|
3471
|
+
- Be specific \u2014 "looks fine" is not a review. Cite concrete evidence.
|
|
3472
|
+
- When scoring, explain the delta from a perfect score.
|
|
3473
|
+
- If you have no basis to evaluate a concern, say so rather than speculating.
|
|
3474
|
+
- Prioritise correctness over style; correctness issues block approval.
|
|
3475
|
+
- Score thresholds: \u22657 = Approve, 4-6 = Needs Revision, <4 = Reject`
|
|
3476
|
+
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
3477
|
+
};
|
|
3344
3478
|
var FLEET_ROSTER = {
|
|
3345
3479
|
"audit-log": AUDIT_LOG_AGENT,
|
|
3346
3480
|
"bug-hunter": BUG_HUNTER_AGENT,
|
|
3347
3481
|
"refactor-planner": REFACTOR_PLANNER_AGENT,
|
|
3348
3482
|
"security-scanner": SECURITY_SCANNER_AGENT,
|
|
3483
|
+
"critic": CRITIC_AGENT,
|
|
3349
3484
|
...Object.fromEntries(
|
|
3350
3485
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.config])
|
|
3351
3486
|
)
|
|
@@ -3355,6 +3490,7 @@ var FLEET_ROSTER_BUDGETS = {
|
|
|
3355
3490
|
"bug-hunter": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
|
|
3356
3491
|
"refactor-planner": { timeoutMs: 7.5 * 60 * 60 * 1e3, maxIterations: 6e3, maxToolCalls: 18e3 },
|
|
3357
3492
|
"security-scanner": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
|
|
3493
|
+
"critic": { timeoutMs: 5 * 60 * 60 * 1e3, maxIterations: 4e3, maxToolCalls: 12e3 },
|
|
3358
3494
|
...Object.fromEntries(
|
|
3359
3495
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.budget])
|
|
3360
3496
|
)
|
|
@@ -4428,7 +4564,6 @@ function makeFleetSessionTool(director) {
|
|
|
4428
4564
|
type: "object",
|
|
4429
4565
|
properties: {
|
|
4430
4566
|
subagentId: { type: "string", description: "Subagent id to read the transcript of." },
|
|
4431
|
-
/** Number of trailing lines to return (last N JSONL lines). Default: all. */
|
|
4432
4567
|
tail: { type: "number", description: "Number of trailing JSONL lines to return. Omit for the full transcript." }
|
|
4433
4568
|
},
|
|
4434
4569
|
required: ["subagentId"]
|
|
@@ -4464,8 +4599,6 @@ function makeFleetHealthTool(director) {
|
|
|
4464
4599
|
id: s.id,
|
|
4465
4600
|
status: s.status,
|
|
4466
4601
|
lastEventAt: usage?.lastEventAt,
|
|
4467
|
-
// Budget pressure: fraction of each limit consumed if we have it.
|
|
4468
|
-
// BudgetWarning events carry used/limit ratios; surface them here.
|
|
4469
4602
|
budgetPressure: {
|
|
4470
4603
|
iterations: usage?.iterations,
|
|
4471
4604
|
toolCalls: usage?.toolCalls,
|
|
@@ -4477,6 +4610,415 @@ function makeFleetHealthTool(director) {
|
|
|
4477
4610
|
}
|
|
4478
4611
|
};
|
|
4479
4612
|
}
|
|
4613
|
+
function makeCollabDebugTool(director) {
|
|
4614
|
+
return {
|
|
4615
|
+
name: "collab_debug",
|
|
4616
|
+
description: "Start a collaborative debugging session: BugHunter, RefactorPlanner, and Critic run in parallel on the same target files. BugHunter finds bugs and emits bug.found events. RefactorPlanner listens for bug.found and emits refactor.plan events. Critic evaluates both and emits critic.evaluation events. Returns a structured report with overall verdict (approve / needs_revision / reject).",
|
|
4617
|
+
permission: "auto",
|
|
4618
|
+
mutating: false,
|
|
4619
|
+
inputSchema: {
|
|
4620
|
+
type: "object",
|
|
4621
|
+
properties: {
|
|
4622
|
+
targetPaths: {
|
|
4623
|
+
type: "array",
|
|
4624
|
+
items: { type: "string" },
|
|
4625
|
+
description: "File paths / glob patterns to scan for bugs."
|
|
4626
|
+
},
|
|
4627
|
+
timeoutMs: {
|
|
4628
|
+
type: "number",
|
|
4629
|
+
description: "Timeout in ms. Default: 600000 (10 minutes)."
|
|
4630
|
+
}
|
|
4631
|
+
},
|
|
4632
|
+
required: ["targetPaths"]
|
|
4633
|
+
},
|
|
4634
|
+
async execute(input) {
|
|
4635
|
+
const i = input;
|
|
4636
|
+
if (!i.targetPaths?.length) {
|
|
4637
|
+
return { error: "collab_debug: targetPaths is required and must be non-empty." };
|
|
4638
|
+
}
|
|
4639
|
+
const options = {
|
|
4640
|
+
targetPaths: i.targetPaths,
|
|
4641
|
+
timeoutMs: i.timeoutMs
|
|
4642
|
+
};
|
|
4643
|
+
try {
|
|
4644
|
+
const report = await director.spawnCollab(options);
|
|
4645
|
+
return {
|
|
4646
|
+
sessionId: report.sessionId,
|
|
4647
|
+
overallVerdict: report.overallVerdict,
|
|
4648
|
+
bugCount: report.bugs.length,
|
|
4649
|
+
planCount: report.refactorPlans.length,
|
|
4650
|
+
evaluationCount: report.evaluations.length,
|
|
4651
|
+
summary: report.summary,
|
|
4652
|
+
bugs: report.bugs,
|
|
4653
|
+
refactorPlans: report.refactorPlans,
|
|
4654
|
+
evaluations: report.evaluations
|
|
4655
|
+
};
|
|
4656
|
+
} catch (err) {
|
|
4657
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4658
|
+
return { error: "collab_debug failed: " + msg };
|
|
4659
|
+
}
|
|
4660
|
+
}
|
|
4661
|
+
};
|
|
4662
|
+
}
|
|
4663
|
+
function makeFleetEmitTool(director) {
|
|
4664
|
+
return {
|
|
4665
|
+
name: "fleet_emit",
|
|
4666
|
+
description: "Emit a structured event on the FleetBus. Use this when you find a bug (bug.found), complete a refactor plan (refactor.plan), or finish an evaluation (critic.evaluation). Events are routed to other agents in the collab session.",
|
|
4667
|
+
permission: "auto",
|
|
4668
|
+
mutating: false,
|
|
4669
|
+
inputSchema: {
|
|
4670
|
+
type: "object",
|
|
4671
|
+
properties: {
|
|
4672
|
+
type: {
|
|
4673
|
+
type: "string",
|
|
4674
|
+
description: "Event type: bug.found | refactor.plan | critic.evaluation"
|
|
4675
|
+
},
|
|
4676
|
+
payload: {
|
|
4677
|
+
type: "object",
|
|
4678
|
+
description: "Event payload matching the event type."
|
|
4679
|
+
}
|
|
4680
|
+
},
|
|
4681
|
+
required: ["type", "payload"]
|
|
4682
|
+
},
|
|
4683
|
+
async execute(input) {
|
|
4684
|
+
const i = input;
|
|
4685
|
+
director.fleet.emit({
|
|
4686
|
+
subagentId: director.id,
|
|
4687
|
+
ts: Date.now(),
|
|
4688
|
+
type: i.type,
|
|
4689
|
+
payload: i.payload
|
|
4690
|
+
});
|
|
4691
|
+
return { ok: true, event: i.type };
|
|
4692
|
+
}
|
|
4693
|
+
};
|
|
4694
|
+
}
|
|
4695
|
+
var CollabSession = class extends EventEmitter {
|
|
4696
|
+
sessionId;
|
|
4697
|
+
options;
|
|
4698
|
+
snapshot;
|
|
4699
|
+
director;
|
|
4700
|
+
fleetBus;
|
|
4701
|
+
subagentIds = /* @__PURE__ */ new Map();
|
|
4702
|
+
// role → subagentId
|
|
4703
|
+
bugs = /* @__PURE__ */ new Map();
|
|
4704
|
+
plans = /* @__PURE__ */ new Map();
|
|
4705
|
+
evaluations = /* @__PURE__ */ new Map();
|
|
4706
|
+
disposers = new Array();
|
|
4707
|
+
settled = false;
|
|
4708
|
+
timeoutMs;
|
|
4709
|
+
constructor(director, fleetBus, options) {
|
|
4710
|
+
super();
|
|
4711
|
+
this.sessionId = randomUUID();
|
|
4712
|
+
this.options = options;
|
|
4713
|
+
this.director = director;
|
|
4714
|
+
this.fleetBus = fleetBus;
|
|
4715
|
+
this.timeoutMs = options.timeoutMs ?? 10 * 60 * 1e3;
|
|
4716
|
+
if (options.prebuiltSnapshot) {
|
|
4717
|
+
this.snapshot = options.prebuiltSnapshot;
|
|
4718
|
+
} else {
|
|
4719
|
+
this.snapshot = {
|
|
4720
|
+
id: this.sessionId,
|
|
4721
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4722
|
+
files: []
|
|
4723
|
+
};
|
|
4724
|
+
}
|
|
4725
|
+
}
|
|
4726
|
+
/**
|
|
4727
|
+
* Read the target files from disk and populate the snapshot.
|
|
4728
|
+
* Call this after construction if you did not provide a prebuiltSnapshot
|
|
4729
|
+
* and want the session to operate on real file contents.
|
|
4730
|
+
*/
|
|
4731
|
+
async buildSnapshot() {
|
|
4732
|
+
if (this.snapshot.files.length > 0) return this.snapshot;
|
|
4733
|
+
for (const filePath of this.options.targetPaths) {
|
|
4734
|
+
try {
|
|
4735
|
+
const content = await fsp5.readFile(filePath, "utf8");
|
|
4736
|
+
const ext = filePath.split(".").pop() ?? "";
|
|
4737
|
+
const language = ext === "ts" || ext === "tsx" ? "typescript" : ext === "js" || ext === "jsx" ? "javascript" : ext === "md" ? "markdown" : ext === "json" ? "json" : void 0;
|
|
4738
|
+
this.snapshot.files.push({ path: filePath, content, language });
|
|
4739
|
+
} catch {
|
|
4740
|
+
this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
|
|
4741
|
+
}
|
|
4742
|
+
}
|
|
4743
|
+
return this.snapshot;
|
|
4744
|
+
}
|
|
4745
|
+
/**
|
|
4746
|
+
* Start the collaborative session: snapshot files, spawn all three agents,
|
|
4747
|
+
* wire up event routing, and wait for completion.
|
|
4748
|
+
*/
|
|
4749
|
+
async start() {
|
|
4750
|
+
if (this.settled) throw new Error("session already settled");
|
|
4751
|
+
this.settled = true;
|
|
4752
|
+
await this.buildSnapshot();
|
|
4753
|
+
this.wireFleetBus();
|
|
4754
|
+
const [bugHunterId, refactorPlannerId, criticId] = await Promise.all([
|
|
4755
|
+
this.spawnAgent("bug-hunter", this.buildBugHunterTask()),
|
|
4756
|
+
this.spawnAgent("refactor-planner", this.buildRefactorPlannerTask()),
|
|
4757
|
+
this.spawnAgent("critic", this.buildCriticTask())
|
|
4758
|
+
]);
|
|
4759
|
+
this.subagentIds.set("bug-hunter", bugHunterId);
|
|
4760
|
+
this.subagentIds.set("refactor-planner", refactorPlannerId);
|
|
4761
|
+
this.subagentIds.set("critic", criticId);
|
|
4762
|
+
const timeout = new Promise((_, reject) => {
|
|
4763
|
+
setTimeout(
|
|
4764
|
+
() => reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`)),
|
|
4765
|
+
this.timeoutMs
|
|
4766
|
+
);
|
|
4767
|
+
});
|
|
4768
|
+
let results;
|
|
4769
|
+
try {
|
|
4770
|
+
results = await Promise.race([
|
|
4771
|
+
Promise.all([
|
|
4772
|
+
this.director.awaitTasks([bugHunterId]),
|
|
4773
|
+
this.director.awaitTasks([refactorPlannerId]),
|
|
4774
|
+
this.director.awaitTasks([criticId])
|
|
4775
|
+
]),
|
|
4776
|
+
timeout
|
|
4777
|
+
]);
|
|
4778
|
+
} catch (err) {
|
|
4779
|
+
this.cleanup();
|
|
4780
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
4781
|
+
this.emit("session.error", error);
|
|
4782
|
+
throw error;
|
|
4783
|
+
}
|
|
4784
|
+
for (const result of results.flat()) {
|
|
4785
|
+
await this.parseAndEmit(result);
|
|
4786
|
+
}
|
|
4787
|
+
const report = this.assembleReport();
|
|
4788
|
+
this.cleanup();
|
|
4789
|
+
this.emit("session.done", report);
|
|
4790
|
+
return report;
|
|
4791
|
+
}
|
|
4792
|
+
/**
|
|
4793
|
+
* Parse a completed agent's final text output and route the structured
|
|
4794
|
+
* findings onto the FleetBus. Agents are instructed to emit one JSON object
|
|
4795
|
+
* per finding / plan / evaluation; we scan the output for balanced JSON
|
|
4796
|
+
* objects (tolerating prose, markdown, and ```json fences around them) and
|
|
4797
|
+
* dispatch each by its discriminating key. The wired FleetBus listeners then
|
|
4798
|
+
* collect them into the bugs / plans / evaluations maps.
|
|
4799
|
+
*
|
|
4800
|
+
* Failed or empty results are ignored, and a single malformed object never
|
|
4801
|
+
* aborts the collation pass — see {@link extractJsonObjects}.
|
|
4802
|
+
*/
|
|
4803
|
+
async parseAndEmit(result) {
|
|
4804
|
+
if (result.status !== "success" || result.result == null) return;
|
|
4805
|
+
const text = typeof result.result === "string" ? result.result : JSON.stringify(result.result);
|
|
4806
|
+
for (const obj of this.extractJsonObjects(text)) {
|
|
4807
|
+
const type = "finding" in obj ? "bug.found" : "plan" in obj ? "refactor.plan" : "evaluation" in obj ? "critic.evaluation" : null;
|
|
4808
|
+
if (!type) continue;
|
|
4809
|
+
this.fleetBus.emit({
|
|
4810
|
+
subagentId: result.subagentId,
|
|
4811
|
+
taskId: result.taskId,
|
|
4812
|
+
ts: Date.now(),
|
|
4813
|
+
type,
|
|
4814
|
+
payload: obj
|
|
4815
|
+
});
|
|
4816
|
+
}
|
|
4817
|
+
}
|
|
4818
|
+
/**
|
|
4819
|
+
* Extract every top-level JSON object embedded in free-form agent text.
|
|
4820
|
+
* Scans for balanced braces while respecting string literals and escape
|
|
4821
|
+
* sequences, so prose, markdown headers, and code fences around the JSON
|
|
4822
|
+
* are skipped. Malformed spans are dropped silently rather than thrown.
|
|
4823
|
+
*/
|
|
4824
|
+
extractJsonObjects(text) {
|
|
4825
|
+
const objects = [];
|
|
4826
|
+
let depth = 0;
|
|
4827
|
+
let start = -1;
|
|
4828
|
+
let inString = false;
|
|
4829
|
+
let escaped = false;
|
|
4830
|
+
for (let i = 0; i < text.length; i++) {
|
|
4831
|
+
const ch = text[i];
|
|
4832
|
+
if (inString) {
|
|
4833
|
+
if (escaped) escaped = false;
|
|
4834
|
+
else if (ch === "\\") escaped = true;
|
|
4835
|
+
else if (ch === '"') inString = false;
|
|
4836
|
+
continue;
|
|
4837
|
+
}
|
|
4838
|
+
if (ch === '"') {
|
|
4839
|
+
inString = true;
|
|
4840
|
+
} else if (ch === "{") {
|
|
4841
|
+
if (depth === 0) start = i;
|
|
4842
|
+
depth++;
|
|
4843
|
+
} else if (ch === "}" && depth > 0) {
|
|
4844
|
+
depth--;
|
|
4845
|
+
if (depth === 0 && start >= 0) {
|
|
4846
|
+
const candidate = text.slice(start, i + 1);
|
|
4847
|
+
try {
|
|
4848
|
+
const parsed = JSON.parse(candidate);
|
|
4849
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
4850
|
+
objects.push(parsed);
|
|
4851
|
+
}
|
|
4852
|
+
} catch {
|
|
4853
|
+
}
|
|
4854
|
+
start = -1;
|
|
4855
|
+
}
|
|
4856
|
+
}
|
|
4857
|
+
}
|
|
4858
|
+
return objects;
|
|
4859
|
+
}
|
|
4860
|
+
async spawnAgent(role, taskBrief) {
|
|
4861
|
+
const cfg = {
|
|
4862
|
+
id: `${role}-${this.sessionId}`,
|
|
4863
|
+
name: role,
|
|
4864
|
+
role
|
|
4865
|
+
// No fleet_emit tool — agents output structured JSON, the Director parses
|
|
4866
|
+
// results and emits FleetBus events. This keeps the agent simple and the
|
|
4867
|
+
// routing deterministic.
|
|
4868
|
+
};
|
|
4869
|
+
const subagentId = await this.director.spawn(cfg);
|
|
4870
|
+
await this.director.assign({
|
|
4871
|
+
id: randomUUID(),
|
|
4872
|
+
subagentId,
|
|
4873
|
+
description: taskBrief
|
|
4874
|
+
});
|
|
4875
|
+
return subagentId;
|
|
4876
|
+
}
|
|
4877
|
+
buildBugHunterTask() {
|
|
4878
|
+
this.options.targetPaths.join(", ");
|
|
4879
|
+
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
4880
|
+
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
4881
|
+
${f.content}`).join("\n\n");
|
|
4882
|
+
return `You are BugHunter. Scan the following files for bugs and code smells.
|
|
4883
|
+
|
|
4884
|
+
Target files:
|
|
4885
|
+
${fileContents}
|
|
4886
|
+
|
|
4887
|
+
For each bug found, write ONE valid JSON line to the scratchpad with this exact structure:
|
|
4888
|
+
{ "finding": { "id": "<uuid>", "type": "<pattern>", "severity": "<critical|high|medium|low>", "location": { "file": "<path>", "line": <n> }, "description": "<explain>", "suggestedFix": "<optional>" } }.
|
|
4889
|
+
After scanning all files, write your full markdown bug report to the shared scratchpad at:
|
|
4890
|
+
${scratchpad}/bug-hunter-report-${this.sessionId}.md`;
|
|
4891
|
+
}
|
|
4892
|
+
buildRefactorPlannerTask() {
|
|
4893
|
+
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
4894
|
+
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
4895
|
+
${f.content}`).join("\n\n");
|
|
4896
|
+
return `You are RefactorPlanner. Plan refactorings for the following files.
|
|
4897
|
+
|
|
4898
|
+
Target files:
|
|
4899
|
+
${fileContents}
|
|
4900
|
+
|
|
4901
|
+
Listen for bug.found events from BugHunter on the fleet bus. For each bug, create a refactor plan and write one JSON line per plan:
|
|
4902
|
+
{ "plan": { "id": "<uuid>", "basedOnBugIds": ["<bug-id>"], "phases": [...], "riskScore": "<low|medium|high>", "estimatedChangeCount": <n>, "rollbackStrategy": "<text>" } }.
|
|
4903
|
+
After planning, write your full markdown plan to:
|
|
4904
|
+
${scratchpad}/refactor-plan-${this.sessionId}.md`;
|
|
4905
|
+
}
|
|
4906
|
+
buildCriticTask() {
|
|
4907
|
+
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
4908
|
+
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
4909
|
+
${f.content}`).join("\n\n");
|
|
4910
|
+
return `You are Critic. Evaluate bug findings and refactor plans as they arrive via fleet bus events.
|
|
4911
|
+
|
|
4912
|
+
Target files:
|
|
4913
|
+
${fileContents}
|
|
4914
|
+
|
|
4915
|
+
Subscribe to bug.found events (from BugHunter) and refactor.plan events (from RefactorPlanner). For each subject, write one JSON line per evaluation:
|
|
4916
|
+
{ "evaluation": { "id": "<uuid>", "subjectType": "<bug_finding|refactor_plan>", "subjectId": "<id>", "score": <0-10>, "verdict": "<approve|needs_revision|reject>", "strengths": [...], "weaknesses": [...], "concerns": [...] } }.
|
|
4917
|
+
After all evaluations, write your markdown critic report to:
|
|
4918
|
+
${scratchpad}/critic-report-${this.sessionId}.md`;
|
|
4919
|
+
}
|
|
4920
|
+
wireFleetBus() {
|
|
4921
|
+
const d1 = this.fleetBus.filter("bug.found", (e) => {
|
|
4922
|
+
const payload = e.payload;
|
|
4923
|
+
if (payload?.finding) {
|
|
4924
|
+
this.bugs.set(payload.finding.id, payload.finding);
|
|
4925
|
+
this.emit("bug.found", payload);
|
|
4926
|
+
}
|
|
4927
|
+
});
|
|
4928
|
+
this.disposers.push(d1);
|
|
4929
|
+
const d2 = this.fleetBus.filter("refactor.plan", (e) => {
|
|
4930
|
+
const payload = e.payload;
|
|
4931
|
+
if (payload?.plan) {
|
|
4932
|
+
this.plans.set(payload.plan.id, payload.plan);
|
|
4933
|
+
this.emit("refactor.plan", payload);
|
|
4934
|
+
}
|
|
4935
|
+
});
|
|
4936
|
+
this.disposers.push(d2);
|
|
4937
|
+
const d3 = this.fleetBus.filter("critic.evaluation", (e) => {
|
|
4938
|
+
const payload = e.payload;
|
|
4939
|
+
if (payload?.evaluation) {
|
|
4940
|
+
this.evaluations.set(payload.evaluation.id, payload.evaluation);
|
|
4941
|
+
this.emit("critic.evaluation", payload);
|
|
4942
|
+
}
|
|
4943
|
+
});
|
|
4944
|
+
this.disposers.push(d3);
|
|
4945
|
+
}
|
|
4946
|
+
assembleReport() {
|
|
4947
|
+
const bugList = Array.from(this.bugs.values());
|
|
4948
|
+
const planList = Array.from(this.plans.values());
|
|
4949
|
+
const evalList = Array.from(this.evaluations.values());
|
|
4950
|
+
const verdictOrder = {
|
|
4951
|
+
approve: 0,
|
|
4952
|
+
needs_revision: 1,
|
|
4953
|
+
reject: 2
|
|
4954
|
+
};
|
|
4955
|
+
const overallVerdict = evalList.reduce(
|
|
4956
|
+
(worst, eval_) => {
|
|
4957
|
+
const w = verdictOrder[worst];
|
|
4958
|
+
const c = verdictOrder[eval_.verdict];
|
|
4959
|
+
return c > w ? eval_.verdict : worst;
|
|
4960
|
+
},
|
|
4961
|
+
"approve"
|
|
4962
|
+
);
|
|
4963
|
+
const summary = this.buildMarkdownSummary(bugList, planList, evalList, overallVerdict);
|
|
4964
|
+
return {
|
|
4965
|
+
sessionId: this.sessionId,
|
|
4966
|
+
startedAt: this.snapshot.createdAt,
|
|
4967
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4968
|
+
targetPaths: this.options.targetPaths,
|
|
4969
|
+
bugs: bugList,
|
|
4970
|
+
refactorPlans: planList,
|
|
4971
|
+
evaluations: evalList,
|
|
4972
|
+
overallVerdict,
|
|
4973
|
+
summary
|
|
4974
|
+
};
|
|
4975
|
+
}
|
|
4976
|
+
buildMarkdownSummary(bugs, plans, evals, overallVerdict) {
|
|
4977
|
+
const lines = [
|
|
4978
|
+
`## Collaborative Debugging Report \u2014 ${this.sessionId}`,
|
|
4979
|
+
"",
|
|
4980
|
+
`**Target:** ${this.options.targetPaths.join(", ")}`,
|
|
4981
|
+
`**Overall Verdict:** **${overallVerdict.toUpperCase()}**`,
|
|
4982
|
+
""
|
|
4983
|
+
];
|
|
4984
|
+
if (bugs.length > 0) {
|
|
4985
|
+
lines.push("### Bugs Found", "");
|
|
4986
|
+
for (const b of bugs) {
|
|
4987
|
+
lines.push(
|
|
4988
|
+
`- **[${b.severity.toUpperCase()}]** \`${b.location.file}:${b.location.line}\` \u2014 ${b.description}`
|
|
4989
|
+
);
|
|
4990
|
+
}
|
|
4991
|
+
lines.push("");
|
|
4992
|
+
}
|
|
4993
|
+
if (plans.length > 0) {
|
|
4994
|
+
lines.push("### Refactor Plans", "");
|
|
4995
|
+
for (const p of plans) {
|
|
4996
|
+
lines.push(`- **Phase plan** (risk: ${p.riskScore}, ~${p.estimatedChangeCount} changes)`);
|
|
4997
|
+
for (const phase of p.phases) {
|
|
4998
|
+
lines.push(` - Phase ${phase.number}: ${phase.title} [${phase.risk}]`);
|
|
4999
|
+
}
|
|
5000
|
+
}
|
|
5001
|
+
lines.push("");
|
|
5002
|
+
}
|
|
5003
|
+
if (evals.length > 0) {
|
|
5004
|
+
lines.push("### Critic Evaluations", "");
|
|
5005
|
+
for (const e of evals) {
|
|
5006
|
+
lines.push(`- [${e.subjectType}] score=${e.score}/10 \u2014 **${e.verdict.toUpperCase()}**`);
|
|
5007
|
+
for (const c of e.concerns) {
|
|
5008
|
+
if (c.severity === "blocking") {
|
|
5009
|
+
lines.push(` - ${c.description}`);
|
|
5010
|
+
}
|
|
5011
|
+
}
|
|
5012
|
+
}
|
|
5013
|
+
lines.push("");
|
|
5014
|
+
}
|
|
5015
|
+
return lines.join("\n");
|
|
5016
|
+
}
|
|
5017
|
+
cleanup() {
|
|
5018
|
+
for (const dispose of this.disposers) dispose();
|
|
5019
|
+
this.disposers.length = 0;
|
|
5020
|
+
}
|
|
5021
|
+
};
|
|
4480
5022
|
|
|
4481
5023
|
// src/coordination/director.ts
|
|
4482
5024
|
var FleetSpawnBudgetError = class extends Error {
|
|
@@ -4555,6 +5097,8 @@ var Director = class {
|
|
|
4555
5097
|
subagentBridges = /* @__PURE__ */ new Map();
|
|
4556
5098
|
/** Tracks per-spawn config + assigned task ids for manifest writing. */
|
|
4557
5099
|
manifestEntries = /* @__PURE__ */ new Map();
|
|
5100
|
+
/** Tracks assigned nicknames so the same name is never reused in one fleet. */
|
|
5101
|
+
_usedNicknames = /* @__PURE__ */ new Set();
|
|
4558
5102
|
manifestPath;
|
|
4559
5103
|
roster;
|
|
4560
5104
|
directorPreamble;
|
|
@@ -4632,7 +5176,7 @@ var Director = class {
|
|
|
4632
5176
|
}, opts.checkpointDebounceMs ?? 250) : null;
|
|
4633
5177
|
this.fleetManager = opts.fleetManager;
|
|
4634
5178
|
if (this.sharedScratchpadPath) {
|
|
4635
|
-
void
|
|
5179
|
+
void fsp5.mkdir(this.sharedScratchpadPath, { recursive: true }).catch(
|
|
4636
5180
|
(err) => this.logShutdownError("shared_scratchpad_mkdir", err)
|
|
4637
5181
|
);
|
|
4638
5182
|
}
|
|
@@ -4848,9 +5392,21 @@ var Director = class {
|
|
|
4848
5392
|
}
|
|
4849
5393
|
}
|
|
4850
5394
|
let result;
|
|
5395
|
+
const needsNickname = config.name === config.role || !config.name || config.name === "subagent";
|
|
5396
|
+
if (needsNickname) {
|
|
5397
|
+
const role = config.role ?? "subagent";
|
|
5398
|
+
if (this.fleetManager) {
|
|
5399
|
+
this.fleetManager.assignNicknameAndRecord("", config, priceLookup);
|
|
5400
|
+
} else {
|
|
5401
|
+
config.name = assignNickname(role, this._usedNicknames);
|
|
5402
|
+
this._usedNicknames.add(config.name.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-"));
|
|
5403
|
+
}
|
|
5404
|
+
}
|
|
4851
5405
|
result = await this.coordinator.spawn(config);
|
|
4852
5406
|
if (this.fleetManager) {
|
|
4853
|
-
|
|
5407
|
+
if (!needsNickname) {
|
|
5408
|
+
this.fleetManager.recordSpawn(result.subagentId, config, priceLookup);
|
|
5409
|
+
}
|
|
4854
5410
|
} else {
|
|
4855
5411
|
this.spawnCount += 1;
|
|
4856
5412
|
this.subagentMeta.set(result.subagentId, {
|
|
@@ -5006,7 +5562,7 @@ var Director = class {
|
|
|
5006
5562
|
})),
|
|
5007
5563
|
usage: this.usage.snapshot()
|
|
5008
5564
|
};
|
|
5009
|
-
await
|
|
5565
|
+
await fsp5.mkdir(path4.dirname(this.manifestPath), { recursive: true });
|
|
5010
5566
|
await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
5011
5567
|
return this.manifestPath;
|
|
5012
5568
|
}
|
|
@@ -5175,7 +5731,7 @@ var Director = class {
|
|
|
5175
5731
|
const filePath = path4.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
|
|
5176
5732
|
let raw;
|
|
5177
5733
|
try {
|
|
5178
|
-
raw = await
|
|
5734
|
+
raw = await fsp5.readFile(filePath, "utf8");
|
|
5179
5735
|
} catch {
|
|
5180
5736
|
return null;
|
|
5181
5737
|
}
|
|
@@ -5286,7 +5842,9 @@ var Director = class {
|
|
|
5286
5842
|
makeFleetStatusTool(this),
|
|
5287
5843
|
makeFleetUsageTool(this),
|
|
5288
5844
|
makeFleetSessionTool(this),
|
|
5289
|
-
makeFleetHealthTool(this)
|
|
5845
|
+
makeFleetHealthTool(this),
|
|
5846
|
+
makeCollabDebugTool(this),
|
|
5847
|
+
makeFleetEmitTool(this)
|
|
5290
5848
|
];
|
|
5291
5849
|
return t;
|
|
5292
5850
|
}
|
|
@@ -5298,6 +5856,17 @@ var Director = class {
|
|
|
5298
5856
|
async acquireCheckpointLock() {
|
|
5299
5857
|
return this.stateCheckpoint ? this.stateCheckpoint.acquireLock() : true;
|
|
5300
5858
|
}
|
|
5859
|
+
/**
|
|
5860
|
+
* Start a collaborative debugging session: BugHunter, RefactorPlanner,
|
|
5861
|
+
* and Critic run in parallel on the same target files, with findings
|
|
5862
|
+
* flowing through the FleetBus (bug.found → refactor.plan →
|
|
5863
|
+
* critic.evaluation). Returns a structured CollabDebugReport when all
|
|
5864
|
+
* three agents complete or the session times out.
|
|
5865
|
+
*/
|
|
5866
|
+
async spawnCollab(options) {
|
|
5867
|
+
const session = new CollabSession(this, this.fleet, options);
|
|
5868
|
+
return session.start();
|
|
5869
|
+
}
|
|
5301
5870
|
/**
|
|
5302
5871
|
* Resume from a prior checkpoint snapshot (loaded via
|
|
5303
5872
|
* `loadDirectorState()`). Re-attach to the fleet mid-flight so
|
|
@@ -5578,7 +6147,7 @@ async function readSubagentPartial(opts, subagentId) {
|
|
|
5578
6147
|
candidates.push(path4.join(opts.sessionsRoot, opts.directorRunId, `${subagentId}.jsonl`));
|
|
5579
6148
|
} else {
|
|
5580
6149
|
try {
|
|
5581
|
-
const entries = await
|
|
6150
|
+
const entries = await fsp5.readdir(opts.sessionsRoot, { withFileTypes: true });
|
|
5582
6151
|
for (const entry of entries) {
|
|
5583
6152
|
if (entry.isDirectory()) {
|
|
5584
6153
|
candidates.push(path4.join(opts.sessionsRoot, entry.name, `${subagentId}.jsonl`));
|
|
@@ -5591,7 +6160,7 @@ async function readSubagentPartial(opts, subagentId) {
|
|
|
5591
6160
|
for (const file of candidates) {
|
|
5592
6161
|
let raw;
|
|
5593
6162
|
try {
|
|
5594
|
-
raw = await
|
|
6163
|
+
raw = await fsp5.readFile(file, "utf8");
|
|
5595
6164
|
} catch {
|
|
5596
6165
|
continue;
|
|
5597
6166
|
}
|
|
@@ -5887,9 +6456,11 @@ function isEmptyMessage(msg) {
|
|
|
5887
6456
|
var DefaultSessionStore = class {
|
|
5888
6457
|
dir;
|
|
5889
6458
|
events;
|
|
6459
|
+
secretScrubber;
|
|
5890
6460
|
constructor(opts) {
|
|
5891
6461
|
this.dir = opts.dir;
|
|
5892
6462
|
this.events = opts.events;
|
|
6463
|
+
this.secretScrubber = opts.secretScrubber;
|
|
5893
6464
|
}
|
|
5894
6465
|
/** Join session ID to its absolute path within the store directory. */
|
|
5895
6466
|
sessionPath(id, ext) {
|
|
@@ -5906,7 +6477,7 @@ var DefaultSessionStore = class {
|
|
|
5906
6477
|
const file = path4.join(shardDir, `${id}.jsonl`);
|
|
5907
6478
|
let handle;
|
|
5908
6479
|
try {
|
|
5909
|
-
handle = await
|
|
6480
|
+
handle = await fsp5.open(file, "a", 384);
|
|
5910
6481
|
} catch (err) {
|
|
5911
6482
|
throw new Error(
|
|
5912
6483
|
`Failed to open session file: ${err instanceof Error ? err.message : String(err)}`,
|
|
@@ -5914,7 +6485,11 @@ var DefaultSessionStore = class {
|
|
|
5914
6485
|
);
|
|
5915
6486
|
}
|
|
5916
6487
|
try {
|
|
5917
|
-
return new FileSessionWriter(id, handle, startedAt, meta, this.events, {
|
|
6488
|
+
return new FileSessionWriter(id, handle, startedAt, meta, this.events, {
|
|
6489
|
+
dir: shardDir,
|
|
6490
|
+
filePath: file,
|
|
6491
|
+
secretScrubber: this.secretScrubber
|
|
6492
|
+
});
|
|
5918
6493
|
} catch (err) {
|
|
5919
6494
|
await handle.close().catch(() => {
|
|
5920
6495
|
});
|
|
@@ -5926,7 +6501,7 @@ var DefaultSessionStore = class {
|
|
|
5926
6501
|
const data = await this.load(id);
|
|
5927
6502
|
let handle;
|
|
5928
6503
|
try {
|
|
5929
|
-
handle = await
|
|
6504
|
+
handle = await fsp5.open(file, "a", 384);
|
|
5930
6505
|
} catch (err) {
|
|
5931
6506
|
throw new Error(
|
|
5932
6507
|
`Failed to open session "${id}" for append: ${err instanceof Error ? err.message : String(err)}`,
|
|
@@ -5944,7 +6519,7 @@ var DefaultSessionStore = class {
|
|
|
5944
6519
|
provider: data.metadata.provider
|
|
5945
6520
|
},
|
|
5946
6521
|
this.events,
|
|
5947
|
-
{ resumed: true, dir: this.dir, filePath: file }
|
|
6522
|
+
{ resumed: true, dir: this.dir, filePath: file, secretScrubber: this.secretScrubber }
|
|
5948
6523
|
);
|
|
5949
6524
|
return { writer, data };
|
|
5950
6525
|
} catch (err) {
|
|
@@ -5955,7 +6530,7 @@ var DefaultSessionStore = class {
|
|
|
5955
6530
|
}
|
|
5956
6531
|
async load(id) {
|
|
5957
6532
|
const file = this.sessionPath(id, ".jsonl");
|
|
5958
|
-
const raw = await
|
|
6533
|
+
const raw = await fsp5.readFile(file, "utf8");
|
|
5959
6534
|
const lines = raw.split("\n").filter((l) => l.trim());
|
|
5960
6535
|
const events = [];
|
|
5961
6536
|
for (const line of lines) {
|
|
@@ -5990,7 +6565,7 @@ var DefaultSessionStore = class {
|
|
|
5990
6565
|
/** Recursively collect all session IDs from shard subdirectories. */
|
|
5991
6566
|
async collectSessionIds(dir) {
|
|
5992
6567
|
const ids = [];
|
|
5993
|
-
const entries = await
|
|
6568
|
+
const entries = await fsp5.readdir(dir, { withFileTypes: true });
|
|
5994
6569
|
for (const entry of entries) {
|
|
5995
6570
|
const full = path4.join(dir, entry.name);
|
|
5996
6571
|
if (entry.isDirectory()) {
|
|
@@ -6004,11 +6579,11 @@ var DefaultSessionStore = class {
|
|
|
6004
6579
|
async summaryFor(id) {
|
|
6005
6580
|
const manifest = this.sessionPath(id, ".summary.json");
|
|
6006
6581
|
try {
|
|
6007
|
-
const raw = await
|
|
6582
|
+
const raw = await fsp5.readFile(manifest, "utf8");
|
|
6008
6583
|
return JSON.parse(raw);
|
|
6009
6584
|
} catch {
|
|
6010
6585
|
const full = this.sessionPath(id, ".jsonl");
|
|
6011
|
-
const stat3 = await
|
|
6586
|
+
const stat3 = await fsp5.stat(full);
|
|
6012
6587
|
const summary = await this.summarize(id, stat3.mtime.toISOString());
|
|
6013
6588
|
await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
|
|
6014
6589
|
console.warn(
|
|
@@ -6020,8 +6595,8 @@ var DefaultSessionStore = class {
|
|
|
6020
6595
|
}
|
|
6021
6596
|
}
|
|
6022
6597
|
async delete(id) {
|
|
6023
|
-
await
|
|
6024
|
-
await
|
|
6598
|
+
await fsp5.unlink(this.sessionPath(id, ".jsonl"));
|
|
6599
|
+
await fsp5.unlink(this.sessionPath(id, ".summary.json")).catch(() => void 0);
|
|
6025
6600
|
}
|
|
6026
6601
|
async clearHistory(id) {
|
|
6027
6602
|
await this.ensureShardDir(id);
|
|
@@ -6035,8 +6610,8 @@ var DefaultSessionStore = class {
|
|
|
6035
6610
|
provider: "unknown"
|
|
6036
6611
|
})}
|
|
6037
6612
|
`;
|
|
6038
|
-
await
|
|
6039
|
-
await
|
|
6613
|
+
await fsp5.writeFile(file, record, "utf8");
|
|
6614
|
+
await fsp5.unlink(meta).catch(() => void 0);
|
|
6040
6615
|
}
|
|
6041
6616
|
async summarize(id, mtime) {
|
|
6042
6617
|
try {
|
|
@@ -6150,6 +6725,7 @@ var FileSessionWriter = class {
|
|
|
6150
6725
|
this.resumed = opts.resumed ?? false;
|
|
6151
6726
|
this.manifestFile = opts.dir ? path4.join(opts.dir, `${id}.summary.json`) : "";
|
|
6152
6727
|
this.filePath = opts.filePath ?? "";
|
|
6728
|
+
this.secretScrubber = opts.secretScrubber;
|
|
6153
6729
|
this.summary = {
|
|
6154
6730
|
id,
|
|
6155
6731
|
title: "(empty session)",
|
|
@@ -6178,6 +6754,29 @@ var FileSessionWriter = class {
|
|
|
6178
6754
|
resumed;
|
|
6179
6755
|
appendFailCount = 0;
|
|
6180
6756
|
lastAppendWarnAt = 0;
|
|
6757
|
+
secretScrubber;
|
|
6758
|
+
/**
|
|
6759
|
+
* Scrub secrets out of conversation-turn events before they are observed
|
|
6760
|
+
* for the summary, written to the JSONL log, or surfaced on resume. Only
|
|
6761
|
+
* `user_input` / `llm_response` carry free-form user/model text; other event
|
|
6762
|
+
* types either have no secret-bearing content or are already scrubbed
|
|
6763
|
+
* upstream (tool results). Returns the event unchanged when no scrubber is
|
|
6764
|
+
* configured.
|
|
6765
|
+
*/
|
|
6766
|
+
scrubEvent(event) {
|
|
6767
|
+
const s = this.secretScrubber;
|
|
6768
|
+
if (!s) return event;
|
|
6769
|
+
if (event.type === "user_input") {
|
|
6770
|
+
return {
|
|
6771
|
+
...event,
|
|
6772
|
+
content: typeof event.content === "string" ? s.scrub(event.content) : s.scrubObject(event.content)
|
|
6773
|
+
};
|
|
6774
|
+
}
|
|
6775
|
+
if (event.type === "llm_response") {
|
|
6776
|
+
return { ...event, content: s.scrubObject(event.content) };
|
|
6777
|
+
}
|
|
6778
|
+
return event;
|
|
6779
|
+
}
|
|
6181
6780
|
promptIndex = 0;
|
|
6182
6781
|
pendingFileSnapshots = [];
|
|
6183
6782
|
/** Tracks open tool_use IDs during the current run to serialize on close for resume. */
|
|
@@ -6199,7 +6798,7 @@ var FileSessionWriter = class {
|
|
|
6199
6798
|
`;
|
|
6200
6799
|
try {
|
|
6201
6800
|
if (this.filePath) {
|
|
6202
|
-
await
|
|
6801
|
+
await fsp5.writeFile(this.filePath, record, { flag: "a", mode: 384 });
|
|
6203
6802
|
}
|
|
6204
6803
|
} catch {
|
|
6205
6804
|
}
|
|
@@ -6210,9 +6809,10 @@ var FileSessionWriter = class {
|
|
|
6210
6809
|
this.initDone = true;
|
|
6211
6810
|
await this.writeSessionStartLazy();
|
|
6212
6811
|
}
|
|
6213
|
-
this.
|
|
6812
|
+
const scrubbed = this.scrubEvent(event);
|
|
6813
|
+
this.observeForSummary(scrubbed);
|
|
6214
6814
|
try {
|
|
6215
|
-
await this.handle.appendFile(`${JSON.stringify(
|
|
6815
|
+
await this.handle.appendFile(`${JSON.stringify(scrubbed)}
|
|
6216
6816
|
`, "utf8");
|
|
6217
6817
|
} catch (err) {
|
|
6218
6818
|
this.appendFailCount++;
|
|
@@ -6292,7 +6892,7 @@ var FileSessionWriter = class {
|
|
|
6292
6892
|
}
|
|
6293
6893
|
async truncateToCheckpoint(targetPromptIndex) {
|
|
6294
6894
|
if (!this.filePath) return 0;
|
|
6295
|
-
const raw = await
|
|
6895
|
+
const raw = await fsp5.readFile(this.filePath, "utf8");
|
|
6296
6896
|
const lines = raw.split("\n");
|
|
6297
6897
|
const kept = [];
|
|
6298
6898
|
let removedCount = 0;
|
|
@@ -6330,13 +6930,13 @@ var FileSessionWriter = class {
|
|
|
6330
6930
|
}
|
|
6331
6931
|
const truncated = kept.join("\n");
|
|
6332
6932
|
const tmpPath = `${this.filePath}.rewind.tmp`;
|
|
6333
|
-
await
|
|
6933
|
+
await fsp5.writeFile(tmpPath, truncated + "\n", "utf8");
|
|
6334
6934
|
try {
|
|
6335
6935
|
await this.handle.close();
|
|
6336
|
-
await
|
|
6337
|
-
this.handle = await
|
|
6936
|
+
await fsp5.rename(tmpPath, this.filePath);
|
|
6937
|
+
this.handle = await fsp5.open(this.filePath, "a", 384);
|
|
6338
6938
|
} catch (err) {
|
|
6339
|
-
await
|
|
6939
|
+
await fsp5.unlink(tmpPath).catch(() => void 0);
|
|
6340
6940
|
throw err;
|
|
6341
6941
|
}
|
|
6342
6942
|
await this.append({
|
|
@@ -6362,7 +6962,7 @@ var FileSessionWriter = class {
|
|
|
6362
6962
|
provider: this.meta.provider ?? "unknown"
|
|
6363
6963
|
})}
|
|
6364
6964
|
`;
|
|
6365
|
-
await
|
|
6965
|
+
await fsp5.writeFile(this.filePath, record, "utf8");
|
|
6366
6966
|
}
|
|
6367
6967
|
};
|
|
6368
6968
|
function userInputTitle(content) {
|
|
@@ -6488,6 +7088,8 @@ var FleetManager = class {
|
|
|
6488
7088
|
pendingTasks = /* @__PURE__ */ new Map();
|
|
6489
7089
|
subagentMeta = /* @__PURE__ */ new Map();
|
|
6490
7090
|
priceLookups = /* @__PURE__ */ new Map();
|
|
7091
|
+
/** Tracks which nickname keys are already assigned — prevents collisions. */
|
|
7092
|
+
_usedNicknames = /* @__PURE__ */ new Set();
|
|
6491
7093
|
/** The coordinator (wired via setCoordinator by Director after construction). */
|
|
6492
7094
|
coordinator = null;
|
|
6493
7095
|
constructor(opts = {}) {
|
|
@@ -6568,6 +7170,30 @@ var FleetManager = class {
|
|
|
6568
7170
|
}
|
|
6569
7171
|
return null;
|
|
6570
7172
|
}
|
|
7173
|
+
/**
|
|
7174
|
+
* Assign a memorable nickname (e.g. "Einstein (Bug Hunter)") to the config,
|
|
7175
|
+
* record it so the same name is never reused, then record the spawn.
|
|
7176
|
+
*
|
|
7177
|
+
* Call this INSTEAD of `recordSpawn` when you want automatic nicknames.
|
|
7178
|
+
* The nickname is written back to `config.name` BEFORE the coordinator
|
|
7179
|
+
* sees the config, so the manifest, logs, and fleet UI all show it.
|
|
7180
|
+
*/
|
|
7181
|
+
assignNicknameAndRecord(subagentId, config, priceLookup) {
|
|
7182
|
+
const role = config.role ?? "subagent";
|
|
7183
|
+
const nickname = assignNickname(role, this._usedNicknames);
|
|
7184
|
+
const baseKey = nickname.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
7185
|
+
this._usedNicknames.add(baseKey);
|
|
7186
|
+
config.name = nickname;
|
|
7187
|
+
this.recordSpawn(subagentId, config, priceLookup);
|
|
7188
|
+
return nickname;
|
|
7189
|
+
}
|
|
7190
|
+
/**
|
|
7191
|
+
* Returns the set of already-assigned nickname keys — useful for debugging
|
|
7192
|
+
* and testing.
|
|
7193
|
+
*/
|
|
7194
|
+
get usedNicknames() {
|
|
7195
|
+
return this._usedNicknames;
|
|
7196
|
+
}
|
|
6571
7197
|
/**
|
|
6572
7198
|
* Records a spawn: increments counter, stores metadata, updates state checkpoint,
|
|
6573
7199
|
* and schedules a debounced manifest write. Call AFTER the coordinator
|
|
@@ -6626,7 +7252,7 @@ var FleetManager = class {
|
|
|
6626
7252
|
})),
|
|
6627
7253
|
usage: this.usage.snapshot()
|
|
6628
7254
|
};
|
|
6629
|
-
await
|
|
7255
|
+
await fsp5.mkdir(path4.dirname(this.manifestPath), { recursive: true });
|
|
6630
7256
|
await atomicWrite(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
6631
7257
|
return this.manifestPath;
|
|
6632
7258
|
}
|
|
@@ -6738,6 +7364,6 @@ var FleetManager = class {
|
|
|
6738
7364
|
}
|
|
6739
7365
|
};
|
|
6740
7366
|
|
|
6741
|
-
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, BUG_HUNTER_AGENT, BUILD_AGENTS, BudgetExceededError, BudgetThresholdSignal, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultMultiAgentCoordinator, Director, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, HEAVY_BUDGET, InMemoryAgentBridge, InMemoryBridgeTransport, KNOWLEDGE_AGENTS, LIGHT_BUDGET, MEDIUM_BUDGET, META_AGENTS, NULL_FLEET_BUS, PLANNING_AGENTS, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, SECURITY_SCANNER_AGENT, SubagentBudget, VERIFY_AGENTS, applyRosterBudget, attachAutoExtend, composeDirectorPrompt, composeSubagentPrompt, createDelegateTool, createMessage, dispatchAgent, getAgentDefinition, makeAgentSubagentRunner, makeDirectorSessionFactory, makeLLMClassifier, rosterSummaryFromConfigs, scoreAgents };
|
|
7367
|
+
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, BUG_HUNTER_AGENT, BUILD_AGENTS, BudgetExceededError, BudgetThresholdSignal, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultMultiAgentCoordinator, Director, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, HEAVY_BUDGET, InMemoryAgentBridge, InMemoryBridgeTransport, KNOWLEDGE_AGENTS, LIGHT_BUDGET, MEDIUM_BUDGET, META_AGENTS, NULL_FLEET_BUS, PLANNING_AGENTS, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, SECURITY_SCANNER_AGENT, SubagentBudget, VERIFY_AGENTS, applyRosterBudget, attachAutoExtend, composeDirectorPrompt, composeSubagentPrompt, createDelegateTool, createMessage, dispatchAgent, getAgentDefinition, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, rosterSummaryFromConfigs, scoreAgents };
|
|
6742
7368
|
//# sourceMappingURL=index.js.map
|
|
6743
7369
|
//# sourceMappingURL=index.js.map
|