@wrongstack/core 0.308.7 → 0.309.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/coordination/director/director-toolset.d.ts +2 -2
- package/dist/coordination/director-mutation-test-tool.d.ts +29 -0
- package/dist/coordination/director-tools.d.ts +2 -0
- package/dist/coordination/director.d.ts +9 -0
- package/dist/coordination/fleet.d.ts +12 -0
- package/dist/coordination/index.d.ts +1 -1
- package/dist/coordination/index.js +705 -48
- package/dist/coordination/mail-tools.d.ts +10 -6
- package/dist/coordination/mailbox-codecs.d.ts +31 -0
- package/dist/coordination/multi-agent-coordinator.d.ts +14 -0
- package/dist/coordination/multi-agent-timeout.d.ts +11 -1
- package/dist/coordination/mutation-engine.d.ts +74 -0
- package/dist/coordination/subagent-budget.d.ts +54 -0
- package/dist/coordination/subagent-finish.d.ts +78 -0
- package/dist/core/index.js +19 -4
- package/dist/defaults/index.js +699 -51
- package/dist/execution/index.js +238 -16
- package/dist/index.d.ts +1 -1
- package/dist/index.js +862 -178
- package/dist/kernel/events/agent-events.d.ts +31 -2
- package/dist/models/index.js +11 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +14 -0
- package/dist/types/multi-agent.d.ts +15 -0
- package/dist/types/provider.d.ts +29 -1
- package/instructions/agents/chaos-monkey.md +57 -0
- package/package.json +3 -3
|
@@ -285,6 +285,46 @@ function createMessage(type, from, payload, to) {
|
|
|
285
285
|
};
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
+
// src/core/btw.ts
|
|
289
|
+
var META_KEY = "_btwNotes";
|
|
290
|
+
var MAX_PENDING = 20;
|
|
291
|
+
function readQueue(ctx) {
|
|
292
|
+
const raw = ctx.meta[META_KEY];
|
|
293
|
+
return Array.isArray(raw) ? raw : [];
|
|
294
|
+
}
|
|
295
|
+
function setBtwNote(ctx, text) {
|
|
296
|
+
const trimmed = text.trim();
|
|
297
|
+
if (!trimmed) return readQueue(ctx).length;
|
|
298
|
+
const next = [...readQueue(ctx), trimmed].slice(-MAX_PENDING);
|
|
299
|
+
ctx.meta[META_KEY] = next;
|
|
300
|
+
return next.length;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// src/coordination/subagent-finish.ts
|
|
304
|
+
var SUBAGENT_FINISH_REQUESTED_EVENT = "subagent.finish_requested";
|
|
305
|
+
var DEFAULT_SUBAGENT_FINISH_GRACE_MS = 12e4;
|
|
306
|
+
function resolveGracefulFinish(config) {
|
|
307
|
+
const raw = config.gracefulFinish;
|
|
308
|
+
if (raw === void 0 || raw === false) return void 0;
|
|
309
|
+
if (raw === true) return { graceMs: DEFAULT_SUBAGENT_FINISH_GRACE_MS };
|
|
310
|
+
const graceMs = typeof raw.graceMs === "number" && Number.isFinite(raw.graceMs) && raw.graceMs > 0 ? Math.floor(raw.graceMs) : DEFAULT_SUBAGENT_FINISH_GRACE_MS;
|
|
311
|
+
return { graceMs };
|
|
312
|
+
}
|
|
313
|
+
function buildSubagentFinishNotice(input) {
|
|
314
|
+
const localTime = new Date(input.deadlineMs).toISOString();
|
|
315
|
+
const seconds = Math.max(1, Math.round(input.graceMs / 1e3));
|
|
316
|
+
const timeLeft = input.graceMs > 0 ? `You have roughly ${seconds} seconds (until ${localTime}) of legitimate working time left.` : `Your working-time window is already spent (deadline was ${localTime}) \u2014 finish now.`;
|
|
317
|
+
return [
|
|
318
|
+
"[SUBAGENT FINISH] The leader agent has finished its work.",
|
|
319
|
+
`Reason: ${input.reason}`,
|
|
320
|
+
timeLeft,
|
|
321
|
+
"Finish your task now, in this turn: complete the thought you are working on, stop",
|
|
322
|
+
"starting new tool calls unless one is strictly required to finish, and write your",
|
|
323
|
+
"final answer or report as your final output, then end your turn.",
|
|
324
|
+
"Do not restart the task and do not begin new work."
|
|
325
|
+
].join("\n");
|
|
326
|
+
}
|
|
327
|
+
|
|
288
328
|
// src/coordination/subagent-budget.ts
|
|
289
329
|
var TIMEOUT_PREEMPT_FRACTION = 0.85;
|
|
290
330
|
var DECISION_TIMEOUT_MS = 6e4;
|
|
@@ -342,6 +382,82 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
342
382
|
this.limits.idleTimeoutMs = ext.idleTimeoutMs;
|
|
343
383
|
}
|
|
344
384
|
}
|
|
385
|
+
/**
|
|
386
|
+
* Graceful-finish state (see coordination/subagent-finish.ts).
|
|
387
|
+
* `_finishNotified` guards the single in-band emission; `_grace` records a
|
|
388
|
+
* granted working-time extension past the original wall-clock deadline.
|
|
389
|
+
* They are separate because the two callers want different semantics:
|
|
390
|
+
* the watchdog grants grace at the deadline crossing (notify + extend),
|
|
391
|
+
* while an explicit leader-finished request only notifies — a subagent
|
|
392
|
+
* well inside its budget keeps its full legitimate working time and simply
|
|
393
|
+
* accelerates.
|
|
394
|
+
*/
|
|
395
|
+
_finishNotified = false;
|
|
396
|
+
_grace = null;
|
|
397
|
+
/** True once the in-band finish notification has been emitted. */
|
|
398
|
+
get finishNotified() {
|
|
399
|
+
return this._finishNotified;
|
|
400
|
+
}
|
|
401
|
+
/** True once a grace window has been granted past the original deadline. */
|
|
402
|
+
get graceGranted() {
|
|
403
|
+
return this._grace !== null;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Notify the subagent in-band to finish its task in its own turn:
|
|
407
|
+
* `subagent.finish_requested` is emitted on the wired EventBus and the
|
|
408
|
+
* agent loop folds the notice into the conversation between tool batches.
|
|
409
|
+
* Nothing aborts — this is a notification, never an interrupt.
|
|
410
|
+
*
|
|
411
|
+
* `opts.graceMs` additionally extends the wall-clock ceiling by that window
|
|
412
|
+
* (used by the watchdog at a deadline crossing, so the model gets working
|
|
413
|
+
* time instead of a kill). Omit it to notify without touching the budget —
|
|
414
|
+
* the subagent keeps its existing time budget and just accelerates.
|
|
415
|
+
*
|
|
416
|
+
* Returns `true` when this call did something (emitted the notification
|
|
417
|
+
* and/or granted grace); `false` when there was nothing to do (already
|
|
418
|
+
* notified, grace already granted, no EventBus wired, budget not started).
|
|
419
|
+
*/
|
|
420
|
+
notifyFinish(reason, opts, now = Date.now) {
|
|
421
|
+
if (!this._events) return false;
|
|
422
|
+
if (this.startTime === null) return false;
|
|
423
|
+
const shouldEmit = !this._finishNotified;
|
|
424
|
+
const rawGrace = opts?.graceMs;
|
|
425
|
+
const shouldGrant = rawGrace !== void 0 && this._grace === null;
|
|
426
|
+
if (!shouldEmit && !shouldGrant) return false;
|
|
427
|
+
let grantedGraceMs = 0;
|
|
428
|
+
let graceDeadlineMs;
|
|
429
|
+
if (shouldGrant && rawGrace !== void 0) {
|
|
430
|
+
grantedGraceMs = Number.isFinite(rawGrace) && rawGrace > 0 ? Math.floor(rawGrace) : DEFAULT_SUBAGENT_FINISH_GRACE_MS;
|
|
431
|
+
graceDeadlineMs = now() + grantedGraceMs;
|
|
432
|
+
this._grace = { deadlineMs: graceDeadlineMs, graceMs: grantedGraceMs };
|
|
433
|
+
this.patchLimits({ timeoutMs: graceDeadlineMs - this.startTime });
|
|
434
|
+
}
|
|
435
|
+
if (shouldEmit) {
|
|
436
|
+
this._finishNotified = true;
|
|
437
|
+
const effectiveDeadlineMs = graceDeadlineMs ?? (this.limits.timeoutMs !== void 0 ? this.startTime + this.limits.timeoutMs : now() + DEFAULT_SUBAGENT_FINISH_GRACE_MS);
|
|
438
|
+
const effectiveGraceMs = Math.max(0, effectiveDeadlineMs - now());
|
|
439
|
+
const subagentId = this._subagentId;
|
|
440
|
+
this._events.emit(SUBAGENT_FINISH_REQUESTED_EVENT, {
|
|
441
|
+
// Omitted entirely when the budget was built without an id — an
|
|
442
|
+
// empty string is an address that matches nothing.
|
|
443
|
+
...subagentId !== void 0 ? { subagentId } : {},
|
|
444
|
+
reason,
|
|
445
|
+
deadlineMs: effectiveDeadlineMs,
|
|
446
|
+
graceMs: effectiveGraceMs,
|
|
447
|
+
notice: buildSubagentFinishNotice({
|
|
448
|
+
reason,
|
|
449
|
+
deadlineMs: effectiveDeadlineMs,
|
|
450
|
+
graceMs: effectiveGraceMs
|
|
451
|
+
})
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
return true;
|
|
455
|
+
}
|
|
456
|
+
/** Epoch ms by which the subagent should have produced its final output,
|
|
457
|
+
* once a grace window was granted. Undefined before that. */
|
|
458
|
+
get finishDeadlineMs() {
|
|
459
|
+
return this._grace?.deadlineMs;
|
|
460
|
+
}
|
|
345
461
|
iterations = 0;
|
|
346
462
|
toolCalls = 0;
|
|
347
463
|
tokenInput = 0;
|
|
@@ -357,6 +473,10 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
357
473
|
lastActivityTime = null;
|
|
358
474
|
_onThreshold;
|
|
359
475
|
_sessionId;
|
|
476
|
+
/** Owning subagent id — used to address the graceful-finish event. */
|
|
477
|
+
_subagentId;
|
|
478
|
+
/** True when only the coordinator watchdog may enforce wall-clock limits. */
|
|
479
|
+
_wallClockWatchdogOwned;
|
|
360
480
|
/**
|
|
361
481
|
* Hard cap on how long `_negotiateExtension` waits for the coordinator to
|
|
362
482
|
* respond before defaulting to 'stop'. Without this fallback an absent
|
|
@@ -428,6 +548,8 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
428
548
|
constructor(limits = {}, mode = "auto", options = {}) {
|
|
429
549
|
this._mode = mode;
|
|
430
550
|
this._sessionId = options.sessionId;
|
|
551
|
+
this._subagentId = options.subagentId;
|
|
552
|
+
this._wallClockWatchdogOwned = options.wallClockWatchdogOwned === true;
|
|
431
553
|
this.limits = { ...limits };
|
|
432
554
|
}
|
|
433
555
|
currentSessionId() {
|
|
@@ -506,7 +628,7 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
506
628
|
if (this.limits.idleTimeoutMs !== void 0 && idle > this.limits.idleTimeoutMs) {
|
|
507
629
|
exceeded.push({ kind: "idle_timeout", used: idle, limit: this.limits.idleTimeoutMs });
|
|
508
630
|
}
|
|
509
|
-
const wallOwnedByWatchdog = this._onThreshold !== void 0 && this._watchdogActive === this.limits.timeoutMs;
|
|
631
|
+
const wallOwnedByWatchdog = this._wallClockWatchdogOwned || this._onThreshold !== void 0 && this._watchdogActive === this.limits.timeoutMs;
|
|
510
632
|
if (this.limits.timeoutMs !== void 0 && elapsedMs > this.limits.timeoutMs && !wallOwnedByWatchdog) {
|
|
511
633
|
exceeded.push({ kind: "timeout", used: elapsedMs, limit: this.limits.timeoutMs });
|
|
512
634
|
}
|
|
@@ -705,7 +827,7 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
705
827
|
if (timeoutMs === void 0 && idleTimeoutMs === void 0) return;
|
|
706
828
|
const elapsed = Date.now() - this.startTime;
|
|
707
829
|
const wallSkipped = this._onThreshold !== void 0 && this._watchdogActive !== void 0 && timeoutMs !== void 0 && this._watchdogActive === timeoutMs;
|
|
708
|
-
const wallTripped = wallSkipped ? false : timeoutMs !== void 0 && elapsed > timeoutMs;
|
|
830
|
+
const wallTripped = this._wallClockWatchdogOwned || wallSkipped ? false : timeoutMs !== void 0 && elapsed > timeoutMs;
|
|
709
831
|
const idleTripped = idleTimeoutMs !== void 0 && this.idleMs() > idleTimeoutMs;
|
|
710
832
|
if (!wallTripped && !idleTripped) return;
|
|
711
833
|
void this.checkLimits(elapsed);
|
|
@@ -1165,6 +1287,14 @@ function makeAgentSubagentRunner(opts) {
|
|
|
1165
1287
|
);
|
|
1166
1288
|
const onParentAbort = () => aborter.abort();
|
|
1167
1289
|
ctx.signal.addEventListener("abort", onParentAbort);
|
|
1290
|
+
if (resolveGracefulFinish(ctx.config)) {
|
|
1291
|
+
unsub.push(
|
|
1292
|
+
events.on("subagent.finish_requested", (e) => {
|
|
1293
|
+
if (e.subagentId && e.subagentId !== ctx.subagentId) return;
|
|
1294
|
+
setBtwNote(agent.ctx, e.notice);
|
|
1295
|
+
})
|
|
1296
|
+
);
|
|
1297
|
+
}
|
|
1168
1298
|
let result;
|
|
1169
1299
|
try {
|
|
1170
1300
|
result = await agent.run(format(task, ctx.config), { signal: aborter.signal });
|
|
@@ -10412,6 +10542,20 @@ var EXPLORE_COMPANION_AGENT = {
|
|
|
10412
10542
|
textStream: "silent",
|
|
10413
10543
|
toolStream: "silent"
|
|
10414
10544
|
};
|
|
10545
|
+
var CHAOS_MONKEY_AGENT = {
|
|
10546
|
+
...defineAgent("chaos-monkey", "Chaos Monkey"),
|
|
10547
|
+
tools: [...TOOLS.build],
|
|
10548
|
+
skillNames: ["testing", "typescript-strict"],
|
|
10549
|
+
spawnBudgetExempt: true,
|
|
10550
|
+
// Follow fleet worktree policy (NOT 'required'): mutation targets are
|
|
10551
|
+
// often freshly written and uncommitted — a worktree spawned from HEAD
|
|
10552
|
+
// would not contain them and every mutant would drift. Callers pass
|
|
10553
|
+
// `worktree: 'off'` in the mutation_test input for uncommitted targets.
|
|
10554
|
+
worktree: "auto",
|
|
10555
|
+
// Report travels via submit_result + final text, not the leader's stream.
|
|
10556
|
+
textStream: "silent",
|
|
10557
|
+
toolStream: "silent"
|
|
10558
|
+
};
|
|
10415
10559
|
var CRITIC_AGENT = defineAgent("critic", "Critic");
|
|
10416
10560
|
var GENERIC_AGENT = defineAgent("generic", "Generic Project Agent");
|
|
10417
10561
|
function withDispatchMetadata(definition) {
|
|
@@ -10431,6 +10575,7 @@ var FLEET_ROSTER = {
|
|
|
10431
10575
|
generic: GENERIC_AGENT,
|
|
10432
10576
|
"shadow-agent": SHADOW_AGENT,
|
|
10433
10577
|
"explore-companion": EXPLORE_COMPANION_AGENT,
|
|
10578
|
+
"chaos-monkey": CHAOS_MONKEY_AGENT,
|
|
10434
10579
|
...Object.fromEntries(
|
|
10435
10580
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, withDispatchMetadata(d)])
|
|
10436
10581
|
)
|
|
@@ -10461,6 +10606,16 @@ var FLEET_ROSTER_BUDGETS = {
|
|
|
10461
10606
|
maxTokens: 96e3,
|
|
10462
10607
|
maxCostUsd: 0.5
|
|
10463
10608
|
},
|
|
10609
|
+
"chaos-monkey": {
|
|
10610
|
+
// A mutation pass is many short apply/run/restore cycles — per-mutant
|
|
10611
|
+
// work is tiny, but a large plan (25 mutants/file × N files) needs
|
|
10612
|
+
// headroom. Idle-based reaping covers a stalled pass.
|
|
10613
|
+
idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS,
|
|
10614
|
+
maxIterations: 2e3,
|
|
10615
|
+
maxToolCalls: 6e3,
|
|
10616
|
+
maxTokens: 96e3,
|
|
10617
|
+
maxCostUsd: 0.5
|
|
10618
|
+
},
|
|
10464
10619
|
...Object.fromEntries(
|
|
10465
10620
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.budget])
|
|
10466
10621
|
)
|
|
@@ -11913,7 +12068,7 @@ function attachDepWatcherBridge(opts) {
|
|
|
11913
12068
|
}
|
|
11914
12069
|
|
|
11915
12070
|
// src/coordination/director.ts
|
|
11916
|
-
import { randomUUID as
|
|
12071
|
+
import { randomUUID as randomUUID16 } from "node:crypto";
|
|
11917
12072
|
import * as fsp25 from "node:fs/promises";
|
|
11918
12073
|
|
|
11919
12074
|
// src/core/instruction-template.ts
|
|
@@ -12988,7 +13143,7 @@ ${JSON.stringify(result.result, null, 2)}
|
|
|
12988
13143
|
};
|
|
12989
13144
|
|
|
12990
13145
|
// src/coordination/director-tools.ts
|
|
12991
|
-
import { randomUUID as
|
|
13146
|
+
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
12992
13147
|
import {
|
|
12993
13148
|
completeKanbanDispatch,
|
|
12994
13149
|
failKanbanDispatch,
|
|
@@ -14188,6 +14343,413 @@ function excerpt(text, max) {
|
|
|
14188
14343
|
...(truncated)`;
|
|
14189
14344
|
}
|
|
14190
14345
|
|
|
14346
|
+
// src/coordination/director-mutation-test-tool.ts
|
|
14347
|
+
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
14348
|
+
import { readFileSync as readFileSync13 } from "node:fs";
|
|
14349
|
+
import { isAbsolute as isAbsolute3, join as join16 } from "node:path";
|
|
14350
|
+
|
|
14351
|
+
// src/coordination/mutation-engine.ts
|
|
14352
|
+
var TOKEN_PATTERNS = [
|
|
14353
|
+
{
|
|
14354
|
+
kind: "relax-boundary",
|
|
14355
|
+
// `>` not followed by `=` and not part of `=>` or `>>`; require code-ish
|
|
14356
|
+
// context on both sides so generic text (JSX, strings) is not touched.
|
|
14357
|
+
regex: /(?<=[\w\)\]\}'"`])\s*\x20?(?<op>>(?!=|>))/g,
|
|
14358
|
+
replace: () => ">="
|
|
14359
|
+
},
|
|
14360
|
+
{
|
|
14361
|
+
kind: "tighten-boundary",
|
|
14362
|
+
regex: /(?<=[\w\)\]\}'"`])\s*\x20?(?<op>>=)/g,
|
|
14363
|
+
replace: () => ">"
|
|
14364
|
+
},
|
|
14365
|
+
{
|
|
14366
|
+
kind: "arith-plus-to-minus",
|
|
14367
|
+
// `+` between operands (binary), not `++`, unary `+x`, or `+=`.
|
|
14368
|
+
regex: /(?<=[\w\)\]\}'"`])\s*\x20?(?<op>\+(?!\+|=))/g,
|
|
14369
|
+
replace: () => "-"
|
|
14370
|
+
},
|
|
14371
|
+
{
|
|
14372
|
+
kind: "arith-minus-to-plus",
|
|
14373
|
+
// Binary `-` between operands, not `--`, `-=` or negative-number literal.
|
|
14374
|
+
regex: /(?<=[\w\)\]\}'"`])\s*\x20?(?<op>-(?!-|=))/g,
|
|
14375
|
+
replace: () => "+"
|
|
14376
|
+
},
|
|
14377
|
+
{
|
|
14378
|
+
kind: "negate-boolean",
|
|
14379
|
+
// Standalone boolean literals used as values, not property names.
|
|
14380
|
+
regex: /(?<![.\w$])(?<op>true|false)(?![\w$])/g,
|
|
14381
|
+
replace: (m) => m === "true" ? "false" : "true"
|
|
14382
|
+
},
|
|
14383
|
+
{
|
|
14384
|
+
kind: "return-null",
|
|
14385
|
+
// `return <expr>;` where expr is not already null/undefined/void.
|
|
14386
|
+
regex: /(?<indent>\breturn\b)(?<expr>\s+[^;{}\n]+?)\s*;/g,
|
|
14387
|
+
replace: () => "return null;"
|
|
14388
|
+
}
|
|
14389
|
+
];
|
|
14390
|
+
function planMutations(file, source, opts = {}) {
|
|
14391
|
+
const maxPerFile = opts.maxPerFile ?? 25;
|
|
14392
|
+
const out = [];
|
|
14393
|
+
const lines = source.split("\n");
|
|
14394
|
+
for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
|
|
14395
|
+
const line = lines[lineIdx];
|
|
14396
|
+
const t = line.trim();
|
|
14397
|
+
if (t.startsWith("//") || t.startsWith("*") || t.startsWith("/*")) continue;
|
|
14398
|
+
for (const pattern of TOKEN_PATTERNS) {
|
|
14399
|
+
pattern.regex.lastIndex = 0;
|
|
14400
|
+
let m;
|
|
14401
|
+
while ((m = pattern.regex.exec(line)) !== null) {
|
|
14402
|
+
const token = m.groups?.["op"] ?? m[0];
|
|
14403
|
+
const tokenStart = m.index + m[0].indexOf(token);
|
|
14404
|
+
if (isMasked(line, tokenStart, token.length)) continue;
|
|
14405
|
+
const original = line.slice(tokenStart, tokenStart + token.length);
|
|
14406
|
+
const replacement = pattern.replace(token);
|
|
14407
|
+
if (replacement === original) continue;
|
|
14408
|
+
out.push({
|
|
14409
|
+
id: `${pattern.kind}#${lineIdx + 1}#${tokenStart + 1}`,
|
|
14410
|
+
kind: pattern.kind,
|
|
14411
|
+
file,
|
|
14412
|
+
line: lineIdx + 1,
|
|
14413
|
+
column: tokenStart + 1,
|
|
14414
|
+
original,
|
|
14415
|
+
replacement
|
|
14416
|
+
});
|
|
14417
|
+
}
|
|
14418
|
+
}
|
|
14419
|
+
if (out.length >= maxPerFile) break;
|
|
14420
|
+
}
|
|
14421
|
+
return out.slice(0, maxPerFile);
|
|
14422
|
+
}
|
|
14423
|
+
function isMasked(line, start, len) {
|
|
14424
|
+
let inSingle = false;
|
|
14425
|
+
let inDouble = false;
|
|
14426
|
+
for (let i = 0; i < start; i++) {
|
|
14427
|
+
const c = line[i];
|
|
14428
|
+
const prev = i > 0 ? line[i - 1] : void 0;
|
|
14429
|
+
if (c === "'" && prev !== "\\") inSingle = !inSingle;
|
|
14430
|
+
else if (c === '"' && prev !== "\\") inDouble = !inDouble;
|
|
14431
|
+
if (!inSingle && !inDouble && c === "/" && prev === "/") return true;
|
|
14432
|
+
}
|
|
14433
|
+
if (inSingle || inDouble) return true;
|
|
14434
|
+
const window = line.slice(start, start + len);
|
|
14435
|
+
return /['"]/.test(window);
|
|
14436
|
+
}
|
|
14437
|
+
function parseMutationReport(text) {
|
|
14438
|
+
const candidates = [];
|
|
14439
|
+
const fence = text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
14440
|
+
if (fence?.[1]) candidates.push(fence[1].trim());
|
|
14441
|
+
const firstBrace = text.indexOf("{");
|
|
14442
|
+
if (firstBrace >= 0) candidates.push(extractBalancedObject(text, firstBrace));
|
|
14443
|
+
for (const candidate of candidates) {
|
|
14444
|
+
if (!candidate) continue;
|
|
14445
|
+
try {
|
|
14446
|
+
const parsed = JSON.parse(candidate);
|
|
14447
|
+
if (!Array.isArray(parsed.mutants)) continue;
|
|
14448
|
+
return {
|
|
14449
|
+
mutants: parsed.mutants.map(normalizeMutantEntry).filter((x) => Boolean(x)),
|
|
14450
|
+
summary: typeof parsed.summary === "string" ? parsed.summary : void 0
|
|
14451
|
+
};
|
|
14452
|
+
} catch {
|
|
14453
|
+
}
|
|
14454
|
+
}
|
|
14455
|
+
return void 0;
|
|
14456
|
+
}
|
|
14457
|
+
function extractBalancedObject(text, start) {
|
|
14458
|
+
let depth = 0;
|
|
14459
|
+
let inString = false;
|
|
14460
|
+
let escaped = false;
|
|
14461
|
+
for (let i = start; i < text.length; i++) {
|
|
14462
|
+
const c = text[i];
|
|
14463
|
+
if (escaped) {
|
|
14464
|
+
escaped = false;
|
|
14465
|
+
continue;
|
|
14466
|
+
}
|
|
14467
|
+
if (c === "\\") {
|
|
14468
|
+
escaped = true;
|
|
14469
|
+
continue;
|
|
14470
|
+
}
|
|
14471
|
+
if (c === '"') inString = !inString;
|
|
14472
|
+
if (inString) continue;
|
|
14473
|
+
if (c === "{") depth++;
|
|
14474
|
+
else if (c === "}") {
|
|
14475
|
+
depth--;
|
|
14476
|
+
if (depth === 0) return text.slice(start, i + 1);
|
|
14477
|
+
}
|
|
14478
|
+
}
|
|
14479
|
+
return text.slice(start);
|
|
14480
|
+
}
|
|
14481
|
+
function normalizeMutantEntry(value) {
|
|
14482
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
14483
|
+
const rec = value;
|
|
14484
|
+
const id = typeof rec["id"] === "string" ? rec["id"] : void 0;
|
|
14485
|
+
const status = rec["status"];
|
|
14486
|
+
if (!id || status !== "killed" && status !== "survived" && status !== "skipped") {
|
|
14487
|
+
return void 0;
|
|
14488
|
+
}
|
|
14489
|
+
return {
|
|
14490
|
+
id,
|
|
14491
|
+
file: typeof rec["file"] === "string" ? rec["file"] : "",
|
|
14492
|
+
line: typeof rec["line"] === "number" ? rec["line"] : 0,
|
|
14493
|
+
kind: typeof rec["kind"] === "string" ? rec["kind"] : "",
|
|
14494
|
+
status,
|
|
14495
|
+
evidence: typeof rec["evidence"] === "string" ? rec["evidence"] : void 0
|
|
14496
|
+
};
|
|
14497
|
+
}
|
|
14498
|
+
|
|
14499
|
+
// src/coordination/director-mutation-test-tool.ts
|
|
14500
|
+
var DEFAULT_MAX_PER_FILE = 10;
|
|
14501
|
+
var DEFAULT_MAX_STRENGTHEN_ATTEMPTS = 2;
|
|
14502
|
+
var CHAOS_ROLE = "chaos-monkey";
|
|
14503
|
+
function makeMutationTestTool(director, roster, opts = {}) {
|
|
14504
|
+
return {
|
|
14505
|
+
name: "mutation_test",
|
|
14506
|
+
description: "Chaos Monkey mutation testing: deterministically sabotage boundary conditions in the target code (> to >=, + to -, boolean flips, return null), re-run the tests per mutant, and report which mutants were killed. Surviving mutants mean the tests are weak \u2014 optionally loop a strengthen-tests repair until they die.",
|
|
14507
|
+
usageHint: "Use after writing new code AND its tests, before delivering. Pass targets (files) and testCommand. Provide repairSubagentId to auto-strengthen weak tests. Survivors that persist are reported as suspected-equivalent.",
|
|
14508
|
+
permission: "auto",
|
|
14509
|
+
mutating: false,
|
|
14510
|
+
capabilities: [ToolCapabilities.SUBAGENT_SPAWN],
|
|
14511
|
+
inputSchema: {
|
|
14512
|
+
type: "object",
|
|
14513
|
+
properties: {
|
|
14514
|
+
targets: {
|
|
14515
|
+
type: "array",
|
|
14516
|
+
items: { type: "string" },
|
|
14517
|
+
description: "Project-relative (or absolute) source files to mutate. Keep to files changed by the current task."
|
|
14518
|
+
},
|
|
14519
|
+
testCommand: {
|
|
14520
|
+
type: "string",
|
|
14521
|
+
description: 'Exact command that runs the relevant tests, e.g. "pnpm exec vitest run packages/core/tests/coordination/mutation-engine.test.ts".'
|
|
14522
|
+
},
|
|
14523
|
+
cwd: { type: "string", description: "Working directory for the test command." },
|
|
14524
|
+
maxPerFile: {
|
|
14525
|
+
type: "number",
|
|
14526
|
+
minimum: 1,
|
|
14527
|
+
maximum: 25,
|
|
14528
|
+
description: "Mutant cap per file per pass. Default 10."
|
|
14529
|
+
},
|
|
14530
|
+
maxStrengthenAttempts: {
|
|
14531
|
+
type: "number",
|
|
14532
|
+
minimum: 0,
|
|
14533
|
+
maximum: 5,
|
|
14534
|
+
description: "Strengthen\u2192re-verify rounds. Default 2 when repairSubagentId is set, else 0."
|
|
14535
|
+
},
|
|
14536
|
+
repairSubagentId: {
|
|
14537
|
+
type: "string",
|
|
14538
|
+
description: "Subagent that owns the tests. When set and mutants survive, it receives a strengthen-tests task and the survivors are re-verified."
|
|
14539
|
+
},
|
|
14540
|
+
chaosWorktree: {
|
|
14541
|
+
anyOf: [{ type: "boolean" }, { type: "string", enum: ["auto", "required", "off"] }],
|
|
14542
|
+
description: "Worktree override for the chaos agent. Use 'off' when targets are uncommitted \u2014 a worktree from HEAD would not contain them."
|
|
14543
|
+
},
|
|
14544
|
+
timeoutMs: { type: "number", minimum: 1, description: "Per-task timeout for chaos/strengthen/rerun tasks." },
|
|
14545
|
+
reportOnly: {
|
|
14546
|
+
type: "boolean",
|
|
14547
|
+
description: "Skip the strengthen loop even when survivors exist. Default false."
|
|
14548
|
+
}
|
|
14549
|
+
},
|
|
14550
|
+
required: ["targets", "testCommand"],
|
|
14551
|
+
additionalProperties: false
|
|
14552
|
+
},
|
|
14553
|
+
async execute(input, ctx) {
|
|
14554
|
+
const i = normalizeMutationTestInput(input);
|
|
14555
|
+
const root = opts.projectRoot ?? ctx.projectRoot;
|
|
14556
|
+
const plan = buildPlan(i, root);
|
|
14557
|
+
if (plan.length === 0) {
|
|
14558
|
+
return {
|
|
14559
|
+
verdict: "inconclusive",
|
|
14560
|
+
passed: false,
|
|
14561
|
+
error: "No mutable sites found in the given targets (after comment/string filtering)."
|
|
14562
|
+
};
|
|
14563
|
+
}
|
|
14564
|
+
const chaosSubagentId = await director.spawn(
|
|
14565
|
+
makeChaosConfig(roster, i.chaosWorktree ?? "off")
|
|
14566
|
+
);
|
|
14567
|
+
const chaosTaskId = await director.assign({
|
|
14568
|
+
id: randomUUID11(),
|
|
14569
|
+
subagentId: chaosSubagentId,
|
|
14570
|
+
description: buildChaosTask(plan, i, 1, []),
|
|
14571
|
+
timeoutMs: i.timeoutMs
|
|
14572
|
+
});
|
|
14573
|
+
const [chaosResult] = await director.awaitTasks([chaosTaskId]);
|
|
14574
|
+
const pass1 = collectOutcomes(chaosResult, plan);
|
|
14575
|
+
const survivors = pass1.filter((m) => m.status === "survived");
|
|
14576
|
+
const maxAttempts = clamp(
|
|
14577
|
+
i.maxStrengthenAttempts ?? (i.repairSubagentId && !i.reportOnly ? DEFAULT_MAX_STRENGTHEN_ATTEMPTS : 0),
|
|
14578
|
+
0,
|
|
14579
|
+
5
|
|
14580
|
+
);
|
|
14581
|
+
const attempts = [];
|
|
14582
|
+
let current = survivors;
|
|
14583
|
+
while (current.length > 0 && attempts.length < maxAttempts && i.repairSubagentId) {
|
|
14584
|
+
const attemptNo = attempts.length + 1;
|
|
14585
|
+
const strengthenTaskId = await director.assign({
|
|
14586
|
+
id: randomUUID11(),
|
|
14587
|
+
subagentId: i.repairSubagentId,
|
|
14588
|
+
description: buildStrengthenTask(current, i, attemptNo),
|
|
14589
|
+
timeoutMs: i.timeoutMs
|
|
14590
|
+
});
|
|
14591
|
+
const [strengthenResult] = await director.awaitTasks([strengthenTaskId]);
|
|
14592
|
+
if (strengthenResult?.status !== "success") {
|
|
14593
|
+
attempts.push({
|
|
14594
|
+
attempt: attemptNo,
|
|
14595
|
+
survivorsBefore: current,
|
|
14596
|
+
strengthenResult: strengthenResult ? { taskId: strengthenResult.taskId, status: strengthenResult.status } : void 0,
|
|
14597
|
+
survivorsAfter: current,
|
|
14598
|
+
suspectedEquivalent: []
|
|
14599
|
+
});
|
|
14600
|
+
break;
|
|
14601
|
+
}
|
|
14602
|
+
const survivorPlan = plan.filter((p) => current.some((s) => s.id === p.id));
|
|
14603
|
+
const rerunSubagentId = await director.spawn(
|
|
14604
|
+
makeChaosConfig(roster, i.chaosWorktree ?? "off")
|
|
14605
|
+
);
|
|
14606
|
+
const rerunTaskId = await director.assign({
|
|
14607
|
+
id: randomUUID11(),
|
|
14608
|
+
subagentId: rerunSubagentId,
|
|
14609
|
+
description: buildChaosTask(survivorPlan, i, attemptNo + 1, current),
|
|
14610
|
+
timeoutMs: i.timeoutMs
|
|
14611
|
+
});
|
|
14612
|
+
const [rerunResult] = await director.awaitTasks([rerunTaskId]);
|
|
14613
|
+
const passN = collectOutcomes(rerunResult, survivorPlan);
|
|
14614
|
+
const stillSurviving = passN.filter((m) => m.status === "survived" || m.status === "skipped");
|
|
14615
|
+
attempts.push({
|
|
14616
|
+
attempt: attemptNo,
|
|
14617
|
+
survivorsBefore: current,
|
|
14618
|
+
strengthenResult: { taskId: strengthenResult.taskId, status: strengthenResult.status },
|
|
14619
|
+
rerunResult: { taskId: rerunTaskId, status: rerunResult?.status ?? "unknown" },
|
|
14620
|
+
survivorsAfter: stillSurviving,
|
|
14621
|
+
suspectedEquivalent: stillSurviving.filter((m) => current.some((c) => c.id === m.id)).map((m) => m.id)
|
|
14622
|
+
});
|
|
14623
|
+
current = stillSurviving.filter((m) => m.status === "survived");
|
|
14624
|
+
if (passN.every((m) => m.status === "skipped")) break;
|
|
14625
|
+
}
|
|
14626
|
+
const finalSurvivors = current;
|
|
14627
|
+
const verifiedCount = pass1.filter((m) => m.status !== "skipped").length;
|
|
14628
|
+
const skippedCount = pass1.filter((m) => m.status === "skipped").length;
|
|
14629
|
+
const score = plan.length === 0 ? 0 : pass1.filter((m) => m.status === "killed").length / plan.length;
|
|
14630
|
+
const verdict = verifiedCount === 0 ? "inconclusive" : finalSurvivors.length === 0 ? skippedCount > 0 ? "partial" : "pass" : score >= 0.8 ? "partial" : "fail";
|
|
14631
|
+
return {
|
|
14632
|
+
verdict,
|
|
14633
|
+
passed: verdict === "pass",
|
|
14634
|
+
mutationScore: Number.parseFloat(score.toFixed(3)),
|
|
14635
|
+
planned: plan.length,
|
|
14636
|
+
killed: pass1.filter((m) => m.status === "killed").length,
|
|
14637
|
+
survived: pass1.filter((m) => m.status === "survived").length,
|
|
14638
|
+
skipped: pass1.filter((m) => m.status === "skipped").length,
|
|
14639
|
+
finalSurvivors: finalSurvivors.map((m) => ({ id: m.id, file: m.file, kind: m.kind })),
|
|
14640
|
+
suspectedEquivalent: attempts.flatMap((a) => a.suspectedEquivalent),
|
|
14641
|
+
strengthenAttempts: attempts.length,
|
|
14642
|
+
attempts,
|
|
14643
|
+
chaosTaskId,
|
|
14644
|
+
nextAction: finalSurvivors.length === 0 ? "accept" : attempts.length >= maxAttempts && i.repairSubagentId ? "manual_review_survivors" : "strengthen_tests"
|
|
14645
|
+
};
|
|
14646
|
+
}
|
|
14647
|
+
};
|
|
14648
|
+
}
|
|
14649
|
+
function normalizeMutationTestInput(input) {
|
|
14650
|
+
const raw = input ?? {};
|
|
14651
|
+
const targets = stringArray2(raw["targets"]) ?? [];
|
|
14652
|
+
const testCommand = typeof raw["testCommand"] === "string" ? raw["testCommand"].trim() : "";
|
|
14653
|
+
return {
|
|
14654
|
+
targets: targets.filter(Boolean),
|
|
14655
|
+
testCommand,
|
|
14656
|
+
cwd: typeof raw["cwd"] === "string" && raw["cwd"].trim() ? raw["cwd"].trim() : void 0,
|
|
14657
|
+
maxPerFile: typeof raw["maxPerFile"] === "number" ? raw["maxPerFile"] : void 0,
|
|
14658
|
+
maxStrengthenAttempts: typeof raw["maxStrengthenAttempts"] === "number" ? raw["maxStrengthenAttempts"] : void 0,
|
|
14659
|
+
repairSubagentId: typeof raw["repairSubagentId"] === "string" && raw["repairSubagentId"].trim() ? raw["repairSubagentId"].trim() : void 0,
|
|
14660
|
+
chaosWorktree: raw["chaosWorktree"] ?? void 0,
|
|
14661
|
+
timeoutMs: typeof raw["timeoutMs"] === "number" ? raw["timeoutMs"] : void 0,
|
|
14662
|
+
reportOnly: raw["reportOnly"] === true
|
|
14663
|
+
};
|
|
14664
|
+
}
|
|
14665
|
+
function clamp(n, lo, hi) {
|
|
14666
|
+
return Math.min(hi, Math.max(lo, n));
|
|
14667
|
+
}
|
|
14668
|
+
function buildPlan(i, projectRoot) {
|
|
14669
|
+
const plan = [];
|
|
14670
|
+
for (const target of i.targets) {
|
|
14671
|
+
const abs = isAbsolute3(target) ? target : join16(projectRoot ?? process.cwd(), target);
|
|
14672
|
+
let source;
|
|
14673
|
+
try {
|
|
14674
|
+
source = readFileSync13(abs, "utf8");
|
|
14675
|
+
} catch {
|
|
14676
|
+
continue;
|
|
14677
|
+
}
|
|
14678
|
+
plan.push(...planMutations(target, source, { maxPerFile: i.maxPerFile ?? DEFAULT_MAX_PER_FILE }));
|
|
14679
|
+
}
|
|
14680
|
+
return plan;
|
|
14681
|
+
}
|
|
14682
|
+
function makeChaosConfig(roster, worktree) {
|
|
14683
|
+
const base = roster?.[CHAOS_ROLE] ?? getAgentDefinition(CHAOS_ROLE)?.config ?? { name: "Chaos Monkey", role: CHAOS_ROLE };
|
|
14684
|
+
return { ...instantiateRosterConfig2(CHAOS_ROLE, base), worktree };
|
|
14685
|
+
}
|
|
14686
|
+
function buildChaosTask(plan, i, pass, priorSurvivors) {
|
|
14687
|
+
const mutants = plan.map(
|
|
14688
|
+
(m) => `- ${m.id} | ${m.file}:${m.line}:${m.column} | ${m.kind} | "${m.original}" -> "${m.replacement}"`
|
|
14689
|
+
).join("\n");
|
|
14690
|
+
const prior = priorSurvivors.length > 0 ? `
|
|
14691
|
+
These mutants survived a previous pass (pass ${pass - 1}) \u2014 re-verify them against the STRENGTHENED tests:
|
|
14692
|
+
${priorSurvivors.map((s) => `- ${s.id} (${s.kind} @ ${s.file}:${s.line})`).join("\n")}` : "";
|
|
14693
|
+
return [
|
|
14694
|
+
"Execute this deterministic mutation plan against the current checkout.",
|
|
14695
|
+
"",
|
|
14696
|
+
"For each mutant, in order:",
|
|
14697
|
+
"1. Apply ONLY that mutation at its exact (file, line, column).",
|
|
14698
|
+
`2. Run the test command: ${i.testCommand}${i.cwd ? ` (cwd: ${i.cwd})` : ""}`,
|
|
14699
|
+
"3. Record killed (tests failed \u2014 quote first failing assertion) or survived (suite green).",
|
|
14700
|
+
"4. Restore the file byte-for-byte before the next mutant.",
|
|
14701
|
+
"",
|
|
14702
|
+
"Mutants:",
|
|
14703
|
+
mutants,
|
|
14704
|
+
prior,
|
|
14705
|
+
"",
|
|
14706
|
+
"Rules: one mutation at a time; never stack; if the anchored token no longer matches, mark skipped with the drift as evidence; do not fix or refactor anything; stay inside the plan.",
|
|
14707
|
+
"Finish with submit_result, then repeat the same JSON as your final text."
|
|
14708
|
+
].join("\n");
|
|
14709
|
+
}
|
|
14710
|
+
function buildStrengthenTask(survivors, i, attempt) {
|
|
14711
|
+
return [
|
|
14712
|
+
`Strengthen the tests so these SURVIVING mutants die (attempt ${attempt}).`,
|
|
14713
|
+
"",
|
|
14714
|
+
"Each survivor below was a deliberate sabotage of production code that the current suite did NOT catch:",
|
|
14715
|
+
...survivors.map((s) => `- ${s.id} | ${s.file}:${s.line} | ${s.kind}${s.evidence ? ` | ${s.evidence}` : ""}`),
|
|
14716
|
+
"",
|
|
14717
|
+
`Test command that must fail under each mutant: ${i.testCommand}`,
|
|
14718
|
+
"",
|
|
14719
|
+
"For each survivor add or tighten exactly one assertion that pins the sabotaged boundary/behavior. Do not change production code. Do not weaken other tests. Run the suite green on clean code before finishing."
|
|
14720
|
+
].join("\n");
|
|
14721
|
+
}
|
|
14722
|
+
function collectOutcomes(result, plan) {
|
|
14723
|
+
const fromText = parseTextOutcomes(result);
|
|
14724
|
+
if (fromText.length > 0) {
|
|
14725
|
+
const planned = new Set(plan.map((p) => p.id));
|
|
14726
|
+
const matched = fromText.filter((m) => planned.has(m.id));
|
|
14727
|
+
if (matched.length > 0) return matched;
|
|
14728
|
+
}
|
|
14729
|
+
return plan.map((p) => ({
|
|
14730
|
+
id: p.id,
|
|
14731
|
+
file: p.file,
|
|
14732
|
+
line: p.line,
|
|
14733
|
+
kind: p.kind,
|
|
14734
|
+
status: "skipped",
|
|
14735
|
+
evidence: result ? `chaos task ended ${result.status}` : "chaos task produced no result"
|
|
14736
|
+
}));
|
|
14737
|
+
}
|
|
14738
|
+
function parseTextOutcomes(result) {
|
|
14739
|
+
const text = typeof result?.result === "string" ? result.result : void 0;
|
|
14740
|
+
if (!text) return [];
|
|
14741
|
+
const parsed = parseMutationReport(text);
|
|
14742
|
+
if (!parsed) return [];
|
|
14743
|
+
return parsed.mutants.map((m) => ({
|
|
14744
|
+
id: m.id,
|
|
14745
|
+
file: m.file,
|
|
14746
|
+
line: m.line,
|
|
14747
|
+
kind: m.kind,
|
|
14748
|
+
status: m.status,
|
|
14749
|
+
evidence: m.evidence
|
|
14750
|
+
}));
|
|
14751
|
+
}
|
|
14752
|
+
|
|
14191
14753
|
// src/coordination/director-tools.ts
|
|
14192
14754
|
function makeSpawnTool(director, roster) {
|
|
14193
14755
|
const dispatchCatalog = () => {
|
|
@@ -14480,7 +15042,7 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
14480
15042
|
try {
|
|
14481
15043
|
const config = buildKanbanSubagentConfig(claim.task, i, roster, instantiateRosterConfig2);
|
|
14482
15044
|
subagentId = await director.spawn(config);
|
|
14483
|
-
const dispatchTaskId =
|
|
15045
|
+
const dispatchTaskId = randomUUID12();
|
|
14484
15046
|
const taskSpec = {
|
|
14485
15047
|
id: dispatchTaskId,
|
|
14486
15048
|
subagentId,
|
|
@@ -14756,6 +15318,7 @@ function buildDirectorToolset(director, roster) {
|
|
|
14756
15318
|
makeAskResultTool(director),
|
|
14757
15319
|
makeRollUpTool(director),
|
|
14758
15320
|
makeQualityGateTool(director, roster),
|
|
15321
|
+
makeMutationTestTool(director, roster),
|
|
14759
15322
|
makeTerminateTool(director),
|
|
14760
15323
|
makeTerminateAllTool(director),
|
|
14761
15324
|
makeFleetTool(director),
|
|
@@ -14842,7 +15405,7 @@ import * as fsp24 from "node:fs/promises";
|
|
|
14842
15405
|
import * as path32 from "node:path";
|
|
14843
15406
|
|
|
14844
15407
|
// src/storage/session-store.ts
|
|
14845
|
-
import { randomUUID as
|
|
15408
|
+
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
14846
15409
|
import * as fsp23 from "node:fs/promises";
|
|
14847
15410
|
import * as path31 from "node:path";
|
|
14848
15411
|
|
|
@@ -16566,7 +17129,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
16566
17129
|
|
|
16567
17130
|
// src/storage/session-checkpoint-cas.ts
|
|
16568
17131
|
import { spawn as spawn3 } from "node:child_process";
|
|
16569
|
-
import { createHash as createHash3, randomUUID as
|
|
17132
|
+
import { createHash as createHash3, randomUUID as randomUUID13 } from "node:crypto";
|
|
16570
17133
|
import * as fsp10 from "node:fs/promises";
|
|
16571
17134
|
import * as path23 from "node:path";
|
|
16572
17135
|
|
|
@@ -16824,7 +17387,7 @@ var SessionCheckpointCas = class {
|
|
|
16824
17387
|
}
|
|
16825
17388
|
const temp = path23.join(
|
|
16826
17389
|
path23.dirname(target),
|
|
16827
|
-
`.${path23.basename(target)}.${process.pid}.${
|
|
17390
|
+
`.${path23.basename(target)}.${process.pid}.${randomUUID13()}.tmp`
|
|
16828
17391
|
);
|
|
16829
17392
|
let handle;
|
|
16830
17393
|
try {
|
|
@@ -18620,7 +19183,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
18620
19183
|
onAppend;
|
|
18621
19184
|
onAppendBatch;
|
|
18622
19185
|
catalogClient;
|
|
18623
|
-
maintenanceHolderId =
|
|
19186
|
+
maintenanceHolderId = randomUUID14();
|
|
18624
19187
|
_loadCache = /* @__PURE__ */ new Map();
|
|
18625
19188
|
loadCache = new SessionLoadCache(this._loadCache);
|
|
18626
19189
|
_indexCache = null;
|
|
@@ -20827,7 +21390,7 @@ function hashStr(s) {
|
|
|
20827
21390
|
}
|
|
20828
21391
|
|
|
20829
21392
|
// src/coordination/multi-agent-coordinator.ts
|
|
20830
|
-
import { randomUUID as
|
|
21393
|
+
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
20831
21394
|
import { EventEmitter as EventEmitter2 } from "node:events";
|
|
20832
21395
|
|
|
20833
21396
|
// src/coordination/coordinator/error-classifier.ts
|
|
@@ -20918,7 +21481,8 @@ async function executeSubagentWithTimeout({
|
|
|
20918
21481
|
budget,
|
|
20919
21482
|
preemptFraction = TIMEOUT_PREEMPT_FRACTION,
|
|
20920
21483
|
abortSubagent,
|
|
20921
|
-
currentSessionId
|
|
21484
|
+
currentSessionId,
|
|
21485
|
+
gracefulFinish
|
|
20922
21486
|
}) {
|
|
20923
21487
|
const initialTimeoutMs = budget.limits.timeoutMs;
|
|
20924
21488
|
const idleLimitMs = budget.limits.idleTimeoutMs;
|
|
@@ -20949,9 +21513,17 @@ async function executeSubagentWithTimeout({
|
|
|
20949
21513
|
const scheduleNext = () => {
|
|
20950
21514
|
const wallLimit = budget.limits.timeoutMs ?? initialTimeoutMs;
|
|
20951
21515
|
const wallRemaining = initialTimeoutMs === void 0 ? Number.POSITIVE_INFINITY : wallLimit - (Date.now() - start);
|
|
20952
|
-
const idleRemaining = idleLimitMs === void 0 ? Number.POSITIVE_INFINITY : (budget.limits.idleTimeoutMs ?? idleLimitMs) - budget.idleMs();
|
|
20953
|
-
const preemptRemaining = initialTimeoutMs === void 0 || preemptedCeiling === wallLimit ? Number.POSITIVE_INFINITY : wallLimit * preemptFraction - (Date.now() - start);
|
|
20954
|
-
|
|
21516
|
+
const idleRemaining = idleLimitMs === void 0 || gracefulFinish !== void 0 && initialTimeoutMs !== void 0 ? Number.POSITIVE_INFINITY : (budget.limits.idleTimeoutMs ?? idleLimitMs) - budget.idleMs();
|
|
21517
|
+
const preemptRemaining = initialTimeoutMs === void 0 || preemptedCeiling === wallLimit || gracefulFinish !== void 0 ? Number.POSITIVE_INFINITY : wallLimit * preemptFraction - (Date.now() - start);
|
|
21518
|
+
const next = Math.min(wallRemaining, idleRemaining, preemptRemaining);
|
|
21519
|
+
if (!Number.isFinite(next)) {
|
|
21520
|
+
if (timer) {
|
|
21521
|
+
clearTimeout(timer);
|
|
21522
|
+
timer = null;
|
|
21523
|
+
}
|
|
21524
|
+
return;
|
|
21525
|
+
}
|
|
21526
|
+
armFor(Math.max(25, next));
|
|
20955
21527
|
};
|
|
20956
21528
|
const negotiateTimeout = async (used, limit) => {
|
|
20957
21529
|
const handler = budget.onThreshold;
|
|
@@ -21000,6 +21572,10 @@ async function executeSubagentWithTimeout({
|
|
|
21000
21572
|
const wallExceeded = wallLimit !== void 0 && elapsed >= wallLimit;
|
|
21001
21573
|
const idleExceeded = idleLimit !== void 0 && budget.idleMs() >= idleLimit;
|
|
21002
21574
|
if (idleExceeded && !wallExceeded) {
|
|
21575
|
+
if (gracefulFinish !== void 0 && initialTimeoutMs !== void 0) {
|
|
21576
|
+
scheduleNext();
|
|
21577
|
+
return;
|
|
21578
|
+
}
|
|
21003
21579
|
const sessionId = currentSessionId();
|
|
21004
21580
|
budget._events?.emit("budget.threshold_reached", {
|
|
21005
21581
|
...sessionId ? { sessionId } : {},
|
|
@@ -21016,7 +21592,7 @@ async function executeSubagentWithTimeout({
|
|
|
21016
21592
|
reject(new BudgetExceededError("idle_timeout", idleLimit ?? 0, budget.idleMs()));
|
|
21017
21593
|
return;
|
|
21018
21594
|
}
|
|
21019
|
-
if (wallLimit !== void 0 && !wallExceeded && budget.onThreshold && preemptState === "active" /* ACTIVE */ && elapsed >= wallLimit * preemptFraction) {
|
|
21595
|
+
if (wallLimit !== void 0 && !wallExceeded && gracefulFinish === void 0 && budget.onThreshold && preemptState === "active" /* ACTIVE */ && elapsed >= wallLimit * preemptFraction) {
|
|
21020
21596
|
const activityTs = Date.now() - budget.idleMs();
|
|
21021
21597
|
if (activityTs <= lastGrantActivityTs) {
|
|
21022
21598
|
preemptState = "locked" /* LOCKED */;
|
|
@@ -21050,6 +21626,22 @@ async function executeSubagentWithTimeout({
|
|
|
21050
21626
|
return;
|
|
21051
21627
|
}
|
|
21052
21628
|
const limit = wallLimit ?? 0;
|
|
21629
|
+
if (gracefulFinish !== void 0) {
|
|
21630
|
+
if (!budget.graceGranted) {
|
|
21631
|
+
const reason = `wall-clock budget of ${Math.round(limit / 1e3)}s reached`;
|
|
21632
|
+
if (budget.notifyFinish(reason, { graceMs: gracefulFinish.graceMs })) {
|
|
21633
|
+
scheduleNext();
|
|
21634
|
+
return;
|
|
21635
|
+
}
|
|
21636
|
+
abortSubagent(ctx.subagentId);
|
|
21637
|
+
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
21638
|
+
return;
|
|
21639
|
+
} else {
|
|
21640
|
+
abortSubagent(ctx.subagentId);
|
|
21641
|
+
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
21642
|
+
return;
|
|
21643
|
+
}
|
|
21644
|
+
}
|
|
21053
21645
|
if (!budget.onThreshold) {
|
|
21054
21646
|
abortSubagent(ctx.subagentId);
|
|
21055
21647
|
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
@@ -21197,7 +21789,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
21197
21789
|
return { ...subagent, name: display };
|
|
21198
21790
|
}
|
|
21199
21791
|
async spawn(subagent) {
|
|
21200
|
-
const id = subagent.id ||
|
|
21792
|
+
const id = subagent.id || randomUUID15();
|
|
21201
21793
|
const cfg = this.withNickname(subagent, id);
|
|
21202
21794
|
if (this.subagents.has(id)) {
|
|
21203
21795
|
throw new Error(`Subagent id "${id}" already exists \u2014 refusing to overwrite`);
|
|
@@ -21434,6 +22026,32 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
21434
22026
|
completeTask(result) {
|
|
21435
22027
|
this.recordCompletion(result);
|
|
21436
22028
|
}
|
|
22029
|
+
/**
|
|
22030
|
+
* Ask every RUNNING subagent that opted into `gracefulFinish` to finish its
|
|
22031
|
+
* task in its own turn (see coordination/subagent-finish.ts). This is the
|
|
22032
|
+
* leader-side entry point for "the leader agent has finished": it delivers
|
|
22033
|
+
* an in-band notification between tool batches — never an interrupt, never
|
|
22034
|
+
* an abort. Each notified subagent keeps its existing time budget and
|
|
22035
|
+
* accelerates; the watchdog still bounds the maximum lifetime.
|
|
22036
|
+
*
|
|
22037
|
+
* Subagents without the policy opted in are deliberately untouched — their
|
|
22038
|
+
* lifecycle remains the legacy watchdog contract.
|
|
22039
|
+
*
|
|
22040
|
+
* Returns the number of subagents actually notified.
|
|
22041
|
+
*/
|
|
22042
|
+
requestFinish(reason) {
|
|
22043
|
+
let notified = 0;
|
|
22044
|
+
for (const subagent of this.subagents.values()) {
|
|
22045
|
+
if (subagent.status !== "running") continue;
|
|
22046
|
+
if (!resolveGracefulFinish(subagent.config)) continue;
|
|
22047
|
+
const budget = subagent.activeBudget;
|
|
22048
|
+
if (!budget) continue;
|
|
22049
|
+
const usage = budget.usage();
|
|
22050
|
+
if (usage.iterations === 0 && usage.toolCalls === 0) continue;
|
|
22051
|
+
if (budget.notifyFinish(reason)) notified++;
|
|
22052
|
+
}
|
|
22053
|
+
return notified;
|
|
22054
|
+
}
|
|
21437
22055
|
// --- internal dispatching ---------------------------------------------
|
|
21438
22056
|
tryDispatchNext() {
|
|
21439
22057
|
while (this.canDispatch()) {
|
|
@@ -21607,7 +22225,14 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
21607
22225
|
idleTimeoutMs: rawIdleTimeoutMs ?? this.config.defaultBudget?.idleTimeoutMs ?? configWithRosterDefaults.idleTimeoutMs
|
|
21608
22226
|
},
|
|
21609
22227
|
"auto",
|
|
21610
|
-
{
|
|
22228
|
+
{
|
|
22229
|
+
sessionId: () => this.currentSessionId(),
|
|
22230
|
+
subagentId,
|
|
22231
|
+
// Graceful-finish runs own wall-clock enforcement to the watchdog so
|
|
22232
|
+
// the notify-then-bound lifecycle cannot be raced by tool.progress
|
|
22233
|
+
// heartbeats calling checkTimeout() (see subagent-budget.ts).
|
|
22234
|
+
...resolveGracefulFinish(subagent.config) ? { wallClockWatchdogOwned: true } : {}
|
|
22235
|
+
}
|
|
21611
22236
|
);
|
|
21612
22237
|
subagent.activeBudget = budget;
|
|
21613
22238
|
if (!this.runner) {
|
|
@@ -21640,7 +22265,8 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
21640
22265
|
task,
|
|
21641
22266
|
runCtx,
|
|
21642
22267
|
budget,
|
|
21643
|
-
subagent.config.preemptFraction
|
|
22268
|
+
subagent.config.preemptFraction,
|
|
22269
|
+
resolveGracefulFinish(subagent.config)
|
|
21644
22270
|
);
|
|
21645
22271
|
result = {
|
|
21646
22272
|
subagentId,
|
|
@@ -21670,13 +22296,14 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
21670
22296
|
}
|
|
21671
22297
|
this.recordCompletion(result);
|
|
21672
22298
|
}
|
|
21673
|
-
async executeWithTimeout(runner, task, ctx, budget, preemptFraction) {
|
|
22299
|
+
async executeWithTimeout(runner, task, ctx, budget, preemptFraction, gracefulFinish) {
|
|
21674
22300
|
return executeSubagentWithTimeout({
|
|
21675
22301
|
runner,
|
|
21676
22302
|
task,
|
|
21677
22303
|
ctx,
|
|
21678
22304
|
budget,
|
|
21679
22305
|
preemptFraction,
|
|
22306
|
+
gracefulFinish,
|
|
21680
22307
|
abortSubagent: (subagentId) => this.subagents.get(subagentId)?.abortController.abort(),
|
|
21681
22308
|
currentSessionId: () => this.currentSessionId()
|
|
21682
22309
|
});
|
|
@@ -22082,7 +22709,7 @@ var Director = class _Director {
|
|
|
22082
22709
|
sessionProvider;
|
|
22083
22710
|
sessionModel;
|
|
22084
22711
|
constructor(opts) {
|
|
22085
|
-
this.id = opts.config.coordinatorId ||
|
|
22712
|
+
this.id = opts.config.coordinatorId || randomUUID16();
|
|
22086
22713
|
this.manifestPath = opts.manifestPath;
|
|
22087
22714
|
this.roster = opts.roster;
|
|
22088
22715
|
this.directorPreamble = opts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
|
|
@@ -22289,6 +22916,17 @@ var Director = class _Director {
|
|
|
22289
22916
|
isWorkComplete() {
|
|
22290
22917
|
return this.workCompleteFlag;
|
|
22291
22918
|
}
|
|
22919
|
+
/**
|
|
22920
|
+
* Ask every running background subagent that opted into `gracefulFinish`
|
|
22921
|
+
* to finish its task in its own turn. In-band notification between tool
|
|
22922
|
+
* batches — no interrupt, no abort; each subagent keeps its time budget and
|
|
22923
|
+
* accelerates. Session shutdown calls this before draining Chimera work so
|
|
22924
|
+
* the post-session reviewer is nudged to complete rather than killed.
|
|
22925
|
+
* Returns the number of subagents notified.
|
|
22926
|
+
*/
|
|
22927
|
+
requestFinish(reason) {
|
|
22928
|
+
return this.coordinator.requestFinish(reason);
|
|
22929
|
+
}
|
|
22292
22930
|
setLeaderBtwNote(note) {
|
|
22293
22931
|
return this.btwNotes.add(note);
|
|
22294
22932
|
}
|
|
@@ -22365,7 +23003,7 @@ var Director = class _Director {
|
|
|
22365
23003
|
);
|
|
22366
23004
|
}
|
|
22367
23005
|
const msg = {
|
|
22368
|
-
id:
|
|
23006
|
+
id: randomUUID16(),
|
|
22369
23007
|
type: "task",
|
|
22370
23008
|
from: this.id,
|
|
22371
23009
|
to: subagentId,
|
|
@@ -22619,7 +23257,7 @@ var Director = class _Director {
|
|
|
22619
23257
|
};
|
|
22620
23258
|
|
|
22621
23259
|
// src/coordination/fleet-manager.ts
|
|
22622
|
-
import { randomUUID as
|
|
23260
|
+
import { randomUUID as randomUUID17 } from "node:crypto";
|
|
22623
23261
|
import * as fsp26 from "node:fs/promises";
|
|
22624
23262
|
import * as path33 from "node:path";
|
|
22625
23263
|
var FleetManager = class {
|
|
@@ -22685,7 +23323,7 @@ var FleetManager = class {
|
|
|
22685
23323
|
maxContext;
|
|
22686
23324
|
constructor(opts = {}) {
|
|
22687
23325
|
this.manifestPath = opts.manifestPath;
|
|
22688
|
-
this.directorRunId = opts.directorRunId ??
|
|
23326
|
+
this.directorRunId = opts.directorRunId ?? randomUUID17();
|
|
22689
23327
|
this.maxSpawns = opts.maxSpawns ?? Number.POSITIVE_INFINITY;
|
|
22690
23328
|
this.maxSpawnDepth = resolveMaxSpawnDepth(opts.maxSpawnDepth);
|
|
22691
23329
|
this.spawnDepth = opts.spawnDepth ?? 0;
|
|
@@ -24490,7 +25128,7 @@ function makeFleetStatusTool(opts = {}) {
|
|
|
24490
25128
|
}
|
|
24491
25129
|
|
|
24492
25130
|
// src/coordination/fleet-supervisor.ts
|
|
24493
|
-
import { randomUUID as
|
|
25131
|
+
import { randomUUID as randomUUID18 } from "node:crypto";
|
|
24494
25132
|
var COLLAB_ID_PREFIXES2 = ["bug-hunter-", "refactor-planner-", "critic-"];
|
|
24495
25133
|
var DEFAULTS = {
|
|
24496
25134
|
intervalMs: 2e4,
|
|
@@ -24768,7 +25406,7 @@ var FleetSupervisor = class {
|
|
|
24768
25406
|
*/
|
|
24769
25407
|
async decide(question, context, options, risk) {
|
|
24770
25408
|
const request = {
|
|
24771
|
-
id: `fleetsup-${
|
|
25409
|
+
id: `fleetsup-${randomUUID18()}`,
|
|
24772
25410
|
sessionId: this.opts.sessionId?.(),
|
|
24773
25411
|
source: "system",
|
|
24774
25412
|
question,
|
|
@@ -25128,6 +25766,22 @@ var SEND_ALLOWED_FIELDS = /* @__PURE__ */ new Set([
|
|
|
25128
25766
|
// receiver trusts the sender-asserted `sessionId`; the boundary must
|
|
25129
25767
|
// refuse the field entirely.
|
|
25130
25768
|
]);
|
|
25769
|
+
var SEND_FORBIDDEN_FIELDS = /* @__PURE__ */ new Set([
|
|
25770
|
+
"from",
|
|
25771
|
+
"sessionAffinity"
|
|
25772
|
+
]);
|
|
25773
|
+
function filterMailboxSendPayload(input) {
|
|
25774
|
+
const payload = {};
|
|
25775
|
+
const stripped = [];
|
|
25776
|
+
for (const key of Object.keys(input)) {
|
|
25777
|
+
if (SEND_ALLOWED_FIELDS.has(key) || SEND_FORBIDDEN_FIELDS.has(key)) {
|
|
25778
|
+
payload[key] = input[key];
|
|
25779
|
+
} else {
|
|
25780
|
+
stripped.push(key);
|
|
25781
|
+
}
|
|
25782
|
+
}
|
|
25783
|
+
return { payload, stripped };
|
|
25784
|
+
}
|
|
25131
25785
|
var ACK_ALLOWED_FIELDS = /* @__PURE__ */ new Set([
|
|
25132
25786
|
"messageId",
|
|
25133
25787
|
"read",
|
|
@@ -25365,7 +26019,9 @@ function makeMailSendTool(opts = {}) {
|
|
|
25365
26019
|
required: ["to", "subject", "body"]
|
|
25366
26020
|
},
|
|
25367
26021
|
async execute(input, ctx) {
|
|
25368
|
-
const i
|
|
26022
|
+
const { payload: i, stripped } = filterMailboxSendPayload(
|
|
26023
|
+
input ?? {}
|
|
26024
|
+
);
|
|
25369
26025
|
const rawTo = i.to;
|
|
25370
26026
|
const subject = i.subject;
|
|
25371
26027
|
const body = i.body;
|
|
@@ -25388,15 +26044,13 @@ function makeMailSendTool(opts = {}) {
|
|
|
25388
26044
|
recipientAliases: /* @__PURE__ */ new Set([codecIdentity.baseId]),
|
|
25389
26045
|
sessionId: codecIdentity.sessionId
|
|
25390
26046
|
};
|
|
26047
|
+
let parsed;
|
|
25391
26048
|
try {
|
|
25392
|
-
parseMailboxSendInput(i, codecActor);
|
|
26049
|
+
parsed = parseMailboxSendInput(i, codecActor);
|
|
25393
26050
|
} catch (err) {
|
|
25394
26051
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
25395
26052
|
}
|
|
25396
|
-
const audience =
|
|
25397
|
-
if (audience !== void 0 && audience !== "all" && audience !== "leaders") {
|
|
25398
|
-
return { ok: false, error: '"audience" must be "all" or "leaders".' };
|
|
25399
|
-
}
|
|
26053
|
+
const audience = parsed.audience;
|
|
25400
26054
|
const mb = resolveMailbox(ctx);
|
|
25401
26055
|
const identity = await register(mb, ctx);
|
|
25402
26056
|
const requestedTo = normalizeRecipient(rawTo, identity.sessionId);
|
|
@@ -25410,10 +26064,10 @@ function makeMailSendTool(opts = {}) {
|
|
|
25410
26064
|
to: delivery.to,
|
|
25411
26065
|
type: resolvedType,
|
|
25412
26066
|
audience: delivery.audience,
|
|
25413
|
-
subject,
|
|
25414
|
-
body,
|
|
25415
|
-
priority:
|
|
25416
|
-
replyTo:
|
|
26067
|
+
subject: parsed.subject,
|
|
26068
|
+
body: parsed.body,
|
|
26069
|
+
priority: parsed.priority,
|
|
26070
|
+
replyTo: parsed.replyTo,
|
|
25417
26071
|
senderSessionId: identity.sessionId
|
|
25418
26072
|
});
|
|
25419
26073
|
return {
|
|
@@ -25421,7 +26075,9 @@ function makeMailSendTool(opts = {}) {
|
|
|
25421
26075
|
messageId: msg.id,
|
|
25422
26076
|
from: identity.callerId,
|
|
25423
26077
|
to: msg.to,
|
|
25424
|
-
|
|
26078
|
+
// Surfacing what was stripped keeps the send auditable without
|
|
26079
|
+
// re-introducing the clutter into the payload itself.
|
|
26080
|
+
...stripped.length > 0 ? { strippedFields: stripped, summary: `Mail sent to ${msg.to === "*" ? "all agents" : msg.to} as ${identity.callerId}. Ignored ${stripped.length} unrecognized field(s): ${stripped.join(", ")}.` } : { summary: `Mail sent to ${msg.to === "*" ? "all agents" : msg.to} as ${identity.callerId}.` }
|
|
25425
26081
|
};
|
|
25426
26082
|
}
|
|
25427
26083
|
};
|
|
@@ -28943,7 +29599,7 @@ function createAgentMonitorService(opts) {
|
|
|
28943
29599
|
}
|
|
28944
29600
|
|
|
28945
29601
|
// src/coordination/autonomous-brain.ts
|
|
28946
|
-
import { randomUUID as
|
|
29602
|
+
import { randomUUID as randomUUID19 } from "node:crypto";
|
|
28947
29603
|
var AutonomousBrain = class {
|
|
28948
29604
|
graph;
|
|
28949
29605
|
// Fleet bus for emitting decisions — null-safe, no-op if not provided
|
|
@@ -29049,7 +29705,7 @@ var AutonomousBrain = class {
|
|
|
29049
29705
|
consequence: i === 0 ? `Spawn the most appropriate agent for: ${taskDescription.slice(0, 80)}` : `Spawn an alternative agent for the same task`
|
|
29050
29706
|
}));
|
|
29051
29707
|
return this.decideAuto({
|
|
29052
|
-
id:
|
|
29708
|
+
id: randomUUID19(),
|
|
29053
29709
|
source,
|
|
29054
29710
|
decisionType: "spawn",
|
|
29055
29711
|
question: `Should we spawn a subagent for this task?`,
|
|
@@ -29092,7 +29748,7 @@ var AutonomousBrain = class {
|
|
|
29092
29748
|
}
|
|
29093
29749
|
];
|
|
29094
29750
|
return this.decideAuto({
|
|
29095
|
-
id:
|
|
29751
|
+
id: randomUUID19(),
|
|
29096
29752
|
source,
|
|
29097
29753
|
decisionType: "approve_change",
|
|
29098
29754
|
question: `Should we approve the change "${change.title}"?`,
|
|
@@ -29151,7 +29807,7 @@ var AutonomousBrain = class {
|
|
|
29151
29807
|
consequence: "Break the task into smaller sub-tasks"
|
|
29152
29808
|
});
|
|
29153
29809
|
return this.decideAuto({
|
|
29154
|
-
id:
|
|
29810
|
+
id: randomUUID19(),
|
|
29155
29811
|
source,
|
|
29156
29812
|
decisionType: "escalate_task",
|
|
29157
29813
|
question: `Task failed: ${error.slice(0, 100)}. How should we proceed?`,
|
|
@@ -29285,10 +29941,10 @@ ${ctx.error}`);
|
|
|
29285
29941
|
};
|
|
29286
29942
|
|
|
29287
29943
|
// src/coordination/autonomous-coordinator.ts
|
|
29288
|
-
import { randomUUID as
|
|
29944
|
+
import { randomUUID as randomUUID22 } from "node:crypto";
|
|
29289
29945
|
|
|
29290
29946
|
// src/coordination/knowledge-graph.ts
|
|
29291
|
-
import { randomUUID as
|
|
29947
|
+
import { randomUUID as randomUUID20 } from "node:crypto";
|
|
29292
29948
|
import * as fsp28 from "node:fs/promises";
|
|
29293
29949
|
import * as path41 from "node:path";
|
|
29294
29950
|
var DEFAULT_MAX_NODES = 2e3;
|
|
@@ -29336,7 +29992,7 @@ var KnowledgeGraph = class _KnowledgeGraph {
|
|
|
29336
29992
|
* Returns the node with its assigned id.
|
|
29337
29993
|
*/
|
|
29338
29994
|
async add(node) {
|
|
29339
|
-
const full = { id:
|
|
29995
|
+
const full = { id: randomUUID20(), ...node };
|
|
29340
29996
|
this.nodes.set(full.id, full);
|
|
29341
29997
|
this._trackSeq(full.id);
|
|
29342
29998
|
this._addToIndex(full, this._indexKeys(full));
|
|
@@ -29465,8 +30121,8 @@ var KnowledgeGraph = class _KnowledgeGraph {
|
|
|
29465
30121
|
if (this.subs.size >= MAX_SUBSCRIPTIONS) {
|
|
29466
30122
|
throw new Error(`Knowledge graph subscription limit reached (${MAX_SUBSCRIPTIONS})`);
|
|
29467
30123
|
}
|
|
29468
|
-
const channel =
|
|
29469
|
-
const sub = { id:
|
|
30124
|
+
const channel = randomUUID20();
|
|
30125
|
+
const sub = { id: randomUUID20(), agentId, filter, channel };
|
|
29470
30126
|
this.subs.set(channel, sub);
|
|
29471
30127
|
this.pendingDeliveries.set(channel, []);
|
|
29472
30128
|
return channel;
|
|
@@ -29947,7 +30603,7 @@ var TaskDAG = class {
|
|
|
29947
30603
|
};
|
|
29948
30604
|
|
|
29949
30605
|
// src/coordination/task-auctioneer.ts
|
|
29950
|
-
import { randomUUID as
|
|
30606
|
+
import { randomUUID as randomUUID21 } from "node:crypto";
|
|
29951
30607
|
function isTerminalGoalStatus(status) {
|
|
29952
30608
|
return status === "done" || status === "failed";
|
|
29953
30609
|
}
|
|
@@ -30070,7 +30726,7 @@ var TaskAuctioneer = class {
|
|
|
30070
30726
|
const score = dispatchResult.confidence * (dispatchResult.role === agent.agentRole ? 1.2 : 1);
|
|
30071
30727
|
if (score < this.minConfidence) return false;
|
|
30072
30728
|
const bid = {
|
|
30073
|
-
id:
|
|
30729
|
+
id: randomUUID21(),
|
|
30074
30730
|
taskId,
|
|
30075
30731
|
agentId: agent.agentId,
|
|
30076
30732
|
agentName: agent.agentName,
|
|
@@ -31007,7 +31663,7 @@ var AutonomousCoordinator = class _AutonomousCoordinator {
|
|
|
31007
31663
|
break;
|
|
31008
31664
|
}
|
|
31009
31665
|
const decision = await this.brain.decideAuto({
|
|
31010
|
-
id:
|
|
31666
|
+
id: randomUUID22(),
|
|
31011
31667
|
source: "system",
|
|
31012
31668
|
decisionType: "prioritize_goals",
|
|
31013
31669
|
question: `What should we work on next? Open goals: ${dispatchable.map((g) => g.title).join(", ")}`,
|
|
@@ -31903,6 +32559,7 @@ export {
|
|
|
31903
32559
|
makeMailInboxTool,
|
|
31904
32560
|
makeMailSendTool,
|
|
31905
32561
|
makeMailboxTool,
|
|
32562
|
+
makeMutationTestTool,
|
|
31906
32563
|
makeQualityGateTool,
|
|
31907
32564
|
makeRollUpTool,
|
|
31908
32565
|
makeSpawnTool,
|