@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
package/dist/defaults/index.js
CHANGED
|
@@ -319,9 +319,11 @@ function isEmptyMessage(msg) {
|
|
|
319
319
|
var DefaultSessionStore = class {
|
|
320
320
|
dir;
|
|
321
321
|
events;
|
|
322
|
+
secretScrubber;
|
|
322
323
|
constructor(opts) {
|
|
323
324
|
this.dir = opts.dir;
|
|
324
325
|
this.events = opts.events;
|
|
326
|
+
this.secretScrubber = opts.secretScrubber;
|
|
325
327
|
}
|
|
326
328
|
/** Join session ID to its absolute path within the store directory. */
|
|
327
329
|
sessionPath(id, ext) {
|
|
@@ -346,7 +348,11 @@ var DefaultSessionStore = class {
|
|
|
346
348
|
);
|
|
347
349
|
}
|
|
348
350
|
try {
|
|
349
|
-
return new FileSessionWriter(id, handle, startedAt, meta, this.events, {
|
|
351
|
+
return new FileSessionWriter(id, handle, startedAt, meta, this.events, {
|
|
352
|
+
dir: shardDir,
|
|
353
|
+
filePath: file,
|
|
354
|
+
secretScrubber: this.secretScrubber
|
|
355
|
+
});
|
|
350
356
|
} catch (err) {
|
|
351
357
|
await handle.close().catch(() => {
|
|
352
358
|
});
|
|
@@ -376,7 +382,7 @@ var DefaultSessionStore = class {
|
|
|
376
382
|
provider: data.metadata.provider
|
|
377
383
|
},
|
|
378
384
|
this.events,
|
|
379
|
-
{ resumed: true, dir: this.dir, filePath: file }
|
|
385
|
+
{ resumed: true, dir: this.dir, filePath: file, secretScrubber: this.secretScrubber }
|
|
380
386
|
);
|
|
381
387
|
return { writer, data };
|
|
382
388
|
} catch (err) {
|
|
@@ -582,6 +588,7 @@ var FileSessionWriter = class {
|
|
|
582
588
|
this.resumed = opts.resumed ?? false;
|
|
583
589
|
this.manifestFile = opts.dir ? path15.join(opts.dir, `${id}.summary.json`) : "";
|
|
584
590
|
this.filePath = opts.filePath ?? "";
|
|
591
|
+
this.secretScrubber = opts.secretScrubber;
|
|
585
592
|
this.summary = {
|
|
586
593
|
id,
|
|
587
594
|
title: "(empty session)",
|
|
@@ -610,6 +617,29 @@ var FileSessionWriter = class {
|
|
|
610
617
|
resumed;
|
|
611
618
|
appendFailCount = 0;
|
|
612
619
|
lastAppendWarnAt = 0;
|
|
620
|
+
secretScrubber;
|
|
621
|
+
/**
|
|
622
|
+
* Scrub secrets out of conversation-turn events before they are observed
|
|
623
|
+
* for the summary, written to the JSONL log, or surfaced on resume. Only
|
|
624
|
+
* `user_input` / `llm_response` carry free-form user/model text; other event
|
|
625
|
+
* types either have no secret-bearing content or are already scrubbed
|
|
626
|
+
* upstream (tool results). Returns the event unchanged when no scrubber is
|
|
627
|
+
* configured.
|
|
628
|
+
*/
|
|
629
|
+
scrubEvent(event) {
|
|
630
|
+
const s = this.secretScrubber;
|
|
631
|
+
if (!s) return event;
|
|
632
|
+
if (event.type === "user_input") {
|
|
633
|
+
return {
|
|
634
|
+
...event,
|
|
635
|
+
content: typeof event.content === "string" ? s.scrub(event.content) : s.scrubObject(event.content)
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
if (event.type === "llm_response") {
|
|
639
|
+
return { ...event, content: s.scrubObject(event.content) };
|
|
640
|
+
}
|
|
641
|
+
return event;
|
|
642
|
+
}
|
|
613
643
|
promptIndex = 0;
|
|
614
644
|
pendingFileSnapshots = [];
|
|
615
645
|
/** Tracks open tool_use IDs during the current run to serialize on close for resume. */
|
|
@@ -642,9 +672,10 @@ var FileSessionWriter = class {
|
|
|
642
672
|
this.initDone = true;
|
|
643
673
|
await this.writeSessionStartLazy();
|
|
644
674
|
}
|
|
645
|
-
this.
|
|
675
|
+
const scrubbed = this.scrubEvent(event);
|
|
676
|
+
this.observeForSummary(scrubbed);
|
|
646
677
|
try {
|
|
647
|
-
await this.handle.appendFile(`${JSON.stringify(
|
|
678
|
+
await this.handle.appendFile(`${JSON.stringify(scrubbed)}
|
|
648
679
|
`, "utf8");
|
|
649
680
|
} catch (err) {
|
|
650
681
|
this.appendFailCount++;
|
|
@@ -1266,6 +1297,9 @@ function isSecretField(name) {
|
|
|
1266
1297
|
return SECRET_KEY_PATTERN.test(lc);
|
|
1267
1298
|
}
|
|
1268
1299
|
|
|
1300
|
+
// src/storage/config-loader.ts
|
|
1301
|
+
init_atomic_write();
|
|
1302
|
+
|
|
1269
1303
|
// src/types/context-window.ts
|
|
1270
1304
|
var DEFAULT_CONTEXT_WINDOW_MODE_ID = "balanced";
|
|
1271
1305
|
var CONTEXT_WINDOW_MODES = Object.freeze([
|
|
@@ -1510,6 +1544,40 @@ var DefaultConfigLoader = class {
|
|
|
1510
1544
|
}
|
|
1511
1545
|
return Object.freeze(cfg);
|
|
1512
1546
|
}
|
|
1547
|
+
/**
|
|
1548
|
+
* Persist a sync config to ~/.wrongstack/sync.json, with the token encrypted
|
|
1549
|
+
* by the vault (if provided). The file is isolated from the main config
|
|
1550
|
+
* hierarchy to prevent accidental commits.
|
|
1551
|
+
*/
|
|
1552
|
+
async persistSyncConfig(cfg) {
|
|
1553
|
+
let toWrite = { ...cfg };
|
|
1554
|
+
if (this.vault && toWrite.githubToken && !toWrite.githubToken.startsWith("enc:")) {
|
|
1555
|
+
toWrite = { ...toWrite, githubToken: this.vault.encrypt(toWrite.githubToken) };
|
|
1556
|
+
}
|
|
1557
|
+
await atomicWrite(this.paths.syncConfig, JSON.stringify(toWrite, null, 2), { mode: 384 });
|
|
1558
|
+
}
|
|
1559
|
+
/**
|
|
1560
|
+
* Read ~/.wrongstack/sync.json (encrypted GitHub token storage) and decrypt
|
|
1561
|
+
* the token if a vault is available. Returns null if the file doesn't exist.
|
|
1562
|
+
* This is separate from main config loading because sync.json is intentionally
|
|
1563
|
+
* isolated — it should never be part of project-local or env-driven config.
|
|
1564
|
+
*/
|
|
1565
|
+
async loadSyncConfig() {
|
|
1566
|
+
try {
|
|
1567
|
+
const raw = await fsp.readFile(this.paths.syncConfig, "utf8");
|
|
1568
|
+
const parsed = safeParse(raw);
|
|
1569
|
+
if (!parsed.ok || !parsed.value) return null;
|
|
1570
|
+
if (this.vault) {
|
|
1571
|
+
const decrypted = decryptConfigSecrets({ sync: parsed.value }, this.vault);
|
|
1572
|
+
return decrypted.sync ?? null;
|
|
1573
|
+
}
|
|
1574
|
+
return parsed.value;
|
|
1575
|
+
} catch (err) {
|
|
1576
|
+
if (err.code === "ENOENT") return null;
|
|
1577
|
+
console.warn("[config] Failed to load sync config:", err);
|
|
1578
|
+
return null;
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1513
1581
|
async readJson(file) {
|
|
1514
1582
|
let raw;
|
|
1515
1583
|
try {
|
|
@@ -3126,7 +3194,11 @@ var DefaultPermissionPolicy = class {
|
|
|
3126
3194
|
return { permission: "deny", source: "deny", reason: "session soft deny (user pressed no)" };
|
|
3127
3195
|
}
|
|
3128
3196
|
if (this.sessionAllowed.has(subjectKey)) {
|
|
3129
|
-
return {
|
|
3197
|
+
return {
|
|
3198
|
+
permission: "auto",
|
|
3199
|
+
source: "trust",
|
|
3200
|
+
reason: "session soft allow (user pressed yes)"
|
|
3201
|
+
};
|
|
3130
3202
|
}
|
|
3131
3203
|
if (entry?.deny && subject && matchAny(entry.deny, subject)) {
|
|
3132
3204
|
return { permission: "deny", source: "deny", reason: "matched deny pattern" };
|
|
@@ -3146,7 +3218,11 @@ var DefaultPermissionPolicy = class {
|
|
|
3146
3218
|
const decision = await this.promptDelegate(tool, input, subject ?? tool.name);
|
|
3147
3219
|
if (decision === "always") {
|
|
3148
3220
|
await this.trust({ tool: tool.name, pattern: subject ?? tool.name });
|
|
3149
|
-
return {
|
|
3221
|
+
return {
|
|
3222
|
+
permission: "auto",
|
|
3223
|
+
source: "user",
|
|
3224
|
+
reason: "destructive yolo always-allowed"
|
|
3225
|
+
};
|
|
3150
3226
|
}
|
|
3151
3227
|
if (decision === "deny") {
|
|
3152
3228
|
await this.deny({ tool: tool.name, pattern: subject ?? tool.name });
|
|
@@ -3154,13 +3230,22 @@ var DefaultPermissionPolicy = class {
|
|
|
3154
3230
|
}
|
|
3155
3231
|
return { permission: decision === "yes" ? "auto" : "deny", source: "user" };
|
|
3156
3232
|
}
|
|
3157
|
-
return {
|
|
3233
|
+
return {
|
|
3234
|
+
permission: "confirm",
|
|
3235
|
+
source: "yolo_destructive",
|
|
3236
|
+
riskTier: "destructive",
|
|
3237
|
+
reason: "destructive tool needs explicit approval even in yolo mode"
|
|
3238
|
+
};
|
|
3158
3239
|
}
|
|
3159
3240
|
return { permission: "auto", source: "yolo" };
|
|
3160
3241
|
}
|
|
3161
3242
|
if (tool.name === "write" && subject) {
|
|
3162
3243
|
if (ctx.hasRead(subject)) {
|
|
3163
|
-
return {
|
|
3244
|
+
return {
|
|
3245
|
+
permission: "auto",
|
|
3246
|
+
source: "context",
|
|
3247
|
+
reason: "file already read in this session"
|
|
3248
|
+
};
|
|
3164
3249
|
}
|
|
3165
3250
|
}
|
|
3166
3251
|
if (tool.permission === "auto" && !tool.mutating) {
|
|
@@ -3268,6 +3353,10 @@ var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
|
|
|
3268
3353
|
// arbitrary shell — use exec for constrained shell
|
|
3269
3354
|
"write",
|
|
3270
3355
|
// arbitrary file write
|
|
3356
|
+
"edit",
|
|
3357
|
+
// arbitrary in-project file modification (equivalent to write)
|
|
3358
|
+
"replace",
|
|
3359
|
+
// arbitrary multi-file find/replace (equivalent to write)
|
|
3271
3360
|
"scaffold",
|
|
3272
3361
|
// arbitrary file generation outside project root
|
|
3273
3362
|
"patch",
|
|
@@ -3277,12 +3366,22 @@ var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
|
|
|
3277
3366
|
"exec"
|
|
3278
3367
|
// restricted shell but with arbitrary command args
|
|
3279
3368
|
]);
|
|
3369
|
+
/**
|
|
3370
|
+
* Tools from MCP servers (`mcp__<server>__<tool>`) are external code of
|
|
3371
|
+
* unknown capability — they may wrap a shell or filesystem. They are
|
|
3372
|
+
* fail-closed here: not auto-approved for subagents by default, so the
|
|
3373
|
+
* leader must allow them explicitly per-spawn.
|
|
3374
|
+
*/
|
|
3375
|
+
static isMcpTool(name) {
|
|
3376
|
+
return name.startsWith("mcp__");
|
|
3377
|
+
}
|
|
3280
3378
|
async evaluate(tool) {
|
|
3281
|
-
|
|
3379
|
+
const blocked = _AutoApprovePermissionPolicy.DENY.has(tool.name) || _AutoApprovePermissionPolicy.isMcpTool(tool.name);
|
|
3380
|
+
if (tool.permission === "deny" || blocked) {
|
|
3282
3381
|
return {
|
|
3283
3382
|
permission: "deny",
|
|
3284
3383
|
source: "subagent_guard",
|
|
3285
|
-
reason:
|
|
3384
|
+
reason: blocked ? `tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow it explicitly` : "tool default deny"
|
|
3286
3385
|
};
|
|
3287
3386
|
}
|
|
3288
3387
|
return { permission: "auto", source: "yolo" };
|
|
@@ -3697,6 +3796,90 @@ function parseDescription(raw) {
|
|
|
3697
3796
|
return { trigger, scope };
|
|
3698
3797
|
}
|
|
3699
3798
|
|
|
3799
|
+
// src/utils/json-repair.ts
|
|
3800
|
+
function completePartialObject(s) {
|
|
3801
|
+
let result = s;
|
|
3802
|
+
const trimmed = result.trim();
|
|
3803
|
+
if (!trimmed.startsWith("{")) return s;
|
|
3804
|
+
for (let pass = 0; pass < 3; pass++) {
|
|
3805
|
+
let braceDepth = 0;
|
|
3806
|
+
let inString2 = false;
|
|
3807
|
+
let escaped2 = false;
|
|
3808
|
+
let foundClose = false;
|
|
3809
|
+
for (const ch of result) {
|
|
3810
|
+
if (escaped2) {
|
|
3811
|
+
escaped2 = false;
|
|
3812
|
+
continue;
|
|
3813
|
+
}
|
|
3814
|
+
if (ch === "\\") {
|
|
3815
|
+
escaped2 = true;
|
|
3816
|
+
continue;
|
|
3817
|
+
}
|
|
3818
|
+
if (ch === '"') {
|
|
3819
|
+
inString2 = !inString2;
|
|
3820
|
+
continue;
|
|
3821
|
+
}
|
|
3822
|
+
if (inString2) continue;
|
|
3823
|
+
if (ch === "{") {
|
|
3824
|
+
braceDepth++;
|
|
3825
|
+
foundClose = false;
|
|
3826
|
+
} else if (ch === "}") {
|
|
3827
|
+
braceDepth--;
|
|
3828
|
+
if (braceDepth === 0) foundClose = true;
|
|
3829
|
+
}
|
|
3830
|
+
}
|
|
3831
|
+
if (foundClose || braceDepth <= 0) break;
|
|
3832
|
+
result += "}".repeat(braceDepth);
|
|
3833
|
+
}
|
|
3834
|
+
if (tryParse(result).ok) return result;
|
|
3835
|
+
let inString = false;
|
|
3836
|
+
let escaped = false;
|
|
3837
|
+
for (let i = result.length - 1; i >= 0; i--) {
|
|
3838
|
+
const ch = result[i];
|
|
3839
|
+
if (escaped) {
|
|
3840
|
+
escaped = false;
|
|
3841
|
+
continue;
|
|
3842
|
+
}
|
|
3843
|
+
if (ch === "\\") {
|
|
3844
|
+
escaped = true;
|
|
3845
|
+
continue;
|
|
3846
|
+
}
|
|
3847
|
+
if (ch === '"') {
|
|
3848
|
+
let nextNonWs;
|
|
3849
|
+
for (let j = i + 1; j < result.length; j++) {
|
|
3850
|
+
const nc = result[j];
|
|
3851
|
+
if (nc === " " || nc === " " || nc === "\n" || nc === "\r") continue;
|
|
3852
|
+
nextNonWs = result[j];
|
|
3853
|
+
break;
|
|
3854
|
+
}
|
|
3855
|
+
if (nextNonWs === ":") {
|
|
3856
|
+
continue;
|
|
3857
|
+
} else {
|
|
3858
|
+
inString = !inString;
|
|
3859
|
+
continue;
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3862
|
+
}
|
|
3863
|
+
if (inString) {
|
|
3864
|
+
result = result.trimEnd();
|
|
3865
|
+
if (result.endsWith("\\")) result = result.slice(0, -1);
|
|
3866
|
+
let depth = 0;
|
|
3867
|
+
for (const ch of result) {
|
|
3868
|
+
if (ch === "{") depth++;
|
|
3869
|
+
else if (ch === "}") depth = Math.max(0, depth - 1);
|
|
3870
|
+
}
|
|
3871
|
+
result += '"' + "}".repeat(Math.max(1, depth));
|
|
3872
|
+
}
|
|
3873
|
+
return result;
|
|
3874
|
+
}
|
|
3875
|
+
function tryParse(s) {
|
|
3876
|
+
try {
|
|
3877
|
+
return { ok: true, value: JSON.parse(s) };
|
|
3878
|
+
} catch {
|
|
3879
|
+
return { ok: false };
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3882
|
+
|
|
3700
3883
|
// src/core/streaming-response-builder.ts
|
|
3701
3884
|
function buildResponse(state) {
|
|
3702
3885
|
const content = [];
|
|
@@ -3794,7 +3977,12 @@ function safeJsonOrRaw(s) {
|
|
|
3794
3977
|
try {
|
|
3795
3978
|
return JSON.parse(s);
|
|
3796
3979
|
} catch {
|
|
3797
|
-
|
|
3980
|
+
const repaired = completePartialObject(s);
|
|
3981
|
+
try {
|
|
3982
|
+
return JSON.parse(repaired);
|
|
3983
|
+
} catch {
|
|
3984
|
+
return { _raw: repaired };
|
|
3985
|
+
}
|
|
3798
3986
|
}
|
|
3799
3987
|
}
|
|
3800
3988
|
function handleToolUseStop(state, ev) {
|
|
@@ -9069,11 +9257,46 @@ Working rules:
|
|
|
9069
9257
|
- When in doubt, flag as medium rather than ignoring potential issues`
|
|
9070
9258
|
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
9071
9259
|
};
|
|
9260
|
+
var CRITIC_AGENT = {
|
|
9261
|
+
id: "critic",
|
|
9262
|
+
name: "Critic",
|
|
9263
|
+
role: "critic",
|
|
9264
|
+
prompt: `You are the Critic agent. Your job is to evaluate code quality,
|
|
9265
|
+
architectural decisions, and proposed changes against project conventions,
|
|
9266
|
+
engineering standards, and known quality gates. You do not write code \u2014
|
|
9267
|
+
you judge it.
|
|
9268
|
+
|
|
9269
|
+
Scope:
|
|
9270
|
+
- Evaluate bug severity and fix quality from Bug Hunter reports
|
|
9271
|
+
- Score refactoring plans from Refactor Planner (risk, completeness, trade-offs)
|
|
9272
|
+
- Flag gaps in test coverage, error handling, and edge case coverage
|
|
9273
|
+
- Assess whether a proposed change aligns with existing project patterns
|
|
9274
|
+
- Detect over-engineering or under-engineering relative to the problem scope
|
|
9275
|
+
|
|
9276
|
+
Input format you accept:
|
|
9277
|
+
{ "task": "evaluate | score | review", "subject": "bug_report | refactor_plan | diff", "focus": "correctness | maintainability | risk | all" }
|
|
9278
|
+
|
|
9279
|
+
Output: Markdown critic report:
|
|
9280
|
+
- ## Overall Score (0-10 with rationale)
|
|
9281
|
+
- ## Strengths (what's solid)
|
|
9282
|
+
- ## Weaknesses (what needs work)
|
|
9283
|
+
- ## Specific Concerns (with file:line when applicable)
|
|
9284
|
+
- ## Verdict: **Approve / Needs Revision / Reject**
|
|
9285
|
+
|
|
9286
|
+
Working rules:
|
|
9287
|
+
- Be specific \u2014 "looks fine" is not a review. Cite concrete evidence.
|
|
9288
|
+
- When scoring, explain the delta from a perfect score.
|
|
9289
|
+
- If you have no basis to evaluate a concern, say so rather than speculating.
|
|
9290
|
+
- Prioritise correctness over style; correctness issues block approval.
|
|
9291
|
+
- Score thresholds: \u22657 = Approve, 4-6 = Needs Revision, <4 = Reject`
|
|
9292
|
+
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
9293
|
+
};
|
|
9072
9294
|
var FLEET_ROSTER = {
|
|
9073
9295
|
"audit-log": AUDIT_LOG_AGENT,
|
|
9074
9296
|
"bug-hunter": BUG_HUNTER_AGENT,
|
|
9075
9297
|
"refactor-planner": REFACTOR_PLANNER_AGENT,
|
|
9076
9298
|
"security-scanner": SECURITY_SCANNER_AGENT,
|
|
9299
|
+
"critic": CRITIC_AGENT,
|
|
9077
9300
|
...Object.fromEntries(
|
|
9078
9301
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.config])
|
|
9079
9302
|
)
|
|
@@ -9083,6 +9306,7 @@ var FLEET_ROSTER_BUDGETS = {
|
|
|
9083
9306
|
"bug-hunter": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
|
|
9084
9307
|
"refactor-planner": { timeoutMs: 7.5 * 60 * 60 * 1e3, maxIterations: 6e3, maxToolCalls: 18e3 },
|
|
9085
9308
|
"security-scanner": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
|
|
9309
|
+
"critic": { timeoutMs: 5 * 60 * 60 * 1e3, maxIterations: 4e3, maxToolCalls: 12e3 },
|
|
9086
9310
|
...Object.fromEntries(
|
|
9087
9311
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.budget])
|
|
9088
9312
|
)
|
|
@@ -10698,6 +10922,106 @@ var FleetUsageAggregator = class {
|
|
|
10698
10922
|
snap.lastEventAt = e.ts;
|
|
10699
10923
|
}
|
|
10700
10924
|
};
|
|
10925
|
+
|
|
10926
|
+
// src/coordination/subagent-nicknames.ts
|
|
10927
|
+
var NICKNAME_POOL = {
|
|
10928
|
+
// Physics & fundamental sciences
|
|
10929
|
+
"einstein": { name: "Einstein", domain: "physics" },
|
|
10930
|
+
"newton": { name: "Newton", domain: "physics" },
|
|
10931
|
+
"feynman": { name: "Feynman", domain: "physics" },
|
|
10932
|
+
"dirac": { name: "Dirac", domain: "physics" },
|
|
10933
|
+
"bohr": { name: "Bohr", domain: "physics" },
|
|
10934
|
+
"planck": { name: "Planck", domain: "physics" },
|
|
10935
|
+
"curie": { name: "Curie", domain: "physics" },
|
|
10936
|
+
"fermi": { name: "Fermi", domain: "physics" },
|
|
10937
|
+
"heisenberg": { name: "Heisenberg", domain: "physics" },
|
|
10938
|
+
"schrodinger": { name: "Schr\xF6dinger", domain: "physics" },
|
|
10939
|
+
// Mathematics
|
|
10940
|
+
"euclid": { name: "Euclid", domain: "math" },
|
|
10941
|
+
"gauss": { name: "Gauss", domain: "math" },
|
|
10942
|
+
"turing": { name: "Turing", domain: "math" },
|
|
10943
|
+
"poincare": { name: "Poincar\xE9", domain: "math" },
|
|
10944
|
+
"riemann": { name: "Riemann", domain: "math" },
|
|
10945
|
+
"hilbert": { name: "Hilbert", domain: "math" },
|
|
10946
|
+
"pythagoras": { name: "Pythagoras", domain: "math" },
|
|
10947
|
+
// Computing & information theory
|
|
10948
|
+
"von-neumann": { name: "Von Neumann", domain: "computing" },
|
|
10949
|
+
"shannon": { name: "Shannon", domain: "computing" },
|
|
10950
|
+
"hopper": { name: "Hopper", domain: "computing" },
|
|
10951
|
+
"backus": { name: "Backus", domain: "computing" },
|
|
10952
|
+
"knuth": { name: "Knuth", domain: "computing" },
|
|
10953
|
+
"torvalds": { name: "Torvalds", domain: "computing" },
|
|
10954
|
+
"stallman": { name: "Stallman", domain: "computing" },
|
|
10955
|
+
"berners-lee": { name: "Berners-Lee", domain: "computing" },
|
|
10956
|
+
"babbage": { name: "Babbage", domain: "computing" },
|
|
10957
|
+
"lovelace": { name: "Lovelace", domain: "computing" },
|
|
10958
|
+
"klein": { name: "Klein", domain: "computing" },
|
|
10959
|
+
// Electronics & electrical engineering
|
|
10960
|
+
"edison": { name: "Edison", domain: "ee" },
|
|
10961
|
+
"tesla": { name: "Tesla", domain: "ee" },
|
|
10962
|
+
"faraday": { name: "Faraday", domain: "ee" },
|
|
10963
|
+
"maxwell": { name: "Maxwell", domain: "ee" },
|
|
10964
|
+
"ohm": { name: "Ohm", domain: "ee" },
|
|
10965
|
+
"bell": { name: "Bell", domain: "ee" },
|
|
10966
|
+
"marconi": { name: "Marconi", domain: "ee" },
|
|
10967
|
+
"lamarr": { name: "Lamarr", domain: "ee" },
|
|
10968
|
+
// General science / multi-disciplinary
|
|
10969
|
+
"darwin": { name: "Darwin", domain: "biology" },
|
|
10970
|
+
"mendel": { name: "Mendel", domain: "biology" },
|
|
10971
|
+
"pasteur": { name: "Pasteur", domain: "biology" },
|
|
10972
|
+
"hawking": { name: "Hawking", domain: "cosmology" },
|
|
10973
|
+
"sagan": { name: "Sagan", domain: "cosmology" },
|
|
10974
|
+
// Chemistry / materials
|
|
10975
|
+
"lavoisier": { name: "Lavoisier", domain: "chemistry" },
|
|
10976
|
+
"mendeleev": { name: "Mendeleev", domain: "chemistry" }
|
|
10977
|
+
};
|
|
10978
|
+
var ALL_NICKNAMES = Object.values(NICKNAME_POOL);
|
|
10979
|
+
var DOMAIN_PREFERENCES = {
|
|
10980
|
+
"security": ["shannon", "turing", "lamarr", "stallman"],
|
|
10981
|
+
"bug-hunter": ["darwin", "curie", "feynman", "fermi"],
|
|
10982
|
+
"refactor": ["gauss", "hilbert", "euclid", "planck"],
|
|
10983
|
+
"audit-log": ["sagan", "hawking", "poincare", "newton"],
|
|
10984
|
+
"planner": ["hilbert", "gauss", "turing", "euclid"],
|
|
10985
|
+
"researcher": ["sagan", "hawking", "darwin", "pasteur"],
|
|
10986
|
+
"explorer": ["marconi", "bell", "columbus", "polo"],
|
|
10987
|
+
"testing": ["pasteur", "curie", "fermi", "bohr"],
|
|
10988
|
+
"frontend": ["lovelace", "hopper", "babbage", "backus"],
|
|
10989
|
+
"backend": ["torvalds", "stallman", "von-neumann", "backus"],
|
|
10990
|
+
"database": ["turing", "shannon", "backus", "knuth"],
|
|
10991
|
+
"devops": ["tesla", "edison", "faraday", "bell"],
|
|
10992
|
+
"security-scanner": ["shannon", "turing", "lamarr", "stallman"],
|
|
10993
|
+
"refactor-planner": ["gauss", "hilbert", "planck", "newton"],
|
|
10994
|
+
"architect": ["von-neumann", "turing", "gauss", "hilbert"],
|
|
10995
|
+
"critic": ["einstein", "feynman", "dirac", "bohr"],
|
|
10996
|
+
"e2e": ["hopper", "bell", "marconi", "tesla"],
|
|
10997
|
+
"performance": ["knuth", "gauss", "planck", "feynman"],
|
|
10998
|
+
"chaos": ["tesla", "edison", "curie", "fermi"],
|
|
10999
|
+
"cost": ["oh", "bell", "marconi", "tesla"],
|
|
11000
|
+
// default fallback
|
|
11001
|
+
"default": ["einstein", "newton", "curie", "tesla", "edison", "turing", "shannon", "hopper", "knuth", "stallman"]
|
|
11002
|
+
};
|
|
11003
|
+
function assignNickname(role, used) {
|
|
11004
|
+
const preferences = [
|
|
11005
|
+
...DOMAIN_PREFERENCES[role] ?? [],
|
|
11006
|
+
...DOMAIN_PREFERENCES["default"] ?? []
|
|
11007
|
+
];
|
|
11008
|
+
for (const key of preferences) {
|
|
11009
|
+
if (!used.has(key)) {
|
|
11010
|
+
return `${NICKNAME_POOL[key].name} (${formatRole(role)})`;
|
|
11011
|
+
}
|
|
11012
|
+
}
|
|
11013
|
+
for (const entry of ALL_NICKNAMES) {
|
|
11014
|
+
const key = Object.entries(NICKNAME_POOL).find(([, v]) => v.name === entry.name)?.[0];
|
|
11015
|
+
if (key && !used.has(key)) {
|
|
11016
|
+
return `${entry.name} (${formatRole(role)})`;
|
|
11017
|
+
}
|
|
11018
|
+
}
|
|
11019
|
+
const counter = used.size + 1;
|
|
11020
|
+
return `Scientist #${counter} (${formatRole(role)})`;
|
|
11021
|
+
}
|
|
11022
|
+
function formatRole(role) {
|
|
11023
|
+
return role.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
11024
|
+
}
|
|
10701
11025
|
function makeSpawnTool(director, roster) {
|
|
10702
11026
|
const inputSchema = {
|
|
10703
11027
|
type: "object",
|
|
@@ -10922,7 +11246,6 @@ function makeFleetSessionTool(director) {
|
|
|
10922
11246
|
type: "object",
|
|
10923
11247
|
properties: {
|
|
10924
11248
|
subagentId: { type: "string", description: "Subagent id to read the transcript of." },
|
|
10925
|
-
/** Number of trailing lines to return (last N JSONL lines). Default: all. */
|
|
10926
11249
|
tail: { type: "number", description: "Number of trailing JSONL lines to return. Omit for the full transcript." }
|
|
10927
11250
|
},
|
|
10928
11251
|
required: ["subagentId"]
|
|
@@ -10958,8 +11281,6 @@ function makeFleetHealthTool(director) {
|
|
|
10958
11281
|
id: s.id,
|
|
10959
11282
|
status: s.status,
|
|
10960
11283
|
lastEventAt: usage?.lastEventAt,
|
|
10961
|
-
// Budget pressure: fraction of each limit consumed if we have it.
|
|
10962
|
-
// BudgetWarning events carry used/limit ratios; surface them here.
|
|
10963
11284
|
budgetPressure: {
|
|
10964
11285
|
iterations: usage?.iterations,
|
|
10965
11286
|
toolCalls: usage?.toolCalls,
|
|
@@ -10971,6 +11292,415 @@ function makeFleetHealthTool(director) {
|
|
|
10971
11292
|
}
|
|
10972
11293
|
};
|
|
10973
11294
|
}
|
|
11295
|
+
function makeCollabDebugTool(director) {
|
|
11296
|
+
return {
|
|
11297
|
+
name: "collab_debug",
|
|
11298
|
+
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).",
|
|
11299
|
+
permission: "auto",
|
|
11300
|
+
mutating: false,
|
|
11301
|
+
inputSchema: {
|
|
11302
|
+
type: "object",
|
|
11303
|
+
properties: {
|
|
11304
|
+
targetPaths: {
|
|
11305
|
+
type: "array",
|
|
11306
|
+
items: { type: "string" },
|
|
11307
|
+
description: "File paths / glob patterns to scan for bugs."
|
|
11308
|
+
},
|
|
11309
|
+
timeoutMs: {
|
|
11310
|
+
type: "number",
|
|
11311
|
+
description: "Timeout in ms. Default: 600000 (10 minutes)."
|
|
11312
|
+
}
|
|
11313
|
+
},
|
|
11314
|
+
required: ["targetPaths"]
|
|
11315
|
+
},
|
|
11316
|
+
async execute(input) {
|
|
11317
|
+
const i = input;
|
|
11318
|
+
if (!i.targetPaths?.length) {
|
|
11319
|
+
return { error: "collab_debug: targetPaths is required and must be non-empty." };
|
|
11320
|
+
}
|
|
11321
|
+
const options = {
|
|
11322
|
+
targetPaths: i.targetPaths,
|
|
11323
|
+
timeoutMs: i.timeoutMs
|
|
11324
|
+
};
|
|
11325
|
+
try {
|
|
11326
|
+
const report = await director.spawnCollab(options);
|
|
11327
|
+
return {
|
|
11328
|
+
sessionId: report.sessionId,
|
|
11329
|
+
overallVerdict: report.overallVerdict,
|
|
11330
|
+
bugCount: report.bugs.length,
|
|
11331
|
+
planCount: report.refactorPlans.length,
|
|
11332
|
+
evaluationCount: report.evaluations.length,
|
|
11333
|
+
summary: report.summary,
|
|
11334
|
+
bugs: report.bugs,
|
|
11335
|
+
refactorPlans: report.refactorPlans,
|
|
11336
|
+
evaluations: report.evaluations
|
|
11337
|
+
};
|
|
11338
|
+
} catch (err) {
|
|
11339
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
11340
|
+
return { error: "collab_debug failed: " + msg };
|
|
11341
|
+
}
|
|
11342
|
+
}
|
|
11343
|
+
};
|
|
11344
|
+
}
|
|
11345
|
+
function makeFleetEmitTool(director) {
|
|
11346
|
+
return {
|
|
11347
|
+
name: "fleet_emit",
|
|
11348
|
+
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.",
|
|
11349
|
+
permission: "auto",
|
|
11350
|
+
mutating: false,
|
|
11351
|
+
inputSchema: {
|
|
11352
|
+
type: "object",
|
|
11353
|
+
properties: {
|
|
11354
|
+
type: {
|
|
11355
|
+
type: "string",
|
|
11356
|
+
description: "Event type: bug.found | refactor.plan | critic.evaluation"
|
|
11357
|
+
},
|
|
11358
|
+
payload: {
|
|
11359
|
+
type: "object",
|
|
11360
|
+
description: "Event payload matching the event type."
|
|
11361
|
+
}
|
|
11362
|
+
},
|
|
11363
|
+
required: ["type", "payload"]
|
|
11364
|
+
},
|
|
11365
|
+
async execute(input) {
|
|
11366
|
+
const i = input;
|
|
11367
|
+
director.fleet.emit({
|
|
11368
|
+
subagentId: director.id,
|
|
11369
|
+
ts: Date.now(),
|
|
11370
|
+
type: i.type,
|
|
11371
|
+
payload: i.payload
|
|
11372
|
+
});
|
|
11373
|
+
return { ok: true, event: i.type };
|
|
11374
|
+
}
|
|
11375
|
+
};
|
|
11376
|
+
}
|
|
11377
|
+
var CollabSession = class extends EventEmitter {
|
|
11378
|
+
sessionId;
|
|
11379
|
+
options;
|
|
11380
|
+
snapshot;
|
|
11381
|
+
director;
|
|
11382
|
+
fleetBus;
|
|
11383
|
+
subagentIds = /* @__PURE__ */ new Map();
|
|
11384
|
+
// role → subagentId
|
|
11385
|
+
bugs = /* @__PURE__ */ new Map();
|
|
11386
|
+
plans = /* @__PURE__ */ new Map();
|
|
11387
|
+
evaluations = /* @__PURE__ */ new Map();
|
|
11388
|
+
disposers = new Array();
|
|
11389
|
+
settled = false;
|
|
11390
|
+
timeoutMs;
|
|
11391
|
+
constructor(director, fleetBus, options) {
|
|
11392
|
+
super();
|
|
11393
|
+
this.sessionId = randomUUID();
|
|
11394
|
+
this.options = options;
|
|
11395
|
+
this.director = director;
|
|
11396
|
+
this.fleetBus = fleetBus;
|
|
11397
|
+
this.timeoutMs = options.timeoutMs ?? 10 * 60 * 1e3;
|
|
11398
|
+
if (options.prebuiltSnapshot) {
|
|
11399
|
+
this.snapshot = options.prebuiltSnapshot;
|
|
11400
|
+
} else {
|
|
11401
|
+
this.snapshot = {
|
|
11402
|
+
id: this.sessionId,
|
|
11403
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11404
|
+
files: []
|
|
11405
|
+
};
|
|
11406
|
+
}
|
|
11407
|
+
}
|
|
11408
|
+
/**
|
|
11409
|
+
* Read the target files from disk and populate the snapshot.
|
|
11410
|
+
* Call this after construction if you did not provide a prebuiltSnapshot
|
|
11411
|
+
* and want the session to operate on real file contents.
|
|
11412
|
+
*/
|
|
11413
|
+
async buildSnapshot() {
|
|
11414
|
+
if (this.snapshot.files.length > 0) return this.snapshot;
|
|
11415
|
+
for (const filePath of this.options.targetPaths) {
|
|
11416
|
+
try {
|
|
11417
|
+
const content = await fsp.readFile(filePath, "utf8");
|
|
11418
|
+
const ext = filePath.split(".").pop() ?? "";
|
|
11419
|
+
const language = ext === "ts" || ext === "tsx" ? "typescript" : ext === "js" || ext === "jsx" ? "javascript" : ext === "md" ? "markdown" : ext === "json" ? "json" : void 0;
|
|
11420
|
+
this.snapshot.files.push({ path: filePath, content, language });
|
|
11421
|
+
} catch {
|
|
11422
|
+
this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
|
|
11423
|
+
}
|
|
11424
|
+
}
|
|
11425
|
+
return this.snapshot;
|
|
11426
|
+
}
|
|
11427
|
+
/**
|
|
11428
|
+
* Start the collaborative session: snapshot files, spawn all three agents,
|
|
11429
|
+
* wire up event routing, and wait for completion.
|
|
11430
|
+
*/
|
|
11431
|
+
async start() {
|
|
11432
|
+
if (this.settled) throw new Error("session already settled");
|
|
11433
|
+
this.settled = true;
|
|
11434
|
+
await this.buildSnapshot();
|
|
11435
|
+
this.wireFleetBus();
|
|
11436
|
+
const [bugHunterId, refactorPlannerId, criticId] = await Promise.all([
|
|
11437
|
+
this.spawnAgent("bug-hunter", this.buildBugHunterTask()),
|
|
11438
|
+
this.spawnAgent("refactor-planner", this.buildRefactorPlannerTask()),
|
|
11439
|
+
this.spawnAgent("critic", this.buildCriticTask())
|
|
11440
|
+
]);
|
|
11441
|
+
this.subagentIds.set("bug-hunter", bugHunterId);
|
|
11442
|
+
this.subagentIds.set("refactor-planner", refactorPlannerId);
|
|
11443
|
+
this.subagentIds.set("critic", criticId);
|
|
11444
|
+
const timeout = new Promise((_, reject) => {
|
|
11445
|
+
setTimeout(
|
|
11446
|
+
() => reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`)),
|
|
11447
|
+
this.timeoutMs
|
|
11448
|
+
);
|
|
11449
|
+
});
|
|
11450
|
+
let results;
|
|
11451
|
+
try {
|
|
11452
|
+
results = await Promise.race([
|
|
11453
|
+
Promise.all([
|
|
11454
|
+
this.director.awaitTasks([bugHunterId]),
|
|
11455
|
+
this.director.awaitTasks([refactorPlannerId]),
|
|
11456
|
+
this.director.awaitTasks([criticId])
|
|
11457
|
+
]),
|
|
11458
|
+
timeout
|
|
11459
|
+
]);
|
|
11460
|
+
} catch (err) {
|
|
11461
|
+
this.cleanup();
|
|
11462
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
11463
|
+
this.emit("session.error", error);
|
|
11464
|
+
throw error;
|
|
11465
|
+
}
|
|
11466
|
+
for (const result of results.flat()) {
|
|
11467
|
+
await this.parseAndEmit(result);
|
|
11468
|
+
}
|
|
11469
|
+
const report = this.assembleReport();
|
|
11470
|
+
this.cleanup();
|
|
11471
|
+
this.emit("session.done", report);
|
|
11472
|
+
return report;
|
|
11473
|
+
}
|
|
11474
|
+
/**
|
|
11475
|
+
* Parse a completed agent's final text output and route the structured
|
|
11476
|
+
* findings onto the FleetBus. Agents are instructed to emit one JSON object
|
|
11477
|
+
* per finding / plan / evaluation; we scan the output for balanced JSON
|
|
11478
|
+
* objects (tolerating prose, markdown, and ```json fences around them) and
|
|
11479
|
+
* dispatch each by its discriminating key. The wired FleetBus listeners then
|
|
11480
|
+
* collect them into the bugs / plans / evaluations maps.
|
|
11481
|
+
*
|
|
11482
|
+
* Failed or empty results are ignored, and a single malformed object never
|
|
11483
|
+
* aborts the collation pass — see {@link extractJsonObjects}.
|
|
11484
|
+
*/
|
|
11485
|
+
async parseAndEmit(result) {
|
|
11486
|
+
if (result.status !== "success" || result.result == null) return;
|
|
11487
|
+
const text = typeof result.result === "string" ? result.result : JSON.stringify(result.result);
|
|
11488
|
+
for (const obj of this.extractJsonObjects(text)) {
|
|
11489
|
+
const type = "finding" in obj ? "bug.found" : "plan" in obj ? "refactor.plan" : "evaluation" in obj ? "critic.evaluation" : null;
|
|
11490
|
+
if (!type) continue;
|
|
11491
|
+
this.fleetBus.emit({
|
|
11492
|
+
subagentId: result.subagentId,
|
|
11493
|
+
taskId: result.taskId,
|
|
11494
|
+
ts: Date.now(),
|
|
11495
|
+
type,
|
|
11496
|
+
payload: obj
|
|
11497
|
+
});
|
|
11498
|
+
}
|
|
11499
|
+
}
|
|
11500
|
+
/**
|
|
11501
|
+
* Extract every top-level JSON object embedded in free-form agent text.
|
|
11502
|
+
* Scans for balanced braces while respecting string literals and escape
|
|
11503
|
+
* sequences, so prose, markdown headers, and code fences around the JSON
|
|
11504
|
+
* are skipped. Malformed spans are dropped silently rather than thrown.
|
|
11505
|
+
*/
|
|
11506
|
+
extractJsonObjects(text) {
|
|
11507
|
+
const objects = [];
|
|
11508
|
+
let depth = 0;
|
|
11509
|
+
let start = -1;
|
|
11510
|
+
let inString = false;
|
|
11511
|
+
let escaped = false;
|
|
11512
|
+
for (let i = 0; i < text.length; i++) {
|
|
11513
|
+
const ch = text[i];
|
|
11514
|
+
if (inString) {
|
|
11515
|
+
if (escaped) escaped = false;
|
|
11516
|
+
else if (ch === "\\") escaped = true;
|
|
11517
|
+
else if (ch === '"') inString = false;
|
|
11518
|
+
continue;
|
|
11519
|
+
}
|
|
11520
|
+
if (ch === '"') {
|
|
11521
|
+
inString = true;
|
|
11522
|
+
} else if (ch === "{") {
|
|
11523
|
+
if (depth === 0) start = i;
|
|
11524
|
+
depth++;
|
|
11525
|
+
} else if (ch === "}" && depth > 0) {
|
|
11526
|
+
depth--;
|
|
11527
|
+
if (depth === 0 && start >= 0) {
|
|
11528
|
+
const candidate = text.slice(start, i + 1);
|
|
11529
|
+
try {
|
|
11530
|
+
const parsed = JSON.parse(candidate);
|
|
11531
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
11532
|
+
objects.push(parsed);
|
|
11533
|
+
}
|
|
11534
|
+
} catch {
|
|
11535
|
+
}
|
|
11536
|
+
start = -1;
|
|
11537
|
+
}
|
|
11538
|
+
}
|
|
11539
|
+
}
|
|
11540
|
+
return objects;
|
|
11541
|
+
}
|
|
11542
|
+
async spawnAgent(role, taskBrief) {
|
|
11543
|
+
const cfg = {
|
|
11544
|
+
id: `${role}-${this.sessionId}`,
|
|
11545
|
+
name: role,
|
|
11546
|
+
role
|
|
11547
|
+
// No fleet_emit tool — agents output structured JSON, the Director parses
|
|
11548
|
+
// results and emits FleetBus events. This keeps the agent simple and the
|
|
11549
|
+
// routing deterministic.
|
|
11550
|
+
};
|
|
11551
|
+
const subagentId = await this.director.spawn(cfg);
|
|
11552
|
+
await this.director.assign({
|
|
11553
|
+
id: randomUUID(),
|
|
11554
|
+
subagentId,
|
|
11555
|
+
description: taskBrief
|
|
11556
|
+
});
|
|
11557
|
+
return subagentId;
|
|
11558
|
+
}
|
|
11559
|
+
buildBugHunterTask() {
|
|
11560
|
+
this.options.targetPaths.join(", ");
|
|
11561
|
+
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
11562
|
+
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
11563
|
+
${f.content}`).join("\n\n");
|
|
11564
|
+
return `You are BugHunter. Scan the following files for bugs and code smells.
|
|
11565
|
+
|
|
11566
|
+
Target files:
|
|
11567
|
+
${fileContents}
|
|
11568
|
+
|
|
11569
|
+
For each bug found, write ONE valid JSON line to the scratchpad with this exact structure:
|
|
11570
|
+
{ "finding": { "id": "<uuid>", "type": "<pattern>", "severity": "<critical|high|medium|low>", "location": { "file": "<path>", "line": <n> }, "description": "<explain>", "suggestedFix": "<optional>" } }.
|
|
11571
|
+
After scanning all files, write your full markdown bug report to the shared scratchpad at:
|
|
11572
|
+
${scratchpad}/bug-hunter-report-${this.sessionId}.md`;
|
|
11573
|
+
}
|
|
11574
|
+
buildRefactorPlannerTask() {
|
|
11575
|
+
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
11576
|
+
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
11577
|
+
${f.content}`).join("\n\n");
|
|
11578
|
+
return `You are RefactorPlanner. Plan refactorings for the following files.
|
|
11579
|
+
|
|
11580
|
+
Target files:
|
|
11581
|
+
${fileContents}
|
|
11582
|
+
|
|
11583
|
+
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:
|
|
11584
|
+
{ "plan": { "id": "<uuid>", "basedOnBugIds": ["<bug-id>"], "phases": [...], "riskScore": "<low|medium|high>", "estimatedChangeCount": <n>, "rollbackStrategy": "<text>" } }.
|
|
11585
|
+
After planning, write your full markdown plan to:
|
|
11586
|
+
${scratchpad}/refactor-plan-${this.sessionId}.md`;
|
|
11587
|
+
}
|
|
11588
|
+
buildCriticTask() {
|
|
11589
|
+
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
11590
|
+
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
11591
|
+
${f.content}`).join("\n\n");
|
|
11592
|
+
return `You are Critic. Evaluate bug findings and refactor plans as they arrive via fleet bus events.
|
|
11593
|
+
|
|
11594
|
+
Target files:
|
|
11595
|
+
${fileContents}
|
|
11596
|
+
|
|
11597
|
+
Subscribe to bug.found events (from BugHunter) and refactor.plan events (from RefactorPlanner). For each subject, write one JSON line per evaluation:
|
|
11598
|
+
{ "evaluation": { "id": "<uuid>", "subjectType": "<bug_finding|refactor_plan>", "subjectId": "<id>", "score": <0-10>, "verdict": "<approve|needs_revision|reject>", "strengths": [...], "weaknesses": [...], "concerns": [...] } }.
|
|
11599
|
+
After all evaluations, write your markdown critic report to:
|
|
11600
|
+
${scratchpad}/critic-report-${this.sessionId}.md`;
|
|
11601
|
+
}
|
|
11602
|
+
wireFleetBus() {
|
|
11603
|
+
const d1 = this.fleetBus.filter("bug.found", (e) => {
|
|
11604
|
+
const payload = e.payload;
|
|
11605
|
+
if (payload?.finding) {
|
|
11606
|
+
this.bugs.set(payload.finding.id, payload.finding);
|
|
11607
|
+
this.emit("bug.found", payload);
|
|
11608
|
+
}
|
|
11609
|
+
});
|
|
11610
|
+
this.disposers.push(d1);
|
|
11611
|
+
const d2 = this.fleetBus.filter("refactor.plan", (e) => {
|
|
11612
|
+
const payload = e.payload;
|
|
11613
|
+
if (payload?.plan) {
|
|
11614
|
+
this.plans.set(payload.plan.id, payload.plan);
|
|
11615
|
+
this.emit("refactor.plan", payload);
|
|
11616
|
+
}
|
|
11617
|
+
});
|
|
11618
|
+
this.disposers.push(d2);
|
|
11619
|
+
const d3 = this.fleetBus.filter("critic.evaluation", (e) => {
|
|
11620
|
+
const payload = e.payload;
|
|
11621
|
+
if (payload?.evaluation) {
|
|
11622
|
+
this.evaluations.set(payload.evaluation.id, payload.evaluation);
|
|
11623
|
+
this.emit("critic.evaluation", payload);
|
|
11624
|
+
}
|
|
11625
|
+
});
|
|
11626
|
+
this.disposers.push(d3);
|
|
11627
|
+
}
|
|
11628
|
+
assembleReport() {
|
|
11629
|
+
const bugList = Array.from(this.bugs.values());
|
|
11630
|
+
const planList = Array.from(this.plans.values());
|
|
11631
|
+
const evalList = Array.from(this.evaluations.values());
|
|
11632
|
+
const verdictOrder = {
|
|
11633
|
+
approve: 0,
|
|
11634
|
+
needs_revision: 1,
|
|
11635
|
+
reject: 2
|
|
11636
|
+
};
|
|
11637
|
+
const overallVerdict = evalList.reduce(
|
|
11638
|
+
(worst, eval_) => {
|
|
11639
|
+
const w = verdictOrder[worst];
|
|
11640
|
+
const c = verdictOrder[eval_.verdict];
|
|
11641
|
+
return c > w ? eval_.verdict : worst;
|
|
11642
|
+
},
|
|
11643
|
+
"approve"
|
|
11644
|
+
);
|
|
11645
|
+
const summary = this.buildMarkdownSummary(bugList, planList, evalList, overallVerdict);
|
|
11646
|
+
return {
|
|
11647
|
+
sessionId: this.sessionId,
|
|
11648
|
+
startedAt: this.snapshot.createdAt,
|
|
11649
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11650
|
+
targetPaths: this.options.targetPaths,
|
|
11651
|
+
bugs: bugList,
|
|
11652
|
+
refactorPlans: planList,
|
|
11653
|
+
evaluations: evalList,
|
|
11654
|
+
overallVerdict,
|
|
11655
|
+
summary
|
|
11656
|
+
};
|
|
11657
|
+
}
|
|
11658
|
+
buildMarkdownSummary(bugs, plans, evals, overallVerdict) {
|
|
11659
|
+
const lines = [
|
|
11660
|
+
`## Collaborative Debugging Report \u2014 ${this.sessionId}`,
|
|
11661
|
+
"",
|
|
11662
|
+
`**Target:** ${this.options.targetPaths.join(", ")}`,
|
|
11663
|
+
`**Overall Verdict:** **${overallVerdict.toUpperCase()}**`,
|
|
11664
|
+
""
|
|
11665
|
+
];
|
|
11666
|
+
if (bugs.length > 0) {
|
|
11667
|
+
lines.push("### Bugs Found", "");
|
|
11668
|
+
for (const b of bugs) {
|
|
11669
|
+
lines.push(
|
|
11670
|
+
`- **[${b.severity.toUpperCase()}]** \`${b.location.file}:${b.location.line}\` \u2014 ${b.description}`
|
|
11671
|
+
);
|
|
11672
|
+
}
|
|
11673
|
+
lines.push("");
|
|
11674
|
+
}
|
|
11675
|
+
if (plans.length > 0) {
|
|
11676
|
+
lines.push("### Refactor Plans", "");
|
|
11677
|
+
for (const p of plans) {
|
|
11678
|
+
lines.push(`- **Phase plan** (risk: ${p.riskScore}, ~${p.estimatedChangeCount} changes)`);
|
|
11679
|
+
for (const phase of p.phases) {
|
|
11680
|
+
lines.push(` - Phase ${phase.number}: ${phase.title} [${phase.risk}]`);
|
|
11681
|
+
}
|
|
11682
|
+
}
|
|
11683
|
+
lines.push("");
|
|
11684
|
+
}
|
|
11685
|
+
if (evals.length > 0) {
|
|
11686
|
+
lines.push("### Critic Evaluations", "");
|
|
11687
|
+
for (const e of evals) {
|
|
11688
|
+
lines.push(`- [${e.subjectType}] score=${e.score}/10 \u2014 **${e.verdict.toUpperCase()}**`);
|
|
11689
|
+
for (const c of e.concerns) {
|
|
11690
|
+
if (c.severity === "blocking") {
|
|
11691
|
+
lines.push(` - ${c.description}`);
|
|
11692
|
+
}
|
|
11693
|
+
}
|
|
11694
|
+
}
|
|
11695
|
+
lines.push("");
|
|
11696
|
+
}
|
|
11697
|
+
return lines.join("\n");
|
|
11698
|
+
}
|
|
11699
|
+
cleanup() {
|
|
11700
|
+
for (const dispose of this.disposers) dispose();
|
|
11701
|
+
this.disposers.length = 0;
|
|
11702
|
+
}
|
|
11703
|
+
};
|
|
10974
11704
|
|
|
10975
11705
|
// src/coordination/director.ts
|
|
10976
11706
|
var FleetSpawnBudgetError = class extends Error {
|
|
@@ -11049,6 +11779,8 @@ var Director = class {
|
|
|
11049
11779
|
subagentBridges = /* @__PURE__ */ new Map();
|
|
11050
11780
|
/** Tracks per-spawn config + assigned task ids for manifest writing. */
|
|
11051
11781
|
manifestEntries = /* @__PURE__ */ new Map();
|
|
11782
|
+
/** Tracks assigned nicknames so the same name is never reused in one fleet. */
|
|
11783
|
+
_usedNicknames = /* @__PURE__ */ new Set();
|
|
11052
11784
|
manifestPath;
|
|
11053
11785
|
roster;
|
|
11054
11786
|
directorPreamble;
|
|
@@ -11342,9 +12074,21 @@ var Director = class {
|
|
|
11342
12074
|
}
|
|
11343
12075
|
}
|
|
11344
12076
|
let result;
|
|
12077
|
+
const needsNickname = config.name === config.role || !config.name || config.name === "subagent";
|
|
12078
|
+
if (needsNickname) {
|
|
12079
|
+
const role = config.role ?? "subagent";
|
|
12080
|
+
if (this.fleetManager) {
|
|
12081
|
+
this.fleetManager.assignNicknameAndRecord("", config, priceLookup);
|
|
12082
|
+
} else {
|
|
12083
|
+
config.name = assignNickname(role, this._usedNicknames);
|
|
12084
|
+
this._usedNicknames.add(config.name.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-"));
|
|
12085
|
+
}
|
|
12086
|
+
}
|
|
11345
12087
|
result = await this.coordinator.spawn(config);
|
|
11346
12088
|
if (this.fleetManager) {
|
|
11347
|
-
|
|
12089
|
+
if (!needsNickname) {
|
|
12090
|
+
this.fleetManager.recordSpawn(result.subagentId, config, priceLookup);
|
|
12091
|
+
}
|
|
11348
12092
|
} else {
|
|
11349
12093
|
this.spawnCount += 1;
|
|
11350
12094
|
this.subagentMeta.set(result.subagentId, {
|
|
@@ -11780,7 +12524,9 @@ var Director = class {
|
|
|
11780
12524
|
makeFleetStatusTool(this),
|
|
11781
12525
|
makeFleetUsageTool(this),
|
|
11782
12526
|
makeFleetSessionTool(this),
|
|
11783
|
-
makeFleetHealthTool(this)
|
|
12527
|
+
makeFleetHealthTool(this),
|
|
12528
|
+
makeCollabDebugTool(this),
|
|
12529
|
+
makeFleetEmitTool(this)
|
|
11784
12530
|
];
|
|
11785
12531
|
return t;
|
|
11786
12532
|
}
|
|
@@ -11792,6 +12538,17 @@ var Director = class {
|
|
|
11792
12538
|
async acquireCheckpointLock() {
|
|
11793
12539
|
return this.stateCheckpoint ? this.stateCheckpoint.acquireLock() : true;
|
|
11794
12540
|
}
|
|
12541
|
+
/**
|
|
12542
|
+
* Start a collaborative debugging session: BugHunter, RefactorPlanner,
|
|
12543
|
+
* and Critic run in parallel on the same target files, with findings
|
|
12544
|
+
* flowing through the FleetBus (bug.found → refactor.plan →
|
|
12545
|
+
* critic.evaluation). Returns a structured CollabDebugReport when all
|
|
12546
|
+
* three agents complete or the session times out.
|
|
12547
|
+
*/
|
|
12548
|
+
async spawnCollab(options) {
|
|
12549
|
+
const session = new CollabSession(this, this.fleet, options);
|
|
12550
|
+
return session.start();
|
|
12551
|
+
}
|
|
11795
12552
|
/**
|
|
11796
12553
|
* Resume from a prior checkpoint snapshot (loaded via
|
|
11797
12554
|
* `loadDirectorState()`). Re-attach to the fleet mid-flight so
|
|
@@ -14025,10 +14782,10 @@ var AISpecBuilder = class {
|
|
|
14025
14782
|
async saveSession() {
|
|
14026
14783
|
if (!this.sessionPath) return;
|
|
14027
14784
|
try {
|
|
14028
|
-
const
|
|
14785
|
+
const fsp15 = await import('fs/promises');
|
|
14029
14786
|
const path18 = await import('path');
|
|
14030
14787
|
const { atomicWrite: atomicWrite2 } = await Promise.resolve().then(() => (init_atomic_write(), atomic_write_exports));
|
|
14031
|
-
await
|
|
14788
|
+
await fsp15.mkdir(path18.dirname(this.sessionPath), { recursive: true });
|
|
14032
14789
|
await atomicWrite2(this.sessionPath, JSON.stringify(this.session, null, 2));
|
|
14033
14790
|
} catch {
|
|
14034
14791
|
}
|
|
@@ -14037,8 +14794,8 @@ var AISpecBuilder = class {
|
|
|
14037
14794
|
async loadSession() {
|
|
14038
14795
|
if (!this.sessionPath) return false;
|
|
14039
14796
|
try {
|
|
14040
|
-
const
|
|
14041
|
-
const raw = await
|
|
14797
|
+
const fsp15 = await import('fs/promises');
|
|
14798
|
+
const raw = await fsp15.readFile(this.sessionPath, "utf8");
|
|
14042
14799
|
const loaded = JSON.parse(raw);
|
|
14043
14800
|
if (loaded?.id && loaded?.phase && loaded?.title) {
|
|
14044
14801
|
this.session = loaded;
|
|
@@ -14052,8 +14809,8 @@ var AISpecBuilder = class {
|
|
|
14052
14809
|
async deleteSession() {
|
|
14053
14810
|
if (!this.sessionPath) return;
|
|
14054
14811
|
try {
|
|
14055
|
-
const
|
|
14056
|
-
await
|
|
14812
|
+
const fsp15 = await import('fs/promises');
|
|
14813
|
+
await fsp15.unlink(this.sessionPath);
|
|
14057
14814
|
} catch {
|
|
14058
14815
|
}
|
|
14059
14816
|
}
|
|
@@ -16420,6 +17177,6 @@ var allServers = () => ({
|
|
|
16420
17177
|
"minimax-vision": { ...miniMaxVisionServer(), enabled: false }
|
|
16421
17178
|
});
|
|
16422
17179
|
|
|
16423
|
-
export { AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, ConfigMigrationError, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPermissionPolicy, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultTaskStore, Director, DirectorStateCheckpoint, DoneConditionChecker, EternalAutonomyEngine, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetSpawnBudgetError, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, IntelligentCompactor, LLMSelector, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, SddParallelRun, SddTaskDecomposer, SelectiveCompactor, SessionAnalyzer, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, ToolExecutor, addPlanItem, allServers, analyzeCriticalPath, applyRosterBudget, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, classifyFamily, clearPlan, composeDirectorPrompt, composeSubagentPrompt, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDelegateTool, createMessage, decryptConfigSecrets2 as decryptConfigSecrets, deriveTodosFromPlanItem, dispatchAgent, emptyPlan, encryptConfigSecrets, everArtServer, filesystemServer, formatPlan, formatPlanTemplates, getAgentDefinition, getPlanTemplate, getTemplate, githubServer, googleMapsServer, listPlanTemplates, listTemplates, loadDirectorState, loadPlan, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAutonomyPromptContributor, makeDirectorSessionFactory, makeLLMClassifier, migratePlaintextSecrets, miniMaxVisionServer, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, savePlan, saveTodosCheckpoint, scoreAgents, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, templateToMarkdown, wireMetricsToEvents, zaiVisionServer };
|
|
17180
|
+
export { AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, ConfigMigrationError, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPermissionPolicy, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultTaskStore, Director, DirectorStateCheckpoint, DoneConditionChecker, EternalAutonomyEngine, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetSpawnBudgetError, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, IntelligentCompactor, LLMSelector, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, SddParallelRun, SddTaskDecomposer, SelectiveCompactor, SessionAnalyzer, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, ToolExecutor, addPlanItem, allServers, analyzeCriticalPath, applyRosterBudget, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, classifyFamily, clearPlan, composeDirectorPrompt, composeSubagentPrompt, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDelegateTool, createMessage, decryptConfigSecrets2 as decryptConfigSecrets, deriveTodosFromPlanItem, dispatchAgent, emptyPlan, encryptConfigSecrets, everArtServer, filesystemServer, formatPlan, formatPlanTemplates, getAgentDefinition, getPlanTemplate, getTemplate, githubServer, googleMapsServer, listPlanTemplates, listTemplates, loadDirectorState, loadPlan, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, migratePlaintextSecrets, miniMaxVisionServer, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, savePlan, saveTodosCheckpoint, scoreAgents, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, templateToMarkdown, wireMetricsToEvents, zaiVisionServer };
|
|
16424
17181
|
//# sourceMappingURL=index.js.map
|
|
16425
17182
|
//# sourceMappingURL=index.js.map
|