@wrongstack/core 0.31.1 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-subagent-runner-DpZTLdBe.d.ts → agent-subagent-runner-C66vi4Gq.d.ts} +1 -1
- package/dist/{config-BUEGM4JP.d.ts → config-ZRCf7sTu.d.ts} +21 -1
- package/dist/coordination/index.d.ts +7 -7
- package/dist/coordination/index.js +3051 -2947
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +8 -8
- package/dist/defaults/index.js +1435 -1321
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +5 -5
- package/dist/extension/index.d.ts +2 -2
- package/dist/{index-ysfO_DlX.d.ts → index-6_csX32J.d.ts} +1 -1
- package/dist/{index-pXJdVLe0.d.ts → index-DkVgH3wC.d.ts} +31 -1
- package/dist/index.d.ts +135 -15
- package/dist/index.js +1236 -931
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +2 -2
- package/dist/infrastructure/index.js +17 -3
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +2 -2
- package/dist/{mcp-servers-BzB3r7_c.d.ts → mcp-servers-DONdo-XM.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-DOXSgtom.d.ts → multi-agent-coordinator-BUsjiRWl.d.ts} +1 -1
- package/dist/{null-fleet-bus-CAQDGsKc.d.ts → null-fleet-bus-FvgHnZah.d.ts} +169 -131
- package/dist/{plan-templates-BZMi-VpU.d.ts → plan-templates-DYCeRCDN.d.ts} +1 -1
- package/dist/sdd/index.d.ts +3 -3
- package/dist/storage/index.d.ts +2 -2
- package/dist/{tool-executor-BAi4WI2d.d.ts → tool-executor-BpK-SWtJ.d.ts} +1 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/index.js +17 -3
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +107 -1
- package/dist/utils/index.js +53 -2
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/multi-agent/SKILL.md +57 -1
package/dist/index.js
CHANGED
|
@@ -1295,11 +1295,62 @@ function deepMerge(a, b) {
|
|
|
1295
1295
|
return out;
|
|
1296
1296
|
}
|
|
1297
1297
|
|
|
1298
|
+
// src/utils/term.ts
|
|
1299
|
+
var hasStdout = () => typeof process !== "undefined" && !!process.stdout;
|
|
1300
|
+
var hasStdin = () => typeof process !== "undefined" && !!process.stdin;
|
|
1301
|
+
function isStdoutTTY() {
|
|
1302
|
+
return hasStdout() && Boolean(process.stdout.isTTY);
|
|
1303
|
+
}
|
|
1304
|
+
function isStdinTTY() {
|
|
1305
|
+
return hasStdin() && Boolean(process.stdin.isTTY);
|
|
1306
|
+
}
|
|
1307
|
+
function isInteractive() {
|
|
1308
|
+
return isStdinTTY() && isStdoutTTY();
|
|
1309
|
+
}
|
|
1310
|
+
function getTermSize() {
|
|
1311
|
+
if (!hasStdout()) return { rows: 24, cols: 80 };
|
|
1312
|
+
return {
|
|
1313
|
+
rows: process.stdout.rows ?? 24,
|
|
1314
|
+
cols: process.stdout.columns ?? 80
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1317
|
+
function onResize(cb, stream = process.stdout) {
|
|
1318
|
+
if (!stream || typeof stream.on !== "function") return () => {
|
|
1319
|
+
};
|
|
1320
|
+
const handler = () => {
|
|
1321
|
+
cb({
|
|
1322
|
+
rows: stream.rows ?? 24,
|
|
1323
|
+
cols: stream.columns ?? 80
|
|
1324
|
+
});
|
|
1325
|
+
};
|
|
1326
|
+
stream.on("resize", handler);
|
|
1327
|
+
return () => {
|
|
1328
|
+
stream.off("resize", handler);
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
function setRawMode(input, mode) {
|
|
1332
|
+
if (!input || input.isTTY !== true) return false;
|
|
1333
|
+
if (typeof input.setRawMode !== "function") return false;
|
|
1334
|
+
input.setRawMode(mode);
|
|
1335
|
+
return true;
|
|
1336
|
+
}
|
|
1337
|
+
function writeTo(s, stream) {
|
|
1338
|
+
if (!stream || typeof stream.write !== "function") return false;
|
|
1339
|
+
stream.write(s);
|
|
1340
|
+
return true;
|
|
1341
|
+
}
|
|
1342
|
+
function writeOut(s, stream = process.stdout) {
|
|
1343
|
+
return writeTo(s, stream);
|
|
1344
|
+
}
|
|
1345
|
+
function writeErr(s, stream = process.stderr) {
|
|
1346
|
+
return writeTo(s, stream);
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1298
1349
|
// src/utils/color.ts
|
|
1299
1350
|
var isColorTty = () => {
|
|
1300
1351
|
if (process.env.NO_COLOR) return false;
|
|
1301
1352
|
if (process.env.FORCE_COLOR) return true;
|
|
1302
|
-
return
|
|
1353
|
+
return isStdoutTTY();
|
|
1303
1354
|
};
|
|
1304
1355
|
var COLOR = isColorTty();
|
|
1305
1356
|
var wrap = (open5, close) => (s) => COLOR ? `\x1B[${open5}m${s}\x1B[${close}m` : s;
|
|
@@ -1403,10 +1454,10 @@ var DefaultLogger = class _DefaultLogger {
|
|
|
1403
1454
|
if (r <= LEVEL_RANK.warn || this.level === "debug" || this.level === "trace") {
|
|
1404
1455
|
const head = `${color.dim(ts)} ${COLORS[level](level.toUpperCase().padEnd(5))} ${msg}`;
|
|
1405
1456
|
if (ctx !== void 0) {
|
|
1406
|
-
|
|
1457
|
+
writeErr(`${head} ${formatCtx(ctx)}
|
|
1407
1458
|
`);
|
|
1408
1459
|
} else {
|
|
1409
|
-
|
|
1460
|
+
writeErr(`${head}
|
|
1410
1461
|
`);
|
|
1411
1462
|
}
|
|
1412
1463
|
}
|
|
@@ -5487,9 +5538,9 @@ var DefaultMemoryStore = class {
|
|
|
5487
5538
|
async readAll() {
|
|
5488
5539
|
const parts = [];
|
|
5489
5540
|
for (const scope of ["project-agents", "project-memory", "user-memory"]) {
|
|
5490
|
-
const
|
|
5491
|
-
if (
|
|
5492
|
-
parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${
|
|
5541
|
+
const writeErr2 = this.writeErrors.get(scope);
|
|
5542
|
+
if (writeErr2) {
|
|
5543
|
+
parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${writeErr2.message}`);
|
|
5493
5544
|
}
|
|
5494
5545
|
const body = await this.read(scope);
|
|
5495
5546
|
if (body.trim()) parts.push(`## ${labelOf(scope)}
|
|
@@ -13686,357 +13737,631 @@ function buildGoalPreamble(goal) {
|
|
|
13686
13737
|
"BEGIN.]"
|
|
13687
13738
|
].join("\n");
|
|
13688
13739
|
}
|
|
13689
|
-
|
|
13690
|
-
// src/coordination/director.ts
|
|
13691
13740
|
init_atomic_write();
|
|
13692
|
-
|
|
13693
|
-
|
|
13694
|
-
|
|
13741
|
+
var DEFAULT_MAX_TARGET_FILES = 30;
|
|
13742
|
+
var CollabSession = class extends EventEmitter {
|
|
13743
|
+
sessionId;
|
|
13744
|
+
options;
|
|
13745
|
+
snapshot;
|
|
13746
|
+
director;
|
|
13747
|
+
fleetBus;
|
|
13748
|
+
subagentIds = /* @__PURE__ */ new Map();
|
|
13749
|
+
// role → subagentId
|
|
13750
|
+
bugs = /* @__PURE__ */ new Map();
|
|
13751
|
+
plans = /* @__PURE__ */ new Map();
|
|
13752
|
+
evaluations = /* @__PURE__ */ new Map();
|
|
13753
|
+
disposers = new Array();
|
|
13754
|
+
settled = false;
|
|
13755
|
+
timeoutMs;
|
|
13756
|
+
cancelled = false;
|
|
13757
|
+
alerts = [];
|
|
13758
|
+
/** Tracks tool call counts per subagent for progress-based timeout decisions. */
|
|
13759
|
+
progressBySubagent = /* @__PURE__ */ new Map();
|
|
13760
|
+
/** Last tool call count when a timeout warning was handled. */
|
|
13761
|
+
lastTimeoutProgress = /* @__PURE__ */ new Map();
|
|
13762
|
+
/** Session-level timeout timer handle (cleared on cancel or natural completion). */
|
|
13763
|
+
_timeoutTimer;
|
|
13764
|
+
constructor(director, fleetBus, options) {
|
|
13765
|
+
super();
|
|
13766
|
+
this.sessionId = randomUUID();
|
|
13767
|
+
this.options = options;
|
|
13768
|
+
this.director = director;
|
|
13769
|
+
this.fleetBus = fleetBus;
|
|
13770
|
+
this.timeoutMs = options.timeoutMs ?? 10 * 60 * 1e3;
|
|
13771
|
+
if (options.prebuiltSnapshot) {
|
|
13772
|
+
this.snapshot = options.prebuiltSnapshot;
|
|
13773
|
+
} else {
|
|
13774
|
+
this.snapshot = {
|
|
13775
|
+
id: this.sessionId,
|
|
13776
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13777
|
+
files: []
|
|
13778
|
+
};
|
|
13779
|
+
}
|
|
13780
|
+
}
|
|
13781
|
+
get id() {
|
|
13782
|
+
return this.sessionId;
|
|
13783
|
+
}
|
|
13784
|
+
getSessionAlerts() {
|
|
13785
|
+
return [...this.alerts];
|
|
13786
|
+
}
|
|
13787
|
+
isCancelled() {
|
|
13788
|
+
return this.cancelled;
|
|
13789
|
+
}
|
|
13695
13790
|
/**
|
|
13696
|
-
*
|
|
13697
|
-
*
|
|
13698
|
-
*
|
|
13791
|
+
* Snapshot of role → subagentId map. The Director calls coordinator.stop()
|
|
13792
|
+
* for each agent when cancelling the session, using this map to enumerate
|
|
13793
|
+
* all three collab agents.
|
|
13699
13794
|
*/
|
|
13700
|
-
|
|
13701
|
-
|
|
13702
|
-
constructor(sizeThreshold = 2e3) {
|
|
13703
|
-
this.sizeThreshold = sizeThreshold;
|
|
13795
|
+
getSubagentIds() {
|
|
13796
|
+
return new Map(this.subagentIds);
|
|
13704
13797
|
}
|
|
13705
13798
|
/**
|
|
13706
|
-
*
|
|
13707
|
-
*
|
|
13799
|
+
* Returns the effective file limit for this session.
|
|
13800
|
+
* Priority: explicit `maxTargetFiles` > dynamic from `contextWindow` > `DEFAULT_MAX_TARGET_FILES`.
|
|
13708
13801
|
*/
|
|
13709
|
-
|
|
13710
|
-
if (
|
|
13711
|
-
return
|
|
13802
|
+
effectiveFileLimit() {
|
|
13803
|
+
if (this.options.maxTargetFiles !== void 0) {
|
|
13804
|
+
return this.options.maxTargetFiles;
|
|
13712
13805
|
}
|
|
13713
|
-
|
|
13714
|
-
|
|
13715
|
-
if (size <= this.sizeThreshold) {
|
|
13716
|
-
return { summary: serialized.slice(0, 500), inline: true };
|
|
13806
|
+
if (this.options.contextWindow !== void 0) {
|
|
13807
|
+
return Math.max(5, Math.floor(this.options.contextWindow * 0.4 / 2e3));
|
|
13717
13808
|
}
|
|
13718
|
-
|
|
13719
|
-
this.store.set(key, {
|
|
13720
|
-
key,
|
|
13721
|
-
value,
|
|
13722
|
-
size,
|
|
13723
|
-
storedAt: Date.now()
|
|
13724
|
-
});
|
|
13725
|
-
return {
|
|
13726
|
-
key,
|
|
13727
|
-
summary: `[stored: ${size} chars \u2014 use roll_up or ask_result tool to retrieve, key=${key}]`,
|
|
13728
|
-
inline: false
|
|
13729
|
-
};
|
|
13809
|
+
return DEFAULT_MAX_TARGET_FILES;
|
|
13730
13810
|
}
|
|
13731
|
-
|
|
13732
|
-
|
|
13733
|
-
|
|
13734
|
-
|
|
13735
|
-
|
|
13736
|
-
|
|
13811
|
+
async buildSnapshot() {
|
|
13812
|
+
if (this.snapshot.files.length > 0) return this.snapshot;
|
|
13813
|
+
const allFiles = [];
|
|
13814
|
+
for (const pattern of this.options.targetPaths) {
|
|
13815
|
+
const expanded = await expandGlob(pattern);
|
|
13816
|
+
allFiles.push(...expanded);
|
|
13817
|
+
}
|
|
13818
|
+
const limit = this.effectiveFileLimit();
|
|
13819
|
+
if (allFiles.length > limit) {
|
|
13820
|
+
const hint = this.options.contextWindow ? `contextWindow=${this.options.contextWindow} \u2192 calculated limit=${limit}` : `default limit=${DEFAULT_MAX_TARGET_FILES}`;
|
|
13821
|
+
throw new Error(
|
|
13822
|
+
`[collab_debug] Target has ${allFiles.length} files, which exceeds the limit (${hint}). Narrow the target or pass maxTargetFiles / contextWindow to override. For large codebases, run package-by-package or module-by-module sessions instead of targeting the entire repo.`
|
|
13823
|
+
);
|
|
13824
|
+
}
|
|
13825
|
+
for (const filePath of allFiles) {
|
|
13826
|
+
try {
|
|
13827
|
+
const content = await fsp3.readFile(filePath, "utf8");
|
|
13828
|
+
const ext = filePath.split(".").pop() ?? "";
|
|
13829
|
+
const language = ext === "ts" || ext === "tsx" ? "typescript" : ext === "js" || ext === "jsx" ? "javascript" : ext === "md" ? "markdown" : ext === "json" ? "json" : void 0;
|
|
13830
|
+
this.snapshot.files.push({ path: filePath, content, language });
|
|
13831
|
+
} catch {
|
|
13832
|
+
this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
|
|
13833
|
+
}
|
|
13834
|
+
}
|
|
13835
|
+
return this.snapshot;
|
|
13737
13836
|
}
|
|
13738
13837
|
/**
|
|
13739
|
-
*
|
|
13838
|
+
* Cancel the session. Emits director.cancel_collab on the FleetBus so all
|
|
13839
|
+
* collab agents finish early. The session-level timeout timer is also cleared.
|
|
13840
|
+
* Safe to call multiple times (idempotent after first call).
|
|
13740
13841
|
*/
|
|
13741
|
-
|
|
13742
|
-
|
|
13743
|
-
|
|
13744
|
-
|
|
13745
|
-
|
|
13746
|
-
|
|
13747
|
-
|
|
13748
|
-
|
|
13749
|
-
|
|
13750
|
-
|
|
13751
|
-
|
|
13752
|
-
|
|
13842
|
+
cancel(reason = "Director cancelled collab session") {
|
|
13843
|
+
if (this.settled) return;
|
|
13844
|
+
this.cancelled = true;
|
|
13845
|
+
if (this._timeoutTimer) {
|
|
13846
|
+
clearTimeout(this._timeoutTimer);
|
|
13847
|
+
this._timeoutTimer = void 0;
|
|
13848
|
+
}
|
|
13849
|
+
this.fleetBus.emit({
|
|
13850
|
+
subagentId: this.director.id,
|
|
13851
|
+
ts: Date.now(),
|
|
13852
|
+
type: "director.cancel_collab",
|
|
13853
|
+
payload: { sessionId: this.sessionId, reason, cancelledAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
13854
|
+
});
|
|
13855
|
+
this.fleetBus.emit({
|
|
13856
|
+
subagentId: this.director.id,
|
|
13857
|
+
ts: Date.now(),
|
|
13858
|
+
type: "collab.cancelled",
|
|
13859
|
+
payload: { sessionId: this.sessionId, reason }
|
|
13860
|
+
});
|
|
13753
13861
|
}
|
|
13754
|
-
|
|
13755
|
-
|
|
13756
|
-
this.
|
|
13862
|
+
async start() {
|
|
13863
|
+
if (this.settled) throw new Error("session already settled");
|
|
13864
|
+
this.settled = true;
|
|
13865
|
+
await this.buildSnapshot();
|
|
13866
|
+
this.wireFleetBus();
|
|
13867
|
+
const [bugHunterId, refactorPlannerId, criticId] = await Promise.all([
|
|
13868
|
+
this.spawnAgent("bug-hunter", this.buildBugHunterTask()),
|
|
13869
|
+
this.spawnAgent("refactor-planner", this.buildRefactorPlannerTask()),
|
|
13870
|
+
this.spawnAgent("critic", this.buildCriticTask())
|
|
13871
|
+
]);
|
|
13872
|
+
this.subagentIds.set("bug-hunter", bugHunterId);
|
|
13873
|
+
this.subagentIds.set("refactor-planner", refactorPlannerId);
|
|
13874
|
+
this.subagentIds.set("critic", criticId);
|
|
13875
|
+
const timeout = new Promise((_, reject) => {
|
|
13876
|
+
this._timeoutTimer = setTimeout(() => {
|
|
13877
|
+
this.cancel("Session-level timeout reached");
|
|
13878
|
+
reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`));
|
|
13879
|
+
}, this.timeoutMs);
|
|
13880
|
+
});
|
|
13881
|
+
let results = null;
|
|
13882
|
+
try {
|
|
13883
|
+
results = await Promise.race([
|
|
13884
|
+
Promise.all([
|
|
13885
|
+
this.director.awaitTasks([bugHunterId]),
|
|
13886
|
+
this.director.awaitTasks([refactorPlannerId]),
|
|
13887
|
+
this.director.awaitTasks([criticId])
|
|
13888
|
+
]),
|
|
13889
|
+
timeout
|
|
13890
|
+
]);
|
|
13891
|
+
} catch (err) {
|
|
13892
|
+
if (this._timeoutTimer) {
|
|
13893
|
+
clearTimeout(this._timeoutTimer);
|
|
13894
|
+
this._timeoutTimer = void 0;
|
|
13895
|
+
}
|
|
13896
|
+
this.cleanup();
|
|
13897
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
13898
|
+
this.emit("session.error", error);
|
|
13899
|
+
throw error;
|
|
13900
|
+
}
|
|
13901
|
+
for (const result of results.flat()) {
|
|
13902
|
+
await this.parseAndEmit(result);
|
|
13903
|
+
}
|
|
13904
|
+
const report = this.assembleReport();
|
|
13905
|
+
this.cleanup();
|
|
13906
|
+
this.emit("session.done", report);
|
|
13907
|
+
return report;
|
|
13757
13908
|
}
|
|
13758
|
-
|
|
13759
|
-
|
|
13760
|
-
|
|
13761
|
-
|
|
13762
|
-
|
|
13909
|
+
async parseAndEmit(result) {
|
|
13910
|
+
if (result.status !== "success" || result.result == null) return;
|
|
13911
|
+
const text = typeof result.result === "string" ? result.result : JSON.stringify(result.result);
|
|
13912
|
+
for (const obj of this.extractJsonObjects(text)) {
|
|
13913
|
+
const type = "finding" in obj ? "bug.found" : "plan" in obj ? "refactor.plan" : "evaluation" in obj ? "critic.evaluation" : null;
|
|
13914
|
+
if (!type) continue;
|
|
13915
|
+
this.fleetBus.emit({
|
|
13916
|
+
subagentId: result.subagentId,
|
|
13917
|
+
taskId: result.taskId,
|
|
13918
|
+
ts: Date.now(),
|
|
13919
|
+
type,
|
|
13920
|
+
payload: obj
|
|
13921
|
+
});
|
|
13922
|
+
}
|
|
13763
13923
|
}
|
|
13764
|
-
|
|
13765
|
-
|
|
13766
|
-
|
|
13767
|
-
|
|
13768
|
-
|
|
13769
|
-
|
|
13770
|
-
|
|
13771
|
-
|
|
13772
|
-
|
|
13773
|
-
|
|
13774
|
-
|
|
13775
|
-
|
|
13776
|
-
|
|
13777
|
-
|
|
13778
|
-
|
|
13779
|
-
|
|
13780
|
-
|
|
13781
|
-
|
|
13782
|
-
|
|
13783
|
-
|
|
13784
|
-
|
|
13785
|
-
|
|
13786
|
-
|
|
13787
|
-
|
|
13788
|
-
|
|
13789
|
-
|
|
13790
|
-
|
|
13791
|
-
|
|
13792
|
-
|
|
13793
|
-
|
|
13794
|
-
|
|
13795
|
-
|
|
13796
|
-
|
|
13797
|
-
7. Wind down when satisfied. When the results are good enough, call
|
|
13798
|
-
work_complete \u2014 no new subagents will spawn and queued tasks complete
|
|
13799
|
-
as aborted. Running subagents finish naturally. Call terminate_subagent
|
|
13800
|
-
only for ones you need to stop immediately.`;
|
|
13801
|
-
var DEFAULT_SUBAGENT_BASELINE = `You are a subagent operating under a Director. You were spawned to handle
|
|
13802
|
-
a specific slice of a larger plan \u2014 do that slice well and report back.
|
|
13803
|
-
|
|
13804
|
-
Bridge contract:
|
|
13805
|
-
- You have a parent (the Director). You may call \`request\` on the
|
|
13806
|
-
parent bridge to ask a clarifying question. Use this sparingly; the
|
|
13807
|
-
parent is also working.
|
|
13808
|
-
- You MAY NOT request the parent's system prompt, tool list, or other
|
|
13809
|
-
subagents' context. Those are not yours to read.
|
|
13810
|
-
- Your final task output is what the Director sees. Be concise,
|
|
13811
|
-
structured, and self-contained \u2014 assume the Director will paste your
|
|
13812
|
-
output into its own context.`;
|
|
13813
|
-
function composeDirectorPrompt(parts = {}) {
|
|
13814
|
-
const sections = [];
|
|
13815
|
-
const preamble = parts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
|
|
13816
|
-
if (preamble && preamble.trim().length > 0) sections.push(preamble.trim());
|
|
13817
|
-
if (parts.rosterSummary && parts.rosterSummary.trim().length > 0) {
|
|
13818
|
-
sections.push(`Available roles you can spawn:
|
|
13819
|
-
${parts.rosterSummary.trim()}`);
|
|
13820
|
-
}
|
|
13821
|
-
if (parts.basePrompt && parts.basePrompt.trim().length > 0) {
|
|
13822
|
-
sections.push(parts.basePrompt.trim());
|
|
13823
|
-
}
|
|
13824
|
-
return sections.join("\n\n");
|
|
13825
|
-
}
|
|
13826
|
-
function composeSubagentPrompt(parts = {}) {
|
|
13827
|
-
const sections = [];
|
|
13828
|
-
const baseline = parts.baseline ?? DEFAULT_SUBAGENT_BASELINE;
|
|
13829
|
-
if (baseline && baseline.trim().length > 0) sections.push(baseline.trim());
|
|
13830
|
-
if (parts.role && parts.role.trim().length > 0) {
|
|
13831
|
-
sections.push(`Role:
|
|
13832
|
-
${parts.role.trim()}`);
|
|
13833
|
-
}
|
|
13834
|
-
if (parts.task && parts.task.trim().length > 0) {
|
|
13835
|
-
sections.push(`Task:
|
|
13836
|
-
${parts.task.trim()}`);
|
|
13837
|
-
}
|
|
13838
|
-
if (parts.sharedScratchpad && parts.sharedScratchpad.trim().length > 0) {
|
|
13839
|
-
sections.push(
|
|
13840
|
-
`Shared notes:
|
|
13841
|
-
A scratchpad shared with the rest of the fleet is mounted at \`${parts.sharedScratchpad.trim()}\`.
|
|
13842
|
-
- Write your final findings as markdown files there (e.g. \`findings.md\`, \`security.md\`).
|
|
13843
|
-
- Before starting, list the directory and read any sibling files relevant to your task \u2014 they may already contain context you can build on.
|
|
13844
|
-
- Use stable filenames (one file per concern); overwrite instead of appending so the Director sees the latest state.`
|
|
13845
|
-
);
|
|
13846
|
-
}
|
|
13847
|
-
if (parts.override && parts.override.trim().length > 0) {
|
|
13848
|
-
sections.push(parts.override.trim());
|
|
13849
|
-
}
|
|
13850
|
-
return sections.join("\n\n");
|
|
13851
|
-
}
|
|
13852
|
-
function rosterSummaryFromConfigs(roster) {
|
|
13853
|
-
const lines = [];
|
|
13854
|
-
for (const [roleId, cfg] of Object.entries(roster)) {
|
|
13855
|
-
const tag = cfg.provider && cfg.model ? ` (${cfg.provider}/${cfg.model})` : "";
|
|
13856
|
-
const headline = cfg.prompt ? (cfg.prompt.split("\n").find((l) => l.trim().length > 0) ?? "").trim().slice(0, 80) : "";
|
|
13857
|
-
const tail = headline ? ` \u2014 ${headline}` : "";
|
|
13858
|
-
lines.push(`- ${roleId}: ${cfg.name}${tag}${tail}`);
|
|
13859
|
-
}
|
|
13860
|
-
return lines.join("\n");
|
|
13861
|
-
}
|
|
13862
|
-
|
|
13863
|
-
// src/coordination/fleet-bus.ts
|
|
13864
|
-
var FleetBus = class {
|
|
13865
|
-
byId = /* @__PURE__ */ new Map();
|
|
13866
|
-
byType = /* @__PURE__ */ new Map();
|
|
13867
|
-
any = /* @__PURE__ */ new Set();
|
|
13868
|
-
/**
|
|
13869
|
-
* Hook a subagent's EventBus into the fleet. Uses `onAny()` (an alias for
|
|
13870
|
-
* `onPattern('*')`) to forward all events with subagent attribution, so
|
|
13871
|
-
* new kernel event types are automatically forwarded without any manual
|
|
13872
|
-
* registration. `subagent.*` events are excluded because they originate
|
|
13873
|
-
* from MultiAgentHost on the parent bus, not the subagent's own bus.
|
|
13874
|
-
*
|
|
13875
|
-
* Returns a disposer that detaches every subscription; call on
|
|
13876
|
-
* subagent teardown so the listeners don't outlive the run.
|
|
13877
|
-
*/
|
|
13878
|
-
attach(subagentId, bus, taskId) {
|
|
13879
|
-
const off = bus.onAny((type, payload) => {
|
|
13880
|
-
if (type.startsWith("subagent.")) return;
|
|
13881
|
-
this.emit({ subagentId, taskId, ts: Date.now(), type, payload });
|
|
13882
|
-
});
|
|
13883
|
-
return () => {
|
|
13884
|
-
off();
|
|
13885
|
-
};
|
|
13886
|
-
}
|
|
13887
|
-
/** Subscribe to every event from one subagent. */
|
|
13888
|
-
subscribe(subagentId, handler) {
|
|
13889
|
-
let set = this.byId.get(subagentId);
|
|
13890
|
-
if (!set) {
|
|
13891
|
-
set = /* @__PURE__ */ new Set();
|
|
13892
|
-
this.byId.set(subagentId, set);
|
|
13924
|
+
extractJsonObjects(text) {
|
|
13925
|
+
const objects = [];
|
|
13926
|
+
let depth = 0;
|
|
13927
|
+
let start = -1;
|
|
13928
|
+
let inString = false;
|
|
13929
|
+
let escaped = false;
|
|
13930
|
+
for (let i = 0; i < text.length; i++) {
|
|
13931
|
+
const ch = text[i];
|
|
13932
|
+
if (inString) {
|
|
13933
|
+
if (escaped) escaped = false;
|
|
13934
|
+
else if (ch === "\\") escaped = true;
|
|
13935
|
+
else if (ch === '"') inString = false;
|
|
13936
|
+
continue;
|
|
13937
|
+
}
|
|
13938
|
+
if (ch === '"') {
|
|
13939
|
+
inString = true;
|
|
13940
|
+
} else if (ch === "{") {
|
|
13941
|
+
if (depth === 0) start = i;
|
|
13942
|
+
depth++;
|
|
13943
|
+
} else if (ch === "}" && depth > 0) {
|
|
13944
|
+
depth--;
|
|
13945
|
+
if (depth === 0 && start >= 0) {
|
|
13946
|
+
const candidate = text.slice(start, i + 1);
|
|
13947
|
+
try {
|
|
13948
|
+
const parsed = JSON.parse(candidate);
|
|
13949
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
13950
|
+
objects.push(parsed);
|
|
13951
|
+
}
|
|
13952
|
+
} catch {
|
|
13953
|
+
}
|
|
13954
|
+
start = -1;
|
|
13955
|
+
}
|
|
13956
|
+
}
|
|
13893
13957
|
}
|
|
13894
|
-
|
|
13895
|
-
return () => {
|
|
13896
|
-
set.delete(handler);
|
|
13897
|
-
};
|
|
13958
|
+
return objects;
|
|
13898
13959
|
}
|
|
13899
|
-
|
|
13900
|
-
|
|
13901
|
-
|
|
13902
|
-
if (!set) {
|
|
13903
|
-
set = /* @__PURE__ */ new Set();
|
|
13904
|
-
this.byType.set(type, set);
|
|
13960
|
+
budgetForRole(role) {
|
|
13961
|
+
if (this.options.budgetOverrides?.[role]) {
|
|
13962
|
+
return this.options.budgetOverrides[role];
|
|
13905
13963
|
}
|
|
13906
|
-
|
|
13907
|
-
|
|
13908
|
-
|
|
13964
|
+
const defaults = {
|
|
13965
|
+
"bug-hunter": { maxIterations: 2e3, maxToolCalls: 5e3, timeoutMs: 10 * 60 * 1e3 },
|
|
13966
|
+
"refactor-planner": { maxIterations: 1500, maxToolCalls: 4e3, timeoutMs: 8 * 60 * 1e3 },
|
|
13967
|
+
"critic": { maxIterations: 1e3, maxToolCalls: 3e3, timeoutMs: 6 * 60 * 1e3 }
|
|
13909
13968
|
};
|
|
13969
|
+
return defaults[role] ?? { maxIterations: 1500, maxToolCalls: 4e3, timeoutMs: 8 * 60 * 1e3 };
|
|
13910
13970
|
}
|
|
13911
|
-
|
|
13912
|
-
|
|
13913
|
-
|
|
13914
|
-
|
|
13915
|
-
|
|
13971
|
+
async spawnAgent(role, taskBrief) {
|
|
13972
|
+
const budget = this.budgetForRole(role);
|
|
13973
|
+
const cfg = {
|
|
13974
|
+
id: `${role}-${this.sessionId}`,
|
|
13975
|
+
name: role,
|
|
13976
|
+
role,
|
|
13977
|
+
tools: ["fleet_emit", "fleet_status", "read", "grep", "glob", "bash", "write"],
|
|
13978
|
+
maxIterations: budget.maxIterations,
|
|
13979
|
+
maxToolCalls: budget.maxToolCalls,
|
|
13980
|
+
timeoutMs: budget.timeoutMs
|
|
13916
13981
|
};
|
|
13982
|
+
const subagentId = await this.director.spawn(cfg);
|
|
13983
|
+
await this.director.assign({ id: randomUUID(), subagentId, description: taskBrief });
|
|
13984
|
+
return subagentId;
|
|
13917
13985
|
}
|
|
13918
|
-
|
|
13919
|
-
const
|
|
13920
|
-
|
|
13921
|
-
|
|
13922
|
-
|
|
13923
|
-
|
|
13924
|
-
|
|
13925
|
-
|
|
13926
|
-
|
|
13927
|
-
|
|
13928
|
-
|
|
13929
|
-
|
|
13930
|
-
|
|
13931
|
-
|
|
13932
|
-
|
|
13933
|
-
|
|
13934
|
-
}
|
|
13935
|
-
for (const h of this.any) {
|
|
13936
|
-
try {
|
|
13937
|
-
h(event);
|
|
13938
|
-
} catch {
|
|
13939
|
-
}
|
|
13940
|
-
}
|
|
13986
|
+
buildBugHunterTask() {
|
|
13987
|
+
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
13988
|
+
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
13989
|
+
${f.content}`).join("\n\n");
|
|
13990
|
+
return `You are BugHunter. Scan the following files for bugs and code smells.
|
|
13991
|
+
|
|
13992
|
+
Target files:
|
|
13993
|
+
${fileContents}
|
|
13994
|
+
|
|
13995
|
+
For each bug found, emit it using the fleet_emit tool immediately:
|
|
13996
|
+
{ "type": "bug.found", "payload": { "finding": { "id": "<uuid>", "type": "<pattern>", "severity": "<critical|high|medium|low>", "location": { "file": "<path>", "line": <n> }, "description": "<explain>", "suggestedFix": "<optional>" } } }
|
|
13997
|
+
|
|
13998
|
+
After scanning all files, write your full markdown bug report to:
|
|
13999
|
+
${scratchpad}/bug-hunter-report-${this.sessionId}.md
|
|
14000
|
+
|
|
14001
|
+
Important: emit each finding as soon as you find it. Do not batch or wait until the end.`;
|
|
13941
14002
|
}
|
|
13942
|
-
|
|
13943
|
-
|
|
13944
|
-
|
|
13945
|
-
this.
|
|
13946
|
-
|
|
13947
|
-
|
|
13948
|
-
|
|
13949
|
-
|
|
14003
|
+
buildRefactorPlannerTask() {
|
|
14004
|
+
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
14005
|
+
const bugHunterReportPath = `${scratchpad}/bug-hunter-report-${this.sessionId}.md`;
|
|
14006
|
+
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
14007
|
+
${f.content}`).join("\n\n");
|
|
14008
|
+
return `You are RefactorPlanner. Plan refactorings for the following files.
|
|
14009
|
+
|
|
14010
|
+
Target files:
|
|
14011
|
+
${fileContents}
|
|
14012
|
+
|
|
14013
|
+
Read the BugHunter report at: ${bugHunterReportPath}
|
|
14014
|
+
|
|
14015
|
+
For each bug you can address, emit a refactor plan using fleet_emit:
|
|
14016
|
+
{ "type": "refactor.plan", "payload": { "plan": { "id": "<uuid>", "basedOnBugIds": ["<bug-id>"], "phases": [{ "number": 1, "title": "<phase>", "tasks": ["<task>"], "risk": "<low|medium|high>" }], "riskScore": "<low|medium|high>", "estimatedChangeCount": <n>, "rollbackStrategy": "<text>" } } }
|
|
14017
|
+
|
|
14018
|
+
Also write your full markdown plan to:
|
|
14019
|
+
${scratchpad}/refactor-plan-${this.sessionId}.md
|
|
14020
|
+
|
|
14021
|
+
Emit each plan immediately. Do not wait until planning is complete.`;
|
|
13950
14022
|
}
|
|
13951
|
-
|
|
13952
|
-
|
|
13953
|
-
|
|
13954
|
-
|
|
13955
|
-
|
|
13956
|
-
|
|
13957
|
-
|
|
13958
|
-
|
|
13959
|
-
|
|
13960
|
-
|
|
13961
|
-
|
|
13962
|
-
|
|
13963
|
-
|
|
13964
|
-
|
|
13965
|
-
|
|
13966
|
-
|
|
13967
|
-
|
|
13968
|
-
|
|
13969
|
-
|
|
13970
|
-
|
|
13971
|
-
|
|
13972
|
-
/** Disposes all fleet-bus subscriptions. Call when the aggregator is no longer needed. */
|
|
13973
|
-
dispose() {
|
|
13974
|
-
for (const off of this.unsub) off();
|
|
13975
|
-
this.unsub.length = 0;
|
|
13976
|
-
}
|
|
13977
|
-
/** Live snapshot — safe to call from a tool's execute() body. */
|
|
13978
|
-
snapshot() {
|
|
13979
|
-
return {
|
|
13980
|
-
total: { ...this.total },
|
|
13981
|
-
perSubagent: Object.fromEntries(
|
|
13982
|
-
Array.from(this.perSubagent.entries()).map(([k, v]) => [k, { ...v }])
|
|
13983
|
-
)
|
|
13984
|
-
};
|
|
14023
|
+
buildCriticTask() {
|
|
14024
|
+
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
14025
|
+
const bugHunterReportPath = `${scratchpad}/bug-hunter-report-${this.sessionId}.md`;
|
|
14026
|
+
const refactorPlanPath = `${scratchpad}/refactor-plan-${this.sessionId}.md`;
|
|
14027
|
+
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
14028
|
+
${f.content}`).join("\n\n");
|
|
14029
|
+
return `You are Critic. Evaluate bug findings and refactor plans.
|
|
14030
|
+
|
|
14031
|
+
Target files:
|
|
14032
|
+
${fileContents}
|
|
14033
|
+
|
|
14034
|
+
Read the BugHunter report at: ${bugHunterReportPath}
|
|
14035
|
+
Read the RefactorPlanner report at: ${refactorPlanPath}
|
|
14036
|
+
|
|
14037
|
+
For each bug and refactor plan, emit your evaluation using fleet_emit:
|
|
14038
|
+
{ "type": "critic.evaluation", "payload": { "evaluation": { "id": "<uuid>", "subjectType": "<bug_finding|refactor_plan>", "subjectId": "<id>", "score": <0-10>, "verdict": "<approve|needs_revision|reject>", "strengths": ["<strength>"], "weaknesses": ["<weakness>"], "concerns": [{ "description": "<concern>", "severity": "<blocking|advisory>" }] } } }
|
|
14039
|
+
|
|
14040
|
+
After all evaluations, write your markdown report to:
|
|
14041
|
+
${scratchpad}/critic-report-${this.sessionId}.md
|
|
14042
|
+
|
|
14043
|
+
Emit each evaluation immediately. Do not wait until you have read all reports.`;
|
|
13985
14044
|
}
|
|
13986
|
-
|
|
13987
|
-
|
|
13988
|
-
|
|
13989
|
-
|
|
13990
|
-
|
|
13991
|
-
|
|
13992
|
-
|
|
13993
|
-
|
|
13994
|
-
|
|
13995
|
-
|
|
13996
|
-
|
|
13997
|
-
|
|
13998
|
-
|
|
13999
|
-
|
|
14000
|
-
|
|
14001
|
-
|
|
14002
|
-
|
|
14045
|
+
wireFleetBus() {
|
|
14046
|
+
const dTool = this.fleetBus.filter("tool.executed", (e) => {
|
|
14047
|
+
this.progressBySubagent.set(e.subagentId, (this.progressBySubagent.get(e.subagentId) ?? 0) + 1);
|
|
14048
|
+
});
|
|
14049
|
+
this.disposers.push(dTool);
|
|
14050
|
+
const dBudget = this.fleetBus.filter("budget.threshold_reached", (e) => {
|
|
14051
|
+
const payload = e.payload;
|
|
14052
|
+
const role = this.roleFromSubagentId(e.subagentId);
|
|
14053
|
+
if (!role) return;
|
|
14054
|
+
const btwNotes = this.director.getLeaderBtwNotes();
|
|
14055
|
+
const alert = {
|
|
14056
|
+
sessionId: this.sessionId,
|
|
14057
|
+
subagentId: e.subagentId,
|
|
14058
|
+
role,
|
|
14059
|
+
level: "warning" /* WARNING */,
|
|
14060
|
+
message: `${role} hit ${payload.kind} soft limit (${payload.used}/${payload.limit})`,
|
|
14061
|
+
budgetKind: payload.kind,
|
|
14062
|
+
elapsedMs: payload.timeoutMs,
|
|
14063
|
+
limit: payload.limit,
|
|
14064
|
+
btwNotes
|
|
14003
14065
|
};
|
|
14004
|
-
this.
|
|
14005
|
-
|
|
14006
|
-
|
|
14066
|
+
this.alerts.push(alert);
|
|
14067
|
+
this.fleetBus.emit({
|
|
14068
|
+
subagentId: e.subagentId,
|
|
14069
|
+
ts: Date.now(),
|
|
14070
|
+
type: "collab.warning",
|
|
14071
|
+
payload: alert
|
|
14072
|
+
});
|
|
14073
|
+
const decision = this.options.onBudgetWarning?.(alert) ?? "ignore";
|
|
14074
|
+
if (decision === "cancel") {
|
|
14075
|
+
this.cancel(`Director cancelled: ${role} ${payload.kind} threshold`);
|
|
14076
|
+
return;
|
|
14077
|
+
}
|
|
14078
|
+
if (payload.kind === "timeout" || payload.kind === "idle_timeout") {
|
|
14079
|
+
const progress = this.progressBySubagent.get(e.subagentId) ?? 0;
|
|
14080
|
+
const lastProgress = this.lastTimeoutProgress.get(e.subagentId) ?? -1;
|
|
14081
|
+
if (progress <= lastProgress) {
|
|
14082
|
+
payload.deny();
|
|
14083
|
+
return;
|
|
14084
|
+
}
|
|
14085
|
+
this.lastTimeoutProgress.set(e.subagentId, progress);
|
|
14086
|
+
const newLimit = Math.min(Math.ceil((payload.timeoutMs ?? payload.limit) * 2), 24 * 60 * 6e4);
|
|
14087
|
+
setImmediate(() => {
|
|
14088
|
+
payload.extend({ timeoutMs: newLimit });
|
|
14089
|
+
});
|
|
14090
|
+
return;
|
|
14091
|
+
}
|
|
14092
|
+
if (decision === "extend") {
|
|
14093
|
+
setImmediate(() => {
|
|
14094
|
+
const base = Math.max(payload.limit, payload.used);
|
|
14095
|
+
const extra = {};
|
|
14096
|
+
switch (payload.kind) {
|
|
14097
|
+
case "iterations":
|
|
14098
|
+
extra.maxIterations = Math.min(Math.ceil(base * 1.5), 5e4);
|
|
14099
|
+
break;
|
|
14100
|
+
case "tool_calls":
|
|
14101
|
+
extra.maxToolCalls = Math.min(Math.ceil(base * 1.5), 1e5);
|
|
14102
|
+
break;
|
|
14103
|
+
case "tokens":
|
|
14104
|
+
extra.maxTokens = Math.min(Math.ceil(base * 1.5), 5e6);
|
|
14105
|
+
break;
|
|
14106
|
+
case "cost":
|
|
14107
|
+
extra.maxCostUsd = Math.min(base * 1.5, 100);
|
|
14108
|
+
break;
|
|
14109
|
+
}
|
|
14110
|
+
payload.extend(extra);
|
|
14111
|
+
});
|
|
14112
|
+
return;
|
|
14113
|
+
}
|
|
14114
|
+
if (payload.kind !== "timeout") {
|
|
14115
|
+
setImmediate(() => {
|
|
14116
|
+
const base = Math.max(payload.limit, payload.used);
|
|
14117
|
+
const extra = {};
|
|
14118
|
+
switch (payload.kind) {
|
|
14119
|
+
case "iterations":
|
|
14120
|
+
extra.maxIterations = Math.min(Math.ceil(base * 1.25), 5e4);
|
|
14121
|
+
break;
|
|
14122
|
+
case "tool_calls":
|
|
14123
|
+
extra.maxToolCalls = Math.min(Math.ceil(base * 1.25), 1e5);
|
|
14124
|
+
break;
|
|
14125
|
+
case "tokens":
|
|
14126
|
+
extra.maxTokens = Math.min(Math.ceil(base * 1.25), 5e6);
|
|
14127
|
+
break;
|
|
14128
|
+
case "cost":
|
|
14129
|
+
extra.maxCostUsd = Math.min(base * 1.25, 100);
|
|
14130
|
+
break;
|
|
14131
|
+
}
|
|
14132
|
+
payload.extend(extra);
|
|
14133
|
+
});
|
|
14134
|
+
}
|
|
14135
|
+
});
|
|
14136
|
+
this.disposers.push(dBudget);
|
|
14137
|
+
const dCancel = this.fleetBus.filter("director.cancel_collab", (e) => {
|
|
14138
|
+
const payload = e.payload;
|
|
14139
|
+
if (payload.sessionId !== this.sessionId) return;
|
|
14140
|
+
this.cancelled = true;
|
|
14141
|
+
if (this._timeoutTimer) {
|
|
14142
|
+
clearTimeout(this._timeoutTimer);
|
|
14143
|
+
this._timeoutTimer = void 0;
|
|
14144
|
+
}
|
|
14145
|
+
this.fleetBus.emit({
|
|
14146
|
+
subagentId: this.director.id,
|
|
14147
|
+
ts: Date.now(),
|
|
14148
|
+
type: "collab.cancelled",
|
|
14149
|
+
payload: { sessionId: this.sessionId, reason: payload.reason }
|
|
14150
|
+
});
|
|
14151
|
+
});
|
|
14152
|
+
this.disposers.push(dCancel);
|
|
14153
|
+
const d1 = this.fleetBus.filter("bug.found", (e) => {
|
|
14154
|
+
const payload = e.payload;
|
|
14155
|
+
if (payload?.finding) {
|
|
14156
|
+
this.bugs.set(payload.finding.id, payload.finding);
|
|
14157
|
+
this.emit("bug.found", payload);
|
|
14158
|
+
}
|
|
14159
|
+
});
|
|
14160
|
+
this.disposers.push(d1);
|
|
14161
|
+
const d2 = this.fleetBus.filter("refactor.plan", (e) => {
|
|
14162
|
+
const payload = e.payload;
|
|
14163
|
+
if (payload?.plan) {
|
|
14164
|
+
this.plans.set(payload.plan.id, payload.plan);
|
|
14165
|
+
this.emit("refactor.plan", payload);
|
|
14166
|
+
}
|
|
14167
|
+
});
|
|
14168
|
+
this.disposers.push(d2);
|
|
14169
|
+
const d3 = this.fleetBus.filter("critic.evaluation", (e) => {
|
|
14170
|
+
const payload = e.payload;
|
|
14171
|
+
if (payload?.evaluation) {
|
|
14172
|
+
this.evaluations.set(payload.evaluation.id, payload.evaluation);
|
|
14173
|
+
this.emit("critic.evaluation", payload);
|
|
14174
|
+
}
|
|
14175
|
+
});
|
|
14176
|
+
this.disposers.push(d3);
|
|
14007
14177
|
}
|
|
14008
|
-
|
|
14009
|
-
const
|
|
14010
|
-
|
|
14011
|
-
const usage = p?.usage;
|
|
14012
|
-
if (!usage) return;
|
|
14013
|
-
snap.input += usage.input ?? 0;
|
|
14014
|
-
snap.output += usage.output ?? 0;
|
|
14015
|
-
snap.cacheRead += usage.cacheRead ?? 0;
|
|
14016
|
-
snap.cacheWrite += usage.cacheWrite ?? 0;
|
|
14017
|
-
this.total.input += usage.input ?? 0;
|
|
14018
|
-
this.total.output += usage.output ?? 0;
|
|
14019
|
-
this.total.cacheRead += usage.cacheRead ?? 0;
|
|
14020
|
-
this.total.cacheWrite += usage.cacheWrite ?? 0;
|
|
14021
|
-
const price = this.priceLookup?.(e.subagentId, snap.provider, snap.model);
|
|
14022
|
-
if (price) {
|
|
14023
|
-
const delta = (usage.input ?? 0) / 1e6 * (price.input ?? 0) + (usage.output ?? 0) / 1e6 * (price.output ?? 0) + (usage.cacheRead ?? 0) / 1e6 * (price.cacheRead ?? 0) + (usage.cacheWrite ?? 0) / 1e6 * (price.cacheWrite ?? 0);
|
|
14024
|
-
snap.cost += delta;
|
|
14025
|
-
this.total.cost += delta;
|
|
14178
|
+
roleFromSubagentId(subagentId) {
|
|
14179
|
+
for (const [role, id] of this.subagentIds) {
|
|
14180
|
+
if (id === subagentId) return role;
|
|
14026
14181
|
}
|
|
14027
|
-
|
|
14182
|
+
const match = subagentId.match(/^(bug-hunter|refactor-planner|critic)/);
|
|
14183
|
+
return match?.[1] ?? null;
|
|
14028
14184
|
}
|
|
14029
|
-
|
|
14030
|
-
const
|
|
14031
|
-
|
|
14032
|
-
|
|
14185
|
+
assembleReport() {
|
|
14186
|
+
const bugList = Array.from(this.bugs.values());
|
|
14187
|
+
const planList = Array.from(this.plans.values());
|
|
14188
|
+
const evalList = Array.from(this.evaluations.values());
|
|
14189
|
+
let disposition = "completed";
|
|
14190
|
+
if (this.cancelled) disposition = "cancelled";
|
|
14191
|
+
const verdictOrder = {
|
|
14192
|
+
approve: 0,
|
|
14193
|
+
needs_revision: 1,
|
|
14194
|
+
reject: 2
|
|
14195
|
+
};
|
|
14196
|
+
const overallVerdict = evalList.reduce(
|
|
14197
|
+
(worst, eval_) => {
|
|
14198
|
+
const w = verdictOrder[worst];
|
|
14199
|
+
const c = verdictOrder[eval_.verdict];
|
|
14200
|
+
return c > w ? eval_.verdict : worst;
|
|
14201
|
+
},
|
|
14202
|
+
"approve"
|
|
14203
|
+
);
|
|
14204
|
+
const summary = this.buildMarkdownSummary(bugList, planList, evalList, overallVerdict, disposition);
|
|
14205
|
+
return {
|
|
14206
|
+
sessionId: this.sessionId,
|
|
14207
|
+
startedAt: this.snapshot.createdAt,
|
|
14208
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14209
|
+
targetPaths: this.options.targetPaths,
|
|
14210
|
+
disposition,
|
|
14211
|
+
bugs: bugList,
|
|
14212
|
+
refactorPlans: planList,
|
|
14213
|
+
evaluations: evalList,
|
|
14214
|
+
alerts: [...this.alerts],
|
|
14215
|
+
overallVerdict,
|
|
14216
|
+
summary
|
|
14217
|
+
};
|
|
14033
14218
|
}
|
|
14034
|
-
|
|
14035
|
-
const
|
|
14036
|
-
|
|
14037
|
-
|
|
14219
|
+
buildMarkdownSummary(bugs, plans, evals, overallVerdict, disposition) {
|
|
14220
|
+
const lines = [
|
|
14221
|
+
`## Collaborative Debugging Report \u2014 ${this.sessionId}`,
|
|
14222
|
+
"",
|
|
14223
|
+
`**Target:** ${this.options.targetPaths.join(", ")}`,
|
|
14224
|
+
`**Disposition:** ${disposition.toUpperCase()}`,
|
|
14225
|
+
`**Overall Verdict:** **${overallVerdict.toUpperCase()}**`,
|
|
14226
|
+
""
|
|
14227
|
+
];
|
|
14228
|
+
if (this.alerts.length > 0) {
|
|
14229
|
+
lines.push("### Alerts", "");
|
|
14230
|
+
for (const alert of this.alerts) {
|
|
14231
|
+
lines.push(`- **[${alert.level.toUpperCase()}]** ${alert.role}: ${alert.message}`);
|
|
14232
|
+
}
|
|
14233
|
+
lines.push("");
|
|
14234
|
+
}
|
|
14235
|
+
if (bugs.length > 0) {
|
|
14236
|
+
lines.push("### Bugs Found", "");
|
|
14237
|
+
for (const b of bugs) {
|
|
14238
|
+
lines.push(`- **[${b.severity.toUpperCase()}]** \`${b.location.file}:${b.location.line}\` \u2014 ${b.description}`);
|
|
14239
|
+
}
|
|
14240
|
+
lines.push("");
|
|
14241
|
+
}
|
|
14242
|
+
if (plans.length > 0) {
|
|
14243
|
+
lines.push("### Refactor Plans", "");
|
|
14244
|
+
for (const p of plans) {
|
|
14245
|
+
lines.push(`- **Phase plan** (risk: ${p.riskScore}, ~${p.estimatedChangeCount} changes)`);
|
|
14246
|
+
for (const phase of p.phases) {
|
|
14247
|
+
lines.push(` - Phase ${phase.number}: ${phase.title} [${phase.risk}]`);
|
|
14248
|
+
}
|
|
14249
|
+
}
|
|
14250
|
+
lines.push("");
|
|
14251
|
+
}
|
|
14252
|
+
if (evals.length > 0) {
|
|
14253
|
+
lines.push("### Critic Evaluations", "");
|
|
14254
|
+
for (const e of evals) {
|
|
14255
|
+
lines.push(`- [${e.subjectType}] score=${e.score}/10 \u2014 **${e.verdict.toUpperCase()}**`);
|
|
14256
|
+
for (const c of e.concerns) {
|
|
14257
|
+
if (c.severity === "blocking") lines.push(` - ${c.description}`);
|
|
14258
|
+
}
|
|
14259
|
+
}
|
|
14260
|
+
lines.push("");
|
|
14261
|
+
}
|
|
14262
|
+
return lines.join("\n");
|
|
14263
|
+
}
|
|
14264
|
+
cleanup() {
|
|
14265
|
+
for (const dispose of this.disposers) dispose();
|
|
14266
|
+
this.disposers.length = 0;
|
|
14038
14267
|
}
|
|
14039
14268
|
};
|
|
14269
|
+
|
|
14270
|
+
// src/coordination/director-prompts.ts
|
|
14271
|
+
var DEFAULT_DIRECTOR_PREAMBLE = `You are the Director of a multi-agent fleet. You orchestrate worker
|
|
14272
|
+
subagents by spawning them, assigning tasks, awaiting completions, and
|
|
14273
|
+
rolling up their outputs into your next decision.
|
|
14274
|
+
|
|
14275
|
+
Core fleet tools available to you:
|
|
14276
|
+
- spawn_subagent \u2014 create a worker with a chosen provider / model / role
|
|
14277
|
+
- assign_task \u2014 hand a piece of work to a specific subagent
|
|
14278
|
+
- await_tasks \u2014 block until named task ids complete (parallel-safe)
|
|
14279
|
+
- ask_subagent \u2014 synchronously query a running subagent via the bridge
|
|
14280
|
+
- roll_up \u2014 aggregate finished tasks into a markdown/json summary
|
|
14281
|
+
- terminate_subagent \u2014 abort a stuck worker (use sparingly)
|
|
14282
|
+
- fleet_status \u2014 snapshot of all subagents and pending tasks
|
|
14283
|
+
- fleet_usage \u2014 token + cost breakdown per subagent and total
|
|
14284
|
+
|
|
14285
|
+
Working rules:
|
|
14286
|
+
1. Decompose first. Before spawning, decide which sub-tasks are
|
|
14287
|
+
independent and can run in parallel. Sequential work doesn't need a
|
|
14288
|
+
subagent \u2014 do it yourself.
|
|
14289
|
+
2. Match worker to job. Cheap/fast model for triage, capable model for
|
|
14290
|
+
synthesis. Different providers per sibling is allowed and encouraged.
|
|
14291
|
+
3. Always pair an assign with an await. Don't fire-and-forget; you owe
|
|
14292
|
+
the user a single coherent answer at the end.
|
|
14293
|
+
4. Roll up before deciding. After await_tasks resolves, call roll_up so
|
|
14294
|
+
the results are folded back into your context in a compact form.
|
|
14295
|
+
5. Budget is real. Check fleet_usage periodically. If a subagent is
|
|
14296
|
+
thrashing, terminate it rather than letting cost climb silently.
|
|
14297
|
+
6. Never claim a subagent's work as your own without verifying it. If a
|
|
14298
|
+
result looks wrong, ask_subagent for clarification before passing it
|
|
14299
|
+
to the user.
|
|
14300
|
+
7. Wind down when satisfied. When the results are good enough, call
|
|
14301
|
+
work_complete \u2014 no new subagents will spawn and queued tasks complete
|
|
14302
|
+
as aborted. Running subagents finish naturally. Call terminate_subagent
|
|
14303
|
+
only for ones you need to stop immediately.`;
|
|
14304
|
+
var DEFAULT_SUBAGENT_BASELINE = `You are a subagent operating under a Director. You were spawned to handle
|
|
14305
|
+
a specific slice of a larger plan \u2014 do that slice well and report back.
|
|
14306
|
+
|
|
14307
|
+
Bridge contract:
|
|
14308
|
+
- You have a parent (the Director). You may call \`request\` on the
|
|
14309
|
+
parent bridge to ask a clarifying question. Use this sparingly; the
|
|
14310
|
+
parent is also working.
|
|
14311
|
+
- You MAY NOT request the parent's system prompt, tool list, or other
|
|
14312
|
+
subagents' context. Those are not yours to read.
|
|
14313
|
+
- Your final task output is what the Director sees. Be concise,
|
|
14314
|
+
structured, and self-contained \u2014 assume the Director will paste your
|
|
14315
|
+
output into its own context.`;
|
|
14316
|
+
function composeDirectorPrompt(parts = {}) {
|
|
14317
|
+
const sections = [];
|
|
14318
|
+
const preamble = parts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
|
|
14319
|
+
if (preamble && preamble.trim().length > 0) sections.push(preamble.trim());
|
|
14320
|
+
if (parts.rosterSummary && parts.rosterSummary.trim().length > 0) {
|
|
14321
|
+
sections.push(`Available roles you can spawn:
|
|
14322
|
+
${parts.rosterSummary.trim()}`);
|
|
14323
|
+
}
|
|
14324
|
+
if (parts.basePrompt && parts.basePrompt.trim().length > 0) {
|
|
14325
|
+
sections.push(parts.basePrompt.trim());
|
|
14326
|
+
}
|
|
14327
|
+
return sections.join("\n\n");
|
|
14328
|
+
}
|
|
14329
|
+
function composeSubagentPrompt(parts = {}) {
|
|
14330
|
+
const sections = [];
|
|
14331
|
+
const baseline = parts.baseline ?? DEFAULT_SUBAGENT_BASELINE;
|
|
14332
|
+
if (baseline && baseline.trim().length > 0) sections.push(baseline.trim());
|
|
14333
|
+
if (parts.role && parts.role.trim().length > 0) {
|
|
14334
|
+
sections.push(`Role:
|
|
14335
|
+
${parts.role.trim()}`);
|
|
14336
|
+
}
|
|
14337
|
+
if (parts.task && parts.task.trim().length > 0) {
|
|
14338
|
+
sections.push(`Task:
|
|
14339
|
+
${parts.task.trim()}`);
|
|
14340
|
+
}
|
|
14341
|
+
if (parts.sharedScratchpad && parts.sharedScratchpad.trim().length > 0) {
|
|
14342
|
+
sections.push(
|
|
14343
|
+
`Shared notes:
|
|
14344
|
+
A scratchpad shared with the rest of the fleet is mounted at \`${parts.sharedScratchpad.trim()}\`.
|
|
14345
|
+
- Write your final findings as markdown files there (e.g. \`findings.md\`, \`security.md\`).
|
|
14346
|
+
- Before starting, list the directory and read any sibling files relevant to your task \u2014 they may already contain context you can build on.
|
|
14347
|
+
- Use stable filenames (one file per concern); overwrite instead of appending so the Director sees the latest state.`
|
|
14348
|
+
);
|
|
14349
|
+
}
|
|
14350
|
+
if (parts.override && parts.override.trim().length > 0) {
|
|
14351
|
+
sections.push(parts.override.trim());
|
|
14352
|
+
}
|
|
14353
|
+
return sections.join("\n\n");
|
|
14354
|
+
}
|
|
14355
|
+
function rosterSummaryFromConfigs(roster) {
|
|
14356
|
+
const lines = [];
|
|
14357
|
+
for (const [roleId, cfg] of Object.entries(roster)) {
|
|
14358
|
+
const tag = cfg.provider && cfg.model ? ` (${cfg.provider}/${cfg.model})` : "";
|
|
14359
|
+
const headline = cfg.prompt ? (cfg.prompt.split("\n").find((l) => l.trim().length > 0) ?? "").trim().slice(0, 80) : "";
|
|
14360
|
+
const tail = headline ? ` \u2014 ${headline}` : "";
|
|
14361
|
+
lines.push(`- ${roleId}: ${cfg.name}${tag}${tail}`);
|
|
14362
|
+
}
|
|
14363
|
+
return lines.join("\n");
|
|
14364
|
+
}
|
|
14040
14365
|
function makeSpawnTool(director, roster) {
|
|
14041
14366
|
const inputSchema = {
|
|
14042
14367
|
type: "object",
|
|
@@ -14380,6 +14705,16 @@ function makeCollabDebugTool(director) {
|
|
|
14380
14705
|
type: "number",
|
|
14381
14706
|
minimum: 1,
|
|
14382
14707
|
description: "Timeout in ms. Default: 600000 (10 minutes)."
|
|
14708
|
+
},
|
|
14709
|
+
maxTargetFiles: {
|
|
14710
|
+
type: "number",
|
|
14711
|
+
minimum: 1,
|
|
14712
|
+
description: "Maximum number of files to include in the snapshot. If not set, the limit is computed dynamically from contextWindow or falls back to the default (30)."
|
|
14713
|
+
},
|
|
14714
|
+
contextWindow: {
|
|
14715
|
+
type: "number",
|
|
14716
|
+
minimum: 1,
|
|
14717
|
+
description: "Context window size (tokens) of the model. When provided and maxTargetFiles is not set, the file limit is computed dynamically as floor((contextWindow * 0.4) / 2000)."
|
|
14383
14718
|
}
|
|
14384
14719
|
},
|
|
14385
14720
|
required: ["targetPaths"]
|
|
@@ -14391,7 +14726,9 @@ function makeCollabDebugTool(director) {
|
|
|
14391
14726
|
}
|
|
14392
14727
|
const options = {
|
|
14393
14728
|
targetPaths: i.targetPaths,
|
|
14394
|
-
timeoutMs: i.timeoutMs
|
|
14729
|
+
timeoutMs: i.timeoutMs,
|
|
14730
|
+
maxTargetFiles: i.maxTargetFiles,
|
|
14731
|
+
contextWindow: i.contextWindow
|
|
14395
14732
|
};
|
|
14396
14733
|
try {
|
|
14397
14734
|
const report = await director.spawnCollab(options);
|
|
@@ -14402,564 +14739,347 @@ function makeCollabDebugTool(director) {
|
|
|
14402
14739
|
planCount: report.refactorPlans.length,
|
|
14403
14740
|
evaluationCount: report.evaluations.length,
|
|
14404
14741
|
summary: report.summary,
|
|
14405
|
-
bugs: report.bugs,
|
|
14406
|
-
refactorPlans: report.refactorPlans,
|
|
14407
|
-
evaluations: report.evaluations
|
|
14408
|
-
};
|
|
14409
|
-
} catch (err) {
|
|
14410
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
14411
|
-
return { error: "collab_debug failed: " + msg };
|
|
14412
|
-
}
|
|
14413
|
-
}
|
|
14414
|
-
};
|
|
14415
|
-
}
|
|
14416
|
-
function makeFleetEmitTool(director) {
|
|
14417
|
-
return {
|
|
14418
|
-
name: "fleet_emit",
|
|
14419
|
-
description: "Emit a structured event on the FleetBus. Any subagent can emit any event type; the Director routes it to all listeners. Use it to stream findings, progress updates, or final results to other agents in real time.",
|
|
14420
|
-
permission: "auto",
|
|
14421
|
-
mutating: false,
|
|
14422
|
-
inputSchema: {
|
|
14423
|
-
type: "object",
|
|
14424
|
-
properties: {
|
|
14425
|
-
type: {
|
|
14426
|
-
type: "string",
|
|
14427
|
-
description: "Event type string (e.g. bug.found, refactor.plan, critic.evaluation, progress, result)."
|
|
14428
|
-
},
|
|
14429
|
-
payload: {
|
|
14430
|
-
type: "object",
|
|
14431
|
-
description: "Event payload. Structure depends on event type. Use null if no payload."
|
|
14432
|
-
}
|
|
14433
|
-
},
|
|
14434
|
-
required: ["type"]
|
|
14435
|
-
},
|
|
14436
|
-
async execute(input) {
|
|
14437
|
-
const i = input;
|
|
14438
|
-
director.fleet.emit({
|
|
14439
|
-
subagentId: director.id,
|
|
14440
|
-
ts: Date.now(),
|
|
14441
|
-
type: i.type,
|
|
14442
|
-
payload: i.payload ?? {}
|
|
14443
|
-
});
|
|
14444
|
-
return { ok: true, event: i.type };
|
|
14445
|
-
}
|
|
14446
|
-
};
|
|
14447
|
-
}
|
|
14448
|
-
function makeWorkCompleteTool(director) {
|
|
14449
|
-
return {
|
|
14450
|
-
name: "work_complete",
|
|
14451
|
-
description: "Signal that the director is satisfied with the results and the fleet should wind down. After calling this, spawn_subagent will refuse with a budget error and assign_task will instantly complete any queued tasks as aborted. Running subagents finish naturally. Call terminate_subagent separately to stop specific subagents immediately.",
|
|
14452
|
-
permission: "auto",
|
|
14453
|
-
mutating: false,
|
|
14454
|
-
inputSchema: { type: "object", properties: {}, required: [] },
|
|
14455
|
-
async execute() {
|
|
14456
|
-
director.workComplete();
|
|
14457
|
-
return { ok: true, message: "Fleet wind-down signaled. No new spawns or task dispatches." };
|
|
14458
|
-
}
|
|
14459
|
-
};
|
|
14460
|
-
}
|
|
14461
|
-
var CollabSession = class extends EventEmitter {
|
|
14462
|
-
sessionId;
|
|
14463
|
-
options;
|
|
14464
|
-
snapshot;
|
|
14465
|
-
director;
|
|
14466
|
-
fleetBus;
|
|
14467
|
-
subagentIds = /* @__PURE__ */ new Map();
|
|
14468
|
-
// role → subagentId
|
|
14469
|
-
bugs = /* @__PURE__ */ new Map();
|
|
14470
|
-
plans = /* @__PURE__ */ new Map();
|
|
14471
|
-
evaluations = /* @__PURE__ */ new Map();
|
|
14472
|
-
disposers = new Array();
|
|
14473
|
-
settled = false;
|
|
14474
|
-
timeoutMs;
|
|
14475
|
-
cancelled = false;
|
|
14476
|
-
alerts = [];
|
|
14477
|
-
/** Tracks tool call counts per subagent for progress-based timeout decisions. */
|
|
14478
|
-
progressBySubagent = /* @__PURE__ */ new Map();
|
|
14479
|
-
/** Last tool call count when a timeout warning was handled. */
|
|
14480
|
-
lastTimeoutProgress = /* @__PURE__ */ new Map();
|
|
14481
|
-
/** Session-level timeout timer handle (cleared on cancel or natural completion). */
|
|
14482
|
-
_timeoutTimer;
|
|
14483
|
-
constructor(director, fleetBus, options) {
|
|
14484
|
-
super();
|
|
14485
|
-
this.sessionId = randomUUID();
|
|
14486
|
-
this.options = options;
|
|
14487
|
-
this.director = director;
|
|
14488
|
-
this.fleetBus = fleetBus;
|
|
14489
|
-
this.timeoutMs = options.timeoutMs ?? 10 * 60 * 1e3;
|
|
14490
|
-
if (options.prebuiltSnapshot) {
|
|
14491
|
-
this.snapshot = options.prebuiltSnapshot;
|
|
14492
|
-
} else {
|
|
14493
|
-
this.snapshot = {
|
|
14494
|
-
id: this.sessionId,
|
|
14495
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14496
|
-
files: []
|
|
14497
|
-
};
|
|
14498
|
-
}
|
|
14499
|
-
}
|
|
14500
|
-
get id() {
|
|
14501
|
-
return this.sessionId;
|
|
14502
|
-
}
|
|
14503
|
-
getSessionAlerts() {
|
|
14504
|
-
return [...this.alerts];
|
|
14505
|
-
}
|
|
14506
|
-
isCancelled() {
|
|
14507
|
-
return this.cancelled;
|
|
14508
|
-
}
|
|
14509
|
-
/**
|
|
14510
|
-
* Snapshot of role → subagentId map. The Director calls coordinator.stop()
|
|
14511
|
-
* for each agent when cancelling the session, using this map to enumerate
|
|
14512
|
-
* all three collab agents.
|
|
14513
|
-
*/
|
|
14514
|
-
getSubagentIds() {
|
|
14515
|
-
return new Map(this.subagentIds);
|
|
14516
|
-
}
|
|
14517
|
-
async buildSnapshot() {
|
|
14518
|
-
if (this.snapshot.files.length > 0) return this.snapshot;
|
|
14519
|
-
for (const filePath of (await Promise.all(this.options.targetPaths.map((p) => expandGlob(p)))).flat()) {
|
|
14520
|
-
try {
|
|
14521
|
-
const content = await fsp3.readFile(filePath, "utf8");
|
|
14522
|
-
const ext = filePath.split(".").pop() ?? "";
|
|
14523
|
-
const language = ext === "ts" || ext === "tsx" ? "typescript" : ext === "js" || ext === "jsx" ? "javascript" : ext === "md" ? "markdown" : ext === "json" ? "json" : void 0;
|
|
14524
|
-
this.snapshot.files.push({ path: filePath, content, language });
|
|
14525
|
-
} catch {
|
|
14526
|
-
this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
|
|
14527
|
-
}
|
|
14528
|
-
}
|
|
14529
|
-
return this.snapshot;
|
|
14530
|
-
}
|
|
14531
|
-
/**
|
|
14532
|
-
* Cancel the session. Emits director.cancel_collab on the FleetBus so all
|
|
14533
|
-
* collab agents finish early. The session-level timeout timer is also cleared.
|
|
14534
|
-
* Safe to call multiple times (idempotent after first call).
|
|
14535
|
-
*/
|
|
14536
|
-
cancel(reason = "Director cancelled collab session") {
|
|
14537
|
-
if (this.settled) return;
|
|
14538
|
-
this.cancelled = true;
|
|
14539
|
-
if (this._timeoutTimer) {
|
|
14540
|
-
clearTimeout(this._timeoutTimer);
|
|
14541
|
-
this._timeoutTimer = void 0;
|
|
14542
|
-
}
|
|
14543
|
-
this.fleetBus.emit({
|
|
14544
|
-
subagentId: this.director.id,
|
|
14545
|
-
ts: Date.now(),
|
|
14546
|
-
type: "director.cancel_collab",
|
|
14547
|
-
payload: { sessionId: this.sessionId, reason, cancelledAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
14548
|
-
});
|
|
14549
|
-
this.fleetBus.emit({
|
|
14550
|
-
subagentId: this.director.id,
|
|
14551
|
-
ts: Date.now(),
|
|
14552
|
-
type: "collab.cancelled",
|
|
14553
|
-
payload: { sessionId: this.sessionId, reason }
|
|
14554
|
-
});
|
|
14555
|
-
}
|
|
14556
|
-
async start() {
|
|
14557
|
-
if (this.settled) throw new Error("session already settled");
|
|
14558
|
-
this.settled = true;
|
|
14559
|
-
await this.buildSnapshot();
|
|
14560
|
-
this.wireFleetBus();
|
|
14561
|
-
const [bugHunterId, refactorPlannerId, criticId] = await Promise.all([
|
|
14562
|
-
this.spawnAgent("bug-hunter", this.buildBugHunterTask()),
|
|
14563
|
-
this.spawnAgent("refactor-planner", this.buildRefactorPlannerTask()),
|
|
14564
|
-
this.spawnAgent("critic", this.buildCriticTask())
|
|
14565
|
-
]);
|
|
14566
|
-
this.subagentIds.set("bug-hunter", bugHunterId);
|
|
14567
|
-
this.subagentIds.set("refactor-planner", refactorPlannerId);
|
|
14568
|
-
this.subagentIds.set("critic", criticId);
|
|
14569
|
-
const timeout = new Promise((_, reject) => {
|
|
14570
|
-
this._timeoutTimer = setTimeout(() => {
|
|
14571
|
-
this.cancel("Session-level timeout reached");
|
|
14572
|
-
reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`));
|
|
14573
|
-
}, this.timeoutMs);
|
|
14574
|
-
});
|
|
14575
|
-
let results = null;
|
|
14576
|
-
try {
|
|
14577
|
-
results = await Promise.race([
|
|
14578
|
-
Promise.all([
|
|
14579
|
-
this.director.awaitTasks([bugHunterId]),
|
|
14580
|
-
this.director.awaitTasks([refactorPlannerId]),
|
|
14581
|
-
this.director.awaitTasks([criticId])
|
|
14582
|
-
]),
|
|
14583
|
-
timeout
|
|
14584
|
-
]);
|
|
14585
|
-
} catch (err) {
|
|
14586
|
-
if (this._timeoutTimer) {
|
|
14587
|
-
clearTimeout(this._timeoutTimer);
|
|
14588
|
-
this._timeoutTimer = void 0;
|
|
14589
|
-
}
|
|
14590
|
-
this.cleanup();
|
|
14591
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
14592
|
-
this.emit("session.error", error);
|
|
14593
|
-
throw error;
|
|
14594
|
-
}
|
|
14595
|
-
for (const result of results.flat()) {
|
|
14596
|
-
await this.parseAndEmit(result);
|
|
14597
|
-
}
|
|
14598
|
-
const report = this.assembleReport();
|
|
14599
|
-
this.cleanup();
|
|
14600
|
-
this.emit("session.done", report);
|
|
14601
|
-
return report;
|
|
14602
|
-
}
|
|
14603
|
-
async parseAndEmit(result) {
|
|
14604
|
-
if (result.status !== "success" || result.result == null) return;
|
|
14605
|
-
const text = typeof result.result === "string" ? result.result : JSON.stringify(result.result);
|
|
14606
|
-
for (const obj of this.extractJsonObjects(text)) {
|
|
14607
|
-
const type = "finding" in obj ? "bug.found" : "plan" in obj ? "refactor.plan" : "evaluation" in obj ? "critic.evaluation" : null;
|
|
14608
|
-
if (!type) continue;
|
|
14609
|
-
this.fleetBus.emit({
|
|
14610
|
-
subagentId: result.subagentId,
|
|
14611
|
-
taskId: result.taskId,
|
|
14612
|
-
ts: Date.now(),
|
|
14613
|
-
type,
|
|
14614
|
-
payload: obj
|
|
14615
|
-
});
|
|
14616
|
-
}
|
|
14617
|
-
}
|
|
14618
|
-
extractJsonObjects(text) {
|
|
14619
|
-
const objects = [];
|
|
14620
|
-
let depth = 0;
|
|
14621
|
-
let start = -1;
|
|
14622
|
-
let inString = false;
|
|
14623
|
-
let escaped = false;
|
|
14624
|
-
for (let i = 0; i < text.length; i++) {
|
|
14625
|
-
const ch = text[i];
|
|
14626
|
-
if (inString) {
|
|
14627
|
-
if (escaped) escaped = false;
|
|
14628
|
-
else if (ch === "\\") escaped = true;
|
|
14629
|
-
else if (ch === '"') inString = false;
|
|
14630
|
-
continue;
|
|
14631
|
-
}
|
|
14632
|
-
if (ch === '"') {
|
|
14633
|
-
inString = true;
|
|
14634
|
-
} else if (ch === "{") {
|
|
14635
|
-
if (depth === 0) start = i;
|
|
14636
|
-
depth++;
|
|
14637
|
-
} else if (ch === "}" && depth > 0) {
|
|
14638
|
-
depth--;
|
|
14639
|
-
if (depth === 0 && start >= 0) {
|
|
14640
|
-
const candidate = text.slice(start, i + 1);
|
|
14641
|
-
try {
|
|
14642
|
-
const parsed = JSON.parse(candidate);
|
|
14643
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
14644
|
-
objects.push(parsed);
|
|
14645
|
-
}
|
|
14646
|
-
} catch {
|
|
14647
|
-
}
|
|
14648
|
-
start = -1;
|
|
14649
|
-
}
|
|
14650
|
-
}
|
|
14651
|
-
}
|
|
14652
|
-
return objects;
|
|
14653
|
-
}
|
|
14654
|
-
budgetForRole(role) {
|
|
14655
|
-
if (this.options.budgetOverrides?.[role]) {
|
|
14656
|
-
return this.options.budgetOverrides[role];
|
|
14657
|
-
}
|
|
14658
|
-
const defaults = {
|
|
14659
|
-
"bug-hunter": { maxIterations: 2e3, maxToolCalls: 5e3, timeoutMs: 10 * 60 * 1e3 },
|
|
14660
|
-
"refactor-planner": { maxIterations: 1500, maxToolCalls: 4e3, timeoutMs: 8 * 60 * 1e3 },
|
|
14661
|
-
"critic": { maxIterations: 1e3, maxToolCalls: 3e3, timeoutMs: 6 * 60 * 1e3 }
|
|
14662
|
-
};
|
|
14663
|
-
return defaults[role] ?? { maxIterations: 1500, maxToolCalls: 4e3, timeoutMs: 8 * 60 * 1e3 };
|
|
14664
|
-
}
|
|
14665
|
-
async spawnAgent(role, taskBrief) {
|
|
14666
|
-
const budget = this.budgetForRole(role);
|
|
14667
|
-
const cfg = {
|
|
14668
|
-
id: `${role}-${this.sessionId}`,
|
|
14669
|
-
name: role,
|
|
14670
|
-
role,
|
|
14671
|
-
tools: ["fleet_emit", "fleet_status", "read", "grep", "glob", "bash", "write"],
|
|
14672
|
-
maxIterations: budget.maxIterations,
|
|
14673
|
-
maxToolCalls: budget.maxToolCalls,
|
|
14674
|
-
timeoutMs: budget.timeoutMs
|
|
14675
|
-
};
|
|
14676
|
-
const subagentId = await this.director.spawn(cfg);
|
|
14677
|
-
await this.director.assign({ id: randomUUID(), subagentId, description: taskBrief });
|
|
14678
|
-
return subagentId;
|
|
14679
|
-
}
|
|
14680
|
-
buildBugHunterTask() {
|
|
14681
|
-
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
14682
|
-
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
14683
|
-
${f.content}`).join("\n\n");
|
|
14684
|
-
return `You are BugHunter. Scan the following files for bugs and code smells.
|
|
14685
|
-
|
|
14686
|
-
Target files:
|
|
14687
|
-
${fileContents}
|
|
14688
|
-
|
|
14689
|
-
For each bug found, emit it using the fleet_emit tool immediately:
|
|
14690
|
-
{ "type": "bug.found", "payload": { "finding": { "id": "<uuid>", "type": "<pattern>", "severity": "<critical|high|medium|low>", "location": { "file": "<path>", "line": <n> }, "description": "<explain>", "suggestedFix": "<optional>" } } }
|
|
14691
|
-
|
|
14692
|
-
After scanning all files, write your full markdown bug report to:
|
|
14693
|
-
${scratchpad}/bug-hunter-report-${this.sessionId}.md
|
|
14694
|
-
|
|
14695
|
-
Important: emit each finding as soon as you find it. Do not batch or wait until the end.`;
|
|
14696
|
-
}
|
|
14697
|
-
buildRefactorPlannerTask() {
|
|
14698
|
-
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
14699
|
-
const bugHunterReportPath = `${scratchpad}/bug-hunter-report-${this.sessionId}.md`;
|
|
14700
|
-
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
14701
|
-
${f.content}`).join("\n\n");
|
|
14702
|
-
return `You are RefactorPlanner. Plan refactorings for the following files.
|
|
14703
|
-
|
|
14704
|
-
Target files:
|
|
14705
|
-
${fileContents}
|
|
14706
|
-
|
|
14707
|
-
Read the BugHunter report at: ${bugHunterReportPath}
|
|
14708
|
-
|
|
14709
|
-
For each bug you can address, emit a refactor plan using fleet_emit:
|
|
14710
|
-
{ "type": "refactor.plan", "payload": { "plan": { "id": "<uuid>", "basedOnBugIds": ["<bug-id>"], "phases": [{ "number": 1, "title": "<phase>", "tasks": ["<task>"], "risk": "<low|medium|high>" }], "riskScore": "<low|medium|high>", "estimatedChangeCount": <n>, "rollbackStrategy": "<text>" } } }
|
|
14711
|
-
|
|
14712
|
-
Also write your full markdown plan to:
|
|
14713
|
-
${scratchpad}/refactor-plan-${this.sessionId}.md
|
|
14714
|
-
|
|
14715
|
-
Emit each plan immediately. Do not wait until planning is complete.`;
|
|
14716
|
-
}
|
|
14717
|
-
buildCriticTask() {
|
|
14718
|
-
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
14719
|
-
const bugHunterReportPath = `${scratchpad}/bug-hunter-report-${this.sessionId}.md`;
|
|
14720
|
-
const refactorPlanPath = `${scratchpad}/refactor-plan-${this.sessionId}.md`;
|
|
14721
|
-
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
14722
|
-
${f.content}`).join("\n\n");
|
|
14723
|
-
return `You are Critic. Evaluate bug findings and refactor plans.
|
|
14724
|
-
|
|
14725
|
-
Target files:
|
|
14726
|
-
${fileContents}
|
|
14727
|
-
|
|
14728
|
-
Read the BugHunter report at: ${bugHunterReportPath}
|
|
14729
|
-
Read the RefactorPlanner report at: ${refactorPlanPath}
|
|
14730
|
-
|
|
14731
|
-
For each bug and refactor plan, emit your evaluation using fleet_emit:
|
|
14732
|
-
{ "type": "critic.evaluation", "payload": { "evaluation": { "id": "<uuid>", "subjectType": "<bug_finding|refactor_plan>", "subjectId": "<id>", "score": <0-10>, "verdict": "<approve|needs_revision|reject>", "strengths": ["<strength>"], "weaknesses": ["<weakness>"], "concerns": [{ "description": "<concern>", "severity": "<blocking|advisory>" }] } } }
|
|
14733
|
-
|
|
14734
|
-
After all evaluations, write your markdown report to:
|
|
14735
|
-
${scratchpad}/critic-report-${this.sessionId}.md
|
|
14736
|
-
|
|
14737
|
-
Emit each evaluation immediately. Do not wait until you have read all reports.`;
|
|
14738
|
-
}
|
|
14739
|
-
wireFleetBus() {
|
|
14740
|
-
const dTool = this.fleetBus.filter("tool.executed", (e) => {
|
|
14741
|
-
this.progressBySubagent.set(e.subagentId, (this.progressBySubagent.get(e.subagentId) ?? 0) + 1);
|
|
14742
|
-
});
|
|
14743
|
-
this.disposers.push(dTool);
|
|
14744
|
-
const dBudget = this.fleetBus.filter("budget.threshold_reached", (e) => {
|
|
14745
|
-
const payload = e.payload;
|
|
14746
|
-
const role = this.roleFromSubagentId(e.subagentId);
|
|
14747
|
-
if (!role) return;
|
|
14748
|
-
const btwNotes = this.director.getLeaderBtwNotes();
|
|
14749
|
-
const alert = {
|
|
14750
|
-
sessionId: this.sessionId,
|
|
14751
|
-
subagentId: e.subagentId,
|
|
14752
|
-
role,
|
|
14753
|
-
level: "warning" /* WARNING */,
|
|
14754
|
-
message: `${role} hit ${payload.kind} soft limit (${payload.used}/${payload.limit})`,
|
|
14755
|
-
budgetKind: payload.kind,
|
|
14756
|
-
elapsedMs: payload.timeoutMs,
|
|
14757
|
-
limit: payload.limit,
|
|
14758
|
-
btwNotes
|
|
14759
|
-
};
|
|
14760
|
-
this.alerts.push(alert);
|
|
14761
|
-
this.fleetBus.emit({
|
|
14762
|
-
subagentId: e.subagentId,
|
|
14763
|
-
ts: Date.now(),
|
|
14764
|
-
type: "collab.warning",
|
|
14765
|
-
payload: alert
|
|
14766
|
-
});
|
|
14767
|
-
const decision = this.options.onBudgetWarning?.(alert) ?? "ignore";
|
|
14768
|
-
if (decision === "cancel") {
|
|
14769
|
-
this.cancel(`Director cancelled: ${role} ${payload.kind} threshold`);
|
|
14770
|
-
return;
|
|
14771
|
-
}
|
|
14772
|
-
if (payload.kind === "timeout" || payload.kind === "idle_timeout") {
|
|
14773
|
-
const progress = this.progressBySubagent.get(e.subagentId) ?? 0;
|
|
14774
|
-
const lastProgress = this.lastTimeoutProgress.get(e.subagentId) ?? -1;
|
|
14775
|
-
if (progress <= lastProgress) {
|
|
14776
|
-
payload.deny();
|
|
14777
|
-
return;
|
|
14778
|
-
}
|
|
14779
|
-
this.lastTimeoutProgress.set(e.subagentId, progress);
|
|
14780
|
-
const newLimit = Math.min(Math.ceil((payload.timeoutMs ?? payload.limit) * 2), 24 * 60 * 6e4);
|
|
14781
|
-
setImmediate(() => {
|
|
14782
|
-
payload.extend({ timeoutMs: newLimit });
|
|
14783
|
-
});
|
|
14784
|
-
return;
|
|
14785
|
-
}
|
|
14786
|
-
if (decision === "extend") {
|
|
14787
|
-
setImmediate(() => {
|
|
14788
|
-
const base = Math.max(payload.limit, payload.used);
|
|
14789
|
-
const extra = {};
|
|
14790
|
-
switch (payload.kind) {
|
|
14791
|
-
case "iterations":
|
|
14792
|
-
extra.maxIterations = Math.min(Math.ceil(base * 1.5), 5e4);
|
|
14793
|
-
break;
|
|
14794
|
-
case "tool_calls":
|
|
14795
|
-
extra.maxToolCalls = Math.min(Math.ceil(base * 1.5), 1e5);
|
|
14796
|
-
break;
|
|
14797
|
-
case "tokens":
|
|
14798
|
-
extra.maxTokens = Math.min(Math.ceil(base * 1.5), 5e6);
|
|
14799
|
-
break;
|
|
14800
|
-
case "cost":
|
|
14801
|
-
extra.maxCostUsd = Math.min(base * 1.5, 100);
|
|
14802
|
-
break;
|
|
14803
|
-
}
|
|
14804
|
-
payload.extend(extra);
|
|
14805
|
-
});
|
|
14806
|
-
return;
|
|
14807
|
-
}
|
|
14808
|
-
if (payload.kind !== "timeout") {
|
|
14809
|
-
setImmediate(() => {
|
|
14810
|
-
const base = Math.max(payload.limit, payload.used);
|
|
14811
|
-
const extra = {};
|
|
14812
|
-
switch (payload.kind) {
|
|
14813
|
-
case "iterations":
|
|
14814
|
-
extra.maxIterations = Math.min(Math.ceil(base * 1.25), 5e4);
|
|
14815
|
-
break;
|
|
14816
|
-
case "tool_calls":
|
|
14817
|
-
extra.maxToolCalls = Math.min(Math.ceil(base * 1.25), 1e5);
|
|
14818
|
-
break;
|
|
14819
|
-
case "tokens":
|
|
14820
|
-
extra.maxTokens = Math.min(Math.ceil(base * 1.25), 5e6);
|
|
14821
|
-
break;
|
|
14822
|
-
case "cost":
|
|
14823
|
-
extra.maxCostUsd = Math.min(base * 1.25, 100);
|
|
14824
|
-
break;
|
|
14825
|
-
}
|
|
14826
|
-
payload.extend(extra);
|
|
14827
|
-
});
|
|
14828
|
-
}
|
|
14829
|
-
});
|
|
14830
|
-
this.disposers.push(dBudget);
|
|
14831
|
-
const dCancel = this.fleetBus.filter("director.cancel_collab", (e) => {
|
|
14832
|
-
const payload = e.payload;
|
|
14833
|
-
if (payload.sessionId !== this.sessionId) return;
|
|
14834
|
-
this.cancelled = true;
|
|
14835
|
-
if (this._timeoutTimer) {
|
|
14836
|
-
clearTimeout(this._timeoutTimer);
|
|
14837
|
-
this._timeoutTimer = void 0;
|
|
14742
|
+
bugs: report.bugs,
|
|
14743
|
+
refactorPlans: report.refactorPlans,
|
|
14744
|
+
evaluations: report.evaluations
|
|
14745
|
+
};
|
|
14746
|
+
} catch (err) {
|
|
14747
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
14748
|
+
return { error: "collab_debug failed: " + msg };
|
|
14838
14749
|
}
|
|
14839
|
-
|
|
14840
|
-
|
|
14750
|
+
}
|
|
14751
|
+
};
|
|
14752
|
+
}
|
|
14753
|
+
function makeFleetEmitTool(director) {
|
|
14754
|
+
return {
|
|
14755
|
+
name: "fleet_emit",
|
|
14756
|
+
description: "Emit a structured event on the FleetBus. Any subagent can emit any event type; the Director routes it to all listeners. Use it to stream findings, progress updates, or final results to other agents in real time.",
|
|
14757
|
+
permission: "auto",
|
|
14758
|
+
mutating: false,
|
|
14759
|
+
inputSchema: {
|
|
14760
|
+
type: "object",
|
|
14761
|
+
properties: {
|
|
14762
|
+
type: {
|
|
14763
|
+
type: "string",
|
|
14764
|
+
description: "Event type string (e.g. bug.found, refactor.plan, critic.evaluation, progress, result)."
|
|
14765
|
+
},
|
|
14766
|
+
payload: {
|
|
14767
|
+
type: "object",
|
|
14768
|
+
description: "Event payload. Structure depends on event type. Use null if no payload."
|
|
14769
|
+
}
|
|
14770
|
+
},
|
|
14771
|
+
required: ["type"]
|
|
14772
|
+
},
|
|
14773
|
+
async execute(input) {
|
|
14774
|
+
const i = input;
|
|
14775
|
+
director.fleet.emit({
|
|
14776
|
+
subagentId: director.id,
|
|
14841
14777
|
ts: Date.now(),
|
|
14842
|
-
type:
|
|
14843
|
-
payload:
|
|
14778
|
+
type: i.type,
|
|
14779
|
+
payload: i.payload ?? {}
|
|
14844
14780
|
});
|
|
14781
|
+
return { ok: true, event: i.type };
|
|
14782
|
+
}
|
|
14783
|
+
};
|
|
14784
|
+
}
|
|
14785
|
+
function makeWorkCompleteTool(director) {
|
|
14786
|
+
return {
|
|
14787
|
+
name: "work_complete",
|
|
14788
|
+
description: "Signal that the director is satisfied with the results and the fleet should wind down. After calling this, spawn_subagent will refuse with a budget error and assign_task will instantly complete any queued tasks as aborted. Running subagents finish naturally. Call terminate_subagent separately to stop specific subagents immediately.",
|
|
14789
|
+
permission: "auto",
|
|
14790
|
+
mutating: false,
|
|
14791
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
14792
|
+
async execute() {
|
|
14793
|
+
director.workComplete();
|
|
14794
|
+
return { ok: true, message: "Fleet wind-down signaled. No new spawns or task dispatches." };
|
|
14795
|
+
}
|
|
14796
|
+
};
|
|
14797
|
+
}
|
|
14798
|
+
|
|
14799
|
+
// src/coordination/fleet-bus.ts
|
|
14800
|
+
var FleetBus = class {
|
|
14801
|
+
byId = /* @__PURE__ */ new Map();
|
|
14802
|
+
byType = /* @__PURE__ */ new Map();
|
|
14803
|
+
any = /* @__PURE__ */ new Set();
|
|
14804
|
+
/**
|
|
14805
|
+
* Hook a subagent's EventBus into the fleet. Uses `onAny()` (an alias for
|
|
14806
|
+
* `onPattern('*')`) to forward all events with subagent attribution, so
|
|
14807
|
+
* new kernel event types are automatically forwarded without any manual
|
|
14808
|
+
* registration. `subagent.*` events are excluded because they originate
|
|
14809
|
+
* from MultiAgentHost on the parent bus, not the subagent's own bus.
|
|
14810
|
+
*
|
|
14811
|
+
* Returns a disposer that detaches every subscription; call on
|
|
14812
|
+
* subagent teardown so the listeners don't outlive the run.
|
|
14813
|
+
*/
|
|
14814
|
+
attach(subagentId, bus, taskId) {
|
|
14815
|
+
const off = bus.onAny((type, payload) => {
|
|
14816
|
+
if (type.startsWith("subagent.")) return;
|
|
14817
|
+
this.emit({ subagentId, taskId, ts: Date.now(), type, payload });
|
|
14845
14818
|
});
|
|
14846
|
-
|
|
14847
|
-
|
|
14848
|
-
|
|
14849
|
-
|
|
14850
|
-
|
|
14851
|
-
|
|
14819
|
+
return () => {
|
|
14820
|
+
off();
|
|
14821
|
+
};
|
|
14822
|
+
}
|
|
14823
|
+
/** Subscribe to every event from one subagent. */
|
|
14824
|
+
subscribe(subagentId, handler) {
|
|
14825
|
+
let set = this.byId.get(subagentId);
|
|
14826
|
+
if (!set) {
|
|
14827
|
+
set = /* @__PURE__ */ new Set();
|
|
14828
|
+
this.byId.set(subagentId, set);
|
|
14829
|
+
}
|
|
14830
|
+
set.add(handler);
|
|
14831
|
+
return () => {
|
|
14832
|
+
set.delete(handler);
|
|
14833
|
+
};
|
|
14834
|
+
}
|
|
14835
|
+
/** Subscribe to one event type across all subagents. */
|
|
14836
|
+
filter(type, handler) {
|
|
14837
|
+
let set = this.byType.get(type);
|
|
14838
|
+
if (!set) {
|
|
14839
|
+
set = /* @__PURE__ */ new Set();
|
|
14840
|
+
this.byType.set(type, set);
|
|
14841
|
+
}
|
|
14842
|
+
set.add(handler);
|
|
14843
|
+
return () => {
|
|
14844
|
+
set.delete(handler);
|
|
14845
|
+
};
|
|
14846
|
+
}
|
|
14847
|
+
/** Subscribe to literally everything. The fleet roll-up uses this. */
|
|
14848
|
+
onAny(handler) {
|
|
14849
|
+
this.any.add(handler);
|
|
14850
|
+
return () => {
|
|
14851
|
+
this.any.delete(handler);
|
|
14852
|
+
};
|
|
14853
|
+
}
|
|
14854
|
+
emit(event) {
|
|
14855
|
+
const byId = this.byId.get(event.subagentId);
|
|
14856
|
+
if (byId)
|
|
14857
|
+
for (const h of byId) {
|
|
14858
|
+
try {
|
|
14859
|
+
h(event);
|
|
14860
|
+
} catch {
|
|
14861
|
+
}
|
|
14852
14862
|
}
|
|
14853
|
-
|
|
14854
|
-
|
|
14855
|
-
|
|
14856
|
-
|
|
14857
|
-
|
|
14858
|
-
|
|
14859
|
-
|
|
14863
|
+
const byType = this.byType.get(event.type);
|
|
14864
|
+
if (byType)
|
|
14865
|
+
for (const h of byType) {
|
|
14866
|
+
try {
|
|
14867
|
+
h(event);
|
|
14868
|
+
} catch {
|
|
14869
|
+
}
|
|
14860
14870
|
}
|
|
14861
|
-
|
|
14862
|
-
|
|
14863
|
-
|
|
14864
|
-
|
|
14865
|
-
if (payload?.evaluation) {
|
|
14866
|
-
this.evaluations.set(payload.evaluation.id, payload.evaluation);
|
|
14867
|
-
this.emit("critic.evaluation", payload);
|
|
14871
|
+
for (const h of this.any) {
|
|
14872
|
+
try {
|
|
14873
|
+
h(event);
|
|
14874
|
+
} catch {
|
|
14868
14875
|
}
|
|
14869
|
-
}
|
|
14870
|
-
|
|
14876
|
+
}
|
|
14877
|
+
}
|
|
14878
|
+
};
|
|
14879
|
+
var FleetUsageAggregator = class {
|
|
14880
|
+
constructor(bus, priceLookup, metaLookup) {
|
|
14881
|
+
this.priceLookup = priceLookup;
|
|
14882
|
+
this.metaLookup = metaLookup;
|
|
14883
|
+
this.unsub.push(bus.filter("provider.response", (e) => this.onProviderResponse(e)));
|
|
14884
|
+
this.unsub.push(bus.filter("tool.executed", (e) => this.onToolExecuted(e)));
|
|
14885
|
+
this.unsub.push(bus.filter("iteration.started", (e) => this.onIterationStarted(e)));
|
|
14886
|
+
}
|
|
14887
|
+
priceLookup;
|
|
14888
|
+
metaLookup;
|
|
14889
|
+
perSubagent = /* @__PURE__ */ new Map();
|
|
14890
|
+
total = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
|
|
14891
|
+
unsub = new Array();
|
|
14892
|
+
/**
|
|
14893
|
+
* Remove a terminated subagent's data from the aggregator and subtract its
|
|
14894
|
+
* contribution from the running totals. Call this when a subagent is removed
|
|
14895
|
+
* from the fleet so the aggregator doesn't accumulate unbounded data for
|
|
14896
|
+
* entities that will never emit events again.
|
|
14897
|
+
*/
|
|
14898
|
+
removeSubagent(subagentId) {
|
|
14899
|
+
const snap = this.perSubagent.get(subagentId);
|
|
14900
|
+
if (!snap) return;
|
|
14901
|
+
this.perSubagent.delete(subagentId);
|
|
14902
|
+
this.total.input -= snap.input;
|
|
14903
|
+
this.total.output -= snap.output;
|
|
14904
|
+
this.total.cacheRead -= snap.cacheRead;
|
|
14905
|
+
this.total.cacheWrite -= snap.cacheWrite;
|
|
14906
|
+
this.total.cost -= snap.cost;
|
|
14907
|
+
}
|
|
14908
|
+
/** Disposes all fleet-bus subscriptions. Call when the aggregator is no longer needed. */
|
|
14909
|
+
dispose() {
|
|
14910
|
+
for (const off of this.unsub) off();
|
|
14911
|
+
this.unsub.length = 0;
|
|
14912
|
+
}
|
|
14913
|
+
/** Live snapshot — safe to call from a tool's execute() body. */
|
|
14914
|
+
snapshot() {
|
|
14915
|
+
return {
|
|
14916
|
+
total: { ...this.total },
|
|
14917
|
+
perSubagent: Object.fromEntries(
|
|
14918
|
+
Array.from(this.perSubagent.entries()).map(([k, v]) => [k, { ...v }])
|
|
14919
|
+
)
|
|
14920
|
+
};
|
|
14921
|
+
}
|
|
14922
|
+
ensure(subagentId) {
|
|
14923
|
+
let snap = this.perSubagent.get(subagentId);
|
|
14924
|
+
if (!snap) {
|
|
14925
|
+
const meta = this.metaLookup?.(subagentId);
|
|
14926
|
+
snap = {
|
|
14927
|
+
subagentId,
|
|
14928
|
+
provider: meta?.provider,
|
|
14929
|
+
model: meta?.model,
|
|
14930
|
+
input: 0,
|
|
14931
|
+
output: 0,
|
|
14932
|
+
cacheRead: 0,
|
|
14933
|
+
cacheWrite: 0,
|
|
14934
|
+
cost: 0,
|
|
14935
|
+
toolCalls: 0,
|
|
14936
|
+
iterations: 0,
|
|
14937
|
+
startedAt: Date.now(),
|
|
14938
|
+
lastEventAt: Date.now()
|
|
14939
|
+
};
|
|
14940
|
+
this.perSubagent.set(subagentId, snap);
|
|
14941
|
+
}
|
|
14942
|
+
return snap;
|
|
14943
|
+
}
|
|
14944
|
+
onProviderResponse(e) {
|
|
14945
|
+
const snap = this.ensure(e.subagentId);
|
|
14946
|
+
const p = e.payload;
|
|
14947
|
+
const usage = p?.usage;
|
|
14948
|
+
if (!usage) return;
|
|
14949
|
+
snap.input += usage.input ?? 0;
|
|
14950
|
+
snap.output += usage.output ?? 0;
|
|
14951
|
+
snap.cacheRead += usage.cacheRead ?? 0;
|
|
14952
|
+
snap.cacheWrite += usage.cacheWrite ?? 0;
|
|
14953
|
+
this.total.input += usage.input ?? 0;
|
|
14954
|
+
this.total.output += usage.output ?? 0;
|
|
14955
|
+
this.total.cacheRead += usage.cacheRead ?? 0;
|
|
14956
|
+
this.total.cacheWrite += usage.cacheWrite ?? 0;
|
|
14957
|
+
const price = this.priceLookup?.(e.subagentId, snap.provider, snap.model);
|
|
14958
|
+
if (price) {
|
|
14959
|
+
const delta = (usage.input ?? 0) / 1e6 * (price.input ?? 0) + (usage.output ?? 0) / 1e6 * (price.output ?? 0) + (usage.cacheRead ?? 0) / 1e6 * (price.cacheRead ?? 0) + (usage.cacheWrite ?? 0) / 1e6 * (price.cacheWrite ?? 0);
|
|
14960
|
+
snap.cost += delta;
|
|
14961
|
+
this.total.cost += delta;
|
|
14962
|
+
}
|
|
14963
|
+
snap.lastEventAt = e.ts;
|
|
14964
|
+
}
|
|
14965
|
+
onToolExecuted(e) {
|
|
14966
|
+
const snap = this.ensure(e.subagentId);
|
|
14967
|
+
snap.toolCalls += 1;
|
|
14968
|
+
snap.lastEventAt = e.ts;
|
|
14969
|
+
}
|
|
14970
|
+
onIterationStarted(e) {
|
|
14971
|
+
const snap = this.ensure(e.subagentId);
|
|
14972
|
+
snap.iterations += 1;
|
|
14973
|
+
snap.lastEventAt = e.ts;
|
|
14974
|
+
}
|
|
14975
|
+
};
|
|
14976
|
+
|
|
14977
|
+
// src/coordination/large-answer-store.ts
|
|
14978
|
+
var LargeAnswerStore = class {
|
|
14979
|
+
/**
|
|
14980
|
+
* Responses above this size (in characters) are stored out-of-context.
|
|
14981
|
+
* Below this, the full answer is returned inline (no overhead).
|
|
14982
|
+
* Default: 2000 chars ≈ 400-600 tokens.
|
|
14983
|
+
*/
|
|
14984
|
+
sizeThreshold;
|
|
14985
|
+
store = /* @__PURE__ */ new Map();
|
|
14986
|
+
constructor(sizeThreshold = 2e3) {
|
|
14987
|
+
this.sizeThreshold = sizeThreshold;
|
|
14871
14988
|
}
|
|
14872
|
-
|
|
14873
|
-
|
|
14874
|
-
|
|
14989
|
+
/**
|
|
14990
|
+
* Store a value, returning a summary + key for inline use.
|
|
14991
|
+
* If the value is below sizeThreshold, returns it as-is (no store entry).
|
|
14992
|
+
*/
|
|
14993
|
+
storeAnswer(value) {
|
|
14994
|
+
if (value === void 0 || value === null) {
|
|
14995
|
+
return { summary: String(value), inline: true };
|
|
14875
14996
|
}
|
|
14876
|
-
const
|
|
14877
|
-
|
|
14878
|
-
|
|
14879
|
-
|
|
14880
|
-
|
|
14881
|
-
const
|
|
14882
|
-
|
|
14883
|
-
|
|
14884
|
-
|
|
14885
|
-
|
|
14886
|
-
|
|
14887
|
-
|
|
14888
|
-
reject: 2
|
|
14889
|
-
};
|
|
14890
|
-
const overallVerdict = evalList.reduce(
|
|
14891
|
-
(worst, eval_) => {
|
|
14892
|
-
const w = verdictOrder[worst];
|
|
14893
|
-
const c = verdictOrder[eval_.verdict];
|
|
14894
|
-
return c > w ? eval_.verdict : worst;
|
|
14895
|
-
},
|
|
14896
|
-
"approve"
|
|
14897
|
-
);
|
|
14898
|
-
const summary = this.buildMarkdownSummary(bugList, planList, evalList, overallVerdict, disposition);
|
|
14997
|
+
const serialized = typeof value === "string" ? value : JSON.stringify(value);
|
|
14998
|
+
const size = serialized.length;
|
|
14999
|
+
if (size <= this.sizeThreshold) {
|
|
15000
|
+
return { summary: serialized.slice(0, 500), inline: true };
|
|
15001
|
+
}
|
|
15002
|
+
const key = `a-${hashStr(serialized)}`;
|
|
15003
|
+
this.store.set(key, {
|
|
15004
|
+
key,
|
|
15005
|
+
value,
|
|
15006
|
+
size,
|
|
15007
|
+
storedAt: Date.now()
|
|
15008
|
+
});
|
|
14899
15009
|
return {
|
|
14900
|
-
|
|
14901
|
-
|
|
14902
|
-
|
|
14903
|
-
targetPaths: this.options.targetPaths,
|
|
14904
|
-
disposition,
|
|
14905
|
-
bugs: bugList,
|
|
14906
|
-
refactorPlans: planList,
|
|
14907
|
-
evaluations: evalList,
|
|
14908
|
-
alerts: [...this.alerts],
|
|
14909
|
-
overallVerdict,
|
|
14910
|
-
summary
|
|
15010
|
+
key,
|
|
15011
|
+
summary: `[stored: ${size} chars \u2014 use roll_up or ask_result tool to retrieve, key=${key}]`,
|
|
15012
|
+
inline: false
|
|
14911
15013
|
};
|
|
14912
15014
|
}
|
|
14913
|
-
|
|
14914
|
-
|
|
14915
|
-
|
|
14916
|
-
|
|
14917
|
-
|
|
14918
|
-
|
|
14919
|
-
`**Overall Verdict:** **${overallVerdict.toUpperCase()}**`,
|
|
14920
|
-
""
|
|
14921
|
-
];
|
|
14922
|
-
if (this.alerts.length > 0) {
|
|
14923
|
-
lines.push("### Alerts", "");
|
|
14924
|
-
for (const alert of this.alerts) {
|
|
14925
|
-
lines.push(`- **[${alert.level.toUpperCase()}]** ${alert.role}: ${alert.message}`);
|
|
14926
|
-
}
|
|
14927
|
-
lines.push("");
|
|
14928
|
-
}
|
|
14929
|
-
if (bugs.length > 0) {
|
|
14930
|
-
lines.push("### Bugs Found", "");
|
|
14931
|
-
for (const b of bugs) {
|
|
14932
|
-
lines.push(`- **[${b.severity.toUpperCase()}]** \`${b.location.file}:${b.location.line}\` \u2014 ${b.description}`);
|
|
14933
|
-
}
|
|
14934
|
-
lines.push("");
|
|
14935
|
-
}
|
|
14936
|
-
if (plans.length > 0) {
|
|
14937
|
-
lines.push("### Refactor Plans", "");
|
|
14938
|
-
for (const p of plans) {
|
|
14939
|
-
lines.push(`- **Phase plan** (risk: ${p.riskScore}, ~${p.estimatedChangeCount} changes)`);
|
|
14940
|
-
for (const phase of p.phases) {
|
|
14941
|
-
lines.push(` - Phase ${phase.number}: ${phase.title} [${phase.risk}]`);
|
|
14942
|
-
}
|
|
14943
|
-
}
|
|
14944
|
-
lines.push("");
|
|
14945
|
-
}
|
|
14946
|
-
if (evals.length > 0) {
|
|
14947
|
-
lines.push("### Critic Evaluations", "");
|
|
14948
|
-
for (const e of evals) {
|
|
14949
|
-
lines.push(`- [${e.subjectType}] score=${e.score}/10 \u2014 **${e.verdict.toUpperCase()}**`);
|
|
14950
|
-
for (const c of e.concerns) {
|
|
14951
|
-
if (c.severity === "blocking") lines.push(` - ${c.description}`);
|
|
14952
|
-
}
|
|
14953
|
-
}
|
|
14954
|
-
lines.push("");
|
|
14955
|
-
}
|
|
14956
|
-
return lines.join("\n");
|
|
15015
|
+
/**
|
|
15016
|
+
* Retrieve a previously stored answer by its key.
|
|
15017
|
+
* Returns undefined if the key is unknown or the store was cleared.
|
|
15018
|
+
*/
|
|
15019
|
+
retrieveAnswer(key) {
|
|
15020
|
+
return this.store.get(key)?.value;
|
|
14957
15021
|
}
|
|
14958
|
-
|
|
14959
|
-
|
|
14960
|
-
|
|
15022
|
+
/**
|
|
15023
|
+
* Check if a key exists in the store.
|
|
15024
|
+
*/
|
|
15025
|
+
hasAnswer(key) {
|
|
15026
|
+
return this.store.has(key);
|
|
15027
|
+
}
|
|
15028
|
+
/** Number of stored entries. */
|
|
15029
|
+
get size() {
|
|
15030
|
+
return this.store.size;
|
|
15031
|
+
}
|
|
15032
|
+
/** Total characters stored. */
|
|
15033
|
+
get totalChars() {
|
|
15034
|
+
let total = 0;
|
|
15035
|
+
for (const e of this.store.values()) total += e.size;
|
|
15036
|
+
return total;
|
|
15037
|
+
}
|
|
15038
|
+
/** Clear all stored entries. Call at the end of a director run. */
|
|
15039
|
+
clear() {
|
|
15040
|
+
this.store.clear();
|
|
14961
15041
|
}
|
|
14962
15042
|
};
|
|
15043
|
+
function hashStr(s) {
|
|
15044
|
+
let h = 5381;
|
|
15045
|
+
for (let i = 0; i < s.length; i++) {
|
|
15046
|
+
h = h * 33 ^ s.charCodeAt(i);
|
|
15047
|
+
}
|
|
15048
|
+
return (h >>> 0).toString(36);
|
|
15049
|
+
}
|
|
15050
|
+
|
|
15051
|
+
// src/coordination/model-matrix.ts
|
|
15052
|
+
var MATRIX_PHASE_KEYS = Object.keys(AGENTS_BY_PHASE);
|
|
15053
|
+
var ROLE_TO_PHASE = (() => {
|
|
15054
|
+
const map = {};
|
|
15055
|
+
for (const [phase, defs] of Object.entries(AGENTS_BY_PHASE)) {
|
|
15056
|
+
for (const def of defs) {
|
|
15057
|
+
const role = def.config.role;
|
|
15058
|
+
if (role) map[role] = phase;
|
|
15059
|
+
}
|
|
15060
|
+
}
|
|
15061
|
+
return map;
|
|
15062
|
+
})();
|
|
15063
|
+
function phaseForRole(role) {
|
|
15064
|
+
return role ? ROLE_TO_PHASE[role] : void 0;
|
|
15065
|
+
}
|
|
15066
|
+
function resolveModelMatrix(matrix, role) {
|
|
15067
|
+
if (!matrix) return void 0;
|
|
15068
|
+
if (role && matrix[role]) return matrix[role];
|
|
15069
|
+
const phase = phaseForRole(role);
|
|
15070
|
+
if (phase && matrix[phase]) return matrix[phase];
|
|
15071
|
+
if (matrix["*"]) return matrix["*"];
|
|
15072
|
+
return void 0;
|
|
15073
|
+
}
|
|
15074
|
+
function matrixKeyKind(key) {
|
|
15075
|
+
if (key === "*") return "default";
|
|
15076
|
+
if (key in AGENT_CATALOG) return "role";
|
|
15077
|
+
if (MATRIX_PHASE_KEYS.includes(key)) return "phase";
|
|
15078
|
+
return "unknown";
|
|
15079
|
+
}
|
|
15080
|
+
function isValidMatrixKey(key) {
|
|
15081
|
+
return matrixKeyKind(key) !== "unknown";
|
|
15082
|
+
}
|
|
14963
15083
|
|
|
14964
15084
|
// src/coordination/director.ts
|
|
14965
15085
|
var FleetSpawnBudgetError = class extends Error {
|
|
@@ -15131,6 +15251,9 @@ var Director = class _Director {
|
|
|
15131
15251
|
maxLeaderContextLoad;
|
|
15132
15252
|
/** Provider's max context window in tokens. */
|
|
15133
15253
|
maxContext;
|
|
15254
|
+
/** Per-task model matrix (static record or live getter); resolved
|
|
15255
|
+
* per-spawn when no explicit model is set. */
|
|
15256
|
+
modelMatrix;
|
|
15134
15257
|
/**
|
|
15135
15258
|
* When set by `workComplete()`, the director stops dispatching new tasks
|
|
15136
15259
|
* and terminates all running subagents. Used when the director's LLM decides
|
|
@@ -15161,20 +15284,23 @@ var Director = class _Director {
|
|
|
15161
15284
|
this.maxBudgetExtensions = opts.maxBudgetExtensions ?? 5;
|
|
15162
15285
|
this.maxLeaderContextLoad = opts.maxLeaderContextLoad ?? 0.85;
|
|
15163
15286
|
this.maxContext = opts.maxContext ?? 128e3;
|
|
15287
|
+
this.modelMatrix = opts.modelMatrix;
|
|
15164
15288
|
this.sessionsRoot = opts.sessionsRoot;
|
|
15165
15289
|
this.directorRunId = opts.directorRunId ?? this.id;
|
|
15166
|
-
this.stateCheckpoint = opts.stateCheckpointPath ? new DirectorStateCheckpoint(
|
|
15167
|
-
|
|
15168
|
-
|
|
15169
|
-
|
|
15170
|
-
|
|
15171
|
-
|
|
15172
|
-
|
|
15290
|
+
this.stateCheckpoint = opts.stateCheckpointPath ? new DirectorStateCheckpoint(
|
|
15291
|
+
opts.stateCheckpointPath,
|
|
15292
|
+
{
|
|
15293
|
+
directorRunId: this.id,
|
|
15294
|
+
maxSpawns: opts.maxSpawns,
|
|
15295
|
+
spawnDepth: this.spawnDepth,
|
|
15296
|
+
maxSpawnDepth: this.maxSpawnDepth,
|
|
15297
|
+
directorBudget: opts.directorBudget
|
|
15298
|
+
},
|
|
15299
|
+
opts.checkpointDebounceMs ?? 250
|
|
15300
|
+
) : null;
|
|
15173
15301
|
this.fleetManager = opts.fleetManager;
|
|
15174
15302
|
if (this.sharedScratchpadPath) {
|
|
15175
|
-
void fsp3.mkdir(this.sharedScratchpadPath, { recursive: true }).catch(
|
|
15176
|
-
(err) => this.logShutdownError("shared_scratchpad_mkdir", err)
|
|
15177
|
-
);
|
|
15303
|
+
void fsp3.mkdir(this.sharedScratchpadPath, { recursive: true }).catch((err) => this.logShutdownError("shared_scratchpad_mkdir", err));
|
|
15178
15304
|
}
|
|
15179
15305
|
this.transport = new InMemoryBridgeTransport();
|
|
15180
15306
|
this.bridge = new InMemoryAgentBridge(
|
|
@@ -15352,7 +15478,12 @@ var Director = class _Director {
|
|
|
15352
15478
|
*/
|
|
15353
15479
|
workComplete() {
|
|
15354
15480
|
this.workCompleteFlag = true;
|
|
15355
|
-
this.fleet.emit({
|
|
15481
|
+
this.fleet.emit({
|
|
15482
|
+
subagentId: this.id,
|
|
15483
|
+
ts: Date.now(),
|
|
15484
|
+
type: "director.work_complete",
|
|
15485
|
+
payload: {}
|
|
15486
|
+
});
|
|
15356
15487
|
}
|
|
15357
15488
|
/** Returns true if `workComplete()` has been called on this director. */
|
|
15358
15489
|
isWorkComplete() {
|
|
@@ -15484,13 +15615,25 @@ var Director = class _Director {
|
|
|
15484
15615
|
"workComplete() has been called \u2014 director closed further spawning"
|
|
15485
15616
|
);
|
|
15486
15617
|
}
|
|
15618
|
+
if (!config.model && this.modelMatrix) {
|
|
15619
|
+
const matrix = typeof this.modelMatrix === "function" ? this.modelMatrix() : this.modelMatrix;
|
|
15620
|
+
const entry = resolveModelMatrix(matrix, config.role);
|
|
15621
|
+
if (entry) {
|
|
15622
|
+
config.model = entry.model;
|
|
15623
|
+
if (entry.provider) config.provider = entry.provider;
|
|
15624
|
+
}
|
|
15625
|
+
}
|
|
15487
15626
|
if (this.fleetManager) {
|
|
15488
15627
|
const rejection = this.fleetManager.canSpawn(config);
|
|
15489
15628
|
if (rejection) {
|
|
15490
|
-
if (rejection.kind === "max_spawn_depth")
|
|
15491
|
-
|
|
15492
|
-
if (rejection.kind === "
|
|
15493
|
-
|
|
15629
|
+
if (rejection.kind === "max_spawn_depth")
|
|
15630
|
+
throw new FleetSpawnBudgetError("max_spawn_depth", rejection.limit, rejection.observed);
|
|
15631
|
+
if (rejection.kind === "max_spawns")
|
|
15632
|
+
throw new FleetSpawnBudgetError("max_spawns", rejection.limit, rejection.observed);
|
|
15633
|
+
if (rejection.kind === "max_cost_usd")
|
|
15634
|
+
throw new FleetCostCapError(rejection.limit, rejection.observed);
|
|
15635
|
+
if (rejection.kind === "max_context_load")
|
|
15636
|
+
throw new FleetContextOverflowError(rejection.limit, rejection.observed);
|
|
15494
15637
|
}
|
|
15495
15638
|
} else {
|
|
15496
15639
|
if (this.spawnDepth >= this.maxSpawnDepth) {
|
|
@@ -15520,7 +15663,9 @@ var Director = class _Director {
|
|
|
15520
15663
|
this.fleetManager.assignNicknameAndRecord(config);
|
|
15521
15664
|
} else {
|
|
15522
15665
|
config.name = assignNickname(role, this._usedNicknames);
|
|
15523
|
-
this._usedNicknames.add(
|
|
15666
|
+
this._usedNicknames.add(
|
|
15667
|
+
config.name.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-")
|
|
15668
|
+
);
|
|
15524
15669
|
}
|
|
15525
15670
|
}
|
|
15526
15671
|
result = await this.coordinator.spawn(config);
|
|
@@ -15542,6 +15687,20 @@ var Director = class _Director {
|
|
|
15542
15687
|
);
|
|
15543
15688
|
this.coordinator.setSubagentBridge(result.subagentId, subagentBridge);
|
|
15544
15689
|
this.subagentBridges.set(result.subagentId, subagentBridge);
|
|
15690
|
+
this.fleet.emit({
|
|
15691
|
+
subagentId: result.subagentId,
|
|
15692
|
+
ts: Date.now(),
|
|
15693
|
+
type: "subagent.spawned",
|
|
15694
|
+
payload: {
|
|
15695
|
+
subagentId: result.subagentId,
|
|
15696
|
+
taskId: "",
|
|
15697
|
+
// taskId will be set when assign() is called
|
|
15698
|
+
name: config.name,
|
|
15699
|
+
role: config.role,
|
|
15700
|
+
provider: config.provider,
|
|
15701
|
+
model: config.model
|
|
15702
|
+
}
|
|
15703
|
+
});
|
|
15545
15704
|
if (!this.fleetManager) {
|
|
15546
15705
|
this.manifestEntries.set(result.subagentId, {
|
|
15547
15706
|
subagentId: result.subagentId,
|
|
@@ -15741,7 +15900,11 @@ var Director = class _Director {
|
|
|
15741
15900
|
subagentId: taskWithId.subagentId ?? "unassigned",
|
|
15742
15901
|
taskId: taskWithId.id,
|
|
15743
15902
|
status: "stopped",
|
|
15744
|
-
error: {
|
|
15903
|
+
error: {
|
|
15904
|
+
kind: "aborted_by_parent",
|
|
15905
|
+
message: "Director called workComplete() \u2014 no further tasks will run",
|
|
15906
|
+
retryable: false
|
|
15907
|
+
},
|
|
15745
15908
|
iterations: 0,
|
|
15746
15909
|
toolCalls: 0,
|
|
15747
15910
|
durationMs: 0
|
|
@@ -22931,22 +23094,24 @@ var ReportGenerator = class {
|
|
|
22931
23094
|
medium: "#ca8a04",
|
|
22932
23095
|
low: "#16a34a"
|
|
22933
23096
|
};
|
|
22934
|
-
const rows = result.findings.map(
|
|
22935
|
-
|
|
23097
|
+
const rows = result.findings.map((f) => {
|
|
23098
|
+
const sevColor = severityColors[f.severity] ?? "#000000";
|
|
23099
|
+
const loc = `${escapeHtml(f.file)}${f.line ? `:${escapeHtml(String(f.line))}` : ""}`;
|
|
23100
|
+
return `
|
|
22936
23101
|
<tr>
|
|
22937
|
-
<td style="color: ${
|
|
22938
|
-
<td>${f.category}</td>
|
|
22939
|
-
<td>${f.title}</td>
|
|
22940
|
-
<td><code>${
|
|
22941
|
-
<td>${f.remediation}</td>
|
|
23102
|
+
<td style="color: ${escapeHtml(sevColor)}; font-weight: bold;">${escapeHtml(f.severity.toUpperCase())}</td>
|
|
23103
|
+
<td>${escapeHtml(f.category)}</td>
|
|
23104
|
+
<td>${escapeHtml(f.title)}</td>
|
|
23105
|
+
<td><code>${loc}</code></td>
|
|
23106
|
+
<td>${escapeHtml(f.remediation)}</td>
|
|
22942
23107
|
</tr>
|
|
22943
|
-
|
|
22944
|
-
).join("");
|
|
23108
|
+
`;
|
|
23109
|
+
}).join("");
|
|
22945
23110
|
return `
|
|
22946
23111
|
<!DOCTYPE html>
|
|
22947
23112
|
<html>
|
|
22948
23113
|
<head>
|
|
22949
|
-
<title>Security Scan Report - ${new Date(result.timestamp).toLocaleDateString()}</title>
|
|
23114
|
+
<title>Security Scan Report - ${escapeHtml(new Date(result.timestamp).toLocaleDateString())}</title>
|
|
22950
23115
|
<style>
|
|
22951
23116
|
body { font-family: system-ui, sans-serif; margin: 2rem; }
|
|
22952
23117
|
table { border-collapse: collapse; width: 100%; }
|
|
@@ -22957,9 +23122,9 @@ var ReportGenerator = class {
|
|
|
22957
23122
|
</head>
|
|
22958
23123
|
<body>
|
|
22959
23124
|
<h1>Security Scan Report</h1>
|
|
22960
|
-
<p><strong>Generated:</strong> ${new Date(result.timestamp).toLocaleString()}</p>
|
|
22961
|
-
<p><strong>Project:</strong> ${result.projectRoot}</p>
|
|
22962
|
-
<p><strong>Tech Stack:</strong> ${result.techStack.stack} (${result.techStack.packageManager})</p>
|
|
23125
|
+
<p><strong>Generated:</strong> ${escapeHtml(new Date(result.timestamp).toLocaleString())}</p>
|
|
23126
|
+
<p><strong>Project:</strong> ${escapeHtml(result.projectRoot)}</p>
|
|
23127
|
+
<p><strong>Tech Stack:</strong> ${escapeHtml(result.techStack.stack)} (${escapeHtml(result.techStack.packageManager)})</p>
|
|
22963
23128
|
|
|
22964
23129
|
<h2>Summary</h2>
|
|
22965
23130
|
<ul>
|
|
@@ -22985,6 +23150,9 @@ var ReportGenerator = class {
|
|
|
22985
23150
|
`.trim();
|
|
22986
23151
|
}
|
|
22987
23152
|
};
|
|
23153
|
+
function escapeHtml(value) {
|
|
23154
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
23155
|
+
}
|
|
22988
23156
|
var defaultReportGenerator = new ReportGenerator();
|
|
22989
23157
|
|
|
22990
23158
|
// src/security-scanner/gitignore-updater.ts
|
|
@@ -26996,6 +27164,7 @@ var PhaseOrchestrator = class {
|
|
|
26996
27164
|
maxConcurrentPhases: opts.maxConcurrentPhases ?? 1,
|
|
26997
27165
|
maxConcurrentTasks: opts.maxConcurrentTasks ?? 2,
|
|
26998
27166
|
maxRetries: opts.maxRetries ?? 2,
|
|
27167
|
+
maxVerifyAttempts: opts.maxVerifyAttempts ?? 2,
|
|
26999
27168
|
autonomous: opts.autonomous ?? true,
|
|
27000
27169
|
phaseDelayMs: opts.phaseDelayMs ?? 0,
|
|
27001
27170
|
stopOnFailure: opts.stopOnFailure ?? true,
|
|
@@ -27042,7 +27211,10 @@ var PhaseOrchestrator = class {
|
|
|
27042
27211
|
resume() {
|
|
27043
27212
|
this.paused = false;
|
|
27044
27213
|
this.tick().catch((err) => {
|
|
27045
|
-
console.error(
|
|
27214
|
+
console.error(
|
|
27215
|
+
"[phase-orchestrator] tick failed:",
|
|
27216
|
+
err instanceof Error ? err.message : String(err)
|
|
27217
|
+
);
|
|
27046
27218
|
});
|
|
27047
27219
|
}
|
|
27048
27220
|
/** Tamamen durdur — aktif fazlar da durdurulur */
|
|
@@ -27123,33 +27295,30 @@ var PhaseOrchestrator = class {
|
|
|
27123
27295
|
failed: failedTasks
|
|
27124
27296
|
});
|
|
27125
27297
|
if (failedTasks > 0 && this.opts.stopOnFailure) {
|
|
27126
|
-
this.
|
|
27127
|
-
|
|
27128
|
-
|
|
27129
|
-
|
|
27130
|
-
|
|
27131
|
-
this.
|
|
27132
|
-
|
|
27133
|
-
|
|
27134
|
-
|
|
27135
|
-
|
|
27136
|
-
this.ctx.onPhaseFail?.(phase, new Error(`${failedTasks} task(s) failed`));
|
|
27137
|
-
await this.keepWorktreeForReview(phase);
|
|
27138
|
-
} else {
|
|
27139
|
-
this.updatePhaseStatus(phase, "completed");
|
|
27140
|
-
phase.completedAt = Date.now();
|
|
27141
|
-
phase.actualDurationMs = Date.now() - (phase.startedAt ?? Date.now());
|
|
27142
|
-
this.runningPhases.delete(phase.id);
|
|
27143
|
-
this.graph.activePhaseIds = this.graph.activePhaseIds.filter((id) => id !== phase.id);
|
|
27144
|
-
this.graph.completedPhaseIds.push(phase.id);
|
|
27145
|
-
this.emit("phase.completed", {
|
|
27146
|
-
phaseId: phase.id,
|
|
27147
|
-
name: phase.name,
|
|
27148
|
-
durationMs: phase.actualDurationMs
|
|
27149
|
-
});
|
|
27150
|
-
this.ctx.onPhaseComplete?.(phase);
|
|
27151
|
-
await this.commitAndEnqueueMerge(phase);
|
|
27298
|
+
await this.failPhaseAfterTasks(phase, `${failedTasks} task(s) failed`);
|
|
27299
|
+
return;
|
|
27300
|
+
}
|
|
27301
|
+
const verdict = await this.runVerifyGate(phase);
|
|
27302
|
+
if (!verdict.ok) {
|
|
27303
|
+
await this.failPhaseAfterTasks(
|
|
27304
|
+
phase,
|
|
27305
|
+
`verification failed${verdict.output ? `: ${this.truncate(verdict.output)}` : ""}`
|
|
27306
|
+
);
|
|
27307
|
+
return;
|
|
27152
27308
|
}
|
|
27309
|
+
this.updatePhaseStatus(phase, "completed");
|
|
27310
|
+
phase.completedAt = Date.now();
|
|
27311
|
+
phase.actualDurationMs = Date.now() - (phase.startedAt ?? Date.now());
|
|
27312
|
+
this.runningPhases.delete(phase.id);
|
|
27313
|
+
this.graph.activePhaseIds = this.graph.activePhaseIds.filter((id) => id !== phase.id);
|
|
27314
|
+
this.graph.completedPhaseIds.push(phase.id);
|
|
27315
|
+
this.emit("phase.completed", {
|
|
27316
|
+
phaseId: phase.id,
|
|
27317
|
+
name: phase.name,
|
|
27318
|
+
durationMs: phase.actualDurationMs
|
|
27319
|
+
});
|
|
27320
|
+
this.ctx.onPhaseComplete?.(phase);
|
|
27321
|
+
await this.commitAndEnqueueMerge(phase);
|
|
27153
27322
|
} catch (error) {
|
|
27154
27323
|
this.updatePhaseStatus(phase, "failed");
|
|
27155
27324
|
phase.completedAt = Date.now();
|
|
@@ -27166,6 +27335,69 @@ var PhaseOrchestrator = class {
|
|
|
27166
27335
|
await this.keepWorktreeForReview(phase);
|
|
27167
27336
|
}
|
|
27168
27337
|
}
|
|
27338
|
+
// ─── Verification gate ──────────────────────────────────────────────────────
|
|
27339
|
+
/**
|
|
27340
|
+
* Run the verification gate for a phase whose tasks all succeeded. Verifies in
|
|
27341
|
+
* the phase's worktree; on failure, runs the repair pass and re-verifies, up to
|
|
27342
|
+
* `maxVerifyAttempts` repairs. Returns the final verdict. When no `verifyPhase`
|
|
27343
|
+
* callback is wired the gate is a no-op and always passes.
|
|
27344
|
+
*/
|
|
27345
|
+
async runVerifyGate(phase) {
|
|
27346
|
+
if (!this.ctx.verifyPhase) return { ok: true };
|
|
27347
|
+
const env = this.worktreeEnv(phase);
|
|
27348
|
+
for (let attempt = 0; attempt <= this.opts.maxVerifyAttempts; attempt++) {
|
|
27349
|
+
if (this.stopped) return { ok: false, output: "stopped before verification completed" };
|
|
27350
|
+
this.emit("phase.verifying", { phaseId: phase.id, name: phase.name, attempt });
|
|
27351
|
+
let verdict;
|
|
27352
|
+
try {
|
|
27353
|
+
verdict = await this.ctx.verifyPhase(phase, env);
|
|
27354
|
+
} catch (err) {
|
|
27355
|
+
verdict = { ok: false, output: err instanceof Error ? err.message : String(err) };
|
|
27356
|
+
}
|
|
27357
|
+
if (verdict.ok) return { ok: true };
|
|
27358
|
+
this.emit("phase.verifyFailed", {
|
|
27359
|
+
phaseId: phase.id,
|
|
27360
|
+
name: phase.name,
|
|
27361
|
+
attempt,
|
|
27362
|
+
error: verdict.output
|
|
27363
|
+
});
|
|
27364
|
+
if (attempt >= this.opts.maxVerifyAttempts || !this.ctx.repairPhase || this.stopped) {
|
|
27365
|
+
return { ok: false, output: verdict.output };
|
|
27366
|
+
}
|
|
27367
|
+
this.emit("phase.repairing", { phaseId: phase.id, name: phase.name, attempt: attempt + 1 });
|
|
27368
|
+
try {
|
|
27369
|
+
await this.ctx.repairPhase(
|
|
27370
|
+
phase,
|
|
27371
|
+
verdict.output ?? "verification failed",
|
|
27372
|
+
attempt + 1,
|
|
27373
|
+
env
|
|
27374
|
+
);
|
|
27375
|
+
} catch {
|
|
27376
|
+
}
|
|
27377
|
+
}
|
|
27378
|
+
return { ok: false };
|
|
27379
|
+
}
|
|
27380
|
+
/** Worktree env (cwd/branch) for a phase, or undefined if it runs on the shared tree. */
|
|
27381
|
+
worktreeEnv(phase) {
|
|
27382
|
+
const handle = this.phaseWorktrees.get(phase.id);
|
|
27383
|
+
return handle ? { cwd: handle.dir, branch: handle.branch } : void 0;
|
|
27384
|
+
}
|
|
27385
|
+
/** Shared failure bookkeeping for a phase whose tasks ran but the phase failed. */
|
|
27386
|
+
async failPhaseAfterTasks(phase, error) {
|
|
27387
|
+
this.updatePhaseStatus(phase, "failed");
|
|
27388
|
+
phase.completedAt = Date.now();
|
|
27389
|
+
phase.actualDurationMs = Date.now() - (phase.startedAt ?? Date.now());
|
|
27390
|
+
this.runningPhases.delete(phase.id);
|
|
27391
|
+
this.graph.activePhaseIds = this.graph.activePhaseIds.filter((id) => id !== phase.id);
|
|
27392
|
+
this.emit("phase.failed", { phaseId: phase.id, name: phase.name, error });
|
|
27393
|
+
this.ctx.onPhaseFail?.(phase, new Error(error));
|
|
27394
|
+
await this.keepWorktreeForReview(phase);
|
|
27395
|
+
}
|
|
27396
|
+
/** Trim long verifier output so it fits cleanly in an event/error message. */
|
|
27397
|
+
truncate(text, max = 500) {
|
|
27398
|
+
const t2 = text.trim();
|
|
27399
|
+
return t2.length <= max ? t2 : `${t2.slice(0, max)}\u2026 (+${t2.length - max} chars)`;
|
|
27400
|
+
}
|
|
27169
27401
|
// ─── Worktree integration ───────────────────────────────────────────────────
|
|
27170
27402
|
/**
|
|
27171
27403
|
* Commit the phase's worktree changes, then enqueue the merge back into the
|
|
@@ -27188,11 +27420,26 @@ var PhaseOrchestrator = class {
|
|
|
27188
27420
|
})();
|
|
27189
27421
|
this.phaseMergePromise.set(phase.id, merged);
|
|
27190
27422
|
}
|
|
27191
|
-
/**
|
|
27423
|
+
/**
|
|
27424
|
+
* Squash-merge one phase. When a `resolveConflict` callback is wired, a merge
|
|
27425
|
+
* conflict is handed to it (a resolver subagent) before giving up; only if
|
|
27426
|
+
* that fails does the worktree fall to needs-review and the run continues.
|
|
27427
|
+
*/
|
|
27192
27428
|
async mergeOne(phase, handle) {
|
|
27193
27429
|
if (!this.worktrees) return;
|
|
27194
27430
|
try {
|
|
27195
|
-
const
|
|
27431
|
+
const resolve10 = this.ctx.resolveConflict ? async (info) => {
|
|
27432
|
+
this.emit("phase.conflictResolving", {
|
|
27433
|
+
phaseId: phase.id,
|
|
27434
|
+
name: phase.name,
|
|
27435
|
+
files: info.conflictFiles
|
|
27436
|
+
});
|
|
27437
|
+
return this.ctx.resolveConflict(phase, info);
|
|
27438
|
+
} : void 0;
|
|
27439
|
+
const result = await this.worktrees.merge(handle, { squash: true, resolve: resolve10 });
|
|
27440
|
+
if (result.resolved) {
|
|
27441
|
+
this.emit("phase.conflictResolved", { phaseId: phase.id, name: phase.name });
|
|
27442
|
+
}
|
|
27196
27443
|
await this.worktrees.release(handle, { keep: !result.ok });
|
|
27197
27444
|
} catch (err) {
|
|
27198
27445
|
this.emit("phase.failed", {
|
|
@@ -27256,7 +27503,11 @@ var PhaseOrchestrator = class {
|
|
|
27256
27503
|
const currentRetries = this.taskRetryCounts.get(taskKey) ?? 0;
|
|
27257
27504
|
if (currentRetries < this.opts.maxRetries) {
|
|
27258
27505
|
this.taskRetryCounts.set(taskKey, currentRetries + 1);
|
|
27259
|
-
tracker.updateNodeStatus(
|
|
27506
|
+
tracker.updateNodeStatus(
|
|
27507
|
+
task.id,
|
|
27508
|
+
"pending",
|
|
27509
|
+
`Retry ${currentRetries + 1}/${this.opts.maxRetries}`
|
|
27510
|
+
);
|
|
27260
27511
|
this.emit("phase.taskRetrying", {
|
|
27261
27512
|
phaseId: phase.id,
|
|
27262
27513
|
taskId: task.id,
|
|
@@ -27265,7 +27516,11 @@ var PhaseOrchestrator = class {
|
|
|
27265
27516
|
maxRetries: this.opts.maxRetries
|
|
27266
27517
|
});
|
|
27267
27518
|
} else {
|
|
27268
|
-
tracker.updateNodeStatus(
|
|
27519
|
+
tracker.updateNodeStatus(
|
|
27520
|
+
task.id,
|
|
27521
|
+
"failed",
|
|
27522
|
+
error instanceof Error ? error.message : String(error)
|
|
27523
|
+
);
|
|
27269
27524
|
this.emit("phase.taskFailed", {
|
|
27270
27525
|
phaseId: phase.id,
|
|
27271
27526
|
taskId: task.id,
|
|
@@ -28216,6 +28471,10 @@ var WorktreeManager = class {
|
|
|
28216
28471
|
${merged.stderr}`);
|
|
28217
28472
|
const fromIndex = await this.unmergedFiles();
|
|
28218
28473
|
const conflictFiles = [.../* @__PURE__ */ new Set([...fromOutput, ...fromIndex])];
|
|
28474
|
+
if (opts.resolve) {
|
|
28475
|
+
const finalized = await this.tryResolveConflict(handle, conflictFiles, opts);
|
|
28476
|
+
if (finalized) return finalized;
|
|
28477
|
+
}
|
|
28219
28478
|
await this.runGit(["reset", "--hard", "HEAD"], this.projectRoot);
|
|
28220
28479
|
handle.conflictFiles = conflictFiles;
|
|
28221
28480
|
this.setStatus(handle, "needs-review", { lastError: merged.stderr });
|
|
@@ -28246,6 +28505,52 @@ ${merged.stderr}`);
|
|
|
28246
28505
|
});
|
|
28247
28506
|
return { ok: true };
|
|
28248
28507
|
}
|
|
28508
|
+
/**
|
|
28509
|
+
* Run the caller-supplied resolver against a conflicted squash-merge, then
|
|
28510
|
+
* commit if it cleared every marker. Returns a successful `MergeResult` on a
|
|
28511
|
+
* clean resolution, or `null` to signal the caller should fall back to the
|
|
28512
|
+
* abort path. Never leaves the base tree committed-but-dirty: a partial or
|
|
28513
|
+
* failed resolution returns `null` and the caller hard-resets.
|
|
28514
|
+
*/
|
|
28515
|
+
async tryResolveConflict(handle, conflictFiles, opts) {
|
|
28516
|
+
let resolved = false;
|
|
28517
|
+
try {
|
|
28518
|
+
resolved = await opts.resolve({ conflictFiles, cwd: this.projectRoot });
|
|
28519
|
+
} catch {
|
|
28520
|
+
resolved = false;
|
|
28521
|
+
}
|
|
28522
|
+
if (!resolved) return null;
|
|
28523
|
+
await this.runGit(["add", "-A"], this.projectRoot);
|
|
28524
|
+
if (await this.hasConflictMarkers()) return null;
|
|
28525
|
+
const idArgs = await this.identityArgs(this.projectRoot);
|
|
28526
|
+
const msg = opts.message ?? `merge ${handle.branch} (squash, conflict resolved)`;
|
|
28527
|
+
const commit = await this.runGit([...idArgs, "commit", "-m", msg], this.projectRoot);
|
|
28528
|
+
if (commit.code !== 0 && !/nothing to commit/i.test(commit.stdout + commit.stderr)) {
|
|
28529
|
+
return null;
|
|
28530
|
+
}
|
|
28531
|
+
handle.conflictFiles = conflictFiles;
|
|
28532
|
+
this.setStatus(handle, "merged");
|
|
28533
|
+
this.emit("worktree.merged", {
|
|
28534
|
+
handleId: handle.id,
|
|
28535
|
+
ownerId: handle.ownerId,
|
|
28536
|
+
branch: handle.branch,
|
|
28537
|
+
baseBranch: handle.baseBranch,
|
|
28538
|
+
squash: true
|
|
28539
|
+
});
|
|
28540
|
+
return { ok: true, resolved: true, conflictFiles };
|
|
28541
|
+
}
|
|
28542
|
+
/**
|
|
28543
|
+
* True when staged content still carries conflict markers. `git diff --cached
|
|
28544
|
+
* --check` exits nonzero and prints a "leftover conflict marker" line for each
|
|
28545
|
+
* survivor; whitespace-only errors (also flagged by --check) are ignored so a
|
|
28546
|
+
* clean resolution with unrelated whitespace is not rejected.
|
|
28547
|
+
*/
|
|
28548
|
+
async hasConflictMarkers() {
|
|
28549
|
+
const check = await this.runGit(["diff", "--cached", "--check"], this.projectRoot);
|
|
28550
|
+
if (check.code === 0) return false;
|
|
28551
|
+
return /conflict marker/i.test(`${check.stdout}
|
|
28552
|
+
${check.stderr}`);
|
|
28553
|
+
}
|
|
28249
28554
|
/**
|
|
28250
28555
|
* Remove the worktree + branch. Conflicted/failed handles (or `keep:true`)
|
|
28251
28556
|
* are left on disk for inspection.
|
|
@@ -29648,6 +29953,6 @@ ${formatPlan(updated)}`
|
|
|
29648
29953
|
};
|
|
29649
29954
|
}
|
|
29650
29955
|
|
|
29651
|
-
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, CORE_RECONSTRUCT_EVENTS, CheckpointManager, CloudSync, CollaborationBus, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MAX_JOURNAL_ENTRIES, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReplayLogStore, ReplayProviderRunner, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, STANDARD_AUDIT_EVENTS, ScopedEventBus, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionRecovery, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertSafePath, atomicWrite, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, consumeBtwNotes, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMcpControlTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createSyncPlugin, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, dispatchAgent, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, ensureDir, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob, extractRunEnv, filesystemServer, findCriticalPath, formatContextWindowModeList, formatGoal, formatPlan, formatPlanTemplates, formatTodosList, getAgentDefinition, getCalibrationState, getContextWindowMode, getPlanTemplate, getTemplate, githubServer, goalFilePath, googleMapsServer, hashRequest, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isPluginError, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, matchAny, matchGlob, mergeModelsPayload, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, parseContinueDirective, parseSkillRef, pendingBtwCount, projectHash, recordActualUsage, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resetCalibration, resolveAuditLevel, resolveContextWindowPolicy, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, runProviderWithRetry, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, scoreAgents, securitySlashCommand, sentinelServer, setBtwNote, setPlanItemStatus, slackServer, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, zaiVisionServer };
|
|
29956
|
+
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, CORE_RECONSTRUCT_EVENTS, CheckpointManager, CloudSync, CollaborationBus, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MATRIX_PHASE_KEYS, MAX_JOURNAL_ENTRIES, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReplayLogStore, ReplayProviderRunner, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, STANDARD_AUDIT_EVENTS, ScopedEventBus, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionRecovery, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertSafePath, atomicWrite, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, consumeBtwNotes, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMcpControlTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createSyncPlugin, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, dispatchAgent, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, ensureDir, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob, extractRunEnv, filesystemServer, findCriticalPath, formatContextWindowModeList, formatGoal, formatPlan, formatPlanTemplates, formatTodosList, getAgentDefinition, getCalibrationState, getContextWindowMode, getPlanTemplate, getTemplate, getTermSize, githubServer, goalFilePath, googleMapsServer, hashRequest, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isInteractive, isPluginError, isSessionError, isStdinTTY, isStdoutTTY, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isValidMatrixKey, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, matchAny, matchGlob, matrixKeyKind, mergeModelsPayload, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, onResize, parseContinueDirective, parseSkillRef, pendingBtwCount, phaseForRole, projectHash, recordActualUsage, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resetCalibration, resolveAuditLevel, resolveContextWindowPolicy, resolveModelMatrix, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, runProviderWithRetry, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, scoreAgents, securitySlashCommand, sentinelServer, setBtwNote, setPlanItemStatus, setRawMode, slackServer, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, writeErr, writeOut, zaiVisionServer };
|
|
29652
29957
|
//# sourceMappingURL=index.js.map
|
|
29653
29958
|
//# sourceMappingURL=index.js.map
|