@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
package/dist/defaults/index.js
CHANGED
|
@@ -356,6 +356,46 @@ function createMessage(type, from, payload, to) {
|
|
|
356
356
|
};
|
|
357
357
|
}
|
|
358
358
|
|
|
359
|
+
// src/core/btw.ts
|
|
360
|
+
var META_KEY = "_btwNotes";
|
|
361
|
+
var MAX_PENDING = 20;
|
|
362
|
+
function readQueue(ctx) {
|
|
363
|
+
const raw = ctx.meta[META_KEY];
|
|
364
|
+
return Array.isArray(raw) ? raw : [];
|
|
365
|
+
}
|
|
366
|
+
function setBtwNote(ctx, text) {
|
|
367
|
+
const trimmed = text.trim();
|
|
368
|
+
if (!trimmed) return readQueue(ctx).length;
|
|
369
|
+
const next = [...readQueue(ctx), trimmed].slice(-MAX_PENDING);
|
|
370
|
+
ctx.meta[META_KEY] = next;
|
|
371
|
+
return next.length;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// src/coordination/subagent-finish.ts
|
|
375
|
+
var SUBAGENT_FINISH_REQUESTED_EVENT = "subagent.finish_requested";
|
|
376
|
+
var DEFAULT_SUBAGENT_FINISH_GRACE_MS = 12e4;
|
|
377
|
+
function resolveGracefulFinish(config) {
|
|
378
|
+
const raw = config.gracefulFinish;
|
|
379
|
+
if (raw === void 0 || raw === false) return void 0;
|
|
380
|
+
if (raw === true) return { graceMs: DEFAULT_SUBAGENT_FINISH_GRACE_MS };
|
|
381
|
+
const graceMs = typeof raw.graceMs === "number" && Number.isFinite(raw.graceMs) && raw.graceMs > 0 ? Math.floor(raw.graceMs) : DEFAULT_SUBAGENT_FINISH_GRACE_MS;
|
|
382
|
+
return { graceMs };
|
|
383
|
+
}
|
|
384
|
+
function buildSubagentFinishNotice(input) {
|
|
385
|
+
const localTime = new Date(input.deadlineMs).toISOString();
|
|
386
|
+
const seconds = Math.max(1, Math.round(input.graceMs / 1e3));
|
|
387
|
+
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.`;
|
|
388
|
+
return [
|
|
389
|
+
"[SUBAGENT FINISH] The leader agent has finished its work.",
|
|
390
|
+
`Reason: ${input.reason}`,
|
|
391
|
+
timeLeft,
|
|
392
|
+
"Finish your task now, in this turn: complete the thought you are working on, stop",
|
|
393
|
+
"starting new tool calls unless one is strictly required to finish, and write your",
|
|
394
|
+
"final answer or report as your final output, then end your turn.",
|
|
395
|
+
"Do not restart the task and do not begin new work."
|
|
396
|
+
].join("\n");
|
|
397
|
+
}
|
|
398
|
+
|
|
359
399
|
// src/coordination/subagent-budget.ts
|
|
360
400
|
var TIMEOUT_PREEMPT_FRACTION = 0.85;
|
|
361
401
|
var DECISION_TIMEOUT_MS = 6e4;
|
|
@@ -413,6 +453,82 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
413
453
|
this.limits.idleTimeoutMs = ext.idleTimeoutMs;
|
|
414
454
|
}
|
|
415
455
|
}
|
|
456
|
+
/**
|
|
457
|
+
* Graceful-finish state (see coordination/subagent-finish.ts).
|
|
458
|
+
* `_finishNotified` guards the single in-band emission; `_grace` records a
|
|
459
|
+
* granted working-time extension past the original wall-clock deadline.
|
|
460
|
+
* They are separate because the two callers want different semantics:
|
|
461
|
+
* the watchdog grants grace at the deadline crossing (notify + extend),
|
|
462
|
+
* while an explicit leader-finished request only notifies — a subagent
|
|
463
|
+
* well inside its budget keeps its full legitimate working time and simply
|
|
464
|
+
* accelerates.
|
|
465
|
+
*/
|
|
466
|
+
_finishNotified = false;
|
|
467
|
+
_grace = null;
|
|
468
|
+
/** True once the in-band finish notification has been emitted. */
|
|
469
|
+
get finishNotified() {
|
|
470
|
+
return this._finishNotified;
|
|
471
|
+
}
|
|
472
|
+
/** True once a grace window has been granted past the original deadline. */
|
|
473
|
+
get graceGranted() {
|
|
474
|
+
return this._grace !== null;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Notify the subagent in-band to finish its task in its own turn:
|
|
478
|
+
* `subagent.finish_requested` is emitted on the wired EventBus and the
|
|
479
|
+
* agent loop folds the notice into the conversation between tool batches.
|
|
480
|
+
* Nothing aborts — this is a notification, never an interrupt.
|
|
481
|
+
*
|
|
482
|
+
* `opts.graceMs` additionally extends the wall-clock ceiling by that window
|
|
483
|
+
* (used by the watchdog at a deadline crossing, so the model gets working
|
|
484
|
+
* time instead of a kill). Omit it to notify without touching the budget —
|
|
485
|
+
* the subagent keeps its existing time budget and just accelerates.
|
|
486
|
+
*
|
|
487
|
+
* Returns `true` when this call did something (emitted the notification
|
|
488
|
+
* and/or granted grace); `false` when there was nothing to do (already
|
|
489
|
+
* notified, grace already granted, no EventBus wired, budget not started).
|
|
490
|
+
*/
|
|
491
|
+
notifyFinish(reason, opts, now = Date.now) {
|
|
492
|
+
if (!this._events) return false;
|
|
493
|
+
if (this.startTime === null) return false;
|
|
494
|
+
const shouldEmit = !this._finishNotified;
|
|
495
|
+
const rawGrace = opts?.graceMs;
|
|
496
|
+
const shouldGrant = rawGrace !== void 0 && this._grace === null;
|
|
497
|
+
if (!shouldEmit && !shouldGrant) return false;
|
|
498
|
+
let grantedGraceMs = 0;
|
|
499
|
+
let graceDeadlineMs;
|
|
500
|
+
if (shouldGrant && rawGrace !== void 0) {
|
|
501
|
+
grantedGraceMs = Number.isFinite(rawGrace) && rawGrace > 0 ? Math.floor(rawGrace) : DEFAULT_SUBAGENT_FINISH_GRACE_MS;
|
|
502
|
+
graceDeadlineMs = now() + grantedGraceMs;
|
|
503
|
+
this._grace = { deadlineMs: graceDeadlineMs, graceMs: grantedGraceMs };
|
|
504
|
+
this.patchLimits({ timeoutMs: graceDeadlineMs - this.startTime });
|
|
505
|
+
}
|
|
506
|
+
if (shouldEmit) {
|
|
507
|
+
this._finishNotified = true;
|
|
508
|
+
const effectiveDeadlineMs = graceDeadlineMs ?? (this.limits.timeoutMs !== void 0 ? this.startTime + this.limits.timeoutMs : now() + DEFAULT_SUBAGENT_FINISH_GRACE_MS);
|
|
509
|
+
const effectiveGraceMs = Math.max(0, effectiveDeadlineMs - now());
|
|
510
|
+
const subagentId = this._subagentId;
|
|
511
|
+
this._events.emit(SUBAGENT_FINISH_REQUESTED_EVENT, {
|
|
512
|
+
// Omitted entirely when the budget was built without an id — an
|
|
513
|
+
// empty string is an address that matches nothing.
|
|
514
|
+
...subagentId !== void 0 ? { subagentId } : {},
|
|
515
|
+
reason,
|
|
516
|
+
deadlineMs: effectiveDeadlineMs,
|
|
517
|
+
graceMs: effectiveGraceMs,
|
|
518
|
+
notice: buildSubagentFinishNotice({
|
|
519
|
+
reason,
|
|
520
|
+
deadlineMs: effectiveDeadlineMs,
|
|
521
|
+
graceMs: effectiveGraceMs
|
|
522
|
+
})
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
return true;
|
|
526
|
+
}
|
|
527
|
+
/** Epoch ms by which the subagent should have produced its final output,
|
|
528
|
+
* once a grace window was granted. Undefined before that. */
|
|
529
|
+
get finishDeadlineMs() {
|
|
530
|
+
return this._grace?.deadlineMs;
|
|
531
|
+
}
|
|
416
532
|
iterations = 0;
|
|
417
533
|
toolCalls = 0;
|
|
418
534
|
tokenInput = 0;
|
|
@@ -428,6 +544,10 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
428
544
|
lastActivityTime = null;
|
|
429
545
|
_onThreshold;
|
|
430
546
|
_sessionId;
|
|
547
|
+
/** Owning subagent id — used to address the graceful-finish event. */
|
|
548
|
+
_subagentId;
|
|
549
|
+
/** True when only the coordinator watchdog may enforce wall-clock limits. */
|
|
550
|
+
_wallClockWatchdogOwned;
|
|
431
551
|
/**
|
|
432
552
|
* Hard cap on how long `_negotiateExtension` waits for the coordinator to
|
|
433
553
|
* respond before defaulting to 'stop'. Without this fallback an absent
|
|
@@ -499,6 +619,8 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
499
619
|
constructor(limits = {}, mode = "auto", options = {}) {
|
|
500
620
|
this._mode = mode;
|
|
501
621
|
this._sessionId = options.sessionId;
|
|
622
|
+
this._subagentId = options.subagentId;
|
|
623
|
+
this._wallClockWatchdogOwned = options.wallClockWatchdogOwned === true;
|
|
502
624
|
this.limits = { ...limits };
|
|
503
625
|
}
|
|
504
626
|
currentSessionId() {
|
|
@@ -577,7 +699,7 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
577
699
|
if (this.limits.idleTimeoutMs !== void 0 && idle > this.limits.idleTimeoutMs) {
|
|
578
700
|
exceeded.push({ kind: "idle_timeout", used: idle, limit: this.limits.idleTimeoutMs });
|
|
579
701
|
}
|
|
580
|
-
const wallOwnedByWatchdog = this._onThreshold !== void 0 && this._watchdogActive === this.limits.timeoutMs;
|
|
702
|
+
const wallOwnedByWatchdog = this._wallClockWatchdogOwned || this._onThreshold !== void 0 && this._watchdogActive === this.limits.timeoutMs;
|
|
581
703
|
if (this.limits.timeoutMs !== void 0 && elapsedMs2 > this.limits.timeoutMs && !wallOwnedByWatchdog) {
|
|
582
704
|
exceeded.push({ kind: "timeout", used: elapsedMs2, limit: this.limits.timeoutMs });
|
|
583
705
|
}
|
|
@@ -776,7 +898,7 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
776
898
|
if (timeoutMs === void 0 && idleTimeoutMs === void 0) return;
|
|
777
899
|
const elapsed = Date.now() - this.startTime;
|
|
778
900
|
const wallSkipped = this._onThreshold !== void 0 && this._watchdogActive !== void 0 && timeoutMs !== void 0 && this._watchdogActive === timeoutMs;
|
|
779
|
-
const wallTripped = wallSkipped ? false : timeoutMs !== void 0 && elapsed > timeoutMs;
|
|
901
|
+
const wallTripped = this._wallClockWatchdogOwned || wallSkipped ? false : timeoutMs !== void 0 && elapsed > timeoutMs;
|
|
780
902
|
const idleTripped = idleTimeoutMs !== void 0 && this.idleMs() > idleTimeoutMs;
|
|
781
903
|
if (!wallTripped && !idleTripped) return;
|
|
782
904
|
void this.checkLimits(elapsed);
|
|
@@ -1178,6 +1300,14 @@ function makeAgentSubagentRunner(opts) {
|
|
|
1178
1300
|
);
|
|
1179
1301
|
const onParentAbort = () => aborter.abort();
|
|
1180
1302
|
ctx.signal.addEventListener("abort", onParentAbort);
|
|
1303
|
+
if (resolveGracefulFinish(ctx.config)) {
|
|
1304
|
+
unsub.push(
|
|
1305
|
+
events.on("subagent.finish_requested", (e) => {
|
|
1306
|
+
if (e.subagentId && e.subagentId !== ctx.subagentId) return;
|
|
1307
|
+
setBtwNote(agent.ctx, e.notice);
|
|
1308
|
+
})
|
|
1309
|
+
);
|
|
1310
|
+
}
|
|
1181
1311
|
let result;
|
|
1182
1312
|
try {
|
|
1183
1313
|
result = await agent.run(format(task, ctx.config), { signal: aborter.signal });
|
|
@@ -5440,6 +5570,20 @@ var EXPLORE_COMPANION_AGENT = {
|
|
|
5440
5570
|
textStream: "silent",
|
|
5441
5571
|
toolStream: "silent"
|
|
5442
5572
|
};
|
|
5573
|
+
var CHAOS_MONKEY_AGENT = {
|
|
5574
|
+
...defineAgent("chaos-monkey", "Chaos Monkey"),
|
|
5575
|
+
tools: [...TOOLS.build],
|
|
5576
|
+
skillNames: ["testing", "typescript-strict"],
|
|
5577
|
+
spawnBudgetExempt: true,
|
|
5578
|
+
// Follow fleet worktree policy (NOT 'required'): mutation targets are
|
|
5579
|
+
// often freshly written and uncommitted — a worktree spawned from HEAD
|
|
5580
|
+
// would not contain them and every mutant would drift. Callers pass
|
|
5581
|
+
// `worktree: 'off'` in the mutation_test input for uncommitted targets.
|
|
5582
|
+
worktree: "auto",
|
|
5583
|
+
// Report travels via submit_result + final text, not the leader's stream.
|
|
5584
|
+
textStream: "silent",
|
|
5585
|
+
toolStream: "silent"
|
|
5586
|
+
};
|
|
5443
5587
|
var CRITIC_AGENT = defineAgent("critic", "Critic");
|
|
5444
5588
|
var GENERIC_AGENT = defineAgent("generic", "Generic Project Agent");
|
|
5445
5589
|
function withDispatchMetadata(definition) {
|
|
@@ -5459,6 +5603,7 @@ var FLEET_ROSTER = {
|
|
|
5459
5603
|
generic: GENERIC_AGENT,
|
|
5460
5604
|
"shadow-agent": SHADOW_AGENT,
|
|
5461
5605
|
"explore-companion": EXPLORE_COMPANION_AGENT,
|
|
5606
|
+
"chaos-monkey": CHAOS_MONKEY_AGENT,
|
|
5462
5607
|
...Object.fromEntries(
|
|
5463
5608
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, withDispatchMetadata(d)])
|
|
5464
5609
|
)
|
|
@@ -5489,6 +5634,16 @@ var FLEET_ROSTER_BUDGETS = {
|
|
|
5489
5634
|
maxTokens: 96e3,
|
|
5490
5635
|
maxCostUsd: 0.5
|
|
5491
5636
|
},
|
|
5637
|
+
"chaos-monkey": {
|
|
5638
|
+
// A mutation pass is many short apply/run/restore cycles — per-mutant
|
|
5639
|
+
// work is tiny, but a large plan (25 mutants/file × N files) needs
|
|
5640
|
+
// headroom. Idle-based reaping covers a stalled pass.
|
|
5641
|
+
idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS,
|
|
5642
|
+
maxIterations: 2e3,
|
|
5643
|
+
maxToolCalls: 6e3,
|
|
5644
|
+
maxTokens: 96e3,
|
|
5645
|
+
maxCostUsd: 0.5
|
|
5646
|
+
},
|
|
5492
5647
|
...Object.fromEntries(
|
|
5493
5648
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.budget])
|
|
5494
5649
|
)
|
|
@@ -6228,7 +6383,7 @@ async function readSubagentPartial(opts, subagentId) {
|
|
|
6228
6383
|
}
|
|
6229
6384
|
|
|
6230
6385
|
// src/coordination/director.ts
|
|
6231
|
-
import { randomUUID as
|
|
6386
|
+
import { randomUUID as randomUUID13 } from "node:crypto";
|
|
6232
6387
|
import * as fsp25 from "node:fs/promises";
|
|
6233
6388
|
|
|
6234
6389
|
// src/core/instruction-template.ts
|
|
@@ -8244,7 +8399,7 @@ ${JSON.stringify(result.result, null, 2)}
|
|
|
8244
8399
|
};
|
|
8245
8400
|
|
|
8246
8401
|
// src/coordination/director-tools.ts
|
|
8247
|
-
import { randomUUID as
|
|
8402
|
+
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
8248
8403
|
import {
|
|
8249
8404
|
completeKanbanDispatch,
|
|
8250
8405
|
failKanbanDispatch,
|
|
@@ -9444,6 +9599,413 @@ function excerpt(text, max) {
|
|
|
9444
9599
|
...(truncated)`;
|
|
9445
9600
|
}
|
|
9446
9601
|
|
|
9602
|
+
// src/coordination/director-mutation-test-tool.ts
|
|
9603
|
+
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
9604
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
9605
|
+
import { isAbsolute as isAbsolute2, join as join10 } from "node:path";
|
|
9606
|
+
|
|
9607
|
+
// src/coordination/mutation-engine.ts
|
|
9608
|
+
var TOKEN_PATTERNS = [
|
|
9609
|
+
{
|
|
9610
|
+
kind: "relax-boundary",
|
|
9611
|
+
// `>` not followed by `=` and not part of `=>` or `>>`; require code-ish
|
|
9612
|
+
// context on both sides so generic text (JSX, strings) is not touched.
|
|
9613
|
+
regex: /(?<=[\w\)\]\}'"`])\s*\x20?(?<op>>(?!=|>))/g,
|
|
9614
|
+
replace: () => ">="
|
|
9615
|
+
},
|
|
9616
|
+
{
|
|
9617
|
+
kind: "tighten-boundary",
|
|
9618
|
+
regex: /(?<=[\w\)\]\}'"`])\s*\x20?(?<op>>=)/g,
|
|
9619
|
+
replace: () => ">"
|
|
9620
|
+
},
|
|
9621
|
+
{
|
|
9622
|
+
kind: "arith-plus-to-minus",
|
|
9623
|
+
// `+` between operands (binary), not `++`, unary `+x`, or `+=`.
|
|
9624
|
+
regex: /(?<=[\w\)\]\}'"`])\s*\x20?(?<op>\+(?!\+|=))/g,
|
|
9625
|
+
replace: () => "-"
|
|
9626
|
+
},
|
|
9627
|
+
{
|
|
9628
|
+
kind: "arith-minus-to-plus",
|
|
9629
|
+
// Binary `-` between operands, not `--`, `-=` or negative-number literal.
|
|
9630
|
+
regex: /(?<=[\w\)\]\}'"`])\s*\x20?(?<op>-(?!-|=))/g,
|
|
9631
|
+
replace: () => "+"
|
|
9632
|
+
},
|
|
9633
|
+
{
|
|
9634
|
+
kind: "negate-boolean",
|
|
9635
|
+
// Standalone boolean literals used as values, not property names.
|
|
9636
|
+
regex: /(?<![.\w$])(?<op>true|false)(?![\w$])/g,
|
|
9637
|
+
replace: (m) => m === "true" ? "false" : "true"
|
|
9638
|
+
},
|
|
9639
|
+
{
|
|
9640
|
+
kind: "return-null",
|
|
9641
|
+
// `return <expr>;` where expr is not already null/undefined/void.
|
|
9642
|
+
regex: /(?<indent>\breturn\b)(?<expr>\s+[^;{}\n]+?)\s*;/g,
|
|
9643
|
+
replace: () => "return null;"
|
|
9644
|
+
}
|
|
9645
|
+
];
|
|
9646
|
+
function planMutations(file, source, opts = {}) {
|
|
9647
|
+
const maxPerFile = opts.maxPerFile ?? 25;
|
|
9648
|
+
const out = [];
|
|
9649
|
+
const lines = source.split("\n");
|
|
9650
|
+
for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
|
|
9651
|
+
const line = lines[lineIdx];
|
|
9652
|
+
const t = line.trim();
|
|
9653
|
+
if (t.startsWith("//") || t.startsWith("*") || t.startsWith("/*")) continue;
|
|
9654
|
+
for (const pattern of TOKEN_PATTERNS) {
|
|
9655
|
+
pattern.regex.lastIndex = 0;
|
|
9656
|
+
let m;
|
|
9657
|
+
while ((m = pattern.regex.exec(line)) !== null) {
|
|
9658
|
+
const token = m.groups?.["op"] ?? m[0];
|
|
9659
|
+
const tokenStart = m.index + m[0].indexOf(token);
|
|
9660
|
+
if (isMasked(line, tokenStart, token.length)) continue;
|
|
9661
|
+
const original = line.slice(tokenStart, tokenStart + token.length);
|
|
9662
|
+
const replacement = pattern.replace(token);
|
|
9663
|
+
if (replacement === original) continue;
|
|
9664
|
+
out.push({
|
|
9665
|
+
id: `${pattern.kind}#${lineIdx + 1}#${tokenStart + 1}`,
|
|
9666
|
+
kind: pattern.kind,
|
|
9667
|
+
file,
|
|
9668
|
+
line: lineIdx + 1,
|
|
9669
|
+
column: tokenStart + 1,
|
|
9670
|
+
original,
|
|
9671
|
+
replacement
|
|
9672
|
+
});
|
|
9673
|
+
}
|
|
9674
|
+
}
|
|
9675
|
+
if (out.length >= maxPerFile) break;
|
|
9676
|
+
}
|
|
9677
|
+
return out.slice(0, maxPerFile);
|
|
9678
|
+
}
|
|
9679
|
+
function isMasked(line, start, len) {
|
|
9680
|
+
let inSingle = false;
|
|
9681
|
+
let inDouble = false;
|
|
9682
|
+
for (let i = 0; i < start; i++) {
|
|
9683
|
+
const c = line[i];
|
|
9684
|
+
const prev = i > 0 ? line[i - 1] : void 0;
|
|
9685
|
+
if (c === "'" && prev !== "\\") inSingle = !inSingle;
|
|
9686
|
+
else if (c === '"' && prev !== "\\") inDouble = !inDouble;
|
|
9687
|
+
if (!inSingle && !inDouble && c === "/" && prev === "/") return true;
|
|
9688
|
+
}
|
|
9689
|
+
if (inSingle || inDouble) return true;
|
|
9690
|
+
const window = line.slice(start, start + len);
|
|
9691
|
+
return /['"]/.test(window);
|
|
9692
|
+
}
|
|
9693
|
+
function parseMutationReport(text) {
|
|
9694
|
+
const candidates = [];
|
|
9695
|
+
const fence = text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
9696
|
+
if (fence?.[1]) candidates.push(fence[1].trim());
|
|
9697
|
+
const firstBrace = text.indexOf("{");
|
|
9698
|
+
if (firstBrace >= 0) candidates.push(extractBalancedObject(text, firstBrace));
|
|
9699
|
+
for (const candidate of candidates) {
|
|
9700
|
+
if (!candidate) continue;
|
|
9701
|
+
try {
|
|
9702
|
+
const parsed = JSON.parse(candidate);
|
|
9703
|
+
if (!Array.isArray(parsed.mutants)) continue;
|
|
9704
|
+
return {
|
|
9705
|
+
mutants: parsed.mutants.map(normalizeMutantEntry).filter((x) => Boolean(x)),
|
|
9706
|
+
summary: typeof parsed.summary === "string" ? parsed.summary : void 0
|
|
9707
|
+
};
|
|
9708
|
+
} catch {
|
|
9709
|
+
}
|
|
9710
|
+
}
|
|
9711
|
+
return void 0;
|
|
9712
|
+
}
|
|
9713
|
+
function extractBalancedObject(text, start) {
|
|
9714
|
+
let depth = 0;
|
|
9715
|
+
let inString = false;
|
|
9716
|
+
let escaped = false;
|
|
9717
|
+
for (let i = start; i < text.length; i++) {
|
|
9718
|
+
const c = text[i];
|
|
9719
|
+
if (escaped) {
|
|
9720
|
+
escaped = false;
|
|
9721
|
+
continue;
|
|
9722
|
+
}
|
|
9723
|
+
if (c === "\\") {
|
|
9724
|
+
escaped = true;
|
|
9725
|
+
continue;
|
|
9726
|
+
}
|
|
9727
|
+
if (c === '"') inString = !inString;
|
|
9728
|
+
if (inString) continue;
|
|
9729
|
+
if (c === "{") depth++;
|
|
9730
|
+
else if (c === "}") {
|
|
9731
|
+
depth--;
|
|
9732
|
+
if (depth === 0) return text.slice(start, i + 1);
|
|
9733
|
+
}
|
|
9734
|
+
}
|
|
9735
|
+
return text.slice(start);
|
|
9736
|
+
}
|
|
9737
|
+
function normalizeMutantEntry(value) {
|
|
9738
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
9739
|
+
const rec = value;
|
|
9740
|
+
const id = typeof rec["id"] === "string" ? rec["id"] : void 0;
|
|
9741
|
+
const status = rec["status"];
|
|
9742
|
+
if (!id || status !== "killed" && status !== "survived" && status !== "skipped") {
|
|
9743
|
+
return void 0;
|
|
9744
|
+
}
|
|
9745
|
+
return {
|
|
9746
|
+
id,
|
|
9747
|
+
file: typeof rec["file"] === "string" ? rec["file"] : "",
|
|
9748
|
+
line: typeof rec["line"] === "number" ? rec["line"] : 0,
|
|
9749
|
+
kind: typeof rec["kind"] === "string" ? rec["kind"] : "",
|
|
9750
|
+
status,
|
|
9751
|
+
evidence: typeof rec["evidence"] === "string" ? rec["evidence"] : void 0
|
|
9752
|
+
};
|
|
9753
|
+
}
|
|
9754
|
+
|
|
9755
|
+
// src/coordination/director-mutation-test-tool.ts
|
|
9756
|
+
var DEFAULT_MAX_PER_FILE = 10;
|
|
9757
|
+
var DEFAULT_MAX_STRENGTHEN_ATTEMPTS = 2;
|
|
9758
|
+
var CHAOS_ROLE = "chaos-monkey";
|
|
9759
|
+
function makeMutationTestTool(director, roster, opts = {}) {
|
|
9760
|
+
return {
|
|
9761
|
+
name: "mutation_test",
|
|
9762
|
+
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.",
|
|
9763
|
+
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.",
|
|
9764
|
+
permission: "auto",
|
|
9765
|
+
mutating: false,
|
|
9766
|
+
capabilities: [ToolCapabilities.SUBAGENT_SPAWN],
|
|
9767
|
+
inputSchema: {
|
|
9768
|
+
type: "object",
|
|
9769
|
+
properties: {
|
|
9770
|
+
targets: {
|
|
9771
|
+
type: "array",
|
|
9772
|
+
items: { type: "string" },
|
|
9773
|
+
description: "Project-relative (or absolute) source files to mutate. Keep to files changed by the current task."
|
|
9774
|
+
},
|
|
9775
|
+
testCommand: {
|
|
9776
|
+
type: "string",
|
|
9777
|
+
description: 'Exact command that runs the relevant tests, e.g. "pnpm exec vitest run packages/core/tests/coordination/mutation-engine.test.ts".'
|
|
9778
|
+
},
|
|
9779
|
+
cwd: { type: "string", description: "Working directory for the test command." },
|
|
9780
|
+
maxPerFile: {
|
|
9781
|
+
type: "number",
|
|
9782
|
+
minimum: 1,
|
|
9783
|
+
maximum: 25,
|
|
9784
|
+
description: "Mutant cap per file per pass. Default 10."
|
|
9785
|
+
},
|
|
9786
|
+
maxStrengthenAttempts: {
|
|
9787
|
+
type: "number",
|
|
9788
|
+
minimum: 0,
|
|
9789
|
+
maximum: 5,
|
|
9790
|
+
description: "Strengthen\u2192re-verify rounds. Default 2 when repairSubagentId is set, else 0."
|
|
9791
|
+
},
|
|
9792
|
+
repairSubagentId: {
|
|
9793
|
+
type: "string",
|
|
9794
|
+
description: "Subagent that owns the tests. When set and mutants survive, it receives a strengthen-tests task and the survivors are re-verified."
|
|
9795
|
+
},
|
|
9796
|
+
chaosWorktree: {
|
|
9797
|
+
anyOf: [{ type: "boolean" }, { type: "string", enum: ["auto", "required", "off"] }],
|
|
9798
|
+
description: "Worktree override for the chaos agent. Use 'off' when targets are uncommitted \u2014 a worktree from HEAD would not contain them."
|
|
9799
|
+
},
|
|
9800
|
+
timeoutMs: { type: "number", minimum: 1, description: "Per-task timeout for chaos/strengthen/rerun tasks." },
|
|
9801
|
+
reportOnly: {
|
|
9802
|
+
type: "boolean",
|
|
9803
|
+
description: "Skip the strengthen loop even when survivors exist. Default false."
|
|
9804
|
+
}
|
|
9805
|
+
},
|
|
9806
|
+
required: ["targets", "testCommand"],
|
|
9807
|
+
additionalProperties: false
|
|
9808
|
+
},
|
|
9809
|
+
async execute(input, ctx) {
|
|
9810
|
+
const i = normalizeMutationTestInput(input);
|
|
9811
|
+
const root = opts.projectRoot ?? ctx.projectRoot;
|
|
9812
|
+
const plan = buildPlan(i, root);
|
|
9813
|
+
if (plan.length === 0) {
|
|
9814
|
+
return {
|
|
9815
|
+
verdict: "inconclusive",
|
|
9816
|
+
passed: false,
|
|
9817
|
+
error: "No mutable sites found in the given targets (after comment/string filtering)."
|
|
9818
|
+
};
|
|
9819
|
+
}
|
|
9820
|
+
const chaosSubagentId = await director.spawn(
|
|
9821
|
+
makeChaosConfig(roster, i.chaosWorktree ?? "off")
|
|
9822
|
+
);
|
|
9823
|
+
const chaosTaskId = await director.assign({
|
|
9824
|
+
id: randomUUID8(),
|
|
9825
|
+
subagentId: chaosSubagentId,
|
|
9826
|
+
description: buildChaosTask(plan, i, 1, []),
|
|
9827
|
+
timeoutMs: i.timeoutMs
|
|
9828
|
+
});
|
|
9829
|
+
const [chaosResult] = await director.awaitTasks([chaosTaskId]);
|
|
9830
|
+
const pass1 = collectOutcomes(chaosResult, plan);
|
|
9831
|
+
const survivors = pass1.filter((m) => m.status === "survived");
|
|
9832
|
+
const maxAttempts = clamp(
|
|
9833
|
+
i.maxStrengthenAttempts ?? (i.repairSubagentId && !i.reportOnly ? DEFAULT_MAX_STRENGTHEN_ATTEMPTS : 0),
|
|
9834
|
+
0,
|
|
9835
|
+
5
|
|
9836
|
+
);
|
|
9837
|
+
const attempts = [];
|
|
9838
|
+
let current = survivors;
|
|
9839
|
+
while (current.length > 0 && attempts.length < maxAttempts && i.repairSubagentId) {
|
|
9840
|
+
const attemptNo = attempts.length + 1;
|
|
9841
|
+
const strengthenTaskId = await director.assign({
|
|
9842
|
+
id: randomUUID8(),
|
|
9843
|
+
subagentId: i.repairSubagentId,
|
|
9844
|
+
description: buildStrengthenTask(current, i, attemptNo),
|
|
9845
|
+
timeoutMs: i.timeoutMs
|
|
9846
|
+
});
|
|
9847
|
+
const [strengthenResult] = await director.awaitTasks([strengthenTaskId]);
|
|
9848
|
+
if (strengthenResult?.status !== "success") {
|
|
9849
|
+
attempts.push({
|
|
9850
|
+
attempt: attemptNo,
|
|
9851
|
+
survivorsBefore: current,
|
|
9852
|
+
strengthenResult: strengthenResult ? { taskId: strengthenResult.taskId, status: strengthenResult.status } : void 0,
|
|
9853
|
+
survivorsAfter: current,
|
|
9854
|
+
suspectedEquivalent: []
|
|
9855
|
+
});
|
|
9856
|
+
break;
|
|
9857
|
+
}
|
|
9858
|
+
const survivorPlan = plan.filter((p) => current.some((s) => s.id === p.id));
|
|
9859
|
+
const rerunSubagentId = await director.spawn(
|
|
9860
|
+
makeChaosConfig(roster, i.chaosWorktree ?? "off")
|
|
9861
|
+
);
|
|
9862
|
+
const rerunTaskId = await director.assign({
|
|
9863
|
+
id: randomUUID8(),
|
|
9864
|
+
subagentId: rerunSubagentId,
|
|
9865
|
+
description: buildChaosTask(survivorPlan, i, attemptNo + 1, current),
|
|
9866
|
+
timeoutMs: i.timeoutMs
|
|
9867
|
+
});
|
|
9868
|
+
const [rerunResult] = await director.awaitTasks([rerunTaskId]);
|
|
9869
|
+
const passN = collectOutcomes(rerunResult, survivorPlan);
|
|
9870
|
+
const stillSurviving = passN.filter((m) => m.status === "survived" || m.status === "skipped");
|
|
9871
|
+
attempts.push({
|
|
9872
|
+
attempt: attemptNo,
|
|
9873
|
+
survivorsBefore: current,
|
|
9874
|
+
strengthenResult: { taskId: strengthenResult.taskId, status: strengthenResult.status },
|
|
9875
|
+
rerunResult: { taskId: rerunTaskId, status: rerunResult?.status ?? "unknown" },
|
|
9876
|
+
survivorsAfter: stillSurviving,
|
|
9877
|
+
suspectedEquivalent: stillSurviving.filter((m) => current.some((c) => c.id === m.id)).map((m) => m.id)
|
|
9878
|
+
});
|
|
9879
|
+
current = stillSurviving.filter((m) => m.status === "survived");
|
|
9880
|
+
if (passN.every((m) => m.status === "skipped")) break;
|
|
9881
|
+
}
|
|
9882
|
+
const finalSurvivors = current;
|
|
9883
|
+
const verifiedCount = pass1.filter((m) => m.status !== "skipped").length;
|
|
9884
|
+
const skippedCount = pass1.filter((m) => m.status === "skipped").length;
|
|
9885
|
+
const score = plan.length === 0 ? 0 : pass1.filter((m) => m.status === "killed").length / plan.length;
|
|
9886
|
+
const verdict = verifiedCount === 0 ? "inconclusive" : finalSurvivors.length === 0 ? skippedCount > 0 ? "partial" : "pass" : score >= 0.8 ? "partial" : "fail";
|
|
9887
|
+
return {
|
|
9888
|
+
verdict,
|
|
9889
|
+
passed: verdict === "pass",
|
|
9890
|
+
mutationScore: Number.parseFloat(score.toFixed(3)),
|
|
9891
|
+
planned: plan.length,
|
|
9892
|
+
killed: pass1.filter((m) => m.status === "killed").length,
|
|
9893
|
+
survived: pass1.filter((m) => m.status === "survived").length,
|
|
9894
|
+
skipped: pass1.filter((m) => m.status === "skipped").length,
|
|
9895
|
+
finalSurvivors: finalSurvivors.map((m) => ({ id: m.id, file: m.file, kind: m.kind })),
|
|
9896
|
+
suspectedEquivalent: attempts.flatMap((a) => a.suspectedEquivalent),
|
|
9897
|
+
strengthenAttempts: attempts.length,
|
|
9898
|
+
attempts,
|
|
9899
|
+
chaosTaskId,
|
|
9900
|
+
nextAction: finalSurvivors.length === 0 ? "accept" : attempts.length >= maxAttempts && i.repairSubagentId ? "manual_review_survivors" : "strengthen_tests"
|
|
9901
|
+
};
|
|
9902
|
+
}
|
|
9903
|
+
};
|
|
9904
|
+
}
|
|
9905
|
+
function normalizeMutationTestInput(input) {
|
|
9906
|
+
const raw = input ?? {};
|
|
9907
|
+
const targets = stringArray2(raw["targets"]) ?? [];
|
|
9908
|
+
const testCommand = typeof raw["testCommand"] === "string" ? raw["testCommand"].trim() : "";
|
|
9909
|
+
return {
|
|
9910
|
+
targets: targets.filter(Boolean),
|
|
9911
|
+
testCommand,
|
|
9912
|
+
cwd: typeof raw["cwd"] === "string" && raw["cwd"].trim() ? raw["cwd"].trim() : void 0,
|
|
9913
|
+
maxPerFile: typeof raw["maxPerFile"] === "number" ? raw["maxPerFile"] : void 0,
|
|
9914
|
+
maxStrengthenAttempts: typeof raw["maxStrengthenAttempts"] === "number" ? raw["maxStrengthenAttempts"] : void 0,
|
|
9915
|
+
repairSubagentId: typeof raw["repairSubagentId"] === "string" && raw["repairSubagentId"].trim() ? raw["repairSubagentId"].trim() : void 0,
|
|
9916
|
+
chaosWorktree: raw["chaosWorktree"] ?? void 0,
|
|
9917
|
+
timeoutMs: typeof raw["timeoutMs"] === "number" ? raw["timeoutMs"] : void 0,
|
|
9918
|
+
reportOnly: raw["reportOnly"] === true
|
|
9919
|
+
};
|
|
9920
|
+
}
|
|
9921
|
+
function clamp(n, lo, hi) {
|
|
9922
|
+
return Math.min(hi, Math.max(lo, n));
|
|
9923
|
+
}
|
|
9924
|
+
function buildPlan(i, projectRoot) {
|
|
9925
|
+
const plan = [];
|
|
9926
|
+
for (const target of i.targets) {
|
|
9927
|
+
const abs = isAbsolute2(target) ? target : join10(projectRoot ?? process.cwd(), target);
|
|
9928
|
+
let source;
|
|
9929
|
+
try {
|
|
9930
|
+
source = readFileSync9(abs, "utf8");
|
|
9931
|
+
} catch {
|
|
9932
|
+
continue;
|
|
9933
|
+
}
|
|
9934
|
+
plan.push(...planMutations(target, source, { maxPerFile: i.maxPerFile ?? DEFAULT_MAX_PER_FILE }));
|
|
9935
|
+
}
|
|
9936
|
+
return plan;
|
|
9937
|
+
}
|
|
9938
|
+
function makeChaosConfig(roster, worktree) {
|
|
9939
|
+
const base = roster?.[CHAOS_ROLE] ?? getAgentDefinition(CHAOS_ROLE)?.config ?? { name: "Chaos Monkey", role: CHAOS_ROLE };
|
|
9940
|
+
return { ...instantiateRosterConfig2(CHAOS_ROLE, base), worktree };
|
|
9941
|
+
}
|
|
9942
|
+
function buildChaosTask(plan, i, pass, priorSurvivors) {
|
|
9943
|
+
const mutants = plan.map(
|
|
9944
|
+
(m) => `- ${m.id} | ${m.file}:${m.line}:${m.column} | ${m.kind} | "${m.original}" -> "${m.replacement}"`
|
|
9945
|
+
).join("\n");
|
|
9946
|
+
const prior = priorSurvivors.length > 0 ? `
|
|
9947
|
+
These mutants survived a previous pass (pass ${pass - 1}) \u2014 re-verify them against the STRENGTHENED tests:
|
|
9948
|
+
${priorSurvivors.map((s) => `- ${s.id} (${s.kind} @ ${s.file}:${s.line})`).join("\n")}` : "";
|
|
9949
|
+
return [
|
|
9950
|
+
"Execute this deterministic mutation plan against the current checkout.",
|
|
9951
|
+
"",
|
|
9952
|
+
"For each mutant, in order:",
|
|
9953
|
+
"1. Apply ONLY that mutation at its exact (file, line, column).",
|
|
9954
|
+
`2. Run the test command: ${i.testCommand}${i.cwd ? ` (cwd: ${i.cwd})` : ""}`,
|
|
9955
|
+
"3. Record killed (tests failed \u2014 quote first failing assertion) or survived (suite green).",
|
|
9956
|
+
"4. Restore the file byte-for-byte before the next mutant.",
|
|
9957
|
+
"",
|
|
9958
|
+
"Mutants:",
|
|
9959
|
+
mutants,
|
|
9960
|
+
prior,
|
|
9961
|
+
"",
|
|
9962
|
+
"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.",
|
|
9963
|
+
"Finish with submit_result, then repeat the same JSON as your final text."
|
|
9964
|
+
].join("\n");
|
|
9965
|
+
}
|
|
9966
|
+
function buildStrengthenTask(survivors, i, attempt) {
|
|
9967
|
+
return [
|
|
9968
|
+
`Strengthen the tests so these SURVIVING mutants die (attempt ${attempt}).`,
|
|
9969
|
+
"",
|
|
9970
|
+
"Each survivor below was a deliberate sabotage of production code that the current suite did NOT catch:",
|
|
9971
|
+
...survivors.map((s) => `- ${s.id} | ${s.file}:${s.line} | ${s.kind}${s.evidence ? ` | ${s.evidence}` : ""}`),
|
|
9972
|
+
"",
|
|
9973
|
+
`Test command that must fail under each mutant: ${i.testCommand}`,
|
|
9974
|
+
"",
|
|
9975
|
+
"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."
|
|
9976
|
+
].join("\n");
|
|
9977
|
+
}
|
|
9978
|
+
function collectOutcomes(result, plan) {
|
|
9979
|
+
const fromText = parseTextOutcomes(result);
|
|
9980
|
+
if (fromText.length > 0) {
|
|
9981
|
+
const planned = new Set(plan.map((p) => p.id));
|
|
9982
|
+
const matched = fromText.filter((m) => planned.has(m.id));
|
|
9983
|
+
if (matched.length > 0) return matched;
|
|
9984
|
+
}
|
|
9985
|
+
return plan.map((p) => ({
|
|
9986
|
+
id: p.id,
|
|
9987
|
+
file: p.file,
|
|
9988
|
+
line: p.line,
|
|
9989
|
+
kind: p.kind,
|
|
9990
|
+
status: "skipped",
|
|
9991
|
+
evidence: result ? `chaos task ended ${result.status}` : "chaos task produced no result"
|
|
9992
|
+
}));
|
|
9993
|
+
}
|
|
9994
|
+
function parseTextOutcomes(result) {
|
|
9995
|
+
const text = typeof result?.result === "string" ? result.result : void 0;
|
|
9996
|
+
if (!text) return [];
|
|
9997
|
+
const parsed = parseMutationReport(text);
|
|
9998
|
+
if (!parsed) return [];
|
|
9999
|
+
return parsed.mutants.map((m) => ({
|
|
10000
|
+
id: m.id,
|
|
10001
|
+
file: m.file,
|
|
10002
|
+
line: m.line,
|
|
10003
|
+
kind: m.kind,
|
|
10004
|
+
status: m.status,
|
|
10005
|
+
evidence: m.evidence
|
|
10006
|
+
}));
|
|
10007
|
+
}
|
|
10008
|
+
|
|
9447
10009
|
// src/coordination/director-tools.ts
|
|
9448
10010
|
function makeSpawnTool(director, roster) {
|
|
9449
10011
|
const dispatchCatalog = () => {
|
|
@@ -9736,7 +10298,7 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
9736
10298
|
try {
|
|
9737
10299
|
const config = buildKanbanSubagentConfig(claim.task, i, roster, instantiateRosterConfig2);
|
|
9738
10300
|
subagentId = await director.spawn(config);
|
|
9739
|
-
const dispatchTaskId =
|
|
10301
|
+
const dispatchTaskId = randomUUID9();
|
|
9740
10302
|
const taskSpec = {
|
|
9741
10303
|
id: dispatchTaskId,
|
|
9742
10304
|
subagentId,
|
|
@@ -10012,6 +10574,7 @@ function buildDirectorToolset(director, roster) {
|
|
|
10012
10574
|
makeAskResultTool(director),
|
|
10013
10575
|
makeRollUpTool(director),
|
|
10014
10576
|
makeQualityGateTool(director, roster),
|
|
10577
|
+
makeMutationTestTool(director, roster),
|
|
10015
10578
|
makeTerminateTool(director),
|
|
10016
10579
|
makeTerminateAllTool(director),
|
|
10017
10580
|
makeFleetTool(director),
|
|
@@ -10098,7 +10661,7 @@ import * as fsp24 from "node:fs/promises";
|
|
|
10098
10661
|
import * as path26 from "node:path";
|
|
10099
10662
|
|
|
10100
10663
|
// src/storage/session-store.ts
|
|
10101
|
-
import { randomUUID as
|
|
10664
|
+
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
10102
10665
|
import * as fsp23 from "node:fs/promises";
|
|
10103
10666
|
import * as path25 from "node:path";
|
|
10104
10667
|
|
|
@@ -15417,7 +15980,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
15417
15980
|
|
|
15418
15981
|
// src/storage/session-checkpoint-cas.ts
|
|
15419
15982
|
import { spawn as spawn2 } from "node:child_process";
|
|
15420
|
-
import { createHash as createHash3, randomUUID as
|
|
15983
|
+
import { createHash as createHash3, randomUUID as randomUUID10 } from "node:crypto";
|
|
15421
15984
|
import * as fsp10 from "node:fs/promises";
|
|
15422
15985
|
import * as path17 from "node:path";
|
|
15423
15986
|
|
|
@@ -15675,7 +16238,7 @@ var SessionCheckpointCas = class {
|
|
|
15675
16238
|
}
|
|
15676
16239
|
const temp = path17.join(
|
|
15677
16240
|
path17.dirname(target),
|
|
15678
|
-
`.${path17.basename(target)}.${process.pid}.${
|
|
16241
|
+
`.${path17.basename(target)}.${process.pid}.${randomUUID10()}.tmp`
|
|
15679
16242
|
);
|
|
15680
16243
|
let handle;
|
|
15681
16244
|
try {
|
|
@@ -17482,7 +18045,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
17482
18045
|
onAppend;
|
|
17483
18046
|
onAppendBatch;
|
|
17484
18047
|
catalogClient;
|
|
17485
|
-
maintenanceHolderId =
|
|
18048
|
+
maintenanceHolderId = randomUUID11();
|
|
17486
18049
|
_loadCache = /* @__PURE__ */ new Map();
|
|
17487
18050
|
loadCache = new SessionLoadCache(this._loadCache);
|
|
17488
18051
|
_indexCache = null;
|
|
@@ -18993,7 +19556,7 @@ function hashStr(s) {
|
|
|
18993
19556
|
}
|
|
18994
19557
|
|
|
18995
19558
|
// src/coordination/multi-agent-coordinator.ts
|
|
18996
|
-
import { randomUUID as
|
|
19559
|
+
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
18997
19560
|
import { EventEmitter as EventEmitter2 } from "node:events";
|
|
18998
19561
|
|
|
18999
19562
|
// src/coordination/coordinator/error-classifier.ts
|
|
@@ -19084,7 +19647,8 @@ async function executeSubagentWithTimeout({
|
|
|
19084
19647
|
budget,
|
|
19085
19648
|
preemptFraction = TIMEOUT_PREEMPT_FRACTION,
|
|
19086
19649
|
abortSubagent,
|
|
19087
|
-
currentSessionId
|
|
19650
|
+
currentSessionId,
|
|
19651
|
+
gracefulFinish
|
|
19088
19652
|
}) {
|
|
19089
19653
|
const initialTimeoutMs = budget.limits.timeoutMs;
|
|
19090
19654
|
const idleLimitMs = budget.limits.idleTimeoutMs;
|
|
@@ -19115,9 +19679,17 @@ async function executeSubagentWithTimeout({
|
|
|
19115
19679
|
const scheduleNext = () => {
|
|
19116
19680
|
const wallLimit = budget.limits.timeoutMs ?? initialTimeoutMs;
|
|
19117
19681
|
const wallRemaining = initialTimeoutMs === void 0 ? Number.POSITIVE_INFINITY : wallLimit - (Date.now() - start);
|
|
19118
|
-
const idleRemaining = idleLimitMs === void 0 ? Number.POSITIVE_INFINITY : (budget.limits.idleTimeoutMs ?? idleLimitMs) - budget.idleMs();
|
|
19119
|
-
const preemptRemaining = initialTimeoutMs === void 0 || preemptedCeiling === wallLimit ? Number.POSITIVE_INFINITY : wallLimit * preemptFraction - (Date.now() - start);
|
|
19120
|
-
|
|
19682
|
+
const idleRemaining = idleLimitMs === void 0 || gracefulFinish !== void 0 && initialTimeoutMs !== void 0 ? Number.POSITIVE_INFINITY : (budget.limits.idleTimeoutMs ?? idleLimitMs) - budget.idleMs();
|
|
19683
|
+
const preemptRemaining = initialTimeoutMs === void 0 || preemptedCeiling === wallLimit || gracefulFinish !== void 0 ? Number.POSITIVE_INFINITY : wallLimit * preemptFraction - (Date.now() - start);
|
|
19684
|
+
const next = Math.min(wallRemaining, idleRemaining, preemptRemaining);
|
|
19685
|
+
if (!Number.isFinite(next)) {
|
|
19686
|
+
if (timer) {
|
|
19687
|
+
clearTimeout(timer);
|
|
19688
|
+
timer = null;
|
|
19689
|
+
}
|
|
19690
|
+
return;
|
|
19691
|
+
}
|
|
19692
|
+
armFor(Math.max(25, next));
|
|
19121
19693
|
};
|
|
19122
19694
|
const negotiateTimeout = async (used, limit) => {
|
|
19123
19695
|
const handler = budget.onThreshold;
|
|
@@ -19166,6 +19738,10 @@ async function executeSubagentWithTimeout({
|
|
|
19166
19738
|
const wallExceeded = wallLimit !== void 0 && elapsed >= wallLimit;
|
|
19167
19739
|
const idleExceeded = idleLimit !== void 0 && budget.idleMs() >= idleLimit;
|
|
19168
19740
|
if (idleExceeded && !wallExceeded) {
|
|
19741
|
+
if (gracefulFinish !== void 0 && initialTimeoutMs !== void 0) {
|
|
19742
|
+
scheduleNext();
|
|
19743
|
+
return;
|
|
19744
|
+
}
|
|
19169
19745
|
const sessionId = currentSessionId();
|
|
19170
19746
|
budget._events?.emit("budget.threshold_reached", {
|
|
19171
19747
|
...sessionId ? { sessionId } : {},
|
|
@@ -19182,7 +19758,7 @@ async function executeSubagentWithTimeout({
|
|
|
19182
19758
|
reject(new BudgetExceededError("idle_timeout", idleLimit ?? 0, budget.idleMs()));
|
|
19183
19759
|
return;
|
|
19184
19760
|
}
|
|
19185
|
-
if (wallLimit !== void 0 && !wallExceeded && budget.onThreshold && preemptState === "active" /* ACTIVE */ && elapsed >= wallLimit * preemptFraction) {
|
|
19761
|
+
if (wallLimit !== void 0 && !wallExceeded && gracefulFinish === void 0 && budget.onThreshold && preemptState === "active" /* ACTIVE */ && elapsed >= wallLimit * preemptFraction) {
|
|
19186
19762
|
const activityTs = Date.now() - budget.idleMs();
|
|
19187
19763
|
if (activityTs <= lastGrantActivityTs) {
|
|
19188
19764
|
preemptState = "locked" /* LOCKED */;
|
|
@@ -19216,6 +19792,22 @@ async function executeSubagentWithTimeout({
|
|
|
19216
19792
|
return;
|
|
19217
19793
|
}
|
|
19218
19794
|
const limit = wallLimit ?? 0;
|
|
19795
|
+
if (gracefulFinish !== void 0) {
|
|
19796
|
+
if (!budget.graceGranted) {
|
|
19797
|
+
const reason = `wall-clock budget of ${Math.round(limit / 1e3)}s reached`;
|
|
19798
|
+
if (budget.notifyFinish(reason, { graceMs: gracefulFinish.graceMs })) {
|
|
19799
|
+
scheduleNext();
|
|
19800
|
+
return;
|
|
19801
|
+
}
|
|
19802
|
+
abortSubagent(ctx.subagentId);
|
|
19803
|
+
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
19804
|
+
return;
|
|
19805
|
+
} else {
|
|
19806
|
+
abortSubagent(ctx.subagentId);
|
|
19807
|
+
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
19808
|
+
return;
|
|
19809
|
+
}
|
|
19810
|
+
}
|
|
19219
19811
|
if (!budget.onThreshold) {
|
|
19220
19812
|
abortSubagent(ctx.subagentId);
|
|
19221
19813
|
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
@@ -19363,7 +19955,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
19363
19955
|
return { ...subagent, name: display };
|
|
19364
19956
|
}
|
|
19365
19957
|
async spawn(subagent) {
|
|
19366
|
-
const id = subagent.id ||
|
|
19958
|
+
const id = subagent.id || randomUUID12();
|
|
19367
19959
|
const cfg = this.withNickname(subagent, id);
|
|
19368
19960
|
if (this.subagents.has(id)) {
|
|
19369
19961
|
throw new Error(`Subagent id "${id}" already exists \u2014 refusing to overwrite`);
|
|
@@ -19600,6 +20192,32 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
19600
20192
|
completeTask(result) {
|
|
19601
20193
|
this.recordCompletion(result);
|
|
19602
20194
|
}
|
|
20195
|
+
/**
|
|
20196
|
+
* Ask every RUNNING subagent that opted into `gracefulFinish` to finish its
|
|
20197
|
+
* task in its own turn (see coordination/subagent-finish.ts). This is the
|
|
20198
|
+
* leader-side entry point for "the leader agent has finished": it delivers
|
|
20199
|
+
* an in-band notification between tool batches — never an interrupt, never
|
|
20200
|
+
* an abort. Each notified subagent keeps its existing time budget and
|
|
20201
|
+
* accelerates; the watchdog still bounds the maximum lifetime.
|
|
20202
|
+
*
|
|
20203
|
+
* Subagents without the policy opted in are deliberately untouched — their
|
|
20204
|
+
* lifecycle remains the legacy watchdog contract.
|
|
20205
|
+
*
|
|
20206
|
+
* Returns the number of subagents actually notified.
|
|
20207
|
+
*/
|
|
20208
|
+
requestFinish(reason) {
|
|
20209
|
+
let notified = 0;
|
|
20210
|
+
for (const subagent of this.subagents.values()) {
|
|
20211
|
+
if (subagent.status !== "running") continue;
|
|
20212
|
+
if (!resolveGracefulFinish(subagent.config)) continue;
|
|
20213
|
+
const budget = subagent.activeBudget;
|
|
20214
|
+
if (!budget) continue;
|
|
20215
|
+
const usage = budget.usage();
|
|
20216
|
+
if (usage.iterations === 0 && usage.toolCalls === 0) continue;
|
|
20217
|
+
if (budget.notifyFinish(reason)) notified++;
|
|
20218
|
+
}
|
|
20219
|
+
return notified;
|
|
20220
|
+
}
|
|
19603
20221
|
// --- internal dispatching ---------------------------------------------
|
|
19604
20222
|
tryDispatchNext() {
|
|
19605
20223
|
while (this.canDispatch()) {
|
|
@@ -19773,7 +20391,14 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
19773
20391
|
idleTimeoutMs: rawIdleTimeoutMs ?? this.config.defaultBudget?.idleTimeoutMs ?? configWithRosterDefaults.idleTimeoutMs
|
|
19774
20392
|
},
|
|
19775
20393
|
"auto",
|
|
19776
|
-
{
|
|
20394
|
+
{
|
|
20395
|
+
sessionId: () => this.currentSessionId(),
|
|
20396
|
+
subagentId,
|
|
20397
|
+
// Graceful-finish runs own wall-clock enforcement to the watchdog so
|
|
20398
|
+
// the notify-then-bound lifecycle cannot be raced by tool.progress
|
|
20399
|
+
// heartbeats calling checkTimeout() (see subagent-budget.ts).
|
|
20400
|
+
...resolveGracefulFinish(subagent.config) ? { wallClockWatchdogOwned: true } : {}
|
|
20401
|
+
}
|
|
19777
20402
|
);
|
|
19778
20403
|
subagent.activeBudget = budget;
|
|
19779
20404
|
if (!this.runner) {
|
|
@@ -19806,7 +20431,8 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
19806
20431
|
task,
|
|
19807
20432
|
runCtx,
|
|
19808
20433
|
budget,
|
|
19809
|
-
subagent.config.preemptFraction
|
|
20434
|
+
subagent.config.preemptFraction,
|
|
20435
|
+
resolveGracefulFinish(subagent.config)
|
|
19810
20436
|
);
|
|
19811
20437
|
result = {
|
|
19812
20438
|
subagentId,
|
|
@@ -19836,13 +20462,14 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
19836
20462
|
}
|
|
19837
20463
|
this.recordCompletion(result);
|
|
19838
20464
|
}
|
|
19839
|
-
async executeWithTimeout(runner, task, ctx, budget, preemptFraction) {
|
|
20465
|
+
async executeWithTimeout(runner, task, ctx, budget, preemptFraction, gracefulFinish) {
|
|
19840
20466
|
return executeSubagentWithTimeout({
|
|
19841
20467
|
runner,
|
|
19842
20468
|
task,
|
|
19843
20469
|
ctx,
|
|
19844
20470
|
budget,
|
|
19845
20471
|
preemptFraction,
|
|
20472
|
+
gracefulFinish,
|
|
19846
20473
|
abortSubagent: (subagentId) => this.subagents.get(subagentId)?.abortController.abort(),
|
|
19847
20474
|
currentSessionId: () => this.currentSessionId()
|
|
19848
20475
|
});
|
|
@@ -20248,7 +20875,7 @@ var Director = class _Director {
|
|
|
20248
20875
|
sessionProvider;
|
|
20249
20876
|
sessionModel;
|
|
20250
20877
|
constructor(opts) {
|
|
20251
|
-
this.id = opts.config.coordinatorId ||
|
|
20878
|
+
this.id = opts.config.coordinatorId || randomUUID13();
|
|
20252
20879
|
this.manifestPath = opts.manifestPath;
|
|
20253
20880
|
this.roster = opts.roster;
|
|
20254
20881
|
this.directorPreamble = opts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
|
|
@@ -20455,6 +21082,17 @@ var Director = class _Director {
|
|
|
20455
21082
|
isWorkComplete() {
|
|
20456
21083
|
return this.workCompleteFlag;
|
|
20457
21084
|
}
|
|
21085
|
+
/**
|
|
21086
|
+
* Ask every running background subagent that opted into `gracefulFinish`
|
|
21087
|
+
* to finish its task in its own turn. In-band notification between tool
|
|
21088
|
+
* batches — no interrupt, no abort; each subagent keeps its time budget and
|
|
21089
|
+
* accelerates. Session shutdown calls this before draining Chimera work so
|
|
21090
|
+
* the post-session reviewer is nudged to complete rather than killed.
|
|
21091
|
+
* Returns the number of subagents notified.
|
|
21092
|
+
*/
|
|
21093
|
+
requestFinish(reason) {
|
|
21094
|
+
return this.coordinator.requestFinish(reason);
|
|
21095
|
+
}
|
|
20458
21096
|
setLeaderBtwNote(note) {
|
|
20459
21097
|
return this.btwNotes.add(note);
|
|
20460
21098
|
}
|
|
@@ -20531,7 +21169,7 @@ var Director = class _Director {
|
|
|
20531
21169
|
);
|
|
20532
21170
|
}
|
|
20533
21171
|
const msg = {
|
|
20534
|
-
id:
|
|
21172
|
+
id: randomUUID13(),
|
|
20535
21173
|
type: "task",
|
|
20536
21174
|
from: this.id,
|
|
20537
21175
|
to: subagentId,
|
|
@@ -23961,7 +24599,7 @@ function _resetDesignRulesCache() {
|
|
|
23961
24599
|
}
|
|
23962
24600
|
|
|
23963
24601
|
// src/execution/design-color.ts
|
|
23964
|
-
function
|
|
24602
|
+
function clamp2(n, lo, hi) {
|
|
23965
24603
|
return n < lo ? lo : n > hi ? hi : n;
|
|
23966
24604
|
}
|
|
23967
24605
|
function parseOklch(value) {
|
|
@@ -23977,9 +24615,9 @@ function parseOklch(value) {
|
|
|
23977
24615
|
let a = 1;
|
|
23978
24616
|
if (alphaPart !== void 0) {
|
|
23979
24617
|
const av = parseComponent(alphaPart.trim(), true);
|
|
23980
|
-
if (av !== null) a =
|
|
24618
|
+
if (av !== null) a = clamp2(av, 0, 1);
|
|
23981
24619
|
}
|
|
23982
|
-
return [
|
|
24620
|
+
return [clamp2(L, 0, 1), Math.max(0, C), H, a];
|
|
23983
24621
|
}
|
|
23984
24622
|
function parseComponent(s, percentIsFraction) {
|
|
23985
24623
|
s = s.trim();
|
|
@@ -23998,7 +24636,7 @@ function parseAngle(s) {
|
|
|
23998
24636
|
}
|
|
23999
24637
|
function linearToSrgb(c) {
|
|
24000
24638
|
const v = c <= 31308e-7 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - 0.055;
|
|
24001
|
-
return
|
|
24639
|
+
return clamp2(v, 0, 1);
|
|
24002
24640
|
}
|
|
24003
24641
|
function toHex2(n) {
|
|
24004
24642
|
return Math.round(n * 255).toString(16).padStart(2, "0");
|
|
@@ -24203,16 +24841,16 @@ async function runDesignVerify(projectRoot, tokens, explicitFiles) {
|
|
|
24203
24841
|
}
|
|
24204
24842
|
|
|
24205
24843
|
// src/execution/design-detect.ts
|
|
24206
|
-
var
|
|
24844
|
+
var META_KEY2 = "designStudio";
|
|
24207
24845
|
function getDesignState(ctx) {
|
|
24208
|
-
const v = ctx.meta[
|
|
24846
|
+
const v = ctx.meta[META_KEY2];
|
|
24209
24847
|
return v && typeof v === "object" ? v : void 0;
|
|
24210
24848
|
}
|
|
24211
24849
|
function ensureState(ctx) {
|
|
24212
24850
|
let s = getDesignState(ctx);
|
|
24213
24851
|
if (!s) {
|
|
24214
24852
|
s = { active: false, signals: [] };
|
|
24215
|
-
ctx.meta[
|
|
24853
|
+
ctx.meta[META_KEY2] = s;
|
|
24216
24854
|
}
|
|
24217
24855
|
return s;
|
|
24218
24856
|
}
|
|
@@ -26134,7 +26772,7 @@ ${summaryText}` : summaryText;
|
|
|
26134
26772
|
};
|
|
26135
26773
|
|
|
26136
26774
|
// src/execution/parallel-eternal-engine.ts
|
|
26137
|
-
import { randomUUID as
|
|
26775
|
+
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
26138
26776
|
var GOAL_COMPLETE_MARKER2 = /^\s*\[goal[_\s-]?complete\]\s*$/im;
|
|
26139
26777
|
var ParallelEternalEngine = class {
|
|
26140
26778
|
constructor(opts) {
|
|
@@ -26211,7 +26849,7 @@ var ParallelEternalEngine = class {
|
|
|
26211
26849
|
this.state = "running";
|
|
26212
26850
|
await this.persistState("running");
|
|
26213
26851
|
const config = {
|
|
26214
|
-
coordinatorId: `parallel-${
|
|
26852
|
+
coordinatorId: `parallel-${randomUUID14().slice(0, 8)}`,
|
|
26215
26853
|
maxConcurrent: this.slots,
|
|
26216
26854
|
doneCondition: { type: "all_tasks_done" }
|
|
26217
26855
|
};
|
|
@@ -26265,7 +26903,7 @@ var ParallelEternalEngine = class {
|
|
|
26265
26903
|
}
|
|
26266
26904
|
if (!this.coordinator) {
|
|
26267
26905
|
const config = {
|
|
26268
|
-
coordinatorId: `parallel-${
|
|
26906
|
+
coordinatorId: `parallel-${randomUUID14().slice(0, 8)}`,
|
|
26269
26907
|
maxConcurrent: this.slots,
|
|
26270
26908
|
doneCondition: { type: "all_tasks_done" }
|
|
26271
26909
|
};
|
|
@@ -26351,7 +26989,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
26351
26989
|
const task = expectDefined(tasks[i]);
|
|
26352
26990
|
const route = routes[i] ?? null;
|
|
26353
26991
|
const subagentId = `parallel-${this.iterations}-${i}`;
|
|
26354
|
-
const taskId =
|
|
26992
|
+
const taskId = randomUUID14();
|
|
26355
26993
|
const personaLine = route ? `Acting agent: ${route.definition.config.name} \u2014 ${route.definition.capability.summary}
|
|
26356
26994
|
` : "";
|
|
26357
26995
|
const spec = {
|
|
@@ -26566,7 +27204,7 @@ ${lastFew}` : "No prior iterations.",
|
|
|
26566
27204
|
};
|
|
26567
27205
|
|
|
26568
27206
|
// src/core/streaming-response-builder.ts
|
|
26569
|
-
import { randomUUID as
|
|
27207
|
+
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
26570
27208
|
var STREAM_DRAIN_TIMEOUT_MS = 500;
|
|
26571
27209
|
function buildResponse(state) {
|
|
26572
27210
|
const content = [];
|
|
@@ -26626,7 +27264,7 @@ function handleContentBlockStart(state, ev) {
|
|
|
26626
27264
|
state.textBuffers.push("");
|
|
26627
27265
|
state.blockOrder.push({ kind: "text", idx: state.currentTextIndex });
|
|
26628
27266
|
} else if (kind === "tool_use") {
|
|
26629
|
-
const id = ev.id ??
|
|
27267
|
+
const id = ev.id ?? randomUUID15();
|
|
26630
27268
|
state.tools.set(id, { name: ev.name ?? "unknown", partial: "" });
|
|
26631
27269
|
state.blockOrder.push({ kind: "tool", id });
|
|
26632
27270
|
state.currentTextIndex = -1;
|
|
@@ -27033,7 +27671,7 @@ function replaceAllCaseInsensitive(haystack, needle, replacement) {
|
|
|
27033
27671
|
}
|
|
27034
27672
|
|
|
27035
27673
|
// src/core/provider-runner.ts
|
|
27036
|
-
import { randomUUID as
|
|
27674
|
+
import { randomUUID as randomUUID16 } from "node:crypto";
|
|
27037
27675
|
function scrubProviderBody(body) {
|
|
27038
27676
|
if (!body) return void 0;
|
|
27039
27677
|
return {
|
|
@@ -27055,11 +27693,11 @@ function providerLogCtx(p, r) {
|
|
|
27055
27693
|
}
|
|
27056
27694
|
async function runProviderWithRetry(opts) {
|
|
27057
27695
|
const { provider, request, signal, ctx, events, retry, logger, tracer } = opts;
|
|
27058
|
-
const logicalRequestId =
|
|
27696
|
+
const logicalRequestId = randomUUID16();
|
|
27059
27697
|
const promptManifest = createChroniclePromptManifest(request);
|
|
27060
27698
|
let attempt = 0;
|
|
27061
27699
|
for (; ; ) {
|
|
27062
|
-
const attemptId =
|
|
27700
|
+
const attemptId = randomUUID16();
|
|
27063
27701
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
27064
27702
|
const startedNs = process.hrtime.bigint();
|
|
27065
27703
|
const correlation = {
|
|
@@ -28622,7 +29260,7 @@ function readPolicy(ctx) {
|
|
|
28622
29260
|
}
|
|
28623
29261
|
|
|
28624
29262
|
// src/execution/tool-executor.ts
|
|
28625
|
-
import { randomUUID as
|
|
29263
|
+
import { randomUUID as randomUUID19 } from "node:crypto";
|
|
28626
29264
|
import * as fs10 from "node:fs/promises";
|
|
28627
29265
|
import * as path35 from "node:path";
|
|
28628
29266
|
|
|
@@ -28630,7 +29268,7 @@ import * as path35 from "node:path";
|
|
|
28630
29268
|
var GOVERNED_TOOL_EXECUTOR_META_KEY = "toolExecutor.executeGoverned";
|
|
28631
29269
|
|
|
28632
29270
|
// src/execution/tool-executor-support.ts
|
|
28633
|
-
import { createHash as createHash8, randomUUID as
|
|
29271
|
+
import { createHash as createHash8, randomUUID as randomUUID17 } from "node:crypto";
|
|
28634
29272
|
import * as fs9 from "node:fs/promises";
|
|
28635
29273
|
import * as path33 from "node:path";
|
|
28636
29274
|
|
|
@@ -28761,7 +29399,7 @@ async function maybePersistLargeToolOutput(toolName, content, budget) {
|
|
|
28761
29399
|
await fs9.mkdir(dir, { recursive: true });
|
|
28762
29400
|
const safeTool = toolName.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40) || "tool";
|
|
28763
29401
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
28764
|
-
const filePath = path33.join(dir, `${stamp}-${safeTool}-${
|
|
29402
|
+
const filePath = path33.join(dir, `${stamp}-${safeTool}-${randomUUID17()}.log`);
|
|
28765
29403
|
await fs9.writeFile(filePath, content, "utf8");
|
|
28766
29404
|
const marker = `[full tool output: ${bytes} bytes at ${filePath}; read/grep that file selectively instead of re-running or requesting more output]`;
|
|
28767
29405
|
const fixedBytes = Buffer.byteLength(marker + TOOL_OUTPUT_ARTIFACT_OMISSION, "utf8");
|
|
@@ -29310,7 +29948,7 @@ ${errorDetails}`,
|
|
|
29310
29948
|
}
|
|
29311
29949
|
|
|
29312
29950
|
// src/execution/tool-executor-runner.ts
|
|
29313
|
-
import { randomUUID as
|
|
29951
|
+
import { randomUUID as randomUUID18 } from "node:crypto";
|
|
29314
29952
|
|
|
29315
29953
|
// src/observability/process-telemetry.ts
|
|
29316
29954
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
@@ -29449,7 +30087,7 @@ async function runToolWithTimeout(tool, input, parentSignal, ctx, opts, config,
|
|
|
29449
30087
|
progressTailChars: config.progressTailChars,
|
|
29450
30088
|
progressHeadChars: config.progressHeadChars
|
|
29451
30089
|
}) : (async () => tool.execute(input, ctx, { signal: combined }))();
|
|
29452
|
-
const telemetryToolCallId = toolUseId ?? `nested-${
|
|
30090
|
+
const telemetryToolCallId = toolUseId ?? `nested-${randomUUID18()}`;
|
|
29453
30091
|
const toolPromise = opts.events ? runWithNetworkTelemetry(
|
|
29454
30092
|
{
|
|
29455
30093
|
events: opts.events,
|
|
@@ -29842,7 +30480,7 @@ ${post.additionalContext}`;
|
|
|
29842
30480
|
const bridge = async (toolName, input) => {
|
|
29843
30481
|
const nestedUse = {
|
|
29844
30482
|
type: "tool_use",
|
|
29845
|
-
id: `nested-${
|
|
30483
|
+
id: `nested-${randomUUID19()}`,
|
|
29846
30484
|
name: toolName,
|
|
29847
30485
|
input
|
|
29848
30486
|
};
|
|
@@ -30446,13 +31084,13 @@ import * as fs11 from "node:fs/promises";
|
|
|
30446
31084
|
import * as path37 from "node:path";
|
|
30447
31085
|
|
|
30448
31086
|
// src/types/mode-prompts.ts
|
|
30449
|
-
import { readFileSync as
|
|
31087
|
+
import { readFileSync as readFileSync11, statSync as statSync4 } from "node:fs";
|
|
30450
31088
|
import * as path36 from "node:path";
|
|
30451
31089
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
30452
31090
|
function modePrompt(id) {
|
|
30453
31091
|
for (const dir of modePromptDirCandidates()) {
|
|
30454
31092
|
try {
|
|
30455
|
-
return
|
|
31093
|
+
return readFileSync11(path36.join(dir, `${id}.md`), "utf8").trimEnd();
|
|
30456
31094
|
} catch {
|
|
30457
31095
|
}
|
|
30458
31096
|
}
|
|
@@ -31130,7 +31768,17 @@ function normalizeModelsDevModel(model) {
|
|
|
31130
31768
|
const reasoningConfig = {
|
|
31131
31769
|
default: disableSupported ? "enabled" : "always_on",
|
|
31132
31770
|
disableSupported,
|
|
31133
|
-
|
|
31771
|
+
// Tri-state (see ReasoningConfig.effortSupported):
|
|
31772
|
+
// options present → documented answer (true when effort values exist;
|
|
31773
|
+
// an explicitly EMPTY array is a documented "no
|
|
31774
|
+
// effort control", not an absent field).
|
|
31775
|
+
// field ABSENT → the model is known to reason but its vocabulary is
|
|
31776
|
+
// undocumented → `undefined`, so the resolver forwards
|
|
31777
|
+
// the request and each wire adapter applies its own
|
|
31778
|
+
// transport gating. Sending `false` here would make
|
|
31779
|
+
// the resolver claim "does not support effort" — an
|
|
31780
|
+
// assertion the catalog never made.
|
|
31781
|
+
...raw === void 0 ? {} : { effortSupported: effortLevels.length > 0 },
|
|
31134
31782
|
effortLevels,
|
|
31135
31783
|
preserveThinking: model.interleaved ? "always_on" : "unsupported"
|
|
31136
31784
|
};
|
|
@@ -31717,9 +32365,9 @@ async function startMetricsServer(opts) {
|
|
|
31717
32365
|
let server;
|
|
31718
32366
|
if (useHttps && tls) {
|
|
31719
32367
|
const { createServer } = await import("node:https");
|
|
31720
|
-
const { readFileSync:
|
|
32368
|
+
const { readFileSync: readFileSync13 } = await import("node:fs");
|
|
31721
32369
|
server = createServer(
|
|
31722
|
-
{ cert:
|
|
32370
|
+
{ cert: readFileSync13(tls.cert), key: readFileSync13(tls.key) },
|
|
31723
32371
|
listener
|
|
31724
32372
|
);
|
|
31725
32373
|
} else {
|
|
@@ -35472,7 +36120,7 @@ function deepFreeze(obj) {
|
|
|
35472
36120
|
}
|
|
35473
36121
|
|
|
35474
36122
|
// src/storage/plan-store.ts
|
|
35475
|
-
import { randomUUID as
|
|
36123
|
+
import { randomUUID as randomUUID20 } from "node:crypto";
|
|
35476
36124
|
import * as fsp30 from "node:fs/promises";
|
|
35477
36125
|
async function loadPlan(filePath, events) {
|
|
35478
36126
|
const t0 = Date.now();
|
|
@@ -35571,7 +36219,7 @@ function emptyPlan(sessionId, title) {
|
|
|
35571
36219
|
function addPlanItem(plan, title, details) {
|
|
35572
36220
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
35573
36221
|
const item = {
|
|
35574
|
-
id: `plan_${Date.now()}_${
|
|
36222
|
+
id: `plan_${Date.now()}_${randomUUID20().slice(0, 6)}`,
|
|
35575
36223
|
title,
|
|
35576
36224
|
details,
|
|
35577
36225
|
status: "open",
|
|
@@ -35643,7 +36291,7 @@ function deriveTodosFromPlanItem(plan, idOrIndex, subtasks) {
|
|
|
35643
36291
|
if (subtasks && subtasks.length > 0) {
|
|
35644
36292
|
for (const st of subtasks) {
|
|
35645
36293
|
todos.push({
|
|
35646
|
-
id: `todo_${Date.now()}_${
|
|
36294
|
+
id: `todo_${Date.now()}_${randomUUID20().slice(0, 6)}`,
|
|
35647
36295
|
content: st,
|
|
35648
36296
|
status: "pending",
|
|
35649
36297
|
promotedFromPlan: item.id
|