@wrongstack/core 0.32.0 → 0.51.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-bridge-D_XcS2HL.d.ts → agent-bridge-CjbD-i7-.d.ts} +1 -1
- package/dist/{agent-subagent-runner-DpZTLdBe.d.ts → agent-subagent-runner-DfvlBx5N.d.ts} +3 -3
- package/dist/{config-BUEGM4JP.d.ts → config-ZRCf7sTu.d.ts} +21 -1
- package/dist/coordination/index.d.ts +10 -10
- package/dist/coordination/index.js +3310 -3056
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +13 -13
- package/dist/defaults/index.js +1544 -1390
- package/dist/defaults/index.js.map +1 -1
- package/dist/{events-BrQiweXN.d.ts → events-Bt44ikPN.d.ts} +135 -1
- package/dist/execution/index.d.ts +35 -9
- package/dist/execution/index.js +61 -28
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +3 -3
- package/dist/{index-pXJdVLe0.d.ts → index-OzA1XjHL.d.ts} +35 -3
- package/dist/{index-ysfO_DlX.d.ts → index-mAWBdLyJ.d.ts} +2 -2
- package/dist/index.d.ts +221 -25
- package/dist/index.js +1670 -1017
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +4 -4
- package/dist/infrastructure/index.js +17 -3
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +4 -4
- package/dist/kernel/index.js +3 -1
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mcp-servers-BzB3r7_c.d.ts → mcp-servers-DONdo-XM.d.ts} +1 -1
- package/dist/models/index.js +5 -2
- package/dist/models/index.js.map +1 -1
- package/dist/{multi-agent-C8Z1i__e.d.ts → multi-agent-Ba9Ni2hC.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-DOXSgtom.d.ts → multi-agent-coordinator-BuKq0q89.d.ts} +2 -2
- package/dist/{null-fleet-bus-DLsUjOyB.d.ts → null-fleet-bus-C0xd73YP.d.ts} +169 -138
- package/dist/observability/index.d.ts +1 -1
- package/dist/{path-resolver-DumKAi0n.d.ts → path-resolver-nkmdiFgi.d.ts} +1 -1
- package/dist/{plan-templates-BZMi-VpU.d.ts → plan-templates-BmDdJ7UL.d.ts} +2 -2
- package/dist/{provider-runner-Dlv8Fvw9.d.ts → provider-runner-BGro2qQB.d.ts} +1 -1
- package/dist/sdd/index.d.ts +5 -5
- package/dist/storage/index.d.ts +3 -3
- package/dist/{tool-executor-BAi4WI2d.d.ts → tool-executor-p4tP9tGF.d.ts} +1 -1
- package/dist/types/index.d.ts +8 -8
- package/dist/types/index.js +22 -5
- 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/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { readFile, readdir, stat, mkdir } from 'fs/promises';
|
|
|
5
5
|
import * as path6 from 'path';
|
|
6
6
|
import { join, extname, relative, isAbsolute, resolve, sep } from 'path';
|
|
7
7
|
import * as fs2 from 'fs';
|
|
8
|
-
import * as
|
|
8
|
+
import * as os6 from 'os';
|
|
9
9
|
import { execFile, spawn } from 'child_process';
|
|
10
10
|
import { promisify } from 'util';
|
|
11
11
|
import { EventEmitter } from 'events';
|
|
@@ -93,7 +93,7 @@ async function renameWithRetry(from, to) {
|
|
|
93
93
|
if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
|
|
94
94
|
throw err;
|
|
95
95
|
}
|
|
96
|
-
await new Promise((
|
|
96
|
+
await new Promise((resolve11) => setTimeout(resolve11, delays[i]));
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
throw lastErr;
|
|
@@ -882,7 +882,9 @@ var TOKENS = {
|
|
|
882
882
|
/** Replaces the entire provider call layer — retry, streaming, tracing. */
|
|
883
883
|
ProviderRunner: t("ProviderRunner"),
|
|
884
884
|
/** Optional git-worktree lifecycle manager (per-phase isolation in AutoPhase). */
|
|
885
|
-
WorktreeManager: t("WorktreeManager")
|
|
885
|
+
WorktreeManager: t("WorktreeManager"),
|
|
886
|
+
/** Optional global Brain arbiter for policy/decision escalation. */
|
|
887
|
+
BrainArbiter: t("BrainArbiter")
|
|
886
888
|
};
|
|
887
889
|
|
|
888
890
|
// src/kernel/run-controller.ts
|
|
@@ -1295,11 +1297,62 @@ function deepMerge(a, b) {
|
|
|
1295
1297
|
return out;
|
|
1296
1298
|
}
|
|
1297
1299
|
|
|
1300
|
+
// src/utils/term.ts
|
|
1301
|
+
var hasStdout = () => typeof process !== "undefined" && !!process.stdout;
|
|
1302
|
+
var hasStdin = () => typeof process !== "undefined" && !!process.stdin;
|
|
1303
|
+
function isStdoutTTY() {
|
|
1304
|
+
return hasStdout() && Boolean(process.stdout.isTTY);
|
|
1305
|
+
}
|
|
1306
|
+
function isStdinTTY() {
|
|
1307
|
+
return hasStdin() && Boolean(process.stdin.isTTY);
|
|
1308
|
+
}
|
|
1309
|
+
function isInteractive() {
|
|
1310
|
+
return isStdinTTY() && isStdoutTTY();
|
|
1311
|
+
}
|
|
1312
|
+
function getTermSize() {
|
|
1313
|
+
if (!hasStdout()) return { rows: 24, cols: 80 };
|
|
1314
|
+
return {
|
|
1315
|
+
rows: process.stdout.rows ?? 24,
|
|
1316
|
+
cols: process.stdout.columns ?? 80
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
function onResize(cb, stream = process.stdout) {
|
|
1320
|
+
if (!stream || typeof stream.on !== "function") return () => {
|
|
1321
|
+
};
|
|
1322
|
+
const handler = () => {
|
|
1323
|
+
cb({
|
|
1324
|
+
rows: stream.rows ?? 24,
|
|
1325
|
+
cols: stream.columns ?? 80
|
|
1326
|
+
});
|
|
1327
|
+
};
|
|
1328
|
+
stream.on("resize", handler);
|
|
1329
|
+
return () => {
|
|
1330
|
+
stream.off("resize", handler);
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
function setRawMode(input, mode) {
|
|
1334
|
+
if (!input || input.isTTY !== true) return false;
|
|
1335
|
+
if (typeof input.setRawMode !== "function") return false;
|
|
1336
|
+
input.setRawMode(mode);
|
|
1337
|
+
return true;
|
|
1338
|
+
}
|
|
1339
|
+
function writeTo(s, stream) {
|
|
1340
|
+
if (!stream || typeof stream.write !== "function") return false;
|
|
1341
|
+
stream.write(s);
|
|
1342
|
+
return true;
|
|
1343
|
+
}
|
|
1344
|
+
function writeOut(s, stream = process.stdout) {
|
|
1345
|
+
return writeTo(s, stream);
|
|
1346
|
+
}
|
|
1347
|
+
function writeErr(s, stream = process.stderr) {
|
|
1348
|
+
return writeTo(s, stream);
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1298
1351
|
// src/utils/color.ts
|
|
1299
1352
|
var isColorTty = () => {
|
|
1300
1353
|
if (process.env.NO_COLOR) return false;
|
|
1301
1354
|
if (process.env.FORCE_COLOR) return true;
|
|
1302
|
-
return
|
|
1355
|
+
return isStdoutTTY();
|
|
1303
1356
|
};
|
|
1304
1357
|
var COLOR = isColorTty();
|
|
1305
1358
|
var wrap = (open5, close) => (s) => COLOR ? `\x1B[${open5}m${s}\x1B[${close}m` : s;
|
|
@@ -1403,10 +1456,10 @@ var DefaultLogger = class _DefaultLogger {
|
|
|
1403
1456
|
if (r <= LEVEL_RANK.warn || this.level === "debug" || this.level === "trace") {
|
|
1404
1457
|
const head = `${color.dim(ts)} ${COLORS[level](level.toUpperCase().padEnd(5))} ${msg}`;
|
|
1405
1458
|
if (ctx !== void 0) {
|
|
1406
|
-
|
|
1459
|
+
writeErr(`${head} ${formatCtx(ctx)}
|
|
1407
1460
|
`);
|
|
1408
1461
|
} else {
|
|
1409
|
-
|
|
1462
|
+
writeErr(`${head}
|
|
1410
1463
|
`);
|
|
1411
1464
|
}
|
|
1412
1465
|
}
|
|
@@ -2439,12 +2492,12 @@ var DefaultModelsRegistry = class {
|
|
|
2439
2492
|
*/
|
|
2440
2493
|
async loadOverlay(opts = {}) {
|
|
2441
2494
|
if (this.overlayPayload && !opts.force) return this.overlayPayload;
|
|
2442
|
-
if (this.overlay) {
|
|
2495
|
+
if (hasEntries(this.overlay)) {
|
|
2443
2496
|
this.overlayPayload = this.overlay;
|
|
2444
2497
|
return this.overlayPayload;
|
|
2445
2498
|
}
|
|
2446
2499
|
const fetched = await this.loadOverlayFromUrl(opts);
|
|
2447
|
-
if (fetched) {
|
|
2500
|
+
if (hasEntries(fetched)) {
|
|
2448
2501
|
this.overlayPayload = fetched;
|
|
2449
2502
|
return fetched;
|
|
2450
2503
|
}
|
|
@@ -2571,6 +2624,9 @@ var DefaultModelsRegistry = class {
|
|
|
2571
2624
|
return path6.resolve(this.cacheFile);
|
|
2572
2625
|
}
|
|
2573
2626
|
};
|
|
2627
|
+
function hasEntries(payload) {
|
|
2628
|
+
return payload !== void 0 && Object.keys(payload).length > 0;
|
|
2629
|
+
}
|
|
2574
2630
|
|
|
2575
2631
|
// src/types/mode.ts
|
|
2576
2632
|
var DEFAULT_MODES = [
|
|
@@ -2978,7 +3034,7 @@ var InMemoryAgentBridge = class {
|
|
|
2978
3034
|
);
|
|
2979
3035
|
}
|
|
2980
3036
|
this.inflightGuards.add(correlationId);
|
|
2981
|
-
return new Promise((
|
|
3037
|
+
return new Promise((resolve11, reject) => {
|
|
2982
3038
|
const timer = setTimeout(() => {
|
|
2983
3039
|
this.inflightGuards.delete(correlationId);
|
|
2984
3040
|
this.pendingRequests.delete(correlationId);
|
|
@@ -2990,7 +3046,7 @@ var InMemoryAgentBridge = class {
|
|
|
2990
3046
|
return;
|
|
2991
3047
|
}
|
|
2992
3048
|
this.pendingRequests.set(correlationId, {
|
|
2993
|
-
resolve:
|
|
3049
|
+
resolve: resolve11,
|
|
2994
3050
|
reject,
|
|
2995
3051
|
timer
|
|
2996
3052
|
});
|
|
@@ -3183,11 +3239,11 @@ function validateAgainstSchema(value, schema) {
|
|
|
3183
3239
|
walk2(value, schema, "", errors);
|
|
3184
3240
|
return { ok: errors.length === 0, errors };
|
|
3185
3241
|
}
|
|
3186
|
-
function walk2(value, schema,
|
|
3242
|
+
function walk2(value, schema, path36, errors) {
|
|
3187
3243
|
if (schema.enum !== void 0) {
|
|
3188
3244
|
if (!schema.enum.some((e) => deepEqual(e, value))) {
|
|
3189
3245
|
errors.push({
|
|
3190
|
-
path:
|
|
3246
|
+
path: path36 || "<root>",
|
|
3191
3247
|
message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
|
|
3192
3248
|
});
|
|
3193
3249
|
return;
|
|
@@ -3196,7 +3252,7 @@ function walk2(value, schema, path35, errors) {
|
|
|
3196
3252
|
if (typeof schema.type === "string") {
|
|
3197
3253
|
if (!checkType(value, schema.type)) {
|
|
3198
3254
|
errors.push({
|
|
3199
|
-
path:
|
|
3255
|
+
path: path36 || "<root>",
|
|
3200
3256
|
message: `expected ${schema.type}, got ${describeType(value)}`
|
|
3201
3257
|
});
|
|
3202
3258
|
return;
|
|
@@ -3206,19 +3262,19 @@ function walk2(value, schema, path35, errors) {
|
|
|
3206
3262
|
const obj = value;
|
|
3207
3263
|
for (const req of schema.required ?? []) {
|
|
3208
3264
|
if (!(req in obj)) {
|
|
3209
|
-
errors.push({ path: joinPath(
|
|
3265
|
+
errors.push({ path: joinPath(path36, req), message: "required property missing" });
|
|
3210
3266
|
}
|
|
3211
3267
|
}
|
|
3212
3268
|
if (schema.properties) {
|
|
3213
3269
|
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
3214
3270
|
if (key in obj) {
|
|
3215
|
-
walk2(obj[key], subSchema, joinPath(
|
|
3271
|
+
walk2(obj[key], subSchema, joinPath(path36, key), errors);
|
|
3216
3272
|
}
|
|
3217
3273
|
}
|
|
3218
3274
|
}
|
|
3219
3275
|
}
|
|
3220
3276
|
if (schema.type === "array" && Array.isArray(value) && schema.items) {
|
|
3221
|
-
value.forEach((item, i) => walk2(item, schema.items, `${
|
|
3277
|
+
value.forEach((item, i) => walk2(item, schema.items, `${path36}[${i}]`, errors));
|
|
3222
3278
|
}
|
|
3223
3279
|
}
|
|
3224
3280
|
function checkType(value, type) {
|
|
@@ -4366,7 +4422,7 @@ function projectHash(absRoot) {
|
|
|
4366
4422
|
return createHash("sha256").update(path6.resolve(absRoot)).digest("hex").slice(0, 12);
|
|
4367
4423
|
}
|
|
4368
4424
|
function resolveWstackPaths(opts) {
|
|
4369
|
-
const home = opts.userHome ??
|
|
4425
|
+
const home = opts.userHome ?? os6.homedir();
|
|
4370
4426
|
const globalRoot = opts.globalRoot ?? path6.join(home, ".wrongstack");
|
|
4371
4427
|
const hash = projectHash(opts.projectRoot);
|
|
4372
4428
|
const projectDir = path6.join(globalRoot, "projects", hash);
|
|
@@ -5487,9 +5543,9 @@ var DefaultMemoryStore = class {
|
|
|
5487
5543
|
async readAll() {
|
|
5488
5544
|
const parts = [];
|
|
5489
5545
|
for (const scope of ["project-agents", "project-memory", "user-memory"]) {
|
|
5490
|
-
const
|
|
5491
|
-
if (
|
|
5492
|
-
parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${
|
|
5546
|
+
const writeErr2 = this.writeErrors.get(scope);
|
|
5547
|
+
if (writeErr2) {
|
|
5548
|
+
parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${writeErr2.message}`);
|
|
5493
5549
|
}
|
|
5494
5550
|
const body = await this.read(scope);
|
|
5495
5551
|
if (body.trim()) parts.push(`## ${labelOf(scope)}
|
|
@@ -6041,7 +6097,7 @@ var RecoveryLock = class {
|
|
|
6041
6097
|
constructor(opts) {
|
|
6042
6098
|
this.file = path6.join(opts.dir, LOCK_FILE);
|
|
6043
6099
|
this.pid = opts.pid ?? process.pid;
|
|
6044
|
-
this.hostname = opts.hostname ??
|
|
6100
|
+
this.hostname = opts.hostname ?? os6.hostname();
|
|
6045
6101
|
this.maxAgeMs = opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
|
|
6046
6102
|
this.sessionStore = opts.sessionStore;
|
|
6047
6103
|
this.probe = opts.isPidAlive ?? defaultIsPidAlive;
|
|
@@ -7478,8 +7534,8 @@ async function streamProviderToResponse(provider, req, signal, ctx, events) {
|
|
|
7478
7534
|
try {
|
|
7479
7535
|
await Promise.race([
|
|
7480
7536
|
Promise.resolve(iter.return?.()),
|
|
7481
|
-
new Promise((
|
|
7482
|
-
drainTimer = setTimeout(
|
|
7537
|
+
new Promise((resolve11) => {
|
|
7538
|
+
drainTimer = setTimeout(resolve11, 500);
|
|
7483
7539
|
})
|
|
7484
7540
|
]);
|
|
7485
7541
|
} finally {
|
|
@@ -7540,7 +7596,7 @@ async function runProviderWithRetry(opts) {
|
|
|
7540
7596
|
description
|
|
7541
7597
|
});
|
|
7542
7598
|
}
|
|
7543
|
-
await new Promise((
|
|
7599
|
+
await new Promise((resolve11, reject) => {
|
|
7544
7600
|
let settled = false;
|
|
7545
7601
|
const onAbort = () => {
|
|
7546
7602
|
if (settled) return;
|
|
@@ -7553,7 +7609,7 @@ async function runProviderWithRetry(opts) {
|
|
|
7553
7609
|
settled = true;
|
|
7554
7610
|
clearTimeout(t2);
|
|
7555
7611
|
signal.removeEventListener("abort", onAbort);
|
|
7556
|
-
|
|
7612
|
+
resolve11();
|
|
7557
7613
|
}, delay);
|
|
7558
7614
|
if (signal.aborted) {
|
|
7559
7615
|
onAbort();
|
|
@@ -8560,7 +8616,7 @@ init_atomic_write();
|
|
|
8560
8616
|
var MAX_JOURNAL_ENTRIES = 500;
|
|
8561
8617
|
function goalFilePath(projectRoot) {
|
|
8562
8618
|
const hash = createHash("sha256").update(path6.resolve(projectRoot)).digest("hex").slice(0, 12);
|
|
8563
|
-
return path6.join(
|
|
8619
|
+
return path6.join(os6.homedir(), ".wrongstack", "projects", hash, "goal.json");
|
|
8564
8620
|
}
|
|
8565
8621
|
async function loadGoal(filePath) {
|
|
8566
8622
|
let raw;
|
|
@@ -9232,7 +9288,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
9232
9288
|
}
|
|
9233
9289
|
};
|
|
9234
9290
|
function sleep(ms) {
|
|
9235
|
-
return new Promise((
|
|
9291
|
+
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
9236
9292
|
}
|
|
9237
9293
|
|
|
9238
9294
|
// src/coordination/subagent-budget.ts
|
|
@@ -9448,12 +9504,12 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
9448
9504
|
if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
|
|
9449
9505
|
return Promise.resolve("stop");
|
|
9450
9506
|
}
|
|
9451
|
-
return new Promise((
|
|
9507
|
+
return new Promise((resolve11) => {
|
|
9452
9508
|
let resolved = false;
|
|
9453
9509
|
const respond = (d) => {
|
|
9454
9510
|
if (resolved) return;
|
|
9455
9511
|
resolved = true;
|
|
9456
|
-
|
|
9512
|
+
resolve11(d);
|
|
9457
9513
|
};
|
|
9458
9514
|
const fallback = setTimeout(
|
|
9459
9515
|
() => respond("stop"),
|
|
@@ -12680,7 +12736,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
12680
12736
|
taskIds.map((id) => {
|
|
12681
12737
|
const cached = this.completedResults.find((r) => r.taskId === id);
|
|
12682
12738
|
if (cached) return cached;
|
|
12683
|
-
return new Promise((
|
|
12739
|
+
return new Promise((resolve11, reject) => {
|
|
12684
12740
|
const timeout = setTimeout(() => {
|
|
12685
12741
|
this.off("task.completed", handler);
|
|
12686
12742
|
reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
|
|
@@ -12689,7 +12745,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
12689
12745
|
if (result.taskId === id) {
|
|
12690
12746
|
clearTimeout(timeout);
|
|
12691
12747
|
this.off("task.completed", handler);
|
|
12692
|
-
|
|
12748
|
+
resolve11(result);
|
|
12693
12749
|
}
|
|
12694
12750
|
};
|
|
12695
12751
|
this.on("task.completed", handler);
|
|
@@ -13192,7 +13248,7 @@ function providerErrorToSubagentError(err, message, cause) {
|
|
|
13192
13248
|
|
|
13193
13249
|
// src/execution/parallel-eternal-engine.ts
|
|
13194
13250
|
function sleep2(ms) {
|
|
13195
|
-
return new Promise((
|
|
13251
|
+
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
13196
13252
|
}
|
|
13197
13253
|
var GOAL_COMPLETE_MARKER2 = /^\s*\[goal[_\s-]?complete\]\s*$/im;
|
|
13198
13254
|
var ParallelEternalEngine = class {
|
|
@@ -13258,8 +13314,14 @@ var ParallelEternalEngine = class {
|
|
|
13258
13314
|
await this.runOneIteration();
|
|
13259
13315
|
} catch (err) {
|
|
13260
13316
|
this.consecutiveFailures++;
|
|
13261
|
-
this.opts.onError?.(
|
|
13262
|
-
|
|
13317
|
+
this.opts.onError?.(
|
|
13318
|
+
err instanceof Error ? err : new Error(String(err)),
|
|
13319
|
+
this.consecutiveFailures
|
|
13320
|
+
);
|
|
13321
|
+
await this.appendFailure(
|
|
13322
|
+
"engine error",
|
|
13323
|
+
err instanceof Error ? err.message : String(err)
|
|
13324
|
+
);
|
|
13263
13325
|
}
|
|
13264
13326
|
if (this.stopRequested) break;
|
|
13265
13327
|
await sleep2(2e3);
|
|
@@ -13275,14 +13337,19 @@ var ParallelEternalEngine = class {
|
|
|
13275
13337
|
* Called by the REPL in its main loop (REPL drives, engine is stateless per tick).
|
|
13276
13338
|
*/
|
|
13277
13339
|
async runOneIteration() {
|
|
13340
|
+
const emit = (stage) => {
|
|
13341
|
+
this.opts.onStage?.(stage);
|
|
13342
|
+
};
|
|
13278
13343
|
this.iterations++;
|
|
13279
13344
|
const goal = await loadGoal(this.goalPath);
|
|
13280
13345
|
if (!goal) {
|
|
13281
13346
|
this.stopRequested = true;
|
|
13347
|
+
emit({ phase: "stopped" });
|
|
13282
13348
|
return false;
|
|
13283
13349
|
}
|
|
13284
13350
|
if (goal.goalState !== "active") {
|
|
13285
13351
|
this.stopRequested = true;
|
|
13352
|
+
emit({ phase: "stopped" });
|
|
13286
13353
|
return false;
|
|
13287
13354
|
}
|
|
13288
13355
|
if (!this.coordinator) {
|
|
@@ -13295,10 +13362,13 @@ var ParallelEternalEngine = class {
|
|
|
13295
13362
|
const runner = makeAgentSubagentRunner({ factory: this.agentFactory });
|
|
13296
13363
|
this.coordinator.setRunner?.(runner);
|
|
13297
13364
|
}
|
|
13365
|
+
emit({ phase: "decompose" });
|
|
13298
13366
|
const tasks = await this.decomposeGoal(goal);
|
|
13299
13367
|
if (!tasks || tasks.length === 0) {
|
|
13368
|
+
emit({ phase: "sleep", ms: 2e3 });
|
|
13300
13369
|
return false;
|
|
13301
13370
|
}
|
|
13371
|
+
emit({ phase: "fanout", slots: Math.min(this.slots, tasks.length) });
|
|
13302
13372
|
const fanOut = await this.fanOut(goal, tasks);
|
|
13303
13373
|
this.iterationsSinceCompact++;
|
|
13304
13374
|
const successCount = fanOut.results.filter((r) => r.status === "success").length;
|
|
@@ -13315,12 +13385,20 @@ var ParallelEternalEngine = class {
|
|
|
13315
13385
|
status,
|
|
13316
13386
|
note
|
|
13317
13387
|
});
|
|
13388
|
+
emit({
|
|
13389
|
+
phase: "aggregate",
|
|
13390
|
+
successCount,
|
|
13391
|
+
total: fanOut.results.length,
|
|
13392
|
+
goalComplete: fanOut.goalComplete
|
|
13393
|
+
});
|
|
13318
13394
|
if (fanOut.goalComplete) {
|
|
13319
13395
|
this.stopRequested = true;
|
|
13320
13396
|
this.state = "stopped";
|
|
13397
|
+
emit({ phase: "stopped" });
|
|
13321
13398
|
return true;
|
|
13322
13399
|
}
|
|
13323
13400
|
await this.maybeCompact();
|
|
13401
|
+
emit({ phase: "sleep", ms: 2e3 });
|
|
13324
13402
|
return fanOut.allSuccessful;
|
|
13325
13403
|
}
|
|
13326
13404
|
// -------------------------------------------------------------------------
|
|
@@ -13334,7 +13412,9 @@ var ParallelEternalEngine = class {
|
|
|
13334
13412
|
(t2) => dispatchAgent(t2, { classifier: this.dispatchClassifier }).catch(() => null)
|
|
13335
13413
|
)
|
|
13336
13414
|
) : [];
|
|
13337
|
-
const recentJournal = goal.journal.slice(-5).map(
|
|
13415
|
+
const recentJournal = goal.journal.slice(-5).map(
|
|
13416
|
+
(e) => ` #${e.iteration} [${e.status}] ${e.task}${e.note ? ` \u2014 ${e.note.slice(0, 80)}` : ""}`
|
|
13417
|
+
).join("\n");
|
|
13338
13418
|
const directivePreamble = [
|
|
13339
13419
|
"\u2550\u2550\u2550 ETERNAL AUTONOMY \u2014 parallel task slot \u2550\u2550\u2550",
|
|
13340
13420
|
"",
|
|
@@ -13377,35 +13457,44 @@ ${personaLine}Task: ${task}
|
|
|
13377
13457
|
role: route?.role ?? "generic",
|
|
13378
13458
|
method: route?.method ?? "none"
|
|
13379
13459
|
});
|
|
13380
|
-
spawnPromises.push(
|
|
13381
|
-
|
|
13382
|
-
|
|
13383
|
-
|
|
13384
|
-
|
|
13385
|
-
|
|
13386
|
-
|
|
13387
|
-
|
|
13388
|
-
|
|
13389
|
-
|
|
13390
|
-
|
|
13391
|
-
|
|
13392
|
-
|
|
13393
|
-
|
|
13394
|
-
|
|
13395
|
-
|
|
13396
|
-
|
|
13397
|
-
|
|
13398
|
-
|
|
13399
|
-
|
|
13400
|
-
|
|
13401
|
-
|
|
13402
|
-
|
|
13403
|
-
|
|
13460
|
+
spawnPromises.push(
|
|
13461
|
+
(async () => {
|
|
13462
|
+
try {
|
|
13463
|
+
await coordinator.spawn(
|
|
13464
|
+
route ? {
|
|
13465
|
+
id: subagentId,
|
|
13466
|
+
name: route.definition.config.name,
|
|
13467
|
+
role: route.role,
|
|
13468
|
+
tools: route.definition.config.tools,
|
|
13469
|
+
systemPromptOverride: route.definition.config.prompt,
|
|
13470
|
+
timeoutMs: this.timeoutMs
|
|
13471
|
+
} : {
|
|
13472
|
+
id: subagentId,
|
|
13473
|
+
name: `slot-${subagentId.slice(-6)}`,
|
|
13474
|
+
// Let the coordinator apply its default budget (roster or generic).
|
|
13475
|
+
// Hardcoding low limits here defeats the x10 budget improvement.
|
|
13476
|
+
timeoutMs: this.timeoutMs
|
|
13477
|
+
}
|
|
13478
|
+
);
|
|
13479
|
+
subagentIds.push(subagentId);
|
|
13480
|
+
taskIds.push(taskId);
|
|
13481
|
+
await coordinator.assign(spec);
|
|
13482
|
+
} catch {
|
|
13483
|
+
}
|
|
13484
|
+
})()
|
|
13485
|
+
);
|
|
13404
13486
|
}
|
|
13405
13487
|
await Promise.all(spawnPromises);
|
|
13406
13488
|
if (taskIds.length === 0) {
|
|
13407
|
-
return {
|
|
13489
|
+
return {
|
|
13490
|
+
results: [],
|
|
13491
|
+
allSuccessful: false,
|
|
13492
|
+
goalComplete: false,
|
|
13493
|
+
partialOutput: "",
|
|
13494
|
+
routes: routeInfo
|
|
13495
|
+
};
|
|
13408
13496
|
}
|
|
13497
|
+
this.opts.onStage?.({ phase: "await", taskIds: [...taskIds] });
|
|
13409
13498
|
let results = [];
|
|
13410
13499
|
try {
|
|
13411
13500
|
const ctrl = new AbortController();
|
|
@@ -13686,357 +13775,631 @@ function buildGoalPreamble(goal) {
|
|
|
13686
13775
|
"BEGIN.]"
|
|
13687
13776
|
].join("\n");
|
|
13688
13777
|
}
|
|
13689
|
-
|
|
13690
|
-
// src/coordination/director.ts
|
|
13691
13778
|
init_atomic_write();
|
|
13692
|
-
|
|
13693
|
-
|
|
13694
|
-
|
|
13779
|
+
var DEFAULT_MAX_TARGET_FILES = 30;
|
|
13780
|
+
var CollabSession = class extends EventEmitter {
|
|
13781
|
+
sessionId;
|
|
13782
|
+
options;
|
|
13783
|
+
snapshot;
|
|
13784
|
+
director;
|
|
13785
|
+
fleetBus;
|
|
13786
|
+
subagentIds = /* @__PURE__ */ new Map();
|
|
13787
|
+
// role → subagentId
|
|
13788
|
+
bugs = /* @__PURE__ */ new Map();
|
|
13789
|
+
plans = /* @__PURE__ */ new Map();
|
|
13790
|
+
evaluations = /* @__PURE__ */ new Map();
|
|
13791
|
+
disposers = new Array();
|
|
13792
|
+
settled = false;
|
|
13793
|
+
timeoutMs;
|
|
13794
|
+
cancelled = false;
|
|
13795
|
+
alerts = [];
|
|
13796
|
+
/** Tracks tool call counts per subagent for progress-based timeout decisions. */
|
|
13797
|
+
progressBySubagent = /* @__PURE__ */ new Map();
|
|
13798
|
+
/** Last tool call count when a timeout warning was handled. */
|
|
13799
|
+
lastTimeoutProgress = /* @__PURE__ */ new Map();
|
|
13800
|
+
/** Session-level timeout timer handle (cleared on cancel or natural completion). */
|
|
13801
|
+
_timeoutTimer;
|
|
13802
|
+
constructor(director, fleetBus, options) {
|
|
13803
|
+
super();
|
|
13804
|
+
this.sessionId = randomUUID();
|
|
13805
|
+
this.options = options;
|
|
13806
|
+
this.director = director;
|
|
13807
|
+
this.fleetBus = fleetBus;
|
|
13808
|
+
this.timeoutMs = options.timeoutMs ?? 10 * 60 * 1e3;
|
|
13809
|
+
if (options.prebuiltSnapshot) {
|
|
13810
|
+
this.snapshot = options.prebuiltSnapshot;
|
|
13811
|
+
} else {
|
|
13812
|
+
this.snapshot = {
|
|
13813
|
+
id: this.sessionId,
|
|
13814
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13815
|
+
files: []
|
|
13816
|
+
};
|
|
13817
|
+
}
|
|
13818
|
+
}
|
|
13819
|
+
get id() {
|
|
13820
|
+
return this.sessionId;
|
|
13821
|
+
}
|
|
13822
|
+
getSessionAlerts() {
|
|
13823
|
+
return [...this.alerts];
|
|
13824
|
+
}
|
|
13825
|
+
isCancelled() {
|
|
13826
|
+
return this.cancelled;
|
|
13827
|
+
}
|
|
13695
13828
|
/**
|
|
13696
|
-
*
|
|
13697
|
-
*
|
|
13698
|
-
*
|
|
13829
|
+
* Snapshot of role → subagentId map. The Director calls coordinator.stop()
|
|
13830
|
+
* for each agent when cancelling the session, using this map to enumerate
|
|
13831
|
+
* all three collab agents.
|
|
13699
13832
|
*/
|
|
13700
|
-
|
|
13701
|
-
|
|
13702
|
-
constructor(sizeThreshold = 2e3) {
|
|
13703
|
-
this.sizeThreshold = sizeThreshold;
|
|
13833
|
+
getSubagentIds() {
|
|
13834
|
+
return new Map(this.subagentIds);
|
|
13704
13835
|
}
|
|
13705
13836
|
/**
|
|
13706
|
-
*
|
|
13707
|
-
*
|
|
13837
|
+
* Returns the effective file limit for this session.
|
|
13838
|
+
* Priority: explicit `maxTargetFiles` > dynamic from `contextWindow` > `DEFAULT_MAX_TARGET_FILES`.
|
|
13708
13839
|
*/
|
|
13709
|
-
|
|
13710
|
-
if (
|
|
13711
|
-
return
|
|
13840
|
+
effectiveFileLimit() {
|
|
13841
|
+
if (this.options.maxTargetFiles !== void 0) {
|
|
13842
|
+
return this.options.maxTargetFiles;
|
|
13712
13843
|
}
|
|
13713
|
-
|
|
13714
|
-
|
|
13715
|
-
if (size <= this.sizeThreshold) {
|
|
13716
|
-
return { summary: serialized.slice(0, 500), inline: true };
|
|
13844
|
+
if (this.options.contextWindow !== void 0) {
|
|
13845
|
+
return Math.max(5, Math.floor(this.options.contextWindow * 0.4 / 2e3));
|
|
13717
13846
|
}
|
|
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
|
-
};
|
|
13847
|
+
return DEFAULT_MAX_TARGET_FILES;
|
|
13730
13848
|
}
|
|
13731
|
-
|
|
13732
|
-
|
|
13733
|
-
|
|
13734
|
-
|
|
13735
|
-
|
|
13736
|
-
|
|
13849
|
+
async buildSnapshot() {
|
|
13850
|
+
if (this.snapshot.files.length > 0) return this.snapshot;
|
|
13851
|
+
const allFiles = [];
|
|
13852
|
+
for (const pattern of this.options.targetPaths) {
|
|
13853
|
+
const expanded = await expandGlob(pattern);
|
|
13854
|
+
allFiles.push(...expanded);
|
|
13855
|
+
}
|
|
13856
|
+
const limit = this.effectiveFileLimit();
|
|
13857
|
+
if (allFiles.length > limit) {
|
|
13858
|
+
const hint = this.options.contextWindow ? `contextWindow=${this.options.contextWindow} \u2192 calculated limit=${limit}` : `default limit=${DEFAULT_MAX_TARGET_FILES}`;
|
|
13859
|
+
throw new Error(
|
|
13860
|
+
`[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.`
|
|
13861
|
+
);
|
|
13862
|
+
}
|
|
13863
|
+
for (const filePath of allFiles) {
|
|
13864
|
+
try {
|
|
13865
|
+
const content = await fsp3.readFile(filePath, "utf8");
|
|
13866
|
+
const ext = filePath.split(".").pop() ?? "";
|
|
13867
|
+
const language = ext === "ts" || ext === "tsx" ? "typescript" : ext === "js" || ext === "jsx" ? "javascript" : ext === "md" ? "markdown" : ext === "json" ? "json" : void 0;
|
|
13868
|
+
this.snapshot.files.push({ path: filePath, content, language });
|
|
13869
|
+
} catch {
|
|
13870
|
+
this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
|
|
13871
|
+
}
|
|
13872
|
+
}
|
|
13873
|
+
return this.snapshot;
|
|
13737
13874
|
}
|
|
13738
13875
|
/**
|
|
13739
|
-
*
|
|
13876
|
+
* Cancel the session. Emits director.cancel_collab on the FleetBus so all
|
|
13877
|
+
* collab agents finish early. The session-level timeout timer is also cleared.
|
|
13878
|
+
* Safe to call multiple times (idempotent after first call).
|
|
13740
13879
|
*/
|
|
13741
|
-
|
|
13742
|
-
|
|
13743
|
-
|
|
13744
|
-
|
|
13745
|
-
|
|
13746
|
-
|
|
13747
|
-
|
|
13748
|
-
|
|
13749
|
-
|
|
13750
|
-
|
|
13751
|
-
|
|
13752
|
-
|
|
13880
|
+
cancel(reason = "Director cancelled collab session") {
|
|
13881
|
+
if (this.settled) return;
|
|
13882
|
+
this.cancelled = true;
|
|
13883
|
+
if (this._timeoutTimer) {
|
|
13884
|
+
clearTimeout(this._timeoutTimer);
|
|
13885
|
+
this._timeoutTimer = void 0;
|
|
13886
|
+
}
|
|
13887
|
+
this.fleetBus.emit({
|
|
13888
|
+
subagentId: this.director.id,
|
|
13889
|
+
ts: Date.now(),
|
|
13890
|
+
type: "director.cancel_collab",
|
|
13891
|
+
payload: { sessionId: this.sessionId, reason, cancelledAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
13892
|
+
});
|
|
13893
|
+
this.fleetBus.emit({
|
|
13894
|
+
subagentId: this.director.id,
|
|
13895
|
+
ts: Date.now(),
|
|
13896
|
+
type: "collab.cancelled",
|
|
13897
|
+
payload: { sessionId: this.sessionId, reason }
|
|
13898
|
+
});
|
|
13753
13899
|
}
|
|
13754
|
-
|
|
13755
|
-
|
|
13756
|
-
this.
|
|
13900
|
+
async start() {
|
|
13901
|
+
if (this.settled) throw new Error("session already settled");
|
|
13902
|
+
this.settled = true;
|
|
13903
|
+
await this.buildSnapshot();
|
|
13904
|
+
this.wireFleetBus();
|
|
13905
|
+
const [bugHunterId, refactorPlannerId, criticId] = await Promise.all([
|
|
13906
|
+
this.spawnAgent("bug-hunter", this.buildBugHunterTask()),
|
|
13907
|
+
this.spawnAgent("refactor-planner", this.buildRefactorPlannerTask()),
|
|
13908
|
+
this.spawnAgent("critic", this.buildCriticTask())
|
|
13909
|
+
]);
|
|
13910
|
+
this.subagentIds.set("bug-hunter", bugHunterId);
|
|
13911
|
+
this.subagentIds.set("refactor-planner", refactorPlannerId);
|
|
13912
|
+
this.subagentIds.set("critic", criticId);
|
|
13913
|
+
const timeout = new Promise((_, reject) => {
|
|
13914
|
+
this._timeoutTimer = setTimeout(() => {
|
|
13915
|
+
this.cancel("Session-level timeout reached");
|
|
13916
|
+
reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`));
|
|
13917
|
+
}, this.timeoutMs);
|
|
13918
|
+
});
|
|
13919
|
+
let results = null;
|
|
13920
|
+
try {
|
|
13921
|
+
results = await Promise.race([
|
|
13922
|
+
Promise.all([
|
|
13923
|
+
this.director.awaitTasks([bugHunterId]),
|
|
13924
|
+
this.director.awaitTasks([refactorPlannerId]),
|
|
13925
|
+
this.director.awaitTasks([criticId])
|
|
13926
|
+
]),
|
|
13927
|
+
timeout
|
|
13928
|
+
]);
|
|
13929
|
+
} catch (err) {
|
|
13930
|
+
if (this._timeoutTimer) {
|
|
13931
|
+
clearTimeout(this._timeoutTimer);
|
|
13932
|
+
this._timeoutTimer = void 0;
|
|
13933
|
+
}
|
|
13934
|
+
this.cleanup();
|
|
13935
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
13936
|
+
this.emit("session.error", error);
|
|
13937
|
+
throw error;
|
|
13938
|
+
}
|
|
13939
|
+
for (const result of results.flat()) {
|
|
13940
|
+
await this.parseAndEmit(result);
|
|
13941
|
+
}
|
|
13942
|
+
const report = this.assembleReport();
|
|
13943
|
+
this.cleanup();
|
|
13944
|
+
this.emit("session.done", report);
|
|
13945
|
+
return report;
|
|
13757
13946
|
}
|
|
13758
|
-
|
|
13759
|
-
|
|
13760
|
-
|
|
13761
|
-
|
|
13762
|
-
|
|
13947
|
+
async parseAndEmit(result) {
|
|
13948
|
+
if (result.status !== "success" || result.result == null) return;
|
|
13949
|
+
const text = typeof result.result === "string" ? result.result : JSON.stringify(result.result);
|
|
13950
|
+
for (const obj of this.extractJsonObjects(text)) {
|
|
13951
|
+
const type = "finding" in obj ? "bug.found" : "plan" in obj ? "refactor.plan" : "evaluation" in obj ? "critic.evaluation" : null;
|
|
13952
|
+
if (!type) continue;
|
|
13953
|
+
this.fleetBus.emit({
|
|
13954
|
+
subagentId: result.subagentId,
|
|
13955
|
+
taskId: result.taskId,
|
|
13956
|
+
ts: Date.now(),
|
|
13957
|
+
type,
|
|
13958
|
+
payload: obj
|
|
13959
|
+
});
|
|
13960
|
+
}
|
|
13763
13961
|
}
|
|
13764
|
-
|
|
13765
|
-
|
|
13962
|
+
extractJsonObjects(text) {
|
|
13963
|
+
const objects = [];
|
|
13964
|
+
let depth = 0;
|
|
13965
|
+
let start = -1;
|
|
13966
|
+
let inString = false;
|
|
13967
|
+
let escaped = false;
|
|
13968
|
+
for (let i = 0; i < text.length; i++) {
|
|
13969
|
+
const ch = text[i];
|
|
13970
|
+
if (inString) {
|
|
13971
|
+
if (escaped) escaped = false;
|
|
13972
|
+
else if (ch === "\\") escaped = true;
|
|
13973
|
+
else if (ch === '"') inString = false;
|
|
13974
|
+
continue;
|
|
13975
|
+
}
|
|
13976
|
+
if (ch === '"') {
|
|
13977
|
+
inString = true;
|
|
13978
|
+
} else if (ch === "{") {
|
|
13979
|
+
if (depth === 0) start = i;
|
|
13980
|
+
depth++;
|
|
13981
|
+
} else if (ch === "}" && depth > 0) {
|
|
13982
|
+
depth--;
|
|
13983
|
+
if (depth === 0 && start >= 0) {
|
|
13984
|
+
const candidate = text.slice(start, i + 1);
|
|
13985
|
+
try {
|
|
13986
|
+
const parsed = JSON.parse(candidate);
|
|
13987
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
13988
|
+
objects.push(parsed);
|
|
13989
|
+
}
|
|
13990
|
+
} catch {
|
|
13991
|
+
}
|
|
13992
|
+
start = -1;
|
|
13993
|
+
}
|
|
13994
|
+
}
|
|
13995
|
+
}
|
|
13996
|
+
return objects;
|
|
13997
|
+
}
|
|
13998
|
+
budgetForRole(role) {
|
|
13999
|
+
if (this.options.budgetOverrides?.[role]) {
|
|
14000
|
+
return this.options.budgetOverrides[role];
|
|
14001
|
+
}
|
|
14002
|
+
const defaults = {
|
|
14003
|
+
"bug-hunter": { maxIterations: 2e3, maxToolCalls: 5e3, timeoutMs: 10 * 60 * 1e3 },
|
|
14004
|
+
"refactor-planner": { maxIterations: 1500, maxToolCalls: 4e3, timeoutMs: 8 * 60 * 1e3 },
|
|
14005
|
+
"critic": { maxIterations: 1e3, maxToolCalls: 3e3, timeoutMs: 6 * 60 * 1e3 }
|
|
14006
|
+
};
|
|
14007
|
+
return defaults[role] ?? { maxIterations: 1500, maxToolCalls: 4e3, timeoutMs: 8 * 60 * 1e3 };
|
|
14008
|
+
}
|
|
14009
|
+
async spawnAgent(role, taskBrief) {
|
|
14010
|
+
const budget = this.budgetForRole(role);
|
|
14011
|
+
const cfg = {
|
|
14012
|
+
id: `${role}-${this.sessionId}`,
|
|
14013
|
+
name: role,
|
|
14014
|
+
role,
|
|
14015
|
+
tools: ["fleet_emit", "fleet_status", "read", "grep", "glob", "bash", "write"],
|
|
14016
|
+
maxIterations: budget.maxIterations,
|
|
14017
|
+
maxToolCalls: budget.maxToolCalls,
|
|
14018
|
+
timeoutMs: budget.timeoutMs
|
|
14019
|
+
};
|
|
14020
|
+
const subagentId = await this.director.spawn(cfg);
|
|
14021
|
+
await this.director.assign({ id: randomUUID(), subagentId, description: taskBrief });
|
|
14022
|
+
return subagentId;
|
|
14023
|
+
}
|
|
14024
|
+
buildBugHunterTask() {
|
|
14025
|
+
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
14026
|
+
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
14027
|
+
${f.content}`).join("\n\n");
|
|
14028
|
+
return `You are BugHunter. Scan the following files for bugs and code smells.
|
|
13766
14029
|
|
|
13767
|
-
|
|
13768
|
-
|
|
13769
|
-
subagents by spawning them, assigning tasks, awaiting completions, and
|
|
13770
|
-
rolling up their outputs into your next decision.
|
|
14030
|
+
Target files:
|
|
14031
|
+
${fileContents}
|
|
13771
14032
|
|
|
13772
|
-
|
|
13773
|
-
|
|
13774
|
-
- assign_task \u2014 hand a piece of work to a specific subagent
|
|
13775
|
-
- await_tasks \u2014 block until named task ids complete (parallel-safe)
|
|
13776
|
-
- ask_subagent \u2014 synchronously query a running subagent via the bridge
|
|
13777
|
-
- roll_up \u2014 aggregate finished tasks into a markdown/json summary
|
|
13778
|
-
- terminate_subagent \u2014 abort a stuck worker (use sparingly)
|
|
13779
|
-
- fleet_status \u2014 snapshot of all subagents and pending tasks
|
|
13780
|
-
- fleet_usage \u2014 token + cost breakdown per subagent and total
|
|
14033
|
+
For each bug found, emit it using the fleet_emit tool immediately:
|
|
14034
|
+
{ "type": "bug.found", "payload": { "finding": { "id": "<uuid>", "type": "<pattern>", "severity": "<critical|high|medium|low>", "location": { "file": "<path>", "line": <n> }, "description": "<explain>", "suggestedFix": "<optional>" } } }
|
|
13781
14035
|
|
|
13782
|
-
|
|
13783
|
-
|
|
13784
|
-
independent and can run in parallel. Sequential work doesn't need a
|
|
13785
|
-
subagent \u2014 do it yourself.
|
|
13786
|
-
2. Match worker to job. Cheap/fast model for triage, capable model for
|
|
13787
|
-
synthesis. Different providers per sibling is allowed and encouraged.
|
|
13788
|
-
3. Always pair an assign with an await. Don't fire-and-forget; you owe
|
|
13789
|
-
the user a single coherent answer at the end.
|
|
13790
|
-
4. Roll up before deciding. After await_tasks resolves, call roll_up so
|
|
13791
|
-
the results are folded back into your context in a compact form.
|
|
13792
|
-
5. Budget is real. Check fleet_usage periodically. If a subagent is
|
|
13793
|
-
thrashing, terminate it rather than letting cost climb silently.
|
|
13794
|
-
6. Never claim a subagent's work as your own without verifying it. If a
|
|
13795
|
-
result looks wrong, ask_subagent for clarification before passing it
|
|
13796
|
-
to the user.
|
|
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.
|
|
14036
|
+
After scanning all files, write your full markdown bug report to:
|
|
14037
|
+
${scratchpad}/bug-hunter-report-${this.sessionId}.md
|
|
13803
14038
|
|
|
13804
|
-
|
|
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());
|
|
14039
|
+
Important: emit each finding as soon as you find it. Do not batch or wait until the end.`;
|
|
13849
14040
|
}
|
|
13850
|
-
|
|
13851
|
-
|
|
13852
|
-
|
|
13853
|
-
|
|
13854
|
-
|
|
13855
|
-
|
|
13856
|
-
|
|
13857
|
-
|
|
13858
|
-
|
|
14041
|
+
buildRefactorPlannerTask() {
|
|
14042
|
+
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
14043
|
+
const bugHunterReportPath = `${scratchpad}/bug-hunter-report-${this.sessionId}.md`;
|
|
14044
|
+
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
14045
|
+
${f.content}`).join("\n\n");
|
|
14046
|
+
return `You are RefactorPlanner. Plan refactorings for the following files.
|
|
14047
|
+
|
|
14048
|
+
Target files:
|
|
14049
|
+
${fileContents}
|
|
14050
|
+
|
|
14051
|
+
Read the BugHunter report at: ${bugHunterReportPath}
|
|
14052
|
+
|
|
14053
|
+
For each bug you can address, emit a refactor plan using fleet_emit:
|
|
14054
|
+
{ "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>" } } }
|
|
14055
|
+
|
|
14056
|
+
Also write your full markdown plan to:
|
|
14057
|
+
${scratchpad}/refactor-plan-${this.sessionId}.md
|
|
14058
|
+
|
|
14059
|
+
Emit each plan immediately. Do not wait until planning is complete.`;
|
|
13859
14060
|
}
|
|
13860
|
-
|
|
13861
|
-
|
|
14061
|
+
buildCriticTask() {
|
|
14062
|
+
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
14063
|
+
const bugHunterReportPath = `${scratchpad}/bug-hunter-report-${this.sessionId}.md`;
|
|
14064
|
+
const refactorPlanPath = `${scratchpad}/refactor-plan-${this.sessionId}.md`;
|
|
14065
|
+
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
14066
|
+
${f.content}`).join("\n\n");
|
|
14067
|
+
return `You are Critic. Evaluate bug findings and refactor plans.
|
|
13862
14068
|
|
|
13863
|
-
|
|
13864
|
-
|
|
13865
|
-
|
|
13866
|
-
|
|
13867
|
-
|
|
13868
|
-
|
|
13869
|
-
|
|
13870
|
-
|
|
13871
|
-
|
|
13872
|
-
|
|
13873
|
-
|
|
13874
|
-
|
|
13875
|
-
|
|
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
|
-
};
|
|
14069
|
+
Target files:
|
|
14070
|
+
${fileContents}
|
|
14071
|
+
|
|
14072
|
+
Read the BugHunter report at: ${bugHunterReportPath}
|
|
14073
|
+
Read the RefactorPlanner report at: ${refactorPlanPath}
|
|
14074
|
+
|
|
14075
|
+
For each bug and refactor plan, emit your evaluation using fleet_emit:
|
|
14076
|
+
{ "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>" }] } } }
|
|
14077
|
+
|
|
14078
|
+
After all evaluations, write your markdown report to:
|
|
14079
|
+
${scratchpad}/critic-report-${this.sessionId}.md
|
|
14080
|
+
|
|
14081
|
+
Emit each evaluation immediately. Do not wait until you have read all reports.`;
|
|
13886
14082
|
}
|
|
13887
|
-
|
|
13888
|
-
|
|
13889
|
-
|
|
13890
|
-
|
|
13891
|
-
|
|
13892
|
-
|
|
13893
|
-
|
|
13894
|
-
|
|
13895
|
-
|
|
13896
|
-
|
|
13897
|
-
|
|
14083
|
+
wireFleetBus() {
|
|
14084
|
+
const dTool = this.fleetBus.filter("tool.executed", (e) => {
|
|
14085
|
+
this.progressBySubagent.set(e.subagentId, (this.progressBySubagent.get(e.subagentId) ?? 0) + 1);
|
|
14086
|
+
});
|
|
14087
|
+
this.disposers.push(dTool);
|
|
14088
|
+
const dBudget = this.fleetBus.filter("budget.threshold_reached", (e) => {
|
|
14089
|
+
const payload = e.payload;
|
|
14090
|
+
const role = this.roleFromSubagentId(e.subagentId);
|
|
14091
|
+
if (!role) return;
|
|
14092
|
+
const btwNotes = this.director.getLeaderBtwNotes();
|
|
14093
|
+
const alert = {
|
|
14094
|
+
sessionId: this.sessionId,
|
|
14095
|
+
subagentId: e.subagentId,
|
|
14096
|
+
role,
|
|
14097
|
+
level: "warning" /* WARNING */,
|
|
14098
|
+
message: `${role} hit ${payload.kind} soft limit (${payload.used}/${payload.limit})`,
|
|
14099
|
+
budgetKind: payload.kind,
|
|
14100
|
+
elapsedMs: payload.timeoutMs,
|
|
14101
|
+
limit: payload.limit,
|
|
14102
|
+
btwNotes
|
|
14103
|
+
};
|
|
14104
|
+
this.alerts.push(alert);
|
|
14105
|
+
this.fleetBus.emit({
|
|
14106
|
+
subagentId: e.subagentId,
|
|
14107
|
+
ts: Date.now(),
|
|
14108
|
+
type: "collab.warning",
|
|
14109
|
+
payload: alert
|
|
14110
|
+
});
|
|
14111
|
+
const decision = this.options.onBudgetWarning?.(alert) ?? "ignore";
|
|
14112
|
+
if (decision === "cancel") {
|
|
14113
|
+
this.cancel(`Director cancelled: ${role} ${payload.kind} threshold`);
|
|
14114
|
+
return;
|
|
14115
|
+
}
|
|
14116
|
+
if (payload.kind === "timeout" || payload.kind === "idle_timeout") {
|
|
14117
|
+
const progress = this.progressBySubagent.get(e.subagentId) ?? 0;
|
|
14118
|
+
const lastProgress = this.lastTimeoutProgress.get(e.subagentId) ?? -1;
|
|
14119
|
+
if (progress <= lastProgress) {
|
|
14120
|
+
payload.deny();
|
|
14121
|
+
return;
|
|
14122
|
+
}
|
|
14123
|
+
this.lastTimeoutProgress.set(e.subagentId, progress);
|
|
14124
|
+
const newLimit = Math.min(Math.ceil((payload.timeoutMs ?? payload.limit) * 2), 24 * 60 * 6e4);
|
|
14125
|
+
setImmediate(() => {
|
|
14126
|
+
payload.extend({ timeoutMs: newLimit });
|
|
14127
|
+
});
|
|
14128
|
+
return;
|
|
14129
|
+
}
|
|
14130
|
+
if (decision === "extend") {
|
|
14131
|
+
setImmediate(() => {
|
|
14132
|
+
const base = Math.max(payload.limit, payload.used);
|
|
14133
|
+
const extra = {};
|
|
14134
|
+
switch (payload.kind) {
|
|
14135
|
+
case "iterations":
|
|
14136
|
+
extra.maxIterations = Math.min(Math.ceil(base * 1.5), 5e4);
|
|
14137
|
+
break;
|
|
14138
|
+
case "tool_calls":
|
|
14139
|
+
extra.maxToolCalls = Math.min(Math.ceil(base * 1.5), 1e5);
|
|
14140
|
+
break;
|
|
14141
|
+
case "tokens":
|
|
14142
|
+
extra.maxTokens = Math.min(Math.ceil(base * 1.5), 5e6);
|
|
14143
|
+
break;
|
|
14144
|
+
case "cost":
|
|
14145
|
+
extra.maxCostUsd = Math.min(base * 1.5, 100);
|
|
14146
|
+
break;
|
|
14147
|
+
}
|
|
14148
|
+
payload.extend(extra);
|
|
14149
|
+
});
|
|
14150
|
+
return;
|
|
14151
|
+
}
|
|
14152
|
+
if (payload.kind !== "timeout") {
|
|
14153
|
+
setImmediate(() => {
|
|
14154
|
+
const base = Math.max(payload.limit, payload.used);
|
|
14155
|
+
const extra = {};
|
|
14156
|
+
switch (payload.kind) {
|
|
14157
|
+
case "iterations":
|
|
14158
|
+
extra.maxIterations = Math.min(Math.ceil(base * 1.25), 5e4);
|
|
14159
|
+
break;
|
|
14160
|
+
case "tool_calls":
|
|
14161
|
+
extra.maxToolCalls = Math.min(Math.ceil(base * 1.25), 1e5);
|
|
14162
|
+
break;
|
|
14163
|
+
case "tokens":
|
|
14164
|
+
extra.maxTokens = Math.min(Math.ceil(base * 1.25), 5e6);
|
|
14165
|
+
break;
|
|
14166
|
+
case "cost":
|
|
14167
|
+
extra.maxCostUsd = Math.min(base * 1.25, 100);
|
|
14168
|
+
break;
|
|
14169
|
+
}
|
|
14170
|
+
payload.extend(extra);
|
|
14171
|
+
});
|
|
14172
|
+
}
|
|
14173
|
+
});
|
|
14174
|
+
this.disposers.push(dBudget);
|
|
14175
|
+
const dCancel = this.fleetBus.filter("director.cancel_collab", (e) => {
|
|
14176
|
+
const payload = e.payload;
|
|
14177
|
+
if (payload.sessionId !== this.sessionId) return;
|
|
14178
|
+
this.cancelled = true;
|
|
14179
|
+
if (this._timeoutTimer) {
|
|
14180
|
+
clearTimeout(this._timeoutTimer);
|
|
14181
|
+
this._timeoutTimer = void 0;
|
|
14182
|
+
}
|
|
14183
|
+
this.fleetBus.emit({
|
|
14184
|
+
subagentId: this.director.id,
|
|
14185
|
+
ts: Date.now(),
|
|
14186
|
+
type: "collab.cancelled",
|
|
14187
|
+
payload: { sessionId: this.sessionId, reason: payload.reason }
|
|
14188
|
+
});
|
|
14189
|
+
});
|
|
14190
|
+
this.disposers.push(dCancel);
|
|
14191
|
+
const d1 = this.fleetBus.filter("bug.found", (e) => {
|
|
14192
|
+
const payload = e.payload;
|
|
14193
|
+
if (payload?.finding) {
|
|
14194
|
+
this.bugs.set(payload.finding.id, payload.finding);
|
|
14195
|
+
this.emit("bug.found", payload);
|
|
14196
|
+
}
|
|
14197
|
+
});
|
|
14198
|
+
this.disposers.push(d1);
|
|
14199
|
+
const d2 = this.fleetBus.filter("refactor.plan", (e) => {
|
|
14200
|
+
const payload = e.payload;
|
|
14201
|
+
if (payload?.plan) {
|
|
14202
|
+
this.plans.set(payload.plan.id, payload.plan);
|
|
14203
|
+
this.emit("refactor.plan", payload);
|
|
14204
|
+
}
|
|
14205
|
+
});
|
|
14206
|
+
this.disposers.push(d2);
|
|
14207
|
+
const d3 = this.fleetBus.filter("critic.evaluation", (e) => {
|
|
14208
|
+
const payload = e.payload;
|
|
14209
|
+
if (payload?.evaluation) {
|
|
14210
|
+
this.evaluations.set(payload.evaluation.id, payload.evaluation);
|
|
14211
|
+
this.emit("critic.evaluation", payload);
|
|
14212
|
+
}
|
|
14213
|
+
});
|
|
14214
|
+
this.disposers.push(d3);
|
|
13898
14215
|
}
|
|
13899
|
-
|
|
13900
|
-
|
|
13901
|
-
|
|
13902
|
-
if (!set) {
|
|
13903
|
-
set = /* @__PURE__ */ new Set();
|
|
13904
|
-
this.byType.set(type, set);
|
|
14216
|
+
roleFromSubagentId(subagentId) {
|
|
14217
|
+
for (const [role, id] of this.subagentIds) {
|
|
14218
|
+
if (id === subagentId) return role;
|
|
13905
14219
|
}
|
|
13906
|
-
|
|
13907
|
-
return
|
|
13908
|
-
set.delete(handler);
|
|
13909
|
-
};
|
|
14220
|
+
const match = subagentId.match(/^(bug-hunter|refactor-planner|critic)/);
|
|
14221
|
+
return match?.[1] ?? null;
|
|
13910
14222
|
}
|
|
13911
|
-
|
|
13912
|
-
|
|
13913
|
-
this.
|
|
13914
|
-
|
|
13915
|
-
|
|
14223
|
+
assembleReport() {
|
|
14224
|
+
const bugList = Array.from(this.bugs.values());
|
|
14225
|
+
const planList = Array.from(this.plans.values());
|
|
14226
|
+
const evalList = Array.from(this.evaluations.values());
|
|
14227
|
+
let disposition = "completed";
|
|
14228
|
+
if (this.cancelled) disposition = "cancelled";
|
|
14229
|
+
const verdictOrder = {
|
|
14230
|
+
approve: 0,
|
|
14231
|
+
needs_revision: 1,
|
|
14232
|
+
reject: 2
|
|
14233
|
+
};
|
|
14234
|
+
const overallVerdict = evalList.reduce(
|
|
14235
|
+
(worst, eval_) => {
|
|
14236
|
+
const w = verdictOrder[worst];
|
|
14237
|
+
const c = verdictOrder[eval_.verdict];
|
|
14238
|
+
return c > w ? eval_.verdict : worst;
|
|
14239
|
+
},
|
|
14240
|
+
"approve"
|
|
14241
|
+
);
|
|
14242
|
+
const summary = this.buildMarkdownSummary(bugList, planList, evalList, overallVerdict, disposition);
|
|
14243
|
+
return {
|
|
14244
|
+
sessionId: this.sessionId,
|
|
14245
|
+
startedAt: this.snapshot.createdAt,
|
|
14246
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14247
|
+
targetPaths: this.options.targetPaths,
|
|
14248
|
+
disposition,
|
|
14249
|
+
bugs: bugList,
|
|
14250
|
+
refactorPlans: planList,
|
|
14251
|
+
evaluations: evalList,
|
|
14252
|
+
alerts: [...this.alerts],
|
|
14253
|
+
overallVerdict,
|
|
14254
|
+
summary
|
|
13916
14255
|
};
|
|
13917
14256
|
}
|
|
13918
|
-
|
|
13919
|
-
const
|
|
13920
|
-
|
|
13921
|
-
|
|
13922
|
-
|
|
13923
|
-
|
|
13924
|
-
|
|
13925
|
-
|
|
14257
|
+
buildMarkdownSummary(bugs, plans, evals, overallVerdict, disposition) {
|
|
14258
|
+
const lines = [
|
|
14259
|
+
`## Collaborative Debugging Report \u2014 ${this.sessionId}`,
|
|
14260
|
+
"",
|
|
14261
|
+
`**Target:** ${this.options.targetPaths.join(", ")}`,
|
|
14262
|
+
`**Disposition:** ${disposition.toUpperCase()}`,
|
|
14263
|
+
`**Overall Verdict:** **${overallVerdict.toUpperCase()}**`,
|
|
14264
|
+
""
|
|
14265
|
+
];
|
|
14266
|
+
if (this.alerts.length > 0) {
|
|
14267
|
+
lines.push("### Alerts", "");
|
|
14268
|
+
for (const alert of this.alerts) {
|
|
14269
|
+
lines.push(`- **[${alert.level.toUpperCase()}]** ${alert.role}: ${alert.message}`);
|
|
13926
14270
|
}
|
|
13927
|
-
|
|
13928
|
-
|
|
13929
|
-
|
|
13930
|
-
|
|
13931
|
-
|
|
13932
|
-
}
|
|
14271
|
+
lines.push("");
|
|
14272
|
+
}
|
|
14273
|
+
if (bugs.length > 0) {
|
|
14274
|
+
lines.push("### Bugs Found", "");
|
|
14275
|
+
for (const b of bugs) {
|
|
14276
|
+
lines.push(`- **[${b.severity.toUpperCase()}]** \`${b.location.file}:${b.location.line}\` \u2014 ${b.description}`);
|
|
14277
|
+
}
|
|
14278
|
+
lines.push("");
|
|
14279
|
+
}
|
|
14280
|
+
if (plans.length > 0) {
|
|
14281
|
+
lines.push("### Refactor Plans", "");
|
|
14282
|
+
for (const p of plans) {
|
|
14283
|
+
lines.push(`- **Phase plan** (risk: ${p.riskScore}, ~${p.estimatedChangeCount} changes)`);
|
|
14284
|
+
for (const phase of p.phases) {
|
|
14285
|
+
lines.push(` - Phase ${phase.number}: ${phase.title} [${phase.risk}]`);
|
|
13933
14286
|
}
|
|
13934
14287
|
}
|
|
13935
|
-
|
|
13936
|
-
|
|
13937
|
-
|
|
13938
|
-
|
|
14288
|
+
lines.push("");
|
|
14289
|
+
}
|
|
14290
|
+
if (evals.length > 0) {
|
|
14291
|
+
lines.push("### Critic Evaluations", "");
|
|
14292
|
+
for (const e of evals) {
|
|
14293
|
+
lines.push(`- [${e.subjectType}] score=${e.score}/10 \u2014 **${e.verdict.toUpperCase()}**`);
|
|
14294
|
+
for (const c of e.concerns) {
|
|
14295
|
+
if (c.severity === "blocking") lines.push(` - ${c.description}`);
|
|
14296
|
+
}
|
|
13939
14297
|
}
|
|
14298
|
+
lines.push("");
|
|
13940
14299
|
}
|
|
14300
|
+
return lines.join("\n");
|
|
13941
14301
|
}
|
|
13942
|
-
|
|
13943
|
-
|
|
13944
|
-
|
|
13945
|
-
this.priceLookup = priceLookup;
|
|
13946
|
-
this.metaLookup = metaLookup;
|
|
13947
|
-
this.unsub.push(bus.filter("provider.response", (e) => this.onProviderResponse(e)));
|
|
13948
|
-
this.unsub.push(bus.filter("tool.executed", (e) => this.onToolExecuted(e)));
|
|
13949
|
-
this.unsub.push(bus.filter("iteration.started", (e) => this.onIterationStarted(e)));
|
|
14302
|
+
cleanup() {
|
|
14303
|
+
for (const dispose of this.disposers) dispose();
|
|
14304
|
+
this.disposers.length = 0;
|
|
13950
14305
|
}
|
|
13951
|
-
|
|
13952
|
-
|
|
13953
|
-
|
|
13954
|
-
|
|
13955
|
-
|
|
13956
|
-
|
|
13957
|
-
|
|
13958
|
-
|
|
13959
|
-
|
|
13960
|
-
|
|
13961
|
-
|
|
13962
|
-
|
|
13963
|
-
|
|
13964
|
-
|
|
13965
|
-
|
|
13966
|
-
|
|
13967
|
-
|
|
13968
|
-
|
|
13969
|
-
|
|
13970
|
-
|
|
14306
|
+
};
|
|
14307
|
+
|
|
14308
|
+
// src/coordination/director-prompts.ts
|
|
14309
|
+
var DEFAULT_DIRECTOR_PREAMBLE = `You are the Director of a multi-agent fleet. You orchestrate worker
|
|
14310
|
+
subagents by spawning them, assigning tasks, awaiting completions, and
|
|
14311
|
+
rolling up their outputs into your next decision.
|
|
14312
|
+
|
|
14313
|
+
Core fleet tools available to you:
|
|
14314
|
+
- spawn_subagent \u2014 create a worker with a chosen provider / model / role
|
|
14315
|
+
- assign_task \u2014 hand a piece of work to a specific subagent
|
|
14316
|
+
- await_tasks \u2014 block until named task ids complete (parallel-safe)
|
|
14317
|
+
- ask_subagent \u2014 synchronously query a running subagent via the bridge
|
|
14318
|
+
- roll_up \u2014 aggregate finished tasks into a markdown/json summary
|
|
14319
|
+
- terminate_subagent \u2014 abort a stuck worker (use sparingly)
|
|
14320
|
+
- fleet_status \u2014 snapshot of all subagents and pending tasks
|
|
14321
|
+
- fleet_usage \u2014 token + cost breakdown per subagent and total
|
|
14322
|
+
|
|
14323
|
+
Working rules:
|
|
14324
|
+
1. Decompose first. Before spawning, decide which sub-tasks are
|
|
14325
|
+
independent and can run in parallel. Sequential work doesn't need a
|
|
14326
|
+
subagent \u2014 do it yourself.
|
|
14327
|
+
2. Match worker to job. Cheap/fast model for triage, capable model for
|
|
14328
|
+
synthesis. Different providers per sibling is allowed and encouraged.
|
|
14329
|
+
3. Always pair an assign with an await. Don't fire-and-forget; you owe
|
|
14330
|
+
the user a single coherent answer at the end.
|
|
14331
|
+
4. Roll up before deciding. After await_tasks resolves, call roll_up so
|
|
14332
|
+
the results are folded back into your context in a compact form.
|
|
14333
|
+
5. Budget is real. Check fleet_usage periodically. If a subagent is
|
|
14334
|
+
thrashing, terminate it rather than letting cost climb silently.
|
|
14335
|
+
6. Never claim a subagent's work as your own without verifying it. If a
|
|
14336
|
+
result looks wrong, ask_subagent for clarification before passing it
|
|
14337
|
+
to the user.
|
|
14338
|
+
7. Wind down when satisfied. When the results are good enough, call
|
|
14339
|
+
work_complete \u2014 no new subagents will spawn and queued tasks complete
|
|
14340
|
+
as aborted. Running subagents finish naturally. Call terminate_subagent
|
|
14341
|
+
only for ones you need to stop immediately.`;
|
|
14342
|
+
var DEFAULT_SUBAGENT_BASELINE = `You are a subagent operating under a Director. You were spawned to handle
|
|
14343
|
+
a specific slice of a larger plan \u2014 do that slice well and report back.
|
|
14344
|
+
|
|
14345
|
+
Bridge contract:
|
|
14346
|
+
- You have a parent (the Director). You may call \`request\` on the
|
|
14347
|
+
parent bridge to ask a clarifying question. Use this sparingly; the
|
|
14348
|
+
parent is also working.
|
|
14349
|
+
- You MAY NOT request the parent's system prompt, tool list, or other
|
|
14350
|
+
subagents' context. Those are not yours to read.
|
|
14351
|
+
- Your final task output is what the Director sees. Be concise,
|
|
14352
|
+
structured, and self-contained \u2014 assume the Director will paste your
|
|
14353
|
+
output into its own context.`;
|
|
14354
|
+
function composeDirectorPrompt(parts = {}) {
|
|
14355
|
+
const sections = [];
|
|
14356
|
+
const preamble = parts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
|
|
14357
|
+
if (preamble && preamble.trim().length > 0) sections.push(preamble.trim());
|
|
14358
|
+
if (parts.rosterSummary && parts.rosterSummary.trim().length > 0) {
|
|
14359
|
+
sections.push(`Available roles you can spawn:
|
|
14360
|
+
${parts.rosterSummary.trim()}`);
|
|
13971
14361
|
}
|
|
13972
|
-
|
|
13973
|
-
|
|
13974
|
-
for (const off of this.unsub) off();
|
|
13975
|
-
this.unsub.length = 0;
|
|
14362
|
+
if (parts.basePrompt && parts.basePrompt.trim().length > 0) {
|
|
14363
|
+
sections.push(parts.basePrompt.trim());
|
|
13976
14364
|
}
|
|
13977
|
-
|
|
13978
|
-
|
|
13979
|
-
|
|
13980
|
-
|
|
13981
|
-
|
|
13982
|
-
|
|
13983
|
-
|
|
13984
|
-
|
|
14365
|
+
return sections.join("\n\n");
|
|
14366
|
+
}
|
|
14367
|
+
function composeSubagentPrompt(parts = {}) {
|
|
14368
|
+
const sections = [];
|
|
14369
|
+
const baseline = parts.baseline ?? DEFAULT_SUBAGENT_BASELINE;
|
|
14370
|
+
if (baseline && baseline.trim().length > 0) sections.push(baseline.trim());
|
|
14371
|
+
if (parts.role && parts.role.trim().length > 0) {
|
|
14372
|
+
sections.push(`Role:
|
|
14373
|
+
${parts.role.trim()}`);
|
|
13985
14374
|
}
|
|
13986
|
-
|
|
13987
|
-
|
|
13988
|
-
|
|
13989
|
-
const meta = this.metaLookup?.(subagentId);
|
|
13990
|
-
snap = {
|
|
13991
|
-
subagentId,
|
|
13992
|
-
provider: meta?.provider,
|
|
13993
|
-
model: meta?.model,
|
|
13994
|
-
input: 0,
|
|
13995
|
-
output: 0,
|
|
13996
|
-
cacheRead: 0,
|
|
13997
|
-
cacheWrite: 0,
|
|
13998
|
-
cost: 0,
|
|
13999
|
-
toolCalls: 0,
|
|
14000
|
-
iterations: 0,
|
|
14001
|
-
startedAt: Date.now(),
|
|
14002
|
-
lastEventAt: Date.now()
|
|
14003
|
-
};
|
|
14004
|
-
this.perSubagent.set(subagentId, snap);
|
|
14005
|
-
}
|
|
14006
|
-
return snap;
|
|
14375
|
+
if (parts.task && parts.task.trim().length > 0) {
|
|
14376
|
+
sections.push(`Task:
|
|
14377
|
+
${parts.task.trim()}`);
|
|
14007
14378
|
}
|
|
14008
|
-
|
|
14009
|
-
|
|
14010
|
-
|
|
14011
|
-
|
|
14012
|
-
|
|
14013
|
-
|
|
14014
|
-
|
|
14015
|
-
|
|
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;
|
|
14026
|
-
}
|
|
14027
|
-
snap.lastEventAt = e.ts;
|
|
14379
|
+
if (parts.sharedScratchpad && parts.sharedScratchpad.trim().length > 0) {
|
|
14380
|
+
sections.push(
|
|
14381
|
+
`Shared notes:
|
|
14382
|
+
A scratchpad shared with the rest of the fleet is mounted at \`${parts.sharedScratchpad.trim()}\`.
|
|
14383
|
+
- Write your final findings as markdown files there (e.g. \`findings.md\`, \`security.md\`).
|
|
14384
|
+
- Before starting, list the directory and read any sibling files relevant to your task \u2014 they may already contain context you can build on.
|
|
14385
|
+
- Use stable filenames (one file per concern); overwrite instead of appending so the Director sees the latest state.`
|
|
14386
|
+
);
|
|
14028
14387
|
}
|
|
14029
|
-
|
|
14030
|
-
|
|
14031
|
-
snap.toolCalls += 1;
|
|
14032
|
-
snap.lastEventAt = e.ts;
|
|
14388
|
+
if (parts.override && parts.override.trim().length > 0) {
|
|
14389
|
+
sections.push(parts.override.trim());
|
|
14033
14390
|
}
|
|
14034
|
-
|
|
14035
|
-
|
|
14036
|
-
|
|
14037
|
-
|
|
14391
|
+
return sections.join("\n\n");
|
|
14392
|
+
}
|
|
14393
|
+
function rosterSummaryFromConfigs(roster) {
|
|
14394
|
+
const lines = [];
|
|
14395
|
+
for (const [roleId, cfg] of Object.entries(roster)) {
|
|
14396
|
+
const tag = cfg.provider && cfg.model ? ` (${cfg.provider}/${cfg.model})` : "";
|
|
14397
|
+
const headline = cfg.prompt ? (cfg.prompt.split("\n").find((l) => l.trim().length > 0) ?? "").trim().slice(0, 80) : "";
|
|
14398
|
+
const tail = headline ? ` \u2014 ${headline}` : "";
|
|
14399
|
+
lines.push(`- ${roleId}: ${cfg.name}${tag}${tail}`);
|
|
14038
14400
|
}
|
|
14039
|
-
|
|
14401
|
+
return lines.join("\n");
|
|
14402
|
+
}
|
|
14040
14403
|
function makeSpawnTool(director, roster) {
|
|
14041
14404
|
const inputSchema = {
|
|
14042
14405
|
type: "object",
|
|
@@ -14470,534 +14833,291 @@ function makeWorkCompleteTool(director) {
|
|
|
14470
14833
|
}
|
|
14471
14834
|
};
|
|
14472
14835
|
}
|
|
14473
|
-
|
|
14474
|
-
|
|
14475
|
-
|
|
14476
|
-
|
|
14477
|
-
|
|
14478
|
-
|
|
14479
|
-
fleetBus;
|
|
14480
|
-
subagentIds = /* @__PURE__ */ new Map();
|
|
14481
|
-
// role → subagentId
|
|
14482
|
-
bugs = /* @__PURE__ */ new Map();
|
|
14483
|
-
plans = /* @__PURE__ */ new Map();
|
|
14484
|
-
evaluations = /* @__PURE__ */ new Map();
|
|
14485
|
-
disposers = new Array();
|
|
14486
|
-
settled = false;
|
|
14487
|
-
timeoutMs;
|
|
14488
|
-
cancelled = false;
|
|
14489
|
-
alerts = [];
|
|
14490
|
-
/** Tracks tool call counts per subagent for progress-based timeout decisions. */
|
|
14491
|
-
progressBySubagent = /* @__PURE__ */ new Map();
|
|
14492
|
-
/** Last tool call count when a timeout warning was handled. */
|
|
14493
|
-
lastTimeoutProgress = /* @__PURE__ */ new Map();
|
|
14494
|
-
/** Session-level timeout timer handle (cleared on cancel or natural completion). */
|
|
14495
|
-
_timeoutTimer;
|
|
14496
|
-
constructor(director, fleetBus, options) {
|
|
14497
|
-
super();
|
|
14498
|
-
this.sessionId = randomUUID();
|
|
14499
|
-
this.options = options;
|
|
14500
|
-
this.director = director;
|
|
14501
|
-
this.fleetBus = fleetBus;
|
|
14502
|
-
this.timeoutMs = options.timeoutMs ?? 10 * 60 * 1e3;
|
|
14503
|
-
if (options.prebuiltSnapshot) {
|
|
14504
|
-
this.snapshot = options.prebuiltSnapshot;
|
|
14505
|
-
} else {
|
|
14506
|
-
this.snapshot = {
|
|
14507
|
-
id: this.sessionId,
|
|
14508
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14509
|
-
files: []
|
|
14510
|
-
};
|
|
14511
|
-
}
|
|
14512
|
-
}
|
|
14513
|
-
get id() {
|
|
14514
|
-
return this.sessionId;
|
|
14515
|
-
}
|
|
14516
|
-
getSessionAlerts() {
|
|
14517
|
-
return [...this.alerts];
|
|
14518
|
-
}
|
|
14519
|
-
isCancelled() {
|
|
14520
|
-
return this.cancelled;
|
|
14521
|
-
}
|
|
14522
|
-
/**
|
|
14523
|
-
* Snapshot of role → subagentId map. The Director calls coordinator.stop()
|
|
14524
|
-
* for each agent when cancelling the session, using this map to enumerate
|
|
14525
|
-
* all three collab agents.
|
|
14526
|
-
*/
|
|
14527
|
-
getSubagentIds() {
|
|
14528
|
-
return new Map(this.subagentIds);
|
|
14529
|
-
}
|
|
14836
|
+
|
|
14837
|
+
// src/coordination/fleet-bus.ts
|
|
14838
|
+
var FleetBus = class {
|
|
14839
|
+
byId = /* @__PURE__ */ new Map();
|
|
14840
|
+
byType = /* @__PURE__ */ new Map();
|
|
14841
|
+
any = /* @__PURE__ */ new Set();
|
|
14530
14842
|
/**
|
|
14531
|
-
*
|
|
14532
|
-
*
|
|
14843
|
+
* Hook a subagent's EventBus into the fleet. Uses `onAny()` (an alias for
|
|
14844
|
+
* `onPattern('*')`) to forward all events with subagent attribution, so
|
|
14845
|
+
* new kernel event types are automatically forwarded without any manual
|
|
14846
|
+
* registration. `subagent.*` events are excluded because they originate
|
|
14847
|
+
* from MultiAgentHost on the parent bus, not the subagent's own bus.
|
|
14848
|
+
*
|
|
14849
|
+
* Returns a disposer that detaches every subscription; call on
|
|
14850
|
+
* subagent teardown so the listeners don't outlive the run.
|
|
14533
14851
|
*/
|
|
14534
|
-
|
|
14535
|
-
|
|
14536
|
-
|
|
14537
|
-
|
|
14538
|
-
if (this.options.contextWindow !== void 0) {
|
|
14539
|
-
return Math.max(5, Math.floor(this.options.contextWindow * 0.4 / 2e3));
|
|
14540
|
-
}
|
|
14541
|
-
return DEFAULT_MAX_TARGET_FILES;
|
|
14542
|
-
}
|
|
14543
|
-
async buildSnapshot() {
|
|
14544
|
-
if (this.snapshot.files.length > 0) return this.snapshot;
|
|
14545
|
-
const allFiles = [];
|
|
14546
|
-
for (const pattern of this.options.targetPaths) {
|
|
14547
|
-
const expanded = await expandGlob(pattern);
|
|
14548
|
-
allFiles.push(...expanded);
|
|
14549
|
-
}
|
|
14550
|
-
const limit = this.effectiveFileLimit();
|
|
14551
|
-
if (allFiles.length > limit) {
|
|
14552
|
-
const hint = this.options.contextWindow ? `contextWindow=${this.options.contextWindow} \u2192 calculated limit=${limit}` : `default limit=${DEFAULT_MAX_TARGET_FILES}`;
|
|
14553
|
-
throw new Error(
|
|
14554
|
-
`[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.`
|
|
14555
|
-
);
|
|
14556
|
-
}
|
|
14557
|
-
for (const filePath of allFiles) {
|
|
14558
|
-
try {
|
|
14559
|
-
const content = await fsp3.readFile(filePath, "utf8");
|
|
14560
|
-
const ext = filePath.split(".").pop() ?? "";
|
|
14561
|
-
const language = ext === "ts" || ext === "tsx" ? "typescript" : ext === "js" || ext === "jsx" ? "javascript" : ext === "md" ? "markdown" : ext === "json" ? "json" : void 0;
|
|
14562
|
-
this.snapshot.files.push({ path: filePath, content, language });
|
|
14563
|
-
} catch {
|
|
14564
|
-
this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
|
|
14565
|
-
}
|
|
14566
|
-
}
|
|
14567
|
-
return this.snapshot;
|
|
14568
|
-
}
|
|
14569
|
-
/**
|
|
14570
|
-
* Cancel the session. Emits director.cancel_collab on the FleetBus so all
|
|
14571
|
-
* collab agents finish early. The session-level timeout timer is also cleared.
|
|
14572
|
-
* Safe to call multiple times (idempotent after first call).
|
|
14573
|
-
*/
|
|
14574
|
-
cancel(reason = "Director cancelled collab session") {
|
|
14575
|
-
if (this.settled) return;
|
|
14576
|
-
this.cancelled = true;
|
|
14577
|
-
if (this._timeoutTimer) {
|
|
14578
|
-
clearTimeout(this._timeoutTimer);
|
|
14579
|
-
this._timeoutTimer = void 0;
|
|
14580
|
-
}
|
|
14581
|
-
this.fleetBus.emit({
|
|
14582
|
-
subagentId: this.director.id,
|
|
14583
|
-
ts: Date.now(),
|
|
14584
|
-
type: "director.cancel_collab",
|
|
14585
|
-
payload: { sessionId: this.sessionId, reason, cancelledAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
14586
|
-
});
|
|
14587
|
-
this.fleetBus.emit({
|
|
14588
|
-
subagentId: this.director.id,
|
|
14589
|
-
ts: Date.now(),
|
|
14590
|
-
type: "collab.cancelled",
|
|
14591
|
-
payload: { sessionId: this.sessionId, reason }
|
|
14592
|
-
});
|
|
14593
|
-
}
|
|
14594
|
-
async start() {
|
|
14595
|
-
if (this.settled) throw new Error("session already settled");
|
|
14596
|
-
this.settled = true;
|
|
14597
|
-
await this.buildSnapshot();
|
|
14598
|
-
this.wireFleetBus();
|
|
14599
|
-
const [bugHunterId, refactorPlannerId, criticId] = await Promise.all([
|
|
14600
|
-
this.spawnAgent("bug-hunter", this.buildBugHunterTask()),
|
|
14601
|
-
this.spawnAgent("refactor-planner", this.buildRefactorPlannerTask()),
|
|
14602
|
-
this.spawnAgent("critic", this.buildCriticTask())
|
|
14603
|
-
]);
|
|
14604
|
-
this.subagentIds.set("bug-hunter", bugHunterId);
|
|
14605
|
-
this.subagentIds.set("refactor-planner", refactorPlannerId);
|
|
14606
|
-
this.subagentIds.set("critic", criticId);
|
|
14607
|
-
const timeout = new Promise((_, reject) => {
|
|
14608
|
-
this._timeoutTimer = setTimeout(() => {
|
|
14609
|
-
this.cancel("Session-level timeout reached");
|
|
14610
|
-
reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`));
|
|
14611
|
-
}, this.timeoutMs);
|
|
14612
|
-
});
|
|
14613
|
-
let results = null;
|
|
14614
|
-
try {
|
|
14615
|
-
results = await Promise.race([
|
|
14616
|
-
Promise.all([
|
|
14617
|
-
this.director.awaitTasks([bugHunterId]),
|
|
14618
|
-
this.director.awaitTasks([refactorPlannerId]),
|
|
14619
|
-
this.director.awaitTasks([criticId])
|
|
14620
|
-
]),
|
|
14621
|
-
timeout
|
|
14622
|
-
]);
|
|
14623
|
-
} catch (err) {
|
|
14624
|
-
if (this._timeoutTimer) {
|
|
14625
|
-
clearTimeout(this._timeoutTimer);
|
|
14626
|
-
this._timeoutTimer = void 0;
|
|
14627
|
-
}
|
|
14628
|
-
this.cleanup();
|
|
14629
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
14630
|
-
this.emit("session.error", error);
|
|
14631
|
-
throw error;
|
|
14632
|
-
}
|
|
14633
|
-
for (const result of results.flat()) {
|
|
14634
|
-
await this.parseAndEmit(result);
|
|
14635
|
-
}
|
|
14636
|
-
const report = this.assembleReport();
|
|
14637
|
-
this.cleanup();
|
|
14638
|
-
this.emit("session.done", report);
|
|
14639
|
-
return report;
|
|
14640
|
-
}
|
|
14641
|
-
async parseAndEmit(result) {
|
|
14642
|
-
if (result.status !== "success" || result.result == null) return;
|
|
14643
|
-
const text = typeof result.result === "string" ? result.result : JSON.stringify(result.result);
|
|
14644
|
-
for (const obj of this.extractJsonObjects(text)) {
|
|
14645
|
-
const type = "finding" in obj ? "bug.found" : "plan" in obj ? "refactor.plan" : "evaluation" in obj ? "critic.evaluation" : null;
|
|
14646
|
-
if (!type) continue;
|
|
14647
|
-
this.fleetBus.emit({
|
|
14648
|
-
subagentId: result.subagentId,
|
|
14649
|
-
taskId: result.taskId,
|
|
14650
|
-
ts: Date.now(),
|
|
14651
|
-
type,
|
|
14652
|
-
payload: obj
|
|
14653
|
-
});
|
|
14654
|
-
}
|
|
14655
|
-
}
|
|
14656
|
-
extractJsonObjects(text) {
|
|
14657
|
-
const objects = [];
|
|
14658
|
-
let depth = 0;
|
|
14659
|
-
let start = -1;
|
|
14660
|
-
let inString = false;
|
|
14661
|
-
let escaped = false;
|
|
14662
|
-
for (let i = 0; i < text.length; i++) {
|
|
14663
|
-
const ch = text[i];
|
|
14664
|
-
if (inString) {
|
|
14665
|
-
if (escaped) escaped = false;
|
|
14666
|
-
else if (ch === "\\") escaped = true;
|
|
14667
|
-
else if (ch === '"') inString = false;
|
|
14668
|
-
continue;
|
|
14669
|
-
}
|
|
14670
|
-
if (ch === '"') {
|
|
14671
|
-
inString = true;
|
|
14672
|
-
} else if (ch === "{") {
|
|
14673
|
-
if (depth === 0) start = i;
|
|
14674
|
-
depth++;
|
|
14675
|
-
} else if (ch === "}" && depth > 0) {
|
|
14676
|
-
depth--;
|
|
14677
|
-
if (depth === 0 && start >= 0) {
|
|
14678
|
-
const candidate = text.slice(start, i + 1);
|
|
14679
|
-
try {
|
|
14680
|
-
const parsed = JSON.parse(candidate);
|
|
14681
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
14682
|
-
objects.push(parsed);
|
|
14683
|
-
}
|
|
14684
|
-
} catch {
|
|
14685
|
-
}
|
|
14686
|
-
start = -1;
|
|
14687
|
-
}
|
|
14688
|
-
}
|
|
14689
|
-
}
|
|
14690
|
-
return objects;
|
|
14691
|
-
}
|
|
14692
|
-
budgetForRole(role) {
|
|
14693
|
-
if (this.options.budgetOverrides?.[role]) {
|
|
14694
|
-
return this.options.budgetOverrides[role];
|
|
14695
|
-
}
|
|
14696
|
-
const defaults = {
|
|
14697
|
-
"bug-hunter": { maxIterations: 2e3, maxToolCalls: 5e3, timeoutMs: 10 * 60 * 1e3 },
|
|
14698
|
-
"refactor-planner": { maxIterations: 1500, maxToolCalls: 4e3, timeoutMs: 8 * 60 * 1e3 },
|
|
14699
|
-
"critic": { maxIterations: 1e3, maxToolCalls: 3e3, timeoutMs: 6 * 60 * 1e3 }
|
|
14700
|
-
};
|
|
14701
|
-
return defaults[role] ?? { maxIterations: 1500, maxToolCalls: 4e3, timeoutMs: 8 * 60 * 1e3 };
|
|
14702
|
-
}
|
|
14703
|
-
async spawnAgent(role, taskBrief) {
|
|
14704
|
-
const budget = this.budgetForRole(role);
|
|
14705
|
-
const cfg = {
|
|
14706
|
-
id: `${role}-${this.sessionId}`,
|
|
14707
|
-
name: role,
|
|
14708
|
-
role,
|
|
14709
|
-
tools: ["fleet_emit", "fleet_status", "read", "grep", "glob", "bash", "write"],
|
|
14710
|
-
maxIterations: budget.maxIterations,
|
|
14711
|
-
maxToolCalls: budget.maxToolCalls,
|
|
14712
|
-
timeoutMs: budget.timeoutMs
|
|
14713
|
-
};
|
|
14714
|
-
const subagentId = await this.director.spawn(cfg);
|
|
14715
|
-
await this.director.assign({ id: randomUUID(), subagentId, description: taskBrief });
|
|
14716
|
-
return subagentId;
|
|
14717
|
-
}
|
|
14718
|
-
buildBugHunterTask() {
|
|
14719
|
-
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
14720
|
-
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
14721
|
-
${f.content}`).join("\n\n");
|
|
14722
|
-
return `You are BugHunter. Scan the following files for bugs and code smells.
|
|
14723
|
-
|
|
14724
|
-
Target files:
|
|
14725
|
-
${fileContents}
|
|
14726
|
-
|
|
14727
|
-
For each bug found, emit it using the fleet_emit tool immediately:
|
|
14728
|
-
{ "type": "bug.found", "payload": { "finding": { "id": "<uuid>", "type": "<pattern>", "severity": "<critical|high|medium|low>", "location": { "file": "<path>", "line": <n> }, "description": "<explain>", "suggestedFix": "<optional>" } } }
|
|
14729
|
-
|
|
14730
|
-
After scanning all files, write your full markdown bug report to:
|
|
14731
|
-
${scratchpad}/bug-hunter-report-${this.sessionId}.md
|
|
14732
|
-
|
|
14733
|
-
Important: emit each finding as soon as you find it. Do not batch or wait until the end.`;
|
|
14734
|
-
}
|
|
14735
|
-
buildRefactorPlannerTask() {
|
|
14736
|
-
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
14737
|
-
const bugHunterReportPath = `${scratchpad}/bug-hunter-report-${this.sessionId}.md`;
|
|
14738
|
-
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
14739
|
-
${f.content}`).join("\n\n");
|
|
14740
|
-
return `You are RefactorPlanner. Plan refactorings for the following files.
|
|
14741
|
-
|
|
14742
|
-
Target files:
|
|
14743
|
-
${fileContents}
|
|
14744
|
-
|
|
14745
|
-
Read the BugHunter report at: ${bugHunterReportPath}
|
|
14746
|
-
|
|
14747
|
-
For each bug you can address, emit a refactor plan using fleet_emit:
|
|
14748
|
-
{ "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>" } } }
|
|
14749
|
-
|
|
14750
|
-
Also write your full markdown plan to:
|
|
14751
|
-
${scratchpad}/refactor-plan-${this.sessionId}.md
|
|
14752
|
-
|
|
14753
|
-
Emit each plan immediately. Do not wait until planning is complete.`;
|
|
14754
|
-
}
|
|
14755
|
-
buildCriticTask() {
|
|
14756
|
-
const scratchpad = this.director.sharedScratchpadPath ?? "/tmp";
|
|
14757
|
-
const bugHunterReportPath = `${scratchpad}/bug-hunter-report-${this.sessionId}.md`;
|
|
14758
|
-
const refactorPlanPath = `${scratchpad}/refactor-plan-${this.sessionId}.md`;
|
|
14759
|
-
const fileContents = this.snapshot.files.map((f) => `=== ${f.path} ===
|
|
14760
|
-
${f.content}`).join("\n\n");
|
|
14761
|
-
return `You are Critic. Evaluate bug findings and refactor plans.
|
|
14762
|
-
|
|
14763
|
-
Target files:
|
|
14764
|
-
${fileContents}
|
|
14765
|
-
|
|
14766
|
-
Read the BugHunter report at: ${bugHunterReportPath}
|
|
14767
|
-
Read the RefactorPlanner report at: ${refactorPlanPath}
|
|
14768
|
-
|
|
14769
|
-
For each bug and refactor plan, emit your evaluation using fleet_emit:
|
|
14770
|
-
{ "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>" }] } } }
|
|
14771
|
-
|
|
14772
|
-
After all evaluations, write your markdown report to:
|
|
14773
|
-
${scratchpad}/critic-report-${this.sessionId}.md
|
|
14774
|
-
|
|
14775
|
-
Emit each evaluation immediately. Do not wait until you have read all reports.`;
|
|
14776
|
-
}
|
|
14777
|
-
wireFleetBus() {
|
|
14778
|
-
const dTool = this.fleetBus.filter("tool.executed", (e) => {
|
|
14779
|
-
this.progressBySubagent.set(e.subagentId, (this.progressBySubagent.get(e.subagentId) ?? 0) + 1);
|
|
14780
|
-
});
|
|
14781
|
-
this.disposers.push(dTool);
|
|
14782
|
-
const dBudget = this.fleetBus.filter("budget.threshold_reached", (e) => {
|
|
14783
|
-
const payload = e.payload;
|
|
14784
|
-
const role = this.roleFromSubagentId(e.subagentId);
|
|
14785
|
-
if (!role) return;
|
|
14786
|
-
const btwNotes = this.director.getLeaderBtwNotes();
|
|
14787
|
-
const alert = {
|
|
14788
|
-
sessionId: this.sessionId,
|
|
14789
|
-
subagentId: e.subagentId,
|
|
14790
|
-
role,
|
|
14791
|
-
level: "warning" /* WARNING */,
|
|
14792
|
-
message: `${role} hit ${payload.kind} soft limit (${payload.used}/${payload.limit})`,
|
|
14793
|
-
budgetKind: payload.kind,
|
|
14794
|
-
elapsedMs: payload.timeoutMs,
|
|
14795
|
-
limit: payload.limit,
|
|
14796
|
-
btwNotes
|
|
14797
|
-
};
|
|
14798
|
-
this.alerts.push(alert);
|
|
14799
|
-
this.fleetBus.emit({
|
|
14800
|
-
subagentId: e.subagentId,
|
|
14801
|
-
ts: Date.now(),
|
|
14802
|
-
type: "collab.warning",
|
|
14803
|
-
payload: alert
|
|
14804
|
-
});
|
|
14805
|
-
const decision = this.options.onBudgetWarning?.(alert) ?? "ignore";
|
|
14806
|
-
if (decision === "cancel") {
|
|
14807
|
-
this.cancel(`Director cancelled: ${role} ${payload.kind} threshold`);
|
|
14808
|
-
return;
|
|
14809
|
-
}
|
|
14810
|
-
if (payload.kind === "timeout" || payload.kind === "idle_timeout") {
|
|
14811
|
-
const progress = this.progressBySubagent.get(e.subagentId) ?? 0;
|
|
14812
|
-
const lastProgress = this.lastTimeoutProgress.get(e.subagentId) ?? -1;
|
|
14813
|
-
if (progress <= lastProgress) {
|
|
14814
|
-
payload.deny();
|
|
14815
|
-
return;
|
|
14816
|
-
}
|
|
14817
|
-
this.lastTimeoutProgress.set(e.subagentId, progress);
|
|
14818
|
-
const newLimit = Math.min(Math.ceil((payload.timeoutMs ?? payload.limit) * 2), 24 * 60 * 6e4);
|
|
14819
|
-
setImmediate(() => {
|
|
14820
|
-
payload.extend({ timeoutMs: newLimit });
|
|
14821
|
-
});
|
|
14822
|
-
return;
|
|
14823
|
-
}
|
|
14824
|
-
if (decision === "extend") {
|
|
14825
|
-
setImmediate(() => {
|
|
14826
|
-
const base = Math.max(payload.limit, payload.used);
|
|
14827
|
-
const extra = {};
|
|
14828
|
-
switch (payload.kind) {
|
|
14829
|
-
case "iterations":
|
|
14830
|
-
extra.maxIterations = Math.min(Math.ceil(base * 1.5), 5e4);
|
|
14831
|
-
break;
|
|
14832
|
-
case "tool_calls":
|
|
14833
|
-
extra.maxToolCalls = Math.min(Math.ceil(base * 1.5), 1e5);
|
|
14834
|
-
break;
|
|
14835
|
-
case "tokens":
|
|
14836
|
-
extra.maxTokens = Math.min(Math.ceil(base * 1.5), 5e6);
|
|
14837
|
-
break;
|
|
14838
|
-
case "cost":
|
|
14839
|
-
extra.maxCostUsd = Math.min(base * 1.5, 100);
|
|
14840
|
-
break;
|
|
14841
|
-
}
|
|
14842
|
-
payload.extend(extra);
|
|
14843
|
-
});
|
|
14844
|
-
return;
|
|
14845
|
-
}
|
|
14846
|
-
if (payload.kind !== "timeout") {
|
|
14847
|
-
setImmediate(() => {
|
|
14848
|
-
const base = Math.max(payload.limit, payload.used);
|
|
14849
|
-
const extra = {};
|
|
14850
|
-
switch (payload.kind) {
|
|
14851
|
-
case "iterations":
|
|
14852
|
-
extra.maxIterations = Math.min(Math.ceil(base * 1.25), 5e4);
|
|
14853
|
-
break;
|
|
14854
|
-
case "tool_calls":
|
|
14855
|
-
extra.maxToolCalls = Math.min(Math.ceil(base * 1.25), 1e5);
|
|
14856
|
-
break;
|
|
14857
|
-
case "tokens":
|
|
14858
|
-
extra.maxTokens = Math.min(Math.ceil(base * 1.25), 5e6);
|
|
14859
|
-
break;
|
|
14860
|
-
case "cost":
|
|
14861
|
-
extra.maxCostUsd = Math.min(base * 1.25, 100);
|
|
14862
|
-
break;
|
|
14863
|
-
}
|
|
14864
|
-
payload.extend(extra);
|
|
14865
|
-
});
|
|
14866
|
-
}
|
|
14867
|
-
});
|
|
14868
|
-
this.disposers.push(dBudget);
|
|
14869
|
-
const dCancel = this.fleetBus.filter("director.cancel_collab", (e) => {
|
|
14870
|
-
const payload = e.payload;
|
|
14871
|
-
if (payload.sessionId !== this.sessionId) return;
|
|
14872
|
-
this.cancelled = true;
|
|
14873
|
-
if (this._timeoutTimer) {
|
|
14874
|
-
clearTimeout(this._timeoutTimer);
|
|
14875
|
-
this._timeoutTimer = void 0;
|
|
14876
|
-
}
|
|
14877
|
-
this.fleetBus.emit({
|
|
14878
|
-
subagentId: this.director.id,
|
|
14879
|
-
ts: Date.now(),
|
|
14880
|
-
type: "collab.cancelled",
|
|
14881
|
-
payload: { sessionId: this.sessionId, reason: payload.reason }
|
|
14882
|
-
});
|
|
14883
|
-
});
|
|
14884
|
-
this.disposers.push(dCancel);
|
|
14885
|
-
const d1 = this.fleetBus.filter("bug.found", (e) => {
|
|
14886
|
-
const payload = e.payload;
|
|
14887
|
-
if (payload?.finding) {
|
|
14888
|
-
this.bugs.set(payload.finding.id, payload.finding);
|
|
14889
|
-
this.emit("bug.found", payload);
|
|
14890
|
-
}
|
|
14891
|
-
});
|
|
14892
|
-
this.disposers.push(d1);
|
|
14893
|
-
const d2 = this.fleetBus.filter("refactor.plan", (e) => {
|
|
14894
|
-
const payload = e.payload;
|
|
14895
|
-
if (payload?.plan) {
|
|
14896
|
-
this.plans.set(payload.plan.id, payload.plan);
|
|
14897
|
-
this.emit("refactor.plan", payload);
|
|
14898
|
-
}
|
|
14899
|
-
});
|
|
14900
|
-
this.disposers.push(d2);
|
|
14901
|
-
const d3 = this.fleetBus.filter("critic.evaluation", (e) => {
|
|
14902
|
-
const payload = e.payload;
|
|
14903
|
-
if (payload?.evaluation) {
|
|
14904
|
-
this.evaluations.set(payload.evaluation.id, payload.evaluation);
|
|
14905
|
-
this.emit("critic.evaluation", payload);
|
|
14906
|
-
}
|
|
14852
|
+
attach(subagentId, bus, taskId) {
|
|
14853
|
+
const off = bus.onAny((type, payload) => {
|
|
14854
|
+
if (type.startsWith("subagent.")) return;
|
|
14855
|
+
this.emit({ subagentId, taskId, ts: Date.now(), type, payload });
|
|
14907
14856
|
});
|
|
14908
|
-
|
|
14857
|
+
return () => {
|
|
14858
|
+
off();
|
|
14859
|
+
};
|
|
14909
14860
|
}
|
|
14910
|
-
|
|
14911
|
-
|
|
14912
|
-
|
|
14861
|
+
/** Subscribe to every event from one subagent. */
|
|
14862
|
+
subscribe(subagentId, handler) {
|
|
14863
|
+
let set = this.byId.get(subagentId);
|
|
14864
|
+
if (!set) {
|
|
14865
|
+
set = /* @__PURE__ */ new Set();
|
|
14866
|
+
this.byId.set(subagentId, set);
|
|
14913
14867
|
}
|
|
14914
|
-
|
|
14915
|
-
return
|
|
14868
|
+
set.add(handler);
|
|
14869
|
+
return () => {
|
|
14870
|
+
set.delete(handler);
|
|
14871
|
+
};
|
|
14916
14872
|
}
|
|
14917
|
-
|
|
14918
|
-
|
|
14919
|
-
|
|
14920
|
-
|
|
14921
|
-
|
|
14922
|
-
|
|
14923
|
-
|
|
14924
|
-
|
|
14925
|
-
|
|
14926
|
-
|
|
14873
|
+
/** Subscribe to one event type across all subagents. */
|
|
14874
|
+
filter(type, handler) {
|
|
14875
|
+
let set = this.byType.get(type);
|
|
14876
|
+
if (!set) {
|
|
14877
|
+
set = /* @__PURE__ */ new Set();
|
|
14878
|
+
this.byType.set(type, set);
|
|
14879
|
+
}
|
|
14880
|
+
set.add(handler);
|
|
14881
|
+
return () => {
|
|
14882
|
+
set.delete(handler);
|
|
14927
14883
|
};
|
|
14928
|
-
|
|
14929
|
-
|
|
14930
|
-
|
|
14931
|
-
|
|
14932
|
-
|
|
14933
|
-
|
|
14934
|
-
"approve"
|
|
14935
|
-
);
|
|
14936
|
-
const summary = this.buildMarkdownSummary(bugList, planList, evalList, overallVerdict, disposition);
|
|
14937
|
-
return {
|
|
14938
|
-
sessionId: this.sessionId,
|
|
14939
|
-
startedAt: this.snapshot.createdAt,
|
|
14940
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14941
|
-
targetPaths: this.options.targetPaths,
|
|
14942
|
-
disposition,
|
|
14943
|
-
bugs: bugList,
|
|
14944
|
-
refactorPlans: planList,
|
|
14945
|
-
evaluations: evalList,
|
|
14946
|
-
alerts: [...this.alerts],
|
|
14947
|
-
overallVerdict,
|
|
14948
|
-
summary
|
|
14884
|
+
}
|
|
14885
|
+
/** Subscribe to literally everything. The fleet roll-up uses this. */
|
|
14886
|
+
onAny(handler) {
|
|
14887
|
+
this.any.add(handler);
|
|
14888
|
+
return () => {
|
|
14889
|
+
this.any.delete(handler);
|
|
14949
14890
|
};
|
|
14950
14891
|
}
|
|
14951
|
-
|
|
14952
|
-
const
|
|
14953
|
-
|
|
14954
|
-
|
|
14955
|
-
|
|
14956
|
-
|
|
14957
|
-
|
|
14958
|
-
""
|
|
14959
|
-
];
|
|
14960
|
-
if (this.alerts.length > 0) {
|
|
14961
|
-
lines.push("### Alerts", "");
|
|
14962
|
-
for (const alert of this.alerts) {
|
|
14963
|
-
lines.push(`- **[${alert.level.toUpperCase()}]** ${alert.role}: ${alert.message}`);
|
|
14964
|
-
}
|
|
14965
|
-
lines.push("");
|
|
14966
|
-
}
|
|
14967
|
-
if (bugs.length > 0) {
|
|
14968
|
-
lines.push("### Bugs Found", "");
|
|
14969
|
-
for (const b of bugs) {
|
|
14970
|
-
lines.push(`- **[${b.severity.toUpperCase()}]** \`${b.location.file}:${b.location.line}\` \u2014 ${b.description}`);
|
|
14971
|
-
}
|
|
14972
|
-
lines.push("");
|
|
14973
|
-
}
|
|
14974
|
-
if (plans.length > 0) {
|
|
14975
|
-
lines.push("### Refactor Plans", "");
|
|
14976
|
-
for (const p of plans) {
|
|
14977
|
-
lines.push(`- **Phase plan** (risk: ${p.riskScore}, ~${p.estimatedChangeCount} changes)`);
|
|
14978
|
-
for (const phase of p.phases) {
|
|
14979
|
-
lines.push(` - Phase ${phase.number}: ${phase.title} [${phase.risk}]`);
|
|
14892
|
+
emit(event) {
|
|
14893
|
+
const byId = this.byId.get(event.subagentId);
|
|
14894
|
+
if (byId)
|
|
14895
|
+
for (const h of byId) {
|
|
14896
|
+
try {
|
|
14897
|
+
h(event);
|
|
14898
|
+
} catch {
|
|
14980
14899
|
}
|
|
14981
14900
|
}
|
|
14982
|
-
|
|
14983
|
-
|
|
14984
|
-
|
|
14985
|
-
|
|
14986
|
-
|
|
14987
|
-
|
|
14988
|
-
for (const c of e.concerns) {
|
|
14989
|
-
if (c.severity === "blocking") lines.push(` - ${c.description}`);
|
|
14901
|
+
const byType = this.byType.get(event.type);
|
|
14902
|
+
if (byType)
|
|
14903
|
+
for (const h of byType) {
|
|
14904
|
+
try {
|
|
14905
|
+
h(event);
|
|
14906
|
+
} catch {
|
|
14990
14907
|
}
|
|
14991
14908
|
}
|
|
14992
|
-
|
|
14909
|
+
for (const h of this.any) {
|
|
14910
|
+
try {
|
|
14911
|
+
h(event);
|
|
14912
|
+
} catch {
|
|
14913
|
+
}
|
|
14993
14914
|
}
|
|
14994
|
-
return lines.join("\n");
|
|
14995
14915
|
}
|
|
14996
|
-
|
|
14997
|
-
|
|
14998
|
-
|
|
14916
|
+
};
|
|
14917
|
+
var FleetUsageAggregator = class {
|
|
14918
|
+
constructor(bus, priceLookup, metaLookup) {
|
|
14919
|
+
this.priceLookup = priceLookup;
|
|
14920
|
+
this.metaLookup = metaLookup;
|
|
14921
|
+
this.unsub.push(bus.filter("provider.response", (e) => this.onProviderResponse(e)));
|
|
14922
|
+
this.unsub.push(bus.filter("tool.executed", (e) => this.onToolExecuted(e)));
|
|
14923
|
+
this.unsub.push(bus.filter("iteration.started", (e) => this.onIterationStarted(e)));
|
|
14924
|
+
}
|
|
14925
|
+
priceLookup;
|
|
14926
|
+
metaLookup;
|
|
14927
|
+
perSubagent = /* @__PURE__ */ new Map();
|
|
14928
|
+
total = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
|
|
14929
|
+
unsub = new Array();
|
|
14930
|
+
/**
|
|
14931
|
+
* Remove a terminated subagent's data from the aggregator and subtract its
|
|
14932
|
+
* contribution from the running totals. Call this when a subagent is removed
|
|
14933
|
+
* from the fleet so the aggregator doesn't accumulate unbounded data for
|
|
14934
|
+
* entities that will never emit events again.
|
|
14935
|
+
*/
|
|
14936
|
+
removeSubagent(subagentId) {
|
|
14937
|
+
const snap = this.perSubagent.get(subagentId);
|
|
14938
|
+
if (!snap) return;
|
|
14939
|
+
this.perSubagent.delete(subagentId);
|
|
14940
|
+
this.total.input -= snap.input;
|
|
14941
|
+
this.total.output -= snap.output;
|
|
14942
|
+
this.total.cacheRead -= snap.cacheRead;
|
|
14943
|
+
this.total.cacheWrite -= snap.cacheWrite;
|
|
14944
|
+
this.total.cost -= snap.cost;
|
|
14945
|
+
}
|
|
14946
|
+
/** Disposes all fleet-bus subscriptions. Call when the aggregator is no longer needed. */
|
|
14947
|
+
dispose() {
|
|
14948
|
+
for (const off of this.unsub) off();
|
|
14949
|
+
this.unsub.length = 0;
|
|
14950
|
+
}
|
|
14951
|
+
/** Live snapshot — safe to call from a tool's execute() body. */
|
|
14952
|
+
snapshot() {
|
|
14953
|
+
return {
|
|
14954
|
+
total: { ...this.total },
|
|
14955
|
+
perSubagent: Object.fromEntries(
|
|
14956
|
+
Array.from(this.perSubagent.entries()).map(([k, v]) => [k, { ...v }])
|
|
14957
|
+
)
|
|
14958
|
+
};
|
|
14959
|
+
}
|
|
14960
|
+
ensure(subagentId) {
|
|
14961
|
+
let snap = this.perSubagent.get(subagentId);
|
|
14962
|
+
if (!snap) {
|
|
14963
|
+
const meta = this.metaLookup?.(subagentId);
|
|
14964
|
+
snap = {
|
|
14965
|
+
subagentId,
|
|
14966
|
+
provider: meta?.provider,
|
|
14967
|
+
model: meta?.model,
|
|
14968
|
+
input: 0,
|
|
14969
|
+
output: 0,
|
|
14970
|
+
cacheRead: 0,
|
|
14971
|
+
cacheWrite: 0,
|
|
14972
|
+
cost: 0,
|
|
14973
|
+
toolCalls: 0,
|
|
14974
|
+
iterations: 0,
|
|
14975
|
+
startedAt: Date.now(),
|
|
14976
|
+
lastEventAt: Date.now()
|
|
14977
|
+
};
|
|
14978
|
+
this.perSubagent.set(subagentId, snap);
|
|
14979
|
+
}
|
|
14980
|
+
return snap;
|
|
14981
|
+
}
|
|
14982
|
+
onProviderResponse(e) {
|
|
14983
|
+
const snap = this.ensure(e.subagentId);
|
|
14984
|
+
const p = e.payload;
|
|
14985
|
+
const usage = p?.usage;
|
|
14986
|
+
if (!usage) return;
|
|
14987
|
+
snap.input += usage.input ?? 0;
|
|
14988
|
+
snap.output += usage.output ?? 0;
|
|
14989
|
+
snap.cacheRead += usage.cacheRead ?? 0;
|
|
14990
|
+
snap.cacheWrite += usage.cacheWrite ?? 0;
|
|
14991
|
+
this.total.input += usage.input ?? 0;
|
|
14992
|
+
this.total.output += usage.output ?? 0;
|
|
14993
|
+
this.total.cacheRead += usage.cacheRead ?? 0;
|
|
14994
|
+
this.total.cacheWrite += usage.cacheWrite ?? 0;
|
|
14995
|
+
const price = this.priceLookup?.(e.subagentId, snap.provider, snap.model);
|
|
14996
|
+
if (price) {
|
|
14997
|
+
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);
|
|
14998
|
+
snap.cost += delta;
|
|
14999
|
+
this.total.cost += delta;
|
|
15000
|
+
}
|
|
15001
|
+
snap.lastEventAt = e.ts;
|
|
15002
|
+
}
|
|
15003
|
+
onToolExecuted(e) {
|
|
15004
|
+
const snap = this.ensure(e.subagentId);
|
|
15005
|
+
snap.toolCalls += 1;
|
|
15006
|
+
snap.lastEventAt = e.ts;
|
|
15007
|
+
}
|
|
15008
|
+
onIterationStarted(e) {
|
|
15009
|
+
const snap = this.ensure(e.subagentId);
|
|
15010
|
+
snap.iterations += 1;
|
|
15011
|
+
snap.lastEventAt = e.ts;
|
|
15012
|
+
}
|
|
15013
|
+
};
|
|
15014
|
+
|
|
15015
|
+
// src/coordination/large-answer-store.ts
|
|
15016
|
+
var LargeAnswerStore = class {
|
|
15017
|
+
/**
|
|
15018
|
+
* Responses above this size (in characters) are stored out-of-context.
|
|
15019
|
+
* Below this, the full answer is returned inline (no overhead).
|
|
15020
|
+
* Default: 2000 chars ≈ 400-600 tokens.
|
|
15021
|
+
*/
|
|
15022
|
+
sizeThreshold;
|
|
15023
|
+
store = /* @__PURE__ */ new Map();
|
|
15024
|
+
constructor(sizeThreshold = 2e3) {
|
|
15025
|
+
this.sizeThreshold = sizeThreshold;
|
|
15026
|
+
}
|
|
15027
|
+
/**
|
|
15028
|
+
* Store a value, returning a summary + key for inline use.
|
|
15029
|
+
* If the value is below sizeThreshold, returns it as-is (no store entry).
|
|
15030
|
+
*/
|
|
15031
|
+
storeAnswer(value) {
|
|
15032
|
+
if (value === void 0 || value === null) {
|
|
15033
|
+
return { summary: String(value), inline: true };
|
|
15034
|
+
}
|
|
15035
|
+
const serialized = typeof value === "string" ? value : JSON.stringify(value);
|
|
15036
|
+
const size = serialized.length;
|
|
15037
|
+
if (size <= this.sizeThreshold) {
|
|
15038
|
+
return { summary: serialized.slice(0, 500), inline: true };
|
|
15039
|
+
}
|
|
15040
|
+
const key = `a-${hashStr(serialized)}`;
|
|
15041
|
+
this.store.set(key, {
|
|
15042
|
+
key,
|
|
15043
|
+
value,
|
|
15044
|
+
size,
|
|
15045
|
+
storedAt: Date.now()
|
|
15046
|
+
});
|
|
15047
|
+
return {
|
|
15048
|
+
key,
|
|
15049
|
+
summary: `[stored: ${size} chars \u2014 use roll_up or ask_result tool to retrieve, key=${key}]`,
|
|
15050
|
+
inline: false
|
|
15051
|
+
};
|
|
15052
|
+
}
|
|
15053
|
+
/**
|
|
15054
|
+
* Retrieve a previously stored answer by its key.
|
|
15055
|
+
* Returns undefined if the key is unknown or the store was cleared.
|
|
15056
|
+
*/
|
|
15057
|
+
retrieveAnswer(key) {
|
|
15058
|
+
return this.store.get(key)?.value;
|
|
15059
|
+
}
|
|
15060
|
+
/**
|
|
15061
|
+
* Check if a key exists in the store.
|
|
15062
|
+
*/
|
|
15063
|
+
hasAnswer(key) {
|
|
15064
|
+
return this.store.has(key);
|
|
15065
|
+
}
|
|
15066
|
+
/** Number of stored entries. */
|
|
15067
|
+
get size() {
|
|
15068
|
+
return this.store.size;
|
|
15069
|
+
}
|
|
15070
|
+
/** Total characters stored. */
|
|
15071
|
+
get totalChars() {
|
|
15072
|
+
let total = 0;
|
|
15073
|
+
for (const e of this.store.values()) total += e.size;
|
|
15074
|
+
return total;
|
|
15075
|
+
}
|
|
15076
|
+
/** Clear all stored entries. Call at the end of a director run. */
|
|
15077
|
+
clear() {
|
|
15078
|
+
this.store.clear();
|
|
14999
15079
|
}
|
|
15000
15080
|
};
|
|
15081
|
+
function hashStr(s) {
|
|
15082
|
+
let h = 5381;
|
|
15083
|
+
for (let i = 0; i < s.length; i++) {
|
|
15084
|
+
h = h * 33 ^ s.charCodeAt(i);
|
|
15085
|
+
}
|
|
15086
|
+
return (h >>> 0).toString(36);
|
|
15087
|
+
}
|
|
15088
|
+
|
|
15089
|
+
// src/coordination/model-matrix.ts
|
|
15090
|
+
var MATRIX_PHASE_KEYS = Object.keys(AGENTS_BY_PHASE);
|
|
15091
|
+
var ROLE_TO_PHASE = (() => {
|
|
15092
|
+
const map = {};
|
|
15093
|
+
for (const [phase, defs] of Object.entries(AGENTS_BY_PHASE)) {
|
|
15094
|
+
for (const def of defs) {
|
|
15095
|
+
const role = def.config.role;
|
|
15096
|
+
if (role) map[role] = phase;
|
|
15097
|
+
}
|
|
15098
|
+
}
|
|
15099
|
+
return map;
|
|
15100
|
+
})();
|
|
15101
|
+
function phaseForRole(role) {
|
|
15102
|
+
return role ? ROLE_TO_PHASE[role] : void 0;
|
|
15103
|
+
}
|
|
15104
|
+
function resolveModelMatrix(matrix, role) {
|
|
15105
|
+
if (!matrix) return void 0;
|
|
15106
|
+
if (role && matrix[role]) return matrix[role];
|
|
15107
|
+
const phase = phaseForRole(role);
|
|
15108
|
+
if (phase && matrix[phase]) return matrix[phase];
|
|
15109
|
+
if (matrix["*"]) return matrix["*"];
|
|
15110
|
+
return void 0;
|
|
15111
|
+
}
|
|
15112
|
+
function matrixKeyKind(key) {
|
|
15113
|
+
if (key === "*") return "default";
|
|
15114
|
+
if (key in AGENT_CATALOG) return "role";
|
|
15115
|
+
if (MATRIX_PHASE_KEYS.includes(key)) return "phase";
|
|
15116
|
+
return "unknown";
|
|
15117
|
+
}
|
|
15118
|
+
function isValidMatrixKey(key) {
|
|
15119
|
+
return matrixKeyKind(key) !== "unknown";
|
|
15120
|
+
}
|
|
15001
15121
|
|
|
15002
15122
|
// src/coordination/director.ts
|
|
15003
15123
|
var FleetSpawnBudgetError = class extends Error {
|
|
@@ -15076,6 +15196,12 @@ var Director = class _Director {
|
|
|
15076
15196
|
getLeaderContextPressure() {
|
|
15077
15197
|
return this.leaderContextPressure;
|
|
15078
15198
|
}
|
|
15199
|
+
resolveMaxContext() {
|
|
15200
|
+
const resolved = typeof this.maxContext === "function" ? this.maxContext() : this.maxContext;
|
|
15201
|
+
return resolved && resolved > 0 ? resolved : 128e3;
|
|
15202
|
+
}
|
|
15203
|
+
/** Optional Brain arbiter for director-level policy decisions. */
|
|
15204
|
+
brain;
|
|
15079
15205
|
/**
|
|
15080
15206
|
* Optional fleet-level policy container. When provided the Director
|
|
15081
15207
|
* delegates spawn budgeting, manifest entries, and checkpointing to it
|
|
@@ -15167,8 +15293,11 @@ var Director = class _Director {
|
|
|
15167
15293
|
leaderContextPressure = 0;
|
|
15168
15294
|
/** Maximum context load fraction before spawn is refused. */
|
|
15169
15295
|
maxLeaderContextLoad;
|
|
15170
|
-
/** Provider's max context window in tokens. */
|
|
15296
|
+
/** Provider's max context window in tokens, or a live resolver for runtime model switches. */
|
|
15171
15297
|
maxContext;
|
|
15298
|
+
/** Per-task model matrix (static record or live getter); resolved
|
|
15299
|
+
* per-spawn when no explicit model is set. */
|
|
15300
|
+
modelMatrix;
|
|
15172
15301
|
/**
|
|
15173
15302
|
* When set by `workComplete()`, the director stops dispatching new tasks
|
|
15174
15303
|
* and terminates all running subagents. Used when the director's LLM decides
|
|
@@ -15184,6 +15313,7 @@ var Director = class _Director {
|
|
|
15184
15313
|
largeAnswerStore;
|
|
15185
15314
|
constructor(opts) {
|
|
15186
15315
|
this.id = opts.config.coordinatorId || randomUUID();
|
|
15316
|
+
this.brain = opts.brain;
|
|
15187
15317
|
this.manifestPath = opts.manifestPath;
|
|
15188
15318
|
this.roster = opts.roster;
|
|
15189
15319
|
this.directorPreamble = opts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
|
|
@@ -15199,20 +15329,23 @@ var Director = class _Director {
|
|
|
15199
15329
|
this.maxBudgetExtensions = opts.maxBudgetExtensions ?? 5;
|
|
15200
15330
|
this.maxLeaderContextLoad = opts.maxLeaderContextLoad ?? 0.85;
|
|
15201
15331
|
this.maxContext = opts.maxContext ?? 128e3;
|
|
15332
|
+
this.modelMatrix = opts.modelMatrix;
|
|
15202
15333
|
this.sessionsRoot = opts.sessionsRoot;
|
|
15203
15334
|
this.directorRunId = opts.directorRunId ?? this.id;
|
|
15204
|
-
this.stateCheckpoint = opts.stateCheckpointPath ? new DirectorStateCheckpoint(
|
|
15205
|
-
|
|
15206
|
-
|
|
15207
|
-
|
|
15208
|
-
|
|
15209
|
-
|
|
15210
|
-
|
|
15335
|
+
this.stateCheckpoint = opts.stateCheckpointPath ? new DirectorStateCheckpoint(
|
|
15336
|
+
opts.stateCheckpointPath,
|
|
15337
|
+
{
|
|
15338
|
+
directorRunId: this.id,
|
|
15339
|
+
maxSpawns: opts.maxSpawns,
|
|
15340
|
+
spawnDepth: this.spawnDepth,
|
|
15341
|
+
maxSpawnDepth: this.maxSpawnDepth,
|
|
15342
|
+
directorBudget: opts.directorBudget
|
|
15343
|
+
},
|
|
15344
|
+
opts.checkpointDebounceMs ?? 250
|
|
15345
|
+
) : null;
|
|
15211
15346
|
this.fleetManager = opts.fleetManager;
|
|
15212
15347
|
if (this.sharedScratchpadPath) {
|
|
15213
|
-
void fsp3.mkdir(this.sharedScratchpadPath, { recursive: true }).catch(
|
|
15214
|
-
(err) => this.logShutdownError("shared_scratchpad_mkdir", err)
|
|
15215
|
-
);
|
|
15348
|
+
void fsp3.mkdir(this.sharedScratchpadPath, { recursive: true }).catch((err) => this.logShutdownError("shared_scratchpad_mkdir", err));
|
|
15216
15349
|
}
|
|
15217
15350
|
this.transport = new InMemoryBridgeTransport();
|
|
15218
15351
|
this.bridge = new InMemoryAgentBridge(
|
|
@@ -15325,33 +15458,81 @@ var Director = class _Director {
|
|
|
15325
15458
|
return;
|
|
15326
15459
|
}
|
|
15327
15460
|
}
|
|
15328
|
-
|
|
15329
|
-
|
|
15330
|
-
|
|
15331
|
-
|
|
15332
|
-
|
|
15333
|
-
|
|
15334
|
-
|
|
15335
|
-
|
|
15336
|
-
|
|
15337
|
-
|
|
15338
|
-
|
|
15339
|
-
|
|
15340
|
-
|
|
15341
|
-
|
|
15342
|
-
|
|
15343
|
-
|
|
15344
|
-
|
|
15345
|
-
|
|
15346
|
-
|
|
15347
|
-
|
|
15348
|
-
|
|
15349
|
-
|
|
15350
|
-
|
|
15351
|
-
|
|
15352
|
-
|
|
15353
|
-
|
|
15354
|
-
|
|
15461
|
+
const grantExtension = () => {
|
|
15462
|
+
setImmediate(() => {
|
|
15463
|
+
const extra = {};
|
|
15464
|
+
const base = Math.max(payload.limit, payload.used);
|
|
15465
|
+
const grow = (ceiling) => Math.min(Math.ceil(base * 1.5), ceiling);
|
|
15466
|
+
let newLimit = base;
|
|
15467
|
+
switch (payload.kind) {
|
|
15468
|
+
case "iterations":
|
|
15469
|
+
newLimit = grow(5e4);
|
|
15470
|
+
extra.maxIterations = newLimit;
|
|
15471
|
+
break;
|
|
15472
|
+
case "tool_calls":
|
|
15473
|
+
newLimit = grow(1e5);
|
|
15474
|
+
extra.maxToolCalls = newLimit;
|
|
15475
|
+
break;
|
|
15476
|
+
case "tokens":
|
|
15477
|
+
newLimit = grow(5e6);
|
|
15478
|
+
extra.maxTokens = newLimit;
|
|
15479
|
+
break;
|
|
15480
|
+
case "cost":
|
|
15481
|
+
newLimit = Math.min(base * 1.5, 100);
|
|
15482
|
+
extra.maxCostUsd = newLimit;
|
|
15483
|
+
break;
|
|
15484
|
+
}
|
|
15485
|
+
extendCounts.set(guardKey, prior + 1);
|
|
15486
|
+
this.recordExtension(e.subagentId, e.taskId, payload.kind, newLimit);
|
|
15487
|
+
payload.extend(extra);
|
|
15488
|
+
});
|
|
15489
|
+
};
|
|
15490
|
+
if (this.brain) {
|
|
15491
|
+
void this.brain.decide({
|
|
15492
|
+
id: `director-budget-${e.subagentId}-${payload.kind}`,
|
|
15493
|
+
source: "director",
|
|
15494
|
+
question: `Should the director extend the ${payload.kind} budget for subagent ${e.subagentId}?`,
|
|
15495
|
+
context: [
|
|
15496
|
+
e.taskId ? `Task id: ${e.taskId}` : void 0,
|
|
15497
|
+
`Used: ${payload.used}`,
|
|
15498
|
+
`Limit: ${payload.limit}`,
|
|
15499
|
+
`Prior extensions for this kind: ${prior}`
|
|
15500
|
+
].filter(Boolean).join("\n"),
|
|
15501
|
+
risk: payload.kind === "cost" ? "high" : "medium",
|
|
15502
|
+
fallback: "continue",
|
|
15503
|
+
options: [
|
|
15504
|
+
{
|
|
15505
|
+
id: "extend",
|
|
15506
|
+
label: "Grant the director default budget extension",
|
|
15507
|
+
consequence: "The subagent continues with a larger per-kind budget.",
|
|
15508
|
+
risk: payload.kind === "cost" ? "high" : "medium",
|
|
15509
|
+
recommended: true
|
|
15510
|
+
},
|
|
15511
|
+
{
|
|
15512
|
+
id: "stop",
|
|
15513
|
+
label: "Stop this subagent at the current budget limit",
|
|
15514
|
+
consequence: "The current task will fail or stop due to budget pressure.",
|
|
15515
|
+
risk: "low"
|
|
15516
|
+
}
|
|
15517
|
+
]
|
|
15518
|
+
}).then((decision) => {
|
|
15519
|
+
if (decision.type === "deny") {
|
|
15520
|
+
payload.deny();
|
|
15521
|
+
return;
|
|
15522
|
+
}
|
|
15523
|
+
if (decision.type === "ask_human") {
|
|
15524
|
+
payload.deny();
|
|
15525
|
+
return;
|
|
15526
|
+
}
|
|
15527
|
+
if (decision.optionId === "stop" || /\bstop\b/i.test(decision.text)) {
|
|
15528
|
+
payload.deny();
|
|
15529
|
+
return;
|
|
15530
|
+
}
|
|
15531
|
+
grantExtension();
|
|
15532
|
+
}).catch(() => payload.deny());
|
|
15533
|
+
return;
|
|
15534
|
+
}
|
|
15535
|
+
grantExtension();
|
|
15355
15536
|
});
|
|
15356
15537
|
this.largeAnswerStore = new LargeAnswerStore(2e3);
|
|
15357
15538
|
}
|
|
@@ -15390,7 +15571,12 @@ var Director = class _Director {
|
|
|
15390
15571
|
*/
|
|
15391
15572
|
workComplete() {
|
|
15392
15573
|
this.workCompleteFlag = true;
|
|
15393
|
-
this.fleet.emit({
|
|
15574
|
+
this.fleet.emit({
|
|
15575
|
+
subagentId: this.id,
|
|
15576
|
+
ts: Date.now(),
|
|
15577
|
+
type: "director.work_complete",
|
|
15578
|
+
payload: {}
|
|
15579
|
+
});
|
|
15394
15580
|
}
|
|
15395
15581
|
/** Returns true if `workComplete()` has been called on this director. */
|
|
15396
15582
|
isWorkComplete() {
|
|
@@ -15522,13 +15708,25 @@ var Director = class _Director {
|
|
|
15522
15708
|
"workComplete() has been called \u2014 director closed further spawning"
|
|
15523
15709
|
);
|
|
15524
15710
|
}
|
|
15711
|
+
if (!config.model && this.modelMatrix) {
|
|
15712
|
+
const matrix = typeof this.modelMatrix === "function" ? this.modelMatrix() : this.modelMatrix;
|
|
15713
|
+
const entry = resolveModelMatrix(matrix, config.role);
|
|
15714
|
+
if (entry) {
|
|
15715
|
+
config.model = entry.model;
|
|
15716
|
+
if (entry.provider) config.provider = entry.provider;
|
|
15717
|
+
}
|
|
15718
|
+
}
|
|
15525
15719
|
if (this.fleetManager) {
|
|
15526
15720
|
const rejection = this.fleetManager.canSpawn(config);
|
|
15527
15721
|
if (rejection) {
|
|
15528
|
-
if (rejection.kind === "max_spawn_depth")
|
|
15529
|
-
|
|
15530
|
-
if (rejection.kind === "
|
|
15531
|
-
|
|
15722
|
+
if (rejection.kind === "max_spawn_depth")
|
|
15723
|
+
throw new FleetSpawnBudgetError("max_spawn_depth", rejection.limit, rejection.observed);
|
|
15724
|
+
if (rejection.kind === "max_spawns")
|
|
15725
|
+
throw new FleetSpawnBudgetError("max_spawns", rejection.limit, rejection.observed);
|
|
15726
|
+
if (rejection.kind === "max_cost_usd")
|
|
15727
|
+
throw new FleetCostCapError(rejection.limit, rejection.observed);
|
|
15728
|
+
if (rejection.kind === "max_context_load")
|
|
15729
|
+
throw new FleetContextOverflowError(rejection.limit, rejection.observed);
|
|
15532
15730
|
}
|
|
15533
15731
|
} else {
|
|
15534
15732
|
if (this.spawnDepth >= this.maxSpawnDepth) {
|
|
@@ -15544,7 +15742,8 @@ var Director = class _Director {
|
|
|
15544
15742
|
}
|
|
15545
15743
|
}
|
|
15546
15744
|
if (this.maxLeaderContextLoad < 1) {
|
|
15547
|
-
const
|
|
15745
|
+
const maxContext = this.resolveMaxContext();
|
|
15746
|
+
const threshold = maxContext * this.maxLeaderContextLoad;
|
|
15548
15747
|
if (this.leaderContextPressure >= threshold) {
|
|
15549
15748
|
throw new FleetContextOverflowError(threshold, this.leaderContextPressure);
|
|
15550
15749
|
}
|
|
@@ -15558,7 +15757,9 @@ var Director = class _Director {
|
|
|
15558
15757
|
this.fleetManager.assignNicknameAndRecord(config);
|
|
15559
15758
|
} else {
|
|
15560
15759
|
config.name = assignNickname(role, this._usedNicknames);
|
|
15561
|
-
this._usedNicknames.add(
|
|
15760
|
+
this._usedNicknames.add(
|
|
15761
|
+
config.name.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-")
|
|
15762
|
+
);
|
|
15562
15763
|
}
|
|
15563
15764
|
}
|
|
15564
15765
|
result = await this.coordinator.spawn(config);
|
|
@@ -15793,7 +15994,11 @@ var Director = class _Director {
|
|
|
15793
15994
|
subagentId: taskWithId.subagentId ?? "unassigned",
|
|
15794
15995
|
taskId: taskWithId.id,
|
|
15795
15996
|
status: "stopped",
|
|
15796
|
-
error: {
|
|
15997
|
+
error: {
|
|
15998
|
+
kind: "aborted_by_parent",
|
|
15999
|
+
message: "Director called workComplete() \u2014 no further tasks will run",
|
|
16000
|
+
retryable: false
|
|
16001
|
+
},
|
|
15797
16002
|
iterations: 0,
|
|
15798
16003
|
toolCalls: 0,
|
|
15799
16004
|
durationMs: 0
|
|
@@ -15848,11 +16053,11 @@ var Director = class _Director {
|
|
|
15848
16053
|
if (cached) return cached;
|
|
15849
16054
|
const existing = this.taskWaiters.get(id);
|
|
15850
16055
|
if (existing) return existing.promise;
|
|
15851
|
-
let
|
|
16056
|
+
let resolve11;
|
|
15852
16057
|
const promise = new Promise((res) => {
|
|
15853
|
-
|
|
16058
|
+
resolve11 = res;
|
|
15854
16059
|
});
|
|
15855
|
-
this.taskWaiters.set(id, { promise, resolve:
|
|
16060
|
+
this.taskWaiters.set(id, { promise, resolve: resolve11 });
|
|
15856
16061
|
return promise;
|
|
15857
16062
|
})
|
|
15858
16063
|
);
|
|
@@ -16237,7 +16442,7 @@ function createDelegateTool(opts) {
|
|
|
16237
16442
|
subagentId
|
|
16238
16443
|
});
|
|
16239
16444
|
const dir = director;
|
|
16240
|
-
const result = await new Promise((
|
|
16445
|
+
const result = await new Promise((resolve11) => {
|
|
16241
16446
|
let settled = false;
|
|
16242
16447
|
let timer;
|
|
16243
16448
|
const finish = (value) => {
|
|
@@ -16247,7 +16452,7 @@ function createDelegateTool(opts) {
|
|
|
16247
16452
|
offTool();
|
|
16248
16453
|
offIter();
|
|
16249
16454
|
offProgress();
|
|
16250
|
-
|
|
16455
|
+
resolve11(value);
|
|
16251
16456
|
};
|
|
16252
16457
|
const arm = () => {
|
|
16253
16458
|
if (timer) clearTimeout(timer);
|
|
@@ -17877,9 +18082,9 @@ var AISpecBuilder = class {
|
|
|
17877
18082
|
if (!this.sessionPath) return;
|
|
17878
18083
|
try {
|
|
17879
18084
|
const fsp20 = await import('fs/promises');
|
|
17880
|
-
const
|
|
18085
|
+
const path36 = await import('path');
|
|
17881
18086
|
const { atomicWrite: atomicWrite2 } = await Promise.resolve().then(() => (init_atomic_write(), atomic_write_exports));
|
|
17882
|
-
await fsp20.mkdir(
|
|
18087
|
+
await fsp20.mkdir(path36.dirname(this.sessionPath), { recursive: true });
|
|
17883
18088
|
await atomicWrite2(this.sessionPath, JSON.stringify(this.session, null, 2));
|
|
17884
18089
|
} catch {
|
|
17885
18090
|
}
|
|
@@ -18589,15 +18794,15 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
|
|
|
18589
18794
|
maxId = id;
|
|
18590
18795
|
}
|
|
18591
18796
|
}
|
|
18592
|
-
const
|
|
18797
|
+
const path36 = [];
|
|
18593
18798
|
let current = maxId;
|
|
18594
18799
|
const visited = /* @__PURE__ */ new Set();
|
|
18595
18800
|
while (current && !visited.has(current)) {
|
|
18596
18801
|
visited.add(current);
|
|
18597
|
-
|
|
18802
|
+
path36.unshift(current);
|
|
18598
18803
|
current = prev.get(current) ?? null;
|
|
18599
18804
|
}
|
|
18600
|
-
return
|
|
18805
|
+
return path36;
|
|
18601
18806
|
}
|
|
18602
18807
|
function computeParallelGroups(graph, blockedByMap) {
|
|
18603
18808
|
const groups = [];
|
|
@@ -19390,9 +19595,9 @@ var DefaultHealthRegistry = class {
|
|
|
19390
19595
|
}
|
|
19391
19596
|
async runOne(check) {
|
|
19392
19597
|
let timer = null;
|
|
19393
|
-
const timeout = new Promise((
|
|
19598
|
+
const timeout = new Promise((resolve11) => {
|
|
19394
19599
|
timer = setTimeout(
|
|
19395
|
-
() =>
|
|
19600
|
+
() => resolve11({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
|
|
19396
19601
|
this.timeoutMs
|
|
19397
19602
|
);
|
|
19398
19603
|
});
|
|
@@ -19575,7 +19780,7 @@ async function startMetricsServer(opts) {
|
|
|
19575
19780
|
const tls = opts.tls;
|
|
19576
19781
|
const useHttps = !!(tls?.cert && tls?.key);
|
|
19577
19782
|
const host = opts.host ?? "127.0.0.1";
|
|
19578
|
-
const
|
|
19783
|
+
const path36 = opts.path ?? "/metrics";
|
|
19579
19784
|
const healthPath = opts.healthPath ?? "/healthz";
|
|
19580
19785
|
const healthRegistry = opts.healthRegistry;
|
|
19581
19786
|
const listener = (req, res) => {
|
|
@@ -19585,7 +19790,7 @@ async function startMetricsServer(opts) {
|
|
|
19585
19790
|
return;
|
|
19586
19791
|
}
|
|
19587
19792
|
const url = req.url.split("?")[0];
|
|
19588
|
-
if (url ===
|
|
19793
|
+
if (url === path36) {
|
|
19589
19794
|
let body;
|
|
19590
19795
|
try {
|
|
19591
19796
|
body = renderPrometheus(opts.sink.snapshot());
|
|
@@ -19631,14 +19836,14 @@ async function startMetricsServer(opts) {
|
|
|
19631
19836
|
const { createServer } = await import('http');
|
|
19632
19837
|
server = createServer(listener);
|
|
19633
19838
|
}
|
|
19634
|
-
await new Promise((
|
|
19839
|
+
await new Promise((resolve11, reject) => {
|
|
19635
19840
|
const onError = (err) => {
|
|
19636
19841
|
server.off("listening", onListening);
|
|
19637
19842
|
reject(err);
|
|
19638
19843
|
};
|
|
19639
19844
|
const onListening = () => {
|
|
19640
19845
|
server.off("error", onError);
|
|
19641
|
-
|
|
19846
|
+
resolve11();
|
|
19642
19847
|
};
|
|
19643
19848
|
server.once("error", onError);
|
|
19644
19849
|
server.once("listening", onListening);
|
|
@@ -19649,9 +19854,9 @@ async function startMetricsServer(opts) {
|
|
|
19649
19854
|
const protocol = useHttps ? "https" : "http";
|
|
19650
19855
|
return {
|
|
19651
19856
|
port: boundPort,
|
|
19652
|
-
url: `${protocol}://${host}:${boundPort}${
|
|
19653
|
-
close: () => new Promise((
|
|
19654
|
-
server.close((err) => err ? reject(err) :
|
|
19857
|
+
url: `${protocol}://${host}:${boundPort}${path36}`,
|
|
19858
|
+
close: () => new Promise((resolve11, reject) => {
|
|
19859
|
+
server.close((err) => err ? reject(err) : resolve11());
|
|
19655
19860
|
})
|
|
19656
19861
|
};
|
|
19657
19862
|
}
|
|
@@ -20351,7 +20556,7 @@ async function downloadGitHubTarball(parsed) {
|
|
|
20351
20556
|
`Tarball too large (${(Number.parseInt(contentLength, 10) / 1024 / 1024).toFixed(1)}MB). Max: ${MAX_TARBALL_SIZE / 1024 / 1024}MB`
|
|
20352
20557
|
);
|
|
20353
20558
|
}
|
|
20354
|
-
const tempDir = await fsp3.mkdtemp(path6.join(
|
|
20559
|
+
const tempDir = await fsp3.mkdtemp(path6.join(os6.tmpdir(), "wskill-"));
|
|
20355
20560
|
try {
|
|
20356
20561
|
if (!response.body) {
|
|
20357
20562
|
throw new Error("Empty response body from GitHub API");
|
|
@@ -22851,8 +23056,8 @@ var ReportGenerator = class {
|
|
|
22851
23056
|
try {
|
|
22852
23057
|
await stat(this.options.outputDir);
|
|
22853
23058
|
} catch {
|
|
22854
|
-
const { mkdir:
|
|
22855
|
-
await
|
|
23059
|
+
const { mkdir: mkdir16 } = await import('fs/promises');
|
|
23060
|
+
await mkdir16(this.options.outputDir, { recursive: true });
|
|
22856
23061
|
}
|
|
22857
23062
|
}
|
|
22858
23063
|
generateMarkdown(result) {
|
|
@@ -22983,22 +23188,24 @@ var ReportGenerator = class {
|
|
|
22983
23188
|
medium: "#ca8a04",
|
|
22984
23189
|
low: "#16a34a"
|
|
22985
23190
|
};
|
|
22986
|
-
const rows = result.findings.map(
|
|
22987
|
-
|
|
23191
|
+
const rows = result.findings.map((f) => {
|
|
23192
|
+
const sevColor = severityColors[f.severity] ?? "#000000";
|
|
23193
|
+
const loc = `${escapeHtml(f.file)}${f.line ? `:${escapeHtml(String(f.line))}` : ""}`;
|
|
23194
|
+
return `
|
|
22988
23195
|
<tr>
|
|
22989
|
-
<td style="color: ${
|
|
22990
|
-
<td>${f.category}</td>
|
|
22991
|
-
<td>${f.title}</td>
|
|
22992
|
-
<td><code>${
|
|
22993
|
-
<td>${f.remediation}</td>
|
|
23196
|
+
<td style="color: ${escapeHtml(sevColor)}; font-weight: bold;">${escapeHtml(f.severity.toUpperCase())}</td>
|
|
23197
|
+
<td>${escapeHtml(f.category)}</td>
|
|
23198
|
+
<td>${escapeHtml(f.title)}</td>
|
|
23199
|
+
<td><code>${loc}</code></td>
|
|
23200
|
+
<td>${escapeHtml(f.remediation)}</td>
|
|
22994
23201
|
</tr>
|
|
22995
|
-
|
|
22996
|
-
).join("");
|
|
23202
|
+
`;
|
|
23203
|
+
}).join("");
|
|
22997
23204
|
return `
|
|
22998
23205
|
<!DOCTYPE html>
|
|
22999
23206
|
<html>
|
|
23000
23207
|
<head>
|
|
23001
|
-
<title>Security Scan Report - ${new Date(result.timestamp).toLocaleDateString()}</title>
|
|
23208
|
+
<title>Security Scan Report - ${escapeHtml(new Date(result.timestamp).toLocaleDateString())}</title>
|
|
23002
23209
|
<style>
|
|
23003
23210
|
body { font-family: system-ui, sans-serif; margin: 2rem; }
|
|
23004
23211
|
table { border-collapse: collapse; width: 100%; }
|
|
@@ -23009,9 +23216,9 @@ var ReportGenerator = class {
|
|
|
23009
23216
|
</head>
|
|
23010
23217
|
<body>
|
|
23011
23218
|
<h1>Security Scan Report</h1>
|
|
23012
|
-
<p><strong>Generated:</strong> ${new Date(result.timestamp).toLocaleString()}</p>
|
|
23013
|
-
<p><strong>Project:</strong> ${result.projectRoot}</p>
|
|
23014
|
-
<p><strong>Tech Stack:</strong> ${result.techStack.stack} (${result.techStack.packageManager})</p>
|
|
23219
|
+
<p><strong>Generated:</strong> ${escapeHtml(new Date(result.timestamp).toLocaleString())}</p>
|
|
23220
|
+
<p><strong>Project:</strong> ${escapeHtml(result.projectRoot)}</p>
|
|
23221
|
+
<p><strong>Tech Stack:</strong> ${escapeHtml(result.techStack.stack)} (${escapeHtml(result.techStack.packageManager)})</p>
|
|
23015
23222
|
|
|
23016
23223
|
<h2>Summary</h2>
|
|
23017
23224
|
<ul>
|
|
@@ -23037,6 +23244,9 @@ var ReportGenerator = class {
|
|
|
23037
23244
|
`.trim();
|
|
23038
23245
|
}
|
|
23039
23246
|
};
|
|
23247
|
+
function escapeHtml(value) {
|
|
23248
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
23249
|
+
}
|
|
23040
23250
|
var defaultReportGenerator = new ReportGenerator();
|
|
23041
23251
|
|
|
23042
23252
|
// src/security-scanner/gitignore-updater.ts
|
|
@@ -23127,7 +23337,7 @@ var SecurityScannerOrchestrator = class {
|
|
|
23127
23337
|
const delay = Math.round(policy.delayMs(attempt));
|
|
23128
23338
|
const status = isProviderErr ? err.status : 0;
|
|
23129
23339
|
console.warn(`[SecurityScanner] retry ${attempt + 1} after ${delay}ms (status=${status}) \u2014 ${errAsErr.message}`);
|
|
23130
|
-
await new Promise((
|
|
23340
|
+
await new Promise((resolve11) => setTimeout(resolve11, delay));
|
|
23131
23341
|
return this.completeWithRetry(provider, request, abortController, attempt + 1);
|
|
23132
23342
|
}
|
|
23133
23343
|
}
|
|
@@ -23872,7 +24082,7 @@ var FleetManager = class {
|
|
|
23872
24082
|
leaderContextPressure = 0;
|
|
23873
24083
|
/** Maximum context load fraction before spawn is refused. */
|
|
23874
24084
|
maxLeaderContextLoad;
|
|
23875
|
-
/** Provider's max context window in tokens. */
|
|
24085
|
+
/** Provider's max context window in tokens, or a live resolver for runtime model switches. */
|
|
23876
24086
|
maxContext;
|
|
23877
24087
|
constructor(opts = {}) {
|
|
23878
24088
|
this.manifestPath = opts.manifestPath;
|
|
@@ -23952,7 +24162,8 @@ var FleetManager = class {
|
|
|
23952
24162
|
}
|
|
23953
24163
|
}
|
|
23954
24164
|
if (this.maxLeaderContextLoad < 1) {
|
|
23955
|
-
const
|
|
24165
|
+
const maxContext = this.resolveMaxContext();
|
|
24166
|
+
const threshold = maxContext * this.maxLeaderContextLoad;
|
|
23956
24167
|
if (this.leaderContextPressure >= threshold) {
|
|
23957
24168
|
return {
|
|
23958
24169
|
kind: "max_context_load",
|
|
@@ -23966,6 +24177,10 @@ var FleetManager = class {
|
|
|
23966
24177
|
setLeaderContextPressure(tokens) {
|
|
23967
24178
|
this.leaderContextPressure = tokens;
|
|
23968
24179
|
}
|
|
24180
|
+
resolveMaxContext() {
|
|
24181
|
+
const resolved = typeof this.maxContext === "function" ? this.maxContext() : this.maxContext;
|
|
24182
|
+
return resolved && resolved > 0 ? resolved : 128e3;
|
|
24183
|
+
}
|
|
23969
24184
|
/**
|
|
23970
24185
|
* Assign a memorable nickname (e.g. "Einstein (Bug Hunter)") to the config,
|
|
23971
24186
|
* record it so the same name is never reused, then record the spawn.
|
|
@@ -24698,12 +24913,12 @@ function makeContinueToNextIterationTool() {
|
|
|
24698
24913
|
// src/core/iteration-limit.ts
|
|
24699
24914
|
function requestLimitExtension(opts) {
|
|
24700
24915
|
const { events, currentIterations, currentLimit, autoExtend, timeoutMs = 3e4 } = opts;
|
|
24701
|
-
return new Promise((
|
|
24916
|
+
return new Promise((resolve11) => {
|
|
24702
24917
|
let resolved = false;
|
|
24703
24918
|
const timerFired = () => {
|
|
24704
24919
|
if (!resolved) {
|
|
24705
24920
|
resolved = true;
|
|
24706
|
-
|
|
24921
|
+
resolve11(0);
|
|
24707
24922
|
}
|
|
24708
24923
|
};
|
|
24709
24924
|
const timer = setTimeout(timerFired, timeoutMs);
|
|
@@ -24712,14 +24927,14 @@ function requestLimitExtension(opts) {
|
|
|
24712
24927
|
if (!resolved) {
|
|
24713
24928
|
resolved = true;
|
|
24714
24929
|
clearTimeout(timer);
|
|
24715
|
-
|
|
24930
|
+
resolve11(0);
|
|
24716
24931
|
}
|
|
24717
24932
|
};
|
|
24718
24933
|
const grant = (extra) => {
|
|
24719
24934
|
if (!resolved) {
|
|
24720
24935
|
resolved = true;
|
|
24721
24936
|
clearTimeout(timer);
|
|
24722
|
-
|
|
24937
|
+
resolve11(Math.max(0, extra));
|
|
24723
24938
|
}
|
|
24724
24939
|
};
|
|
24725
24940
|
events.emit("iteration.limit_reached", {
|
|
@@ -24733,7 +24948,7 @@ function requestLimitExtension(opts) {
|
|
|
24733
24948
|
if (!resolved) {
|
|
24734
24949
|
resolved = true;
|
|
24735
24950
|
clearTimeout(timer);
|
|
24736
|
-
|
|
24951
|
+
resolve11(100);
|
|
24737
24952
|
}
|
|
24738
24953
|
});
|
|
24739
24954
|
}
|
|
@@ -25325,13 +25540,13 @@ var Agent = class {
|
|
|
25325
25540
|
}
|
|
25326
25541
|
}
|
|
25327
25542
|
waitForConfirm(info) {
|
|
25328
|
-
return new Promise((
|
|
25543
|
+
return new Promise((resolve11) => {
|
|
25329
25544
|
this.events.emit("tool.confirm_needed", {
|
|
25330
25545
|
tool: info.tool,
|
|
25331
25546
|
input: info.input,
|
|
25332
25547
|
toolUseId: info.toolUseId,
|
|
25333
25548
|
suggestedPattern: info.suggestedPattern,
|
|
25334
|
-
resolve:
|
|
25549
|
+
resolve: resolve11
|
|
25335
25550
|
});
|
|
25336
25551
|
});
|
|
25337
25552
|
}
|
|
@@ -25405,6 +25620,85 @@ function sizeSignals(toolName, content) {
|
|
|
25405
25620
|
}
|
|
25406
25621
|
return { outputBytes, outputTokens, outputLines };
|
|
25407
25622
|
}
|
|
25623
|
+
async function bootConfig(options = {}) {
|
|
25624
|
+
const { flags = {}, appLabel = "wstack", loadSyncConfig = true } = options;
|
|
25625
|
+
const cwd = typeof flags["cwd"] === "string" ? path6.resolve(flags["cwd"]) : process.cwd();
|
|
25626
|
+
const pathResolver = new DefaultPathResolver(cwd);
|
|
25627
|
+
const projectRoot = pathResolver.projectRoot;
|
|
25628
|
+
const userHome = os6.homedir();
|
|
25629
|
+
const wpaths = resolveWstackPaths({ projectRoot, userHome });
|
|
25630
|
+
await fsp3.mkdir(wpaths.globalRoot, { recursive: true });
|
|
25631
|
+
await fsp3.mkdir(wpaths.projectDir, { recursive: true });
|
|
25632
|
+
await fsp3.mkdir(wpaths.projectSessions, { recursive: true });
|
|
25633
|
+
await writeProjectMeta(wpaths, projectRoot);
|
|
25634
|
+
const vault = new DefaultSecretVault({ keyFile: wpaths.secretsKey });
|
|
25635
|
+
for (const file of [wpaths.globalConfig, wpaths.projectLocalConfig]) {
|
|
25636
|
+
try {
|
|
25637
|
+
const { migrated } = await migratePlaintextSecrets(file, vault);
|
|
25638
|
+
if (migrated > 0) {
|
|
25639
|
+
writeErr(`[${appLabel}] Encrypted ${migrated} plaintext secret(s) in ${file}
|
|
25640
|
+
`);
|
|
25641
|
+
}
|
|
25642
|
+
} catch {
|
|
25643
|
+
}
|
|
25644
|
+
}
|
|
25645
|
+
const configLoader = new DefaultConfigLoader({ paths: wpaths, vault });
|
|
25646
|
+
let config = await configLoader.load({ cliFlags: flagsToConfigPatch(flags) });
|
|
25647
|
+
if (loadSyncConfig) {
|
|
25648
|
+
const syncConfig = await configLoader.loadSyncConfig();
|
|
25649
|
+
if (syncConfig) {
|
|
25650
|
+
config = Object.freeze({ ...config, sync: syncConfig });
|
|
25651
|
+
}
|
|
25652
|
+
}
|
|
25653
|
+
const logger = new DefaultLogger({ level: config.log?.level ?? "info", file: wpaths.logFile });
|
|
25654
|
+
return {
|
|
25655
|
+
cwd,
|
|
25656
|
+
projectRoot,
|
|
25657
|
+
userHome,
|
|
25658
|
+
wpaths,
|
|
25659
|
+
pathResolver,
|
|
25660
|
+
config,
|
|
25661
|
+
vault,
|
|
25662
|
+
logger,
|
|
25663
|
+
globalConfigPath: wpaths.globalConfig
|
|
25664
|
+
};
|
|
25665
|
+
}
|
|
25666
|
+
function flagsToConfigPatch(flags) {
|
|
25667
|
+
const patch = {};
|
|
25668
|
+
if (typeof flags["provider"] === "string") patch.provider = flags["provider"];
|
|
25669
|
+
if (typeof flags["model"] === "string") patch.model = flags["model"];
|
|
25670
|
+
if (typeof flags["cwd"] === "string") patch.cwd = flags["cwd"];
|
|
25671
|
+
if (typeof flags["log-level"] === "string") {
|
|
25672
|
+
patch.log = { level: flags["log-level"] };
|
|
25673
|
+
} else if (flags["verbose"]) {
|
|
25674
|
+
patch.log = { level: "debug" };
|
|
25675
|
+
} else if (flags["trace"]) {
|
|
25676
|
+
patch.log = { level: "trace" };
|
|
25677
|
+
}
|
|
25678
|
+
if (flags["yolo"]) patch.yolo = true;
|
|
25679
|
+
if (flags["no-features"]) {
|
|
25680
|
+
patch.features = {
|
|
25681
|
+
mcp: false,
|
|
25682
|
+
plugins: false,
|
|
25683
|
+
memory: false,
|
|
25684
|
+
modelsRegistry: false,
|
|
25685
|
+
skills: false
|
|
25686
|
+
};
|
|
25687
|
+
}
|
|
25688
|
+
return patch;
|
|
25689
|
+
}
|
|
25690
|
+
async function writeProjectMeta(paths, projectRoot) {
|
|
25691
|
+
try {
|
|
25692
|
+
await fsp3.mkdir(paths.projectDir, { recursive: true });
|
|
25693
|
+
const meta = {
|
|
25694
|
+
hash: paths.projectHash,
|
|
25695
|
+
root: projectRoot,
|
|
25696
|
+
lastSeen: (/* @__PURE__ */ new Date()).toISOString()
|
|
25697
|
+
};
|
|
25698
|
+
await fsp3.writeFile(paths.projectMeta, JSON.stringify(meta, null, 2));
|
|
25699
|
+
} catch {
|
|
25700
|
+
}
|
|
25701
|
+
}
|
|
25408
25702
|
|
|
25409
25703
|
// src/core/conversation-state.ts
|
|
25410
25704
|
var ConversationState = class {
|
|
@@ -25704,8 +25998,8 @@ var InputBuilder = class {
|
|
|
25704
25998
|
async registerFile(input) {
|
|
25705
25999
|
const ref = await this.store.add({ ...input, kind: "file" });
|
|
25706
26000
|
this.refs.push(ref);
|
|
25707
|
-
const
|
|
25708
|
-
return `[file:${
|
|
26001
|
+
const path36 = ref.meta.filename ?? ref.meta.label ?? String(ref.seq);
|
|
26002
|
+
return `[file:${path36}]`;
|
|
25709
26003
|
}
|
|
25710
26004
|
/**
|
|
25711
26005
|
* Whether `appendPaste(text)` would collapse the text to a placeholder
|
|
@@ -26036,7 +26330,7 @@ summarize it, and let the tool result hold only the summary.`);
|
|
|
26036
26330
|
const cached = this.envCacheByRoot.get(ctx.projectRoot);
|
|
26037
26331
|
if (cached) return cached;
|
|
26038
26332
|
const today = this.opts.todayIso ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
26039
|
-
const platform2 = `${
|
|
26333
|
+
const platform2 = `${os6.platform()} ${os6.release()}`;
|
|
26040
26334
|
const shell = process.env.SHELL ?? process.env.ComSpec ?? "unknown";
|
|
26041
26335
|
const node = process.version;
|
|
26042
26336
|
const isGit = await this.dirExists(path6.join(ctx.projectRoot, ".git"));
|
|
@@ -26105,12 +26399,12 @@ ${mem}`);
|
|
|
26105
26399
|
}
|
|
26106
26400
|
}
|
|
26107
26401
|
async gitStatus(root) {
|
|
26108
|
-
return new Promise((
|
|
26402
|
+
return new Promise((resolve11) => {
|
|
26109
26403
|
let settled = false;
|
|
26110
26404
|
const finish = (s) => {
|
|
26111
26405
|
if (settled) return;
|
|
26112
26406
|
settled = true;
|
|
26113
|
-
|
|
26407
|
+
resolve11(s);
|
|
26114
26408
|
};
|
|
26115
26409
|
let proc;
|
|
26116
26410
|
const timer = setTimeout(() => {
|
|
@@ -27048,6 +27342,7 @@ var PhaseOrchestrator = class {
|
|
|
27048
27342
|
maxConcurrentPhases: opts.maxConcurrentPhases ?? 1,
|
|
27049
27343
|
maxConcurrentTasks: opts.maxConcurrentTasks ?? 2,
|
|
27050
27344
|
maxRetries: opts.maxRetries ?? 2,
|
|
27345
|
+
maxVerifyAttempts: opts.maxVerifyAttempts ?? 2,
|
|
27051
27346
|
autonomous: opts.autonomous ?? true,
|
|
27052
27347
|
phaseDelayMs: opts.phaseDelayMs ?? 0,
|
|
27053
27348
|
stopOnFailure: opts.stopOnFailure ?? true,
|
|
@@ -27066,11 +27361,15 @@ var PhaseOrchestrator = class {
|
|
|
27066
27361
|
this.graph.updatedAt = Date.now();
|
|
27067
27362
|
let readyPhases = this.getReadyPhases();
|
|
27068
27363
|
while (readyPhases.length > 0 && !this.stopped) {
|
|
27364
|
+
await this.waitWhilePaused();
|
|
27365
|
+
if (this.stopped) break;
|
|
27069
27366
|
const batch = readyPhases.slice(0, this.opts.maxConcurrentPhases);
|
|
27070
27367
|
await Promise.all(batch.map((p) => this.startPhase(p)));
|
|
27071
27368
|
if (this.opts.phaseDelayMs > 0) {
|
|
27072
27369
|
await this.delay(this.opts.phaseDelayMs);
|
|
27073
27370
|
}
|
|
27371
|
+
await this.waitWhilePaused();
|
|
27372
|
+
if (this.stopped) break;
|
|
27074
27373
|
readyPhases = this.getReadyPhases().filter(
|
|
27075
27374
|
(p) => !this.runningPhases.has(p.id) && p.status !== "completed" && p.status !== "failed"
|
|
27076
27375
|
);
|
|
@@ -27094,7 +27393,10 @@ var PhaseOrchestrator = class {
|
|
|
27094
27393
|
resume() {
|
|
27095
27394
|
this.paused = false;
|
|
27096
27395
|
this.tick().catch((err) => {
|
|
27097
|
-
console.error(
|
|
27396
|
+
console.error(
|
|
27397
|
+
"[phase-orchestrator] tick failed:",
|
|
27398
|
+
err instanceof Error ? err.message : String(err)
|
|
27399
|
+
);
|
|
27098
27400
|
});
|
|
27099
27401
|
}
|
|
27100
27402
|
/** Tamamen durdur — aktif fazlar da durdurulur */
|
|
@@ -27175,33 +27477,30 @@ var PhaseOrchestrator = class {
|
|
|
27175
27477
|
failed: failedTasks
|
|
27176
27478
|
});
|
|
27177
27479
|
if (failedTasks > 0 && this.opts.stopOnFailure) {
|
|
27178
|
-
this.
|
|
27179
|
-
|
|
27180
|
-
phase.actualDurationMs = Date.now() - (phase.startedAt ?? Date.now());
|
|
27181
|
-
this.runningPhases.delete(phase.id);
|
|
27182
|
-
this.graph.activePhaseIds = this.graph.activePhaseIds.filter((id) => id !== phase.id);
|
|
27183
|
-
this.emit("phase.failed", {
|
|
27184
|
-
phaseId: phase.id,
|
|
27185
|
-
name: phase.name,
|
|
27186
|
-
error: `${failedTasks} task(s) failed`
|
|
27187
|
-
});
|
|
27188
|
-
this.ctx.onPhaseFail?.(phase, new Error(`${failedTasks} task(s) failed`));
|
|
27189
|
-
await this.keepWorktreeForReview(phase);
|
|
27190
|
-
} else {
|
|
27191
|
-
this.updatePhaseStatus(phase, "completed");
|
|
27192
|
-
phase.completedAt = Date.now();
|
|
27193
|
-
phase.actualDurationMs = Date.now() - (phase.startedAt ?? Date.now());
|
|
27194
|
-
this.runningPhases.delete(phase.id);
|
|
27195
|
-
this.graph.activePhaseIds = this.graph.activePhaseIds.filter((id) => id !== phase.id);
|
|
27196
|
-
this.graph.completedPhaseIds.push(phase.id);
|
|
27197
|
-
this.emit("phase.completed", {
|
|
27198
|
-
phaseId: phase.id,
|
|
27199
|
-
name: phase.name,
|
|
27200
|
-
durationMs: phase.actualDurationMs
|
|
27201
|
-
});
|
|
27202
|
-
this.ctx.onPhaseComplete?.(phase);
|
|
27203
|
-
await this.commitAndEnqueueMerge(phase);
|
|
27480
|
+
await this.failPhaseAfterTasks(phase, `${failedTasks} task(s) failed`);
|
|
27481
|
+
return;
|
|
27204
27482
|
}
|
|
27483
|
+
const verdict = await this.runVerifyGate(phase);
|
|
27484
|
+
if (!verdict.ok) {
|
|
27485
|
+
await this.failPhaseAfterTasks(
|
|
27486
|
+
phase,
|
|
27487
|
+
`verification failed${verdict.output ? `: ${this.truncate(verdict.output)}` : ""}`
|
|
27488
|
+
);
|
|
27489
|
+
return;
|
|
27490
|
+
}
|
|
27491
|
+
this.updatePhaseStatus(phase, "completed");
|
|
27492
|
+
phase.completedAt = Date.now();
|
|
27493
|
+
phase.actualDurationMs = Date.now() - (phase.startedAt ?? Date.now());
|
|
27494
|
+
this.runningPhases.delete(phase.id);
|
|
27495
|
+
this.graph.activePhaseIds = this.graph.activePhaseIds.filter((id) => id !== phase.id);
|
|
27496
|
+
this.graph.completedPhaseIds.push(phase.id);
|
|
27497
|
+
this.emit("phase.completed", {
|
|
27498
|
+
phaseId: phase.id,
|
|
27499
|
+
name: phase.name,
|
|
27500
|
+
durationMs: phase.actualDurationMs
|
|
27501
|
+
});
|
|
27502
|
+
this.ctx.onPhaseComplete?.(phase);
|
|
27503
|
+
await this.commitAndEnqueueMerge(phase);
|
|
27205
27504
|
} catch (error) {
|
|
27206
27505
|
this.updatePhaseStatus(phase, "failed");
|
|
27207
27506
|
phase.completedAt = Date.now();
|
|
@@ -27218,6 +27517,69 @@ var PhaseOrchestrator = class {
|
|
|
27218
27517
|
await this.keepWorktreeForReview(phase);
|
|
27219
27518
|
}
|
|
27220
27519
|
}
|
|
27520
|
+
// ─── Verification gate ──────────────────────────────────────────────────────
|
|
27521
|
+
/**
|
|
27522
|
+
* Run the verification gate for a phase whose tasks all succeeded. Verifies in
|
|
27523
|
+
* the phase's worktree; on failure, runs the repair pass and re-verifies, up to
|
|
27524
|
+
* `maxVerifyAttempts` repairs. Returns the final verdict. When no `verifyPhase`
|
|
27525
|
+
* callback is wired the gate is a no-op and always passes.
|
|
27526
|
+
*/
|
|
27527
|
+
async runVerifyGate(phase) {
|
|
27528
|
+
if (!this.ctx.verifyPhase) return { ok: true };
|
|
27529
|
+
const env = this.worktreeEnv(phase);
|
|
27530
|
+
for (let attempt = 0; attempt <= this.opts.maxVerifyAttempts; attempt++) {
|
|
27531
|
+
if (this.stopped) return { ok: false, output: "stopped before verification completed" };
|
|
27532
|
+
this.emit("phase.verifying", { phaseId: phase.id, name: phase.name, attempt });
|
|
27533
|
+
let verdict;
|
|
27534
|
+
try {
|
|
27535
|
+
verdict = await this.ctx.verifyPhase(phase, env);
|
|
27536
|
+
} catch (err) {
|
|
27537
|
+
verdict = { ok: false, output: err instanceof Error ? err.message : String(err) };
|
|
27538
|
+
}
|
|
27539
|
+
if (verdict.ok) return { ok: true };
|
|
27540
|
+
this.emit("phase.verifyFailed", {
|
|
27541
|
+
phaseId: phase.id,
|
|
27542
|
+
name: phase.name,
|
|
27543
|
+
attempt,
|
|
27544
|
+
error: verdict.output
|
|
27545
|
+
});
|
|
27546
|
+
if (attempt >= this.opts.maxVerifyAttempts || !this.ctx.repairPhase || this.stopped) {
|
|
27547
|
+
return { ok: false, output: verdict.output };
|
|
27548
|
+
}
|
|
27549
|
+
this.emit("phase.repairing", { phaseId: phase.id, name: phase.name, attempt: attempt + 1 });
|
|
27550
|
+
try {
|
|
27551
|
+
await this.ctx.repairPhase(
|
|
27552
|
+
phase,
|
|
27553
|
+
verdict.output ?? "verification failed",
|
|
27554
|
+
attempt + 1,
|
|
27555
|
+
env
|
|
27556
|
+
);
|
|
27557
|
+
} catch {
|
|
27558
|
+
}
|
|
27559
|
+
}
|
|
27560
|
+
return { ok: false };
|
|
27561
|
+
}
|
|
27562
|
+
/** Worktree env (cwd/branch) for a phase, or undefined if it runs on the shared tree. */
|
|
27563
|
+
worktreeEnv(phase) {
|
|
27564
|
+
const handle = this.phaseWorktrees.get(phase.id);
|
|
27565
|
+
return handle ? { cwd: handle.dir, branch: handle.branch } : void 0;
|
|
27566
|
+
}
|
|
27567
|
+
/** Shared failure bookkeeping for a phase whose tasks ran but the phase failed. */
|
|
27568
|
+
async failPhaseAfterTasks(phase, error) {
|
|
27569
|
+
this.updatePhaseStatus(phase, "failed");
|
|
27570
|
+
phase.completedAt = Date.now();
|
|
27571
|
+
phase.actualDurationMs = Date.now() - (phase.startedAt ?? Date.now());
|
|
27572
|
+
this.runningPhases.delete(phase.id);
|
|
27573
|
+
this.graph.activePhaseIds = this.graph.activePhaseIds.filter((id) => id !== phase.id);
|
|
27574
|
+
this.emit("phase.failed", { phaseId: phase.id, name: phase.name, error });
|
|
27575
|
+
this.ctx.onPhaseFail?.(phase, new Error(error));
|
|
27576
|
+
await this.keepWorktreeForReview(phase);
|
|
27577
|
+
}
|
|
27578
|
+
/** Trim long verifier output so it fits cleanly in an event/error message. */
|
|
27579
|
+
truncate(text, max = 500) {
|
|
27580
|
+
const t2 = text.trim();
|
|
27581
|
+
return t2.length <= max ? t2 : `${t2.slice(0, max)}\u2026 (+${t2.length - max} chars)`;
|
|
27582
|
+
}
|
|
27221
27583
|
// ─── Worktree integration ───────────────────────────────────────────────────
|
|
27222
27584
|
/**
|
|
27223
27585
|
* Commit the phase's worktree changes, then enqueue the merge back into the
|
|
@@ -27240,13 +27602,40 @@ var PhaseOrchestrator = class {
|
|
|
27240
27602
|
})();
|
|
27241
27603
|
this.phaseMergePromise.set(phase.id, merged);
|
|
27242
27604
|
}
|
|
27243
|
-
/**
|
|
27605
|
+
/**
|
|
27606
|
+
* Squash-merge one phase. When a `resolveConflict` callback is wired, a merge
|
|
27607
|
+
* conflict is handed to it (a resolver subagent) before giving up; only if
|
|
27608
|
+
* that fails does the worktree fall to needs-review and the run continues.
|
|
27609
|
+
*/
|
|
27244
27610
|
async mergeOne(phase, handle) {
|
|
27245
27611
|
if (!this.worktrees) return;
|
|
27246
27612
|
try {
|
|
27247
|
-
const
|
|
27613
|
+
const resolve11 = this.ctx.resolveConflict ? async (info) => {
|
|
27614
|
+
const shouldResolve = await this.shouldAttemptConflictResolution(phase, info);
|
|
27615
|
+
if (!shouldResolve) return false;
|
|
27616
|
+
this.emit("phase.conflictResolving", {
|
|
27617
|
+
phaseId: phase.id,
|
|
27618
|
+
name: phase.name,
|
|
27619
|
+
files: info.conflictFiles
|
|
27620
|
+
});
|
|
27621
|
+
return this.ctx.resolveConflict(phase, info);
|
|
27622
|
+
} : void 0;
|
|
27623
|
+
const result = await this.worktrees.merge(handle, { squash: true, resolve: resolve11 });
|
|
27624
|
+
if (result.resolved) {
|
|
27625
|
+
this.emit("phase.conflictResolved", { phaseId: phase.id, name: phase.name });
|
|
27626
|
+
}
|
|
27627
|
+
this.setIntegrationMetadata(phase, result.ok ? "merged" : "needs_review", {
|
|
27628
|
+
branch: handle.branch,
|
|
27629
|
+
worktreeDir: handle.dir,
|
|
27630
|
+
conflictFiles: result.conflictFiles
|
|
27631
|
+
});
|
|
27248
27632
|
await this.worktrees.release(handle, { keep: !result.ok });
|
|
27249
27633
|
} catch (err) {
|
|
27634
|
+
this.setIntegrationMetadata(phase, "merge_failed", {
|
|
27635
|
+
branch: handle.branch,
|
|
27636
|
+
worktreeDir: handle.dir,
|
|
27637
|
+
error: err instanceof Error ? err.message : String(err)
|
|
27638
|
+
});
|
|
27250
27639
|
this.emit("phase.failed", {
|
|
27251
27640
|
phaseId: phase.id,
|
|
27252
27641
|
name: phase.name,
|
|
@@ -27254,6 +27643,56 @@ var PhaseOrchestrator = class {
|
|
|
27254
27643
|
});
|
|
27255
27644
|
}
|
|
27256
27645
|
}
|
|
27646
|
+
async shouldAttemptConflictResolution(phase, info) {
|
|
27647
|
+
if (!this.ctx.brain) return true;
|
|
27648
|
+
const decision = await this.ctx.brain.decide({
|
|
27649
|
+
id: `autophase-conflict-${phase.id}`,
|
|
27650
|
+
source: "autophase",
|
|
27651
|
+
question: `Should AutoPhase try to resolve merge conflicts for phase "${phase.name}" automatically?`,
|
|
27652
|
+
context: [
|
|
27653
|
+
`Phase id: ${phase.id}`,
|
|
27654
|
+
`Conflicted files: ${info.conflictFiles.join(", ") || "(unknown)"}`,
|
|
27655
|
+
`Base working tree: ${info.cwd}`
|
|
27656
|
+
].join("\n"),
|
|
27657
|
+
risk: "high",
|
|
27658
|
+
fallback: "ask_human",
|
|
27659
|
+
options: [
|
|
27660
|
+
{
|
|
27661
|
+
id: "resolve",
|
|
27662
|
+
label: "Try the configured conflict resolver",
|
|
27663
|
+
consequence: "A resolver agent may edit conflicted files in the base working tree.",
|
|
27664
|
+
risk: "medium"
|
|
27665
|
+
},
|
|
27666
|
+
{
|
|
27667
|
+
id: "review",
|
|
27668
|
+
label: "Keep the worktree for human review",
|
|
27669
|
+
consequence: "No automatic conflict resolution is attempted.",
|
|
27670
|
+
risk: "low",
|
|
27671
|
+
recommended: true
|
|
27672
|
+
}
|
|
27673
|
+
]
|
|
27674
|
+
});
|
|
27675
|
+
phase.metadata = {
|
|
27676
|
+
...phase.metadata,
|
|
27677
|
+
brainConflictDecision: decision.type,
|
|
27678
|
+
brainConflictDecisionAt: Date.now()
|
|
27679
|
+
};
|
|
27680
|
+
if (decision.type !== "answer") return false;
|
|
27681
|
+
return decision.optionId === "resolve" || /\bresolve\b/i.test(decision.text);
|
|
27682
|
+
}
|
|
27683
|
+
setIntegrationMetadata(phase, status, details = {}) {
|
|
27684
|
+
phase.metadata = {
|
|
27685
|
+
...phase.metadata,
|
|
27686
|
+
integrationStatus: status,
|
|
27687
|
+
integrationBranch: details.branch,
|
|
27688
|
+
integrationWorktreeDir: details.worktreeDir,
|
|
27689
|
+
integrationConflictFiles: details.conflictFiles,
|
|
27690
|
+
integrationError: details.error,
|
|
27691
|
+
integrationUpdatedAt: Date.now()
|
|
27692
|
+
};
|
|
27693
|
+
phase.updatedAt = Date.now();
|
|
27694
|
+
this.graph.updatedAt = Date.now();
|
|
27695
|
+
}
|
|
27257
27696
|
/** A failed phase keeps its worktree on disk for inspection (no merge). */
|
|
27258
27697
|
async keepWorktreeForReview(phase) {
|
|
27259
27698
|
const handle = this.phaseWorktrees.get(phase.id);
|
|
@@ -27262,6 +27701,10 @@ var PhaseOrchestrator = class {
|
|
|
27262
27701
|
await this.worktrees.commitAll(handle, `autophase(${phase.name}) [failed]: ${phase.id}`);
|
|
27263
27702
|
} catch {
|
|
27264
27703
|
}
|
|
27704
|
+
this.setIntegrationMetadata(phase, "not_merged_failed_phase", {
|
|
27705
|
+
branch: handle.branch,
|
|
27706
|
+
worktreeDir: handle.dir
|
|
27707
|
+
});
|
|
27265
27708
|
await this.worktrees.release(handle, { keep: true }).catch(() => {
|
|
27266
27709
|
});
|
|
27267
27710
|
}
|
|
@@ -27308,7 +27751,11 @@ var PhaseOrchestrator = class {
|
|
|
27308
27751
|
const currentRetries = this.taskRetryCounts.get(taskKey) ?? 0;
|
|
27309
27752
|
if (currentRetries < this.opts.maxRetries) {
|
|
27310
27753
|
this.taskRetryCounts.set(taskKey, currentRetries + 1);
|
|
27311
|
-
tracker.updateNodeStatus(
|
|
27754
|
+
tracker.updateNodeStatus(
|
|
27755
|
+
task.id,
|
|
27756
|
+
"pending",
|
|
27757
|
+
`Retry ${currentRetries + 1}/${this.opts.maxRetries}`
|
|
27758
|
+
);
|
|
27312
27759
|
this.emit("phase.taskRetrying", {
|
|
27313
27760
|
phaseId: phase.id,
|
|
27314
27761
|
taskId: task.id,
|
|
@@ -27317,7 +27764,11 @@ var PhaseOrchestrator = class {
|
|
|
27317
27764
|
maxRetries: this.opts.maxRetries
|
|
27318
27765
|
});
|
|
27319
27766
|
} else {
|
|
27320
|
-
tracker.updateNodeStatus(
|
|
27767
|
+
tracker.updateNodeStatus(
|
|
27768
|
+
task.id,
|
|
27769
|
+
"failed",
|
|
27770
|
+
error instanceof Error ? error.message : String(error)
|
|
27771
|
+
);
|
|
27321
27772
|
this.emit("phase.taskFailed", {
|
|
27322
27773
|
phaseId: phase.id,
|
|
27323
27774
|
taskId: task.id,
|
|
@@ -27513,8 +27964,13 @@ var PhaseOrchestrator = class {
|
|
|
27513
27964
|
}
|
|
27514
27965
|
};
|
|
27515
27966
|
}
|
|
27967
|
+
async waitWhilePaused() {
|
|
27968
|
+
while (this.paused && !this.stopped) {
|
|
27969
|
+
await this.delay(100);
|
|
27970
|
+
}
|
|
27971
|
+
}
|
|
27516
27972
|
delay(ms) {
|
|
27517
|
-
return new Promise((
|
|
27973
|
+
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
27518
27974
|
}
|
|
27519
27975
|
};
|
|
27520
27976
|
|
|
@@ -27558,6 +28014,10 @@ var AutoPhaseRunner = class {
|
|
|
27558
28014
|
this.graph = await builder.build();
|
|
27559
28015
|
const ctx = {
|
|
27560
28016
|
executeTask: this.opts.executeTask,
|
|
28017
|
+
verifyPhase: this.opts.verifyPhase,
|
|
28018
|
+
repairPhase: this.opts.repairPhase,
|
|
28019
|
+
resolveConflict: this.opts.resolveConflict,
|
|
28020
|
+
brain: this.opts.brain,
|
|
27561
28021
|
onPhaseComplete: (phase) => {
|
|
27562
28022
|
this.opts.onPhaseComplete?.(phase);
|
|
27563
28023
|
},
|
|
@@ -27574,10 +28034,12 @@ var AutoPhaseRunner = class {
|
|
|
27574
28034
|
maxConcurrentPhases: this.opts.maxConcurrentPhases,
|
|
27575
28035
|
maxConcurrentTasks: this.opts.maxConcurrentTasks,
|
|
27576
28036
|
maxRetries: this.opts.maxRetries,
|
|
28037
|
+
maxVerifyAttempts: this.opts.maxVerifyAttempts,
|
|
27577
28038
|
autonomous: this.opts.autonomous,
|
|
27578
28039
|
phaseDelayMs: this.opts.phaseDelayMs,
|
|
27579
28040
|
stopOnFailure: this.opts.stopOnFailure,
|
|
27580
|
-
events: this.opts.events
|
|
28041
|
+
events: this.opts.events,
|
|
28042
|
+
worktrees: this.opts.worktrees
|
|
27581
28043
|
});
|
|
27582
28044
|
if (this.opts.onProgress) {
|
|
27583
28045
|
this.progressInterval = setInterval(() => {
|
|
@@ -28268,6 +28730,10 @@ var WorktreeManager = class {
|
|
|
28268
28730
|
${merged.stderr}`);
|
|
28269
28731
|
const fromIndex = await this.unmergedFiles();
|
|
28270
28732
|
const conflictFiles = [.../* @__PURE__ */ new Set([...fromOutput, ...fromIndex])];
|
|
28733
|
+
if (opts.resolve) {
|
|
28734
|
+
const finalized = await this.tryResolveConflict(handle, conflictFiles, opts);
|
|
28735
|
+
if (finalized) return finalized;
|
|
28736
|
+
}
|
|
28271
28737
|
await this.runGit(["reset", "--hard", "HEAD"], this.projectRoot);
|
|
28272
28738
|
handle.conflictFiles = conflictFiles;
|
|
28273
28739
|
this.setStatus(handle, "needs-review", { lastError: merged.stderr });
|
|
@@ -28298,6 +28764,52 @@ ${merged.stderr}`);
|
|
|
28298
28764
|
});
|
|
28299
28765
|
return { ok: true };
|
|
28300
28766
|
}
|
|
28767
|
+
/**
|
|
28768
|
+
* Run the caller-supplied resolver against a conflicted squash-merge, then
|
|
28769
|
+
* commit if it cleared every marker. Returns a successful `MergeResult` on a
|
|
28770
|
+
* clean resolution, or `null` to signal the caller should fall back to the
|
|
28771
|
+
* abort path. Never leaves the base tree committed-but-dirty: a partial or
|
|
28772
|
+
* failed resolution returns `null` and the caller hard-resets.
|
|
28773
|
+
*/
|
|
28774
|
+
async tryResolveConflict(handle, conflictFiles, opts) {
|
|
28775
|
+
let resolved = false;
|
|
28776
|
+
try {
|
|
28777
|
+
resolved = await opts.resolve({ conflictFiles, cwd: this.projectRoot });
|
|
28778
|
+
} catch {
|
|
28779
|
+
resolved = false;
|
|
28780
|
+
}
|
|
28781
|
+
if (!resolved) return null;
|
|
28782
|
+
await this.runGit(["add", "-A"], this.projectRoot);
|
|
28783
|
+
if (await this.hasConflictMarkers()) return null;
|
|
28784
|
+
const idArgs = await this.identityArgs(this.projectRoot);
|
|
28785
|
+
const msg = opts.message ?? `merge ${handle.branch} (squash, conflict resolved)`;
|
|
28786
|
+
const commit = await this.runGit([...idArgs, "commit", "-m", msg], this.projectRoot);
|
|
28787
|
+
if (commit.code !== 0 && !/nothing to commit/i.test(commit.stdout + commit.stderr)) {
|
|
28788
|
+
return null;
|
|
28789
|
+
}
|
|
28790
|
+
handle.conflictFiles = conflictFiles;
|
|
28791
|
+
this.setStatus(handle, "merged");
|
|
28792
|
+
this.emit("worktree.merged", {
|
|
28793
|
+
handleId: handle.id,
|
|
28794
|
+
ownerId: handle.ownerId,
|
|
28795
|
+
branch: handle.branch,
|
|
28796
|
+
baseBranch: handle.baseBranch,
|
|
28797
|
+
squash: true
|
|
28798
|
+
});
|
|
28799
|
+
return { ok: true, resolved: true, conflictFiles };
|
|
28800
|
+
}
|
|
28801
|
+
/**
|
|
28802
|
+
* True when staged content still carries conflict markers. `git diff --cached
|
|
28803
|
+
* --check` exits nonzero and prints a "leftover conflict marker" line for each
|
|
28804
|
+
* survivor; whitespace-only errors (also flagged by --check) are ignored so a
|
|
28805
|
+
* clean resolution with unrelated whitespace is not rejected.
|
|
28806
|
+
*/
|
|
28807
|
+
async hasConflictMarkers() {
|
|
28808
|
+
const check = await this.runGit(["diff", "--cached", "--check"], this.projectRoot);
|
|
28809
|
+
if (check.code === 0) return false;
|
|
28810
|
+
return /conflict marker/i.test(`${check.stdout}
|
|
28811
|
+
${check.stderr}`);
|
|
28812
|
+
}
|
|
28301
28813
|
/**
|
|
28302
28814
|
* Remove the worktree + branch. Conflicted/failed handles (or `keep:true`)
|
|
28303
28815
|
* are left on disk for inspection.
|
|
@@ -28446,6 +28958,147 @@ function assertSafePath(dir, projectRoot) {
|
|
|
28446
28958
|
}
|
|
28447
28959
|
}
|
|
28448
28960
|
|
|
28961
|
+
// src/coordination/brain.ts
|
|
28962
|
+
var ObservableBrainArbiter = class {
|
|
28963
|
+
constructor(inner, events) {
|
|
28964
|
+
this.inner = inner;
|
|
28965
|
+
this.events = events;
|
|
28966
|
+
}
|
|
28967
|
+
inner;
|
|
28968
|
+
events;
|
|
28969
|
+
async decide(request) {
|
|
28970
|
+
this.events.emit("brain.decision_requested", { request, at: Date.now() });
|
|
28971
|
+
const decision = await this.inner.decide(request);
|
|
28972
|
+
const event = decision.type === "ask_human" ? "brain.decision_ask_human" : decision.type === "deny" ? "brain.decision_denied" : "brain.decision_answered";
|
|
28973
|
+
this.events.emit(event, { request, decision, at: Date.now() });
|
|
28974
|
+
return decision;
|
|
28975
|
+
}
|
|
28976
|
+
};
|
|
28977
|
+
var BrainDecisionQueue = class {
|
|
28978
|
+
constructor(events, opts = {}) {
|
|
28979
|
+
this.events = events;
|
|
28980
|
+
this.opts = opts;
|
|
28981
|
+
this.offAnswer = this.events.on("brain.human_answered", (answer) => {
|
|
28982
|
+
const pending = this.pending.get(answer.id);
|
|
28983
|
+
if (!pending) return;
|
|
28984
|
+
this.pending.delete(answer.id);
|
|
28985
|
+
if (pending.timer) clearTimeout(pending.timer);
|
|
28986
|
+
if (answer.deny) {
|
|
28987
|
+
pending.resolve({ type: "deny", reason: answer.text ?? "Denied by human." });
|
|
28988
|
+
return;
|
|
28989
|
+
}
|
|
28990
|
+
const option = pending.request.options?.find((o) => o.id === answer.optionId);
|
|
28991
|
+
pending.resolve({
|
|
28992
|
+
type: "answer",
|
|
28993
|
+
optionId: answer.optionId,
|
|
28994
|
+
text: answer.text ?? option?.label ?? answer.optionId ?? "Human answered.",
|
|
28995
|
+
rationale: "Human answered a Brain escalation prompt."
|
|
28996
|
+
});
|
|
28997
|
+
});
|
|
28998
|
+
}
|
|
28999
|
+
events;
|
|
29000
|
+
opts;
|
|
29001
|
+
pending = /* @__PURE__ */ new Map();
|
|
29002
|
+
offAnswer;
|
|
29003
|
+
async requestHumanDecision(request) {
|
|
29004
|
+
const ask = {
|
|
29005
|
+
type: "ask_human",
|
|
29006
|
+
prompt: formatHumanPrompt(request),
|
|
29007
|
+
options: request.options,
|
|
29008
|
+
rationale: "Decision escalated to human authority."
|
|
29009
|
+
};
|
|
29010
|
+
const pending = new Promise((resolve11) => {
|
|
29011
|
+
const entry = { request, resolve: resolve11 };
|
|
29012
|
+
if (this.opts.timeoutMs && this.opts.timeoutMs > 0) {
|
|
29013
|
+
entry.timer = setTimeout(() => {
|
|
29014
|
+
this.pending.delete(request.id);
|
|
29015
|
+
resolve11({ type: "deny", reason: "Brain human decision timed out." });
|
|
29016
|
+
}, this.opts.timeoutMs);
|
|
29017
|
+
}
|
|
29018
|
+
this.pending.set(request.id, entry);
|
|
29019
|
+
});
|
|
29020
|
+
this.events.emit("brain.decision_ask_human", { request, decision: ask, at: Date.now() });
|
|
29021
|
+
return pending;
|
|
29022
|
+
}
|
|
29023
|
+
dispose() {
|
|
29024
|
+
this.offAnswer();
|
|
29025
|
+
for (const [id, pending] of this.pending) {
|
|
29026
|
+
if (pending.timer) clearTimeout(pending.timer);
|
|
29027
|
+
pending.resolve({ type: "deny", reason: "Brain decision queue disposed." });
|
|
29028
|
+
this.pending.delete(id);
|
|
29029
|
+
}
|
|
29030
|
+
}
|
|
29031
|
+
};
|
|
29032
|
+
var HumanEscalatingBrainArbiter = class {
|
|
29033
|
+
constructor(inner, queue) {
|
|
29034
|
+
this.inner = inner;
|
|
29035
|
+
this.queue = queue;
|
|
29036
|
+
}
|
|
29037
|
+
inner;
|
|
29038
|
+
queue;
|
|
29039
|
+
async decide(request) {
|
|
29040
|
+
const decision = await this.inner.decide(request);
|
|
29041
|
+
if (decision.type !== "ask_human") return decision;
|
|
29042
|
+
return this.queue.requestHumanDecision(request);
|
|
29043
|
+
}
|
|
29044
|
+
};
|
|
29045
|
+
var DefaultBrainArbiter = class {
|
|
29046
|
+
allowLowRiskAutoAnswer;
|
|
29047
|
+
constructor(opts = {}) {
|
|
29048
|
+
this.allowLowRiskAutoAnswer = opts.allowLowRiskAutoAnswer ?? true;
|
|
29049
|
+
}
|
|
29050
|
+
async decide(request) {
|
|
29051
|
+
const recommended = request.options?.find((option) => option.recommended);
|
|
29052
|
+
if (this.allowLowRiskAutoAnswer && request.risk === "low" && recommended) {
|
|
29053
|
+
return {
|
|
29054
|
+
type: "answer",
|
|
29055
|
+
optionId: recommended.id,
|
|
29056
|
+
text: recommended.label,
|
|
29057
|
+
rationale: "Low-risk request with an explicit recommended option."
|
|
29058
|
+
};
|
|
29059
|
+
}
|
|
29060
|
+
switch (request.fallback) {
|
|
29061
|
+
case "deny":
|
|
29062
|
+
return {
|
|
29063
|
+
type: "deny",
|
|
29064
|
+
reason: `Brain could not safely decide: ${request.question}`
|
|
29065
|
+
};
|
|
29066
|
+
case "continue":
|
|
29067
|
+
return {
|
|
29068
|
+
type: "answer",
|
|
29069
|
+
text: "Continue with the caller default.",
|
|
29070
|
+
rationale: "No safe Brain decision was available; request fallback is continue."
|
|
29071
|
+
};
|
|
29072
|
+
case "ask_human":
|
|
29073
|
+
return {
|
|
29074
|
+
type: "ask_human",
|
|
29075
|
+
prompt: formatHumanPrompt(request),
|
|
29076
|
+
options: request.options,
|
|
29077
|
+
rationale: "Decision requires human authority or lacks a safe automatic option."
|
|
29078
|
+
};
|
|
29079
|
+
}
|
|
29080
|
+
}
|
|
29081
|
+
};
|
|
29082
|
+
function formatHumanPrompt(request) {
|
|
29083
|
+
const lines = [
|
|
29084
|
+
`Brain requires human decision for ${request.source}:`,
|
|
29085
|
+
`Question: ${request.question}`
|
|
29086
|
+
];
|
|
29087
|
+
if (request.context?.trim()) {
|
|
29088
|
+
lines.push("", "Context:", request.context.trim());
|
|
29089
|
+
}
|
|
29090
|
+
if (request.options?.length) {
|
|
29091
|
+
lines.push("", "Options:");
|
|
29092
|
+
for (const option of request.options) {
|
|
29093
|
+
const risk = option.risk ? ` [risk: ${option.risk}]` : "";
|
|
29094
|
+
const consequence = option.consequence ? ` \u2014 ${option.consequence}` : "";
|
|
29095
|
+
lines.push(`- ${option.id}: ${option.label}${risk}${consequence}`);
|
|
29096
|
+
}
|
|
29097
|
+
}
|
|
29098
|
+
lines.push("", `Risk: ${request.risk}`);
|
|
29099
|
+
return lines.join("\n");
|
|
29100
|
+
}
|
|
29101
|
+
|
|
28449
29102
|
// src/coordination/collab-bus.ts
|
|
28450
29103
|
var CollaborationBus = class {
|
|
28451
29104
|
pausePromise = null;
|
|
@@ -28474,8 +29127,8 @@ var CollaborationBus = class {
|
|
|
28474
29127
|
if (this.isPaused()) return false;
|
|
28475
29128
|
this.pausedAtMs = Date.now();
|
|
28476
29129
|
this.pausedBy = byParticipant;
|
|
28477
|
-
this.pausePromise = new Promise((
|
|
28478
|
-
this.pauseResolve =
|
|
29130
|
+
this.pausePromise = new Promise((resolve11) => {
|
|
29131
|
+
this.pauseResolve = resolve11;
|
|
28479
29132
|
});
|
|
28480
29133
|
return true;
|
|
28481
29134
|
}
|
|
@@ -28511,8 +29164,8 @@ var CollaborationBus = class {
|
|
|
28511
29164
|
return true;
|
|
28512
29165
|
}
|
|
28513
29166
|
let timer;
|
|
28514
|
-
const timeoutPromise = new Promise((
|
|
28515
|
-
timer = setTimeout(() =>
|
|
29167
|
+
const timeoutPromise = new Promise((resolve11) => {
|
|
29168
|
+
timer = setTimeout(() => resolve11("timeout"), timeoutMs);
|
|
28516
29169
|
});
|
|
28517
29170
|
const resumedPromise = this.pausePromise.then(() => "resumed");
|
|
28518
29171
|
const winner = await Promise.race([resumedPromise, timeoutPromise]);
|
|
@@ -29003,7 +29656,7 @@ function createGitPlugin() {
|
|
|
29003
29656
|
}
|
|
29004
29657
|
async function runGit(args, cwd) {
|
|
29005
29658
|
try {
|
|
29006
|
-
return await new Promise((
|
|
29659
|
+
return await new Promise((resolve11, reject) => {
|
|
29007
29660
|
const child = spawn("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
29008
29661
|
let stdout = "";
|
|
29009
29662
|
let stderr = "";
|
|
@@ -29024,7 +29677,7 @@ async function runGit(args, cwd) {
|
|
|
29024
29677
|
})
|
|
29025
29678
|
);
|
|
29026
29679
|
});
|
|
29027
|
-
child.on("close", (code) =>
|
|
29680
|
+
child.on("close", (code) => resolve11({ stdout, stderr, code: code ?? 0 }));
|
|
29028
29681
|
});
|
|
29029
29682
|
} catch (err) {
|
|
29030
29683
|
if (err instanceof WrongStackError) throw err;
|
|
@@ -29349,7 +30002,7 @@ function createSkillsPlugin(opts) {
|
|
|
29349
30002
|
};
|
|
29350
30003
|
}
|
|
29351
30004
|
function makeInstaller(skillLoader, projectRoot) {
|
|
29352
|
-
const globalRoot = path6.join(
|
|
30005
|
+
const globalRoot = path6.join(os6.homedir(), ".wrongstack");
|
|
29353
30006
|
return new SkillInstaller({
|
|
29354
30007
|
manifestPath: path6.join(globalRoot, "installed-skills.json"),
|
|
29355
30008
|
projectSkillsDir: path6.join(projectRoot, ".wrongstack", "skills"),
|
|
@@ -29700,6 +30353,6 @@ ${formatPlan(updated)}`
|
|
|
29700
30353
|
};
|
|
29701
30354
|
}
|
|
29702
30355
|
|
|
29703
|
-
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 };
|
|
30356
|
+
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, BrainDecisionQueue, 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, DefaultBrainArbiter, 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, HumanEscalatingBrainArbiter, 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, ObservableBrainArbiter, 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, bootConfig, 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, flagsToConfigPatch, formatContextWindowModeList, formatGoal, formatHumanPrompt, 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 };
|
|
29704
30357
|
//# sourceMappingURL=index.js.map
|
|
29705
30358
|
//# sourceMappingURL=index.js.map
|