@wrongstack/core 0.308.6 → 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/agents/index.js +1 -0
- package/dist/coordination/agents/role-skills.d.ts +1 -0
- 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/explore-companion.d.ts +191 -0
- package/dist/coordination/fleet.d.ts +26 -0
- package/dist/coordination/index.d.ts +2 -1
- package/dist/coordination/index.js +1396 -370
- 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 +731 -52
- package/dist/execution/compaction-core.d.ts +1 -1
- package/dist/execution/compaction-elision.d.ts +0 -10
- package/dist/execution/index.js +269 -16
- package/dist/goal/index.js +54 -27
- package/dist/goal/phase-orchestrator.d.ts +7 -0
- package/dist/goal/types.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1280 -201
- package/dist/kernel/events/agent-events.d.ts +31 -2
- package/dist/models/index.js +11 -1
- package/dist/plugin/discovery.d.ts +73 -0
- package/dist/plugin/index.d.ts +2 -0
- package/dist/plugin/index.js +270 -29
- package/dist/plugin/loader.d.ts +5 -1
- package/dist/plugin/trust.d.ts +78 -0
- package/dist/tools/index.js +1 -0
- package/dist/types/config/mcp-features.d.ts +21 -0
- package/dist/types/config/skills-fleet-brain.d.ts +18 -0
- 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/instructions/agents/explore-companion.md +35 -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 });
|
|
@@ -2029,6 +2159,7 @@ function inferRuntimeCapabilities(toolNames) {
|
|
|
2029
2159
|
var skillSet = (...names) => names;
|
|
2030
2160
|
var ROLE_SKILL_SETS = {
|
|
2031
2161
|
explore: skillSet("research-web", "node-modern", "typescript-strict"),
|
|
2162
|
+
"explore-companion": skillSet("node-modern", "typescript-strict"),
|
|
2032
2163
|
search: skillSet("bug-hunter", "typescript-strict", "research-web"),
|
|
2033
2164
|
research: skillSet("research-web", "tech-stack", "security-scanner", "api-design"),
|
|
2034
2165
|
analyst: skillSet("sdd", "api-design", "testing", "security-scanner"),
|
|
@@ -5417,6 +5548,42 @@ var SHADOW_AGENT = {
|
|
|
5417
5548
|
...defineAgent("shadow-agent", "Shadow"),
|
|
5418
5549
|
skillNames: [...SHADOW_AGENT_SKILLS]
|
|
5419
5550
|
};
|
|
5551
|
+
var EXPLORE_COMPANION_AGENT = {
|
|
5552
|
+
...defineAgent("explore-companion", "Explore Companion"),
|
|
5553
|
+
tools: [...TOOLS.read, ...TOOLS.index],
|
|
5554
|
+
// Read-only, triple-enforced: allowlist has no write/bash, and the
|
|
5555
|
+
// disabled list blocks the escape hatches explicitly.
|
|
5556
|
+
disabledTools: [
|
|
5557
|
+
"write",
|
|
5558
|
+
"edit",
|
|
5559
|
+
"replace",
|
|
5560
|
+
"patch",
|
|
5561
|
+
"bash",
|
|
5562
|
+
"exec",
|
|
5563
|
+
"delegate",
|
|
5564
|
+
"spawn_subagent",
|
|
5565
|
+
"assign_task"
|
|
5566
|
+
],
|
|
5567
|
+
skillNames: [...ROLE_SKILL_SETS["explore-companion"]],
|
|
5568
|
+
spawnBudgetExempt: true,
|
|
5569
|
+
// Findings travel via mailbox + submit_result, not the leader's stream.
|
|
5570
|
+
textStream: "silent",
|
|
5571
|
+
toolStream: "silent"
|
|
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
|
+
};
|
|
5420
5587
|
var CRITIC_AGENT = defineAgent("critic", "Critic");
|
|
5421
5588
|
var GENERIC_AGENT = defineAgent("generic", "Generic Project Agent");
|
|
5422
5589
|
function withDispatchMetadata(definition) {
|
|
@@ -5435,6 +5602,8 @@ var FLEET_ROSTER = {
|
|
|
5435
5602
|
critic: CRITIC_AGENT,
|
|
5436
5603
|
generic: GENERIC_AGENT,
|
|
5437
5604
|
"shadow-agent": SHADOW_AGENT,
|
|
5605
|
+
"explore-companion": EXPLORE_COMPANION_AGENT,
|
|
5606
|
+
"chaos-monkey": CHAOS_MONKEY_AGENT,
|
|
5438
5607
|
...Object.fromEntries(
|
|
5439
5608
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, withDispatchMetadata(d)])
|
|
5440
5609
|
)
|
|
@@ -5458,6 +5627,23 @@ var FLEET_ROSTER_BUDGETS = {
|
|
|
5458
5627
|
maxTokens: 96e3,
|
|
5459
5628
|
maxCostUsd: 0.5
|
|
5460
5629
|
},
|
|
5630
|
+
"explore-companion": {
|
|
5631
|
+
idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS,
|
|
5632
|
+
maxIterations: 3e3,
|
|
5633
|
+
maxToolCalls: 8e3,
|
|
5634
|
+
maxTokens: 96e3,
|
|
5635
|
+
maxCostUsd: 0.5
|
|
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
|
+
},
|
|
5461
5647
|
...Object.fromEntries(
|
|
5462
5648
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.budget])
|
|
5463
5649
|
)
|
|
@@ -6197,7 +6383,7 @@ async function readSubagentPartial(opts, subagentId) {
|
|
|
6197
6383
|
}
|
|
6198
6384
|
|
|
6199
6385
|
// src/coordination/director.ts
|
|
6200
|
-
import { randomUUID as
|
|
6386
|
+
import { randomUUID as randomUUID13 } from "node:crypto";
|
|
6201
6387
|
import * as fsp25 from "node:fs/promises";
|
|
6202
6388
|
|
|
6203
6389
|
// src/core/instruction-template.ts
|
|
@@ -8213,7 +8399,7 @@ ${JSON.stringify(result.result, null, 2)}
|
|
|
8213
8399
|
};
|
|
8214
8400
|
|
|
8215
8401
|
// src/coordination/director-tools.ts
|
|
8216
|
-
import { randomUUID as
|
|
8402
|
+
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
8217
8403
|
import {
|
|
8218
8404
|
completeKanbanDispatch,
|
|
8219
8405
|
failKanbanDispatch,
|
|
@@ -8307,7 +8493,7 @@ function buildKanbanFleetTaskPrompt(board, task, lease) {
|
|
|
8307
8493
|
const dependencyLines = (task.dependsOn ?? []).map((depId) => board.tasks.find((candidate) => candidate.id === depId)).filter((dep) => Boolean(dep)).map((dep) => `- ${dep.title} [${dep.status}] (${dep.id})`);
|
|
8308
8494
|
const checks = task.successCriteria?.map((check) => `- ${check.description}`).join("\n");
|
|
8309
8495
|
const metrics = task.goalMetrics?.map(
|
|
8310
|
-
(metric) => `- ${metric.name}: ${metric.current ?? "n/a"}${metric.target !== void 0 ? ` / ${metric.target}` : ""}${metric.unit ? ` ${metric.unit}` : ""} [${metric.status}]`
|
|
8496
|
+
(metric) => `- ${metric.name}: ${metric.current ?? "n/a"}${metric.target !== void 0 ? ` / ${metric.direction === "at_most" ? "\u2264" : "\u2265"} ${metric.target}` : ""}${metric.unit ? ` ${metric.unit}` : ""} [${metric.status}]`
|
|
8311
8497
|
).join("\n");
|
|
8312
8498
|
const chain = task.chain ? [
|
|
8313
8499
|
`chainId: ${task.chain.chainId}`,
|
|
@@ -9413,6 +9599,413 @@ function excerpt(text, max) {
|
|
|
9413
9599
|
...(truncated)`;
|
|
9414
9600
|
}
|
|
9415
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
|
+
|
|
9416
10009
|
// src/coordination/director-tools.ts
|
|
9417
10010
|
function makeSpawnTool(director, roster) {
|
|
9418
10011
|
const dispatchCatalog = () => {
|
|
@@ -9705,7 +10298,7 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
9705
10298
|
try {
|
|
9706
10299
|
const config = buildKanbanSubagentConfig(claim.task, i, roster, instantiateRosterConfig2);
|
|
9707
10300
|
subagentId = await director.spawn(config);
|
|
9708
|
-
const dispatchTaskId =
|
|
10301
|
+
const dispatchTaskId = randomUUID9();
|
|
9709
10302
|
const taskSpec = {
|
|
9710
10303
|
id: dispatchTaskId,
|
|
9711
10304
|
subagentId,
|
|
@@ -9981,6 +10574,7 @@ function buildDirectorToolset(director, roster) {
|
|
|
9981
10574
|
makeAskResultTool(director),
|
|
9982
10575
|
makeRollUpTool(director),
|
|
9983
10576
|
makeQualityGateTool(director, roster),
|
|
10577
|
+
makeMutationTestTool(director, roster),
|
|
9984
10578
|
makeTerminateTool(director),
|
|
9985
10579
|
makeTerminateAllTool(director),
|
|
9986
10580
|
makeFleetTool(director),
|
|
@@ -10067,7 +10661,7 @@ import * as fsp24 from "node:fs/promises";
|
|
|
10067
10661
|
import * as path26 from "node:path";
|
|
10068
10662
|
|
|
10069
10663
|
// src/storage/session-store.ts
|
|
10070
|
-
import { randomUUID as
|
|
10664
|
+
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
10071
10665
|
import * as fsp23 from "node:fs/promises";
|
|
10072
10666
|
import * as path25 from "node:path";
|
|
10073
10667
|
|
|
@@ -15386,7 +15980,7 @@ var FileSessionWriter = class _FileSessionWriter {
|
|
|
15386
15980
|
|
|
15387
15981
|
// src/storage/session-checkpoint-cas.ts
|
|
15388
15982
|
import { spawn as spawn2 } from "node:child_process";
|
|
15389
|
-
import { createHash as createHash3, randomUUID as
|
|
15983
|
+
import { createHash as createHash3, randomUUID as randomUUID10 } from "node:crypto";
|
|
15390
15984
|
import * as fsp10 from "node:fs/promises";
|
|
15391
15985
|
import * as path17 from "node:path";
|
|
15392
15986
|
|
|
@@ -15644,7 +16238,7 @@ var SessionCheckpointCas = class {
|
|
|
15644
16238
|
}
|
|
15645
16239
|
const temp = path17.join(
|
|
15646
16240
|
path17.dirname(target),
|
|
15647
|
-
`.${path17.basename(target)}.${process.pid}.${
|
|
16241
|
+
`.${path17.basename(target)}.${process.pid}.${randomUUID10()}.tmp`
|
|
15648
16242
|
);
|
|
15649
16243
|
let handle;
|
|
15650
16244
|
try {
|
|
@@ -17451,7 +18045,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
17451
18045
|
onAppend;
|
|
17452
18046
|
onAppendBatch;
|
|
17453
18047
|
catalogClient;
|
|
17454
|
-
maintenanceHolderId =
|
|
18048
|
+
maintenanceHolderId = randomUUID11();
|
|
17455
18049
|
_loadCache = /* @__PURE__ */ new Map();
|
|
17456
18050
|
loadCache = new SessionLoadCache(this._loadCache);
|
|
17457
18051
|
_indexCache = null;
|
|
@@ -18962,7 +19556,7 @@ function hashStr(s) {
|
|
|
18962
19556
|
}
|
|
18963
19557
|
|
|
18964
19558
|
// src/coordination/multi-agent-coordinator.ts
|
|
18965
|
-
import { randomUUID as
|
|
19559
|
+
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
18966
19560
|
import { EventEmitter as EventEmitter2 } from "node:events";
|
|
18967
19561
|
|
|
18968
19562
|
// src/coordination/coordinator/error-classifier.ts
|
|
@@ -19053,7 +19647,8 @@ async function executeSubagentWithTimeout({
|
|
|
19053
19647
|
budget,
|
|
19054
19648
|
preemptFraction = TIMEOUT_PREEMPT_FRACTION,
|
|
19055
19649
|
abortSubagent,
|
|
19056
|
-
currentSessionId
|
|
19650
|
+
currentSessionId,
|
|
19651
|
+
gracefulFinish
|
|
19057
19652
|
}) {
|
|
19058
19653
|
const initialTimeoutMs = budget.limits.timeoutMs;
|
|
19059
19654
|
const idleLimitMs = budget.limits.idleTimeoutMs;
|
|
@@ -19084,9 +19679,17 @@ async function executeSubagentWithTimeout({
|
|
|
19084
19679
|
const scheduleNext = () => {
|
|
19085
19680
|
const wallLimit = budget.limits.timeoutMs ?? initialTimeoutMs;
|
|
19086
19681
|
const wallRemaining = initialTimeoutMs === void 0 ? Number.POSITIVE_INFINITY : wallLimit - (Date.now() - start);
|
|
19087
|
-
const idleRemaining = idleLimitMs === void 0 ? Number.POSITIVE_INFINITY : (budget.limits.idleTimeoutMs ?? idleLimitMs) - budget.idleMs();
|
|
19088
|
-
const preemptRemaining = initialTimeoutMs === void 0 || preemptedCeiling === wallLimit ? Number.POSITIVE_INFINITY : wallLimit * preemptFraction - (Date.now() - start);
|
|
19089
|
-
|
|
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));
|
|
19090
19693
|
};
|
|
19091
19694
|
const negotiateTimeout = async (used, limit) => {
|
|
19092
19695
|
const handler = budget.onThreshold;
|
|
@@ -19135,6 +19738,10 @@ async function executeSubagentWithTimeout({
|
|
|
19135
19738
|
const wallExceeded = wallLimit !== void 0 && elapsed >= wallLimit;
|
|
19136
19739
|
const idleExceeded = idleLimit !== void 0 && budget.idleMs() >= idleLimit;
|
|
19137
19740
|
if (idleExceeded && !wallExceeded) {
|
|
19741
|
+
if (gracefulFinish !== void 0 && initialTimeoutMs !== void 0) {
|
|
19742
|
+
scheduleNext();
|
|
19743
|
+
return;
|
|
19744
|
+
}
|
|
19138
19745
|
const sessionId = currentSessionId();
|
|
19139
19746
|
budget._events?.emit("budget.threshold_reached", {
|
|
19140
19747
|
...sessionId ? { sessionId } : {},
|
|
@@ -19151,7 +19758,7 @@ async function executeSubagentWithTimeout({
|
|
|
19151
19758
|
reject(new BudgetExceededError("idle_timeout", idleLimit ?? 0, budget.idleMs()));
|
|
19152
19759
|
return;
|
|
19153
19760
|
}
|
|
19154
|
-
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) {
|
|
19155
19762
|
const activityTs = Date.now() - budget.idleMs();
|
|
19156
19763
|
if (activityTs <= lastGrantActivityTs) {
|
|
19157
19764
|
preemptState = "locked" /* LOCKED */;
|
|
@@ -19185,6 +19792,22 @@ async function executeSubagentWithTimeout({
|
|
|
19185
19792
|
return;
|
|
19186
19793
|
}
|
|
19187
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
|
+
}
|
|
19188
19811
|
if (!budget.onThreshold) {
|
|
19189
19812
|
abortSubagent(ctx.subagentId);
|
|
19190
19813
|
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
@@ -19332,7 +19955,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
19332
19955
|
return { ...subagent, name: display };
|
|
19333
19956
|
}
|
|
19334
19957
|
async spawn(subagent) {
|
|
19335
|
-
const id = subagent.id ||
|
|
19958
|
+
const id = subagent.id || randomUUID12();
|
|
19336
19959
|
const cfg = this.withNickname(subagent, id);
|
|
19337
19960
|
if (this.subagents.has(id)) {
|
|
19338
19961
|
throw new Error(`Subagent id "${id}" already exists \u2014 refusing to overwrite`);
|
|
@@ -19569,6 +20192,32 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
19569
20192
|
completeTask(result) {
|
|
19570
20193
|
this.recordCompletion(result);
|
|
19571
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
|
+
}
|
|
19572
20221
|
// --- internal dispatching ---------------------------------------------
|
|
19573
20222
|
tryDispatchNext() {
|
|
19574
20223
|
while (this.canDispatch()) {
|
|
@@ -19742,7 +20391,14 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
19742
20391
|
idleTimeoutMs: rawIdleTimeoutMs ?? this.config.defaultBudget?.idleTimeoutMs ?? configWithRosterDefaults.idleTimeoutMs
|
|
19743
20392
|
},
|
|
19744
20393
|
"auto",
|
|
19745
|
-
{
|
|
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
|
+
}
|
|
19746
20402
|
);
|
|
19747
20403
|
subagent.activeBudget = budget;
|
|
19748
20404
|
if (!this.runner) {
|
|
@@ -19775,7 +20431,8 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
19775
20431
|
task,
|
|
19776
20432
|
runCtx,
|
|
19777
20433
|
budget,
|
|
19778
|
-
subagent.config.preemptFraction
|
|
20434
|
+
subagent.config.preemptFraction,
|
|
20435
|
+
resolveGracefulFinish(subagent.config)
|
|
19779
20436
|
);
|
|
19780
20437
|
result = {
|
|
19781
20438
|
subagentId,
|
|
@@ -19805,13 +20462,14 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
19805
20462
|
}
|
|
19806
20463
|
this.recordCompletion(result);
|
|
19807
20464
|
}
|
|
19808
|
-
async executeWithTimeout(runner, task, ctx, budget, preemptFraction) {
|
|
20465
|
+
async executeWithTimeout(runner, task, ctx, budget, preemptFraction, gracefulFinish) {
|
|
19809
20466
|
return executeSubagentWithTimeout({
|
|
19810
20467
|
runner,
|
|
19811
20468
|
task,
|
|
19812
20469
|
ctx,
|
|
19813
20470
|
budget,
|
|
19814
20471
|
preemptFraction,
|
|
20472
|
+
gracefulFinish,
|
|
19815
20473
|
abortSubagent: (subagentId) => this.subagents.get(subagentId)?.abortController.abort(),
|
|
19816
20474
|
currentSessionId: () => this.currentSessionId()
|
|
19817
20475
|
});
|
|
@@ -20217,7 +20875,7 @@ var Director = class _Director {
|
|
|
20217
20875
|
sessionProvider;
|
|
20218
20876
|
sessionModel;
|
|
20219
20877
|
constructor(opts) {
|
|
20220
|
-
this.id = opts.config.coordinatorId ||
|
|
20878
|
+
this.id = opts.config.coordinatorId || randomUUID13();
|
|
20221
20879
|
this.manifestPath = opts.manifestPath;
|
|
20222
20880
|
this.roster = opts.roster;
|
|
20223
20881
|
this.directorPreamble = opts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
|
|
@@ -20424,6 +21082,17 @@ var Director = class _Director {
|
|
|
20424
21082
|
isWorkComplete() {
|
|
20425
21083
|
return this.workCompleteFlag;
|
|
20426
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
|
+
}
|
|
20427
21096
|
setLeaderBtwNote(note) {
|
|
20428
21097
|
return this.btwNotes.add(note);
|
|
20429
21098
|
}
|
|
@@ -20500,7 +21169,7 @@ var Director = class _Director {
|
|
|
20500
21169
|
);
|
|
20501
21170
|
}
|
|
20502
21171
|
const msg = {
|
|
20503
|
-
id:
|
|
21172
|
+
id: randomUUID13(),
|
|
20504
21173
|
type: "task",
|
|
20505
21174
|
from: this.id,
|
|
20506
21175
|
to: subagentId,
|
|
@@ -23930,7 +24599,7 @@ function _resetDesignRulesCache() {
|
|
|
23930
24599
|
}
|
|
23931
24600
|
|
|
23932
24601
|
// src/execution/design-color.ts
|
|
23933
|
-
function
|
|
24602
|
+
function clamp2(n, lo, hi) {
|
|
23934
24603
|
return n < lo ? lo : n > hi ? hi : n;
|
|
23935
24604
|
}
|
|
23936
24605
|
function parseOklch(value) {
|
|
@@ -23946,9 +24615,9 @@ function parseOklch(value) {
|
|
|
23946
24615
|
let a = 1;
|
|
23947
24616
|
if (alphaPart !== void 0) {
|
|
23948
24617
|
const av = parseComponent(alphaPart.trim(), true);
|
|
23949
|
-
if (av !== null) a =
|
|
24618
|
+
if (av !== null) a = clamp2(av, 0, 1);
|
|
23950
24619
|
}
|
|
23951
|
-
return [
|
|
24620
|
+
return [clamp2(L, 0, 1), Math.max(0, C), H, a];
|
|
23952
24621
|
}
|
|
23953
24622
|
function parseComponent(s, percentIsFraction) {
|
|
23954
24623
|
s = s.trim();
|
|
@@ -23967,7 +24636,7 @@ function parseAngle(s) {
|
|
|
23967
24636
|
}
|
|
23968
24637
|
function linearToSrgb(c) {
|
|
23969
24638
|
const v = c <= 31308e-7 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - 0.055;
|
|
23970
|
-
return
|
|
24639
|
+
return clamp2(v, 0, 1);
|
|
23971
24640
|
}
|
|
23972
24641
|
function toHex2(n) {
|
|
23973
24642
|
return Math.round(n * 255).toString(16).padStart(2, "0");
|
|
@@ -24172,16 +24841,16 @@ async function runDesignVerify(projectRoot, tokens, explicitFiles) {
|
|
|
24172
24841
|
}
|
|
24173
24842
|
|
|
24174
24843
|
// src/execution/design-detect.ts
|
|
24175
|
-
var
|
|
24844
|
+
var META_KEY2 = "designStudio";
|
|
24176
24845
|
function getDesignState(ctx) {
|
|
24177
|
-
const v = ctx.meta[
|
|
24846
|
+
const v = ctx.meta[META_KEY2];
|
|
24178
24847
|
return v && typeof v === "object" ? v : void 0;
|
|
24179
24848
|
}
|
|
24180
24849
|
function ensureState(ctx) {
|
|
24181
24850
|
let s = getDesignState(ctx);
|
|
24182
24851
|
if (!s) {
|
|
24183
24852
|
s = { active: false, signals: [] };
|
|
24184
|
-
ctx.meta[
|
|
24853
|
+
ctx.meta[META_KEY2] = s;
|
|
24185
24854
|
}
|
|
24186
24855
|
return s;
|
|
24187
24856
|
}
|
|
@@ -26103,7 +26772,7 @@ ${summaryText}` : summaryText;
|
|
|
26103
26772
|
};
|
|
26104
26773
|
|
|
26105
26774
|
// src/execution/parallel-eternal-engine.ts
|
|
26106
|
-
import { randomUUID as
|
|
26775
|
+
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
26107
26776
|
var GOAL_COMPLETE_MARKER2 = /^\s*\[goal[_\s-]?complete\]\s*$/im;
|
|
26108
26777
|
var ParallelEternalEngine = class {
|
|
26109
26778
|
constructor(opts) {
|
|
@@ -26180,7 +26849,7 @@ var ParallelEternalEngine = class {
|
|
|
26180
26849
|
this.state = "running";
|
|
26181
26850
|
await this.persistState("running");
|
|
26182
26851
|
const config = {
|
|
26183
|
-
coordinatorId: `parallel-${
|
|
26852
|
+
coordinatorId: `parallel-${randomUUID14().slice(0, 8)}`,
|
|
26184
26853
|
maxConcurrent: this.slots,
|
|
26185
26854
|
doneCondition: { type: "all_tasks_done" }
|
|
26186
26855
|
};
|
|
@@ -26234,7 +26903,7 @@ var ParallelEternalEngine = class {
|
|
|
26234
26903
|
}
|
|
26235
26904
|
if (!this.coordinator) {
|
|
26236
26905
|
const config = {
|
|
26237
|
-
coordinatorId: `parallel-${
|
|
26906
|
+
coordinatorId: `parallel-${randomUUID14().slice(0, 8)}`,
|
|
26238
26907
|
maxConcurrent: this.slots,
|
|
26239
26908
|
doneCondition: { type: "all_tasks_done" }
|
|
26240
26909
|
};
|
|
@@ -26320,7 +26989,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
26320
26989
|
const task = expectDefined(tasks[i]);
|
|
26321
26990
|
const route = routes[i] ?? null;
|
|
26322
26991
|
const subagentId = `parallel-${this.iterations}-${i}`;
|
|
26323
|
-
const taskId =
|
|
26992
|
+
const taskId = randomUUID14();
|
|
26324
26993
|
const personaLine = route ? `Acting agent: ${route.definition.config.name} \u2014 ${route.definition.capability.summary}
|
|
26325
26994
|
` : "";
|
|
26326
26995
|
const spec = {
|
|
@@ -26535,7 +27204,7 @@ ${lastFew}` : "No prior iterations.",
|
|
|
26535
27204
|
};
|
|
26536
27205
|
|
|
26537
27206
|
// src/core/streaming-response-builder.ts
|
|
26538
|
-
import { randomUUID as
|
|
27207
|
+
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
26539
27208
|
var STREAM_DRAIN_TIMEOUT_MS = 500;
|
|
26540
27209
|
function buildResponse(state) {
|
|
26541
27210
|
const content = [];
|
|
@@ -26595,7 +27264,7 @@ function handleContentBlockStart(state, ev) {
|
|
|
26595
27264
|
state.textBuffers.push("");
|
|
26596
27265
|
state.blockOrder.push({ kind: "text", idx: state.currentTextIndex });
|
|
26597
27266
|
} else if (kind === "tool_use") {
|
|
26598
|
-
const id = ev.id ??
|
|
27267
|
+
const id = ev.id ?? randomUUID15();
|
|
26599
27268
|
state.tools.set(id, { name: ev.name ?? "unknown", partial: "" });
|
|
26600
27269
|
state.blockOrder.push({ kind: "tool", id });
|
|
26601
27270
|
state.currentTextIndex = -1;
|
|
@@ -27002,7 +27671,7 @@ function replaceAllCaseInsensitive(haystack, needle, replacement) {
|
|
|
27002
27671
|
}
|
|
27003
27672
|
|
|
27004
27673
|
// src/core/provider-runner.ts
|
|
27005
|
-
import { randomUUID as
|
|
27674
|
+
import { randomUUID as randomUUID16 } from "node:crypto";
|
|
27006
27675
|
function scrubProviderBody(body) {
|
|
27007
27676
|
if (!body) return void 0;
|
|
27008
27677
|
return {
|
|
@@ -27024,11 +27693,11 @@ function providerLogCtx(p, r) {
|
|
|
27024
27693
|
}
|
|
27025
27694
|
async function runProviderWithRetry(opts) {
|
|
27026
27695
|
const { provider, request, signal, ctx, events, retry, logger, tracer } = opts;
|
|
27027
|
-
const logicalRequestId =
|
|
27696
|
+
const logicalRequestId = randomUUID16();
|
|
27028
27697
|
const promptManifest = createChroniclePromptManifest(request);
|
|
27029
27698
|
let attempt = 0;
|
|
27030
27699
|
for (; ; ) {
|
|
27031
|
-
const attemptId =
|
|
27700
|
+
const attemptId = randomUUID16();
|
|
27032
27701
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
27033
27702
|
const startedNs = process.hrtime.bigint();
|
|
27034
27703
|
const correlation = {
|
|
@@ -28591,7 +29260,7 @@ function readPolicy(ctx) {
|
|
|
28591
29260
|
}
|
|
28592
29261
|
|
|
28593
29262
|
// src/execution/tool-executor.ts
|
|
28594
|
-
import { randomUUID as
|
|
29263
|
+
import { randomUUID as randomUUID19 } from "node:crypto";
|
|
28595
29264
|
import * as fs10 from "node:fs/promises";
|
|
28596
29265
|
import * as path35 from "node:path";
|
|
28597
29266
|
|
|
@@ -28599,7 +29268,7 @@ import * as path35 from "node:path";
|
|
|
28599
29268
|
var GOVERNED_TOOL_EXECUTOR_META_KEY = "toolExecutor.executeGoverned";
|
|
28600
29269
|
|
|
28601
29270
|
// src/execution/tool-executor-support.ts
|
|
28602
|
-
import { createHash as createHash8, randomUUID as
|
|
29271
|
+
import { createHash as createHash8, randomUUID as randomUUID17 } from "node:crypto";
|
|
28603
29272
|
import * as fs9 from "node:fs/promises";
|
|
28604
29273
|
import * as path33 from "node:path";
|
|
28605
29274
|
|
|
@@ -28730,7 +29399,7 @@ async function maybePersistLargeToolOutput(toolName, content, budget) {
|
|
|
28730
29399
|
await fs9.mkdir(dir, { recursive: true });
|
|
28731
29400
|
const safeTool = toolName.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40) || "tool";
|
|
28732
29401
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
28733
|
-
const filePath = path33.join(dir, `${stamp}-${safeTool}-${
|
|
29402
|
+
const filePath = path33.join(dir, `${stamp}-${safeTool}-${randomUUID17()}.log`);
|
|
28734
29403
|
await fs9.writeFile(filePath, content, "utf8");
|
|
28735
29404
|
const marker = `[full tool output: ${bytes} bytes at ${filePath}; read/grep that file selectively instead of re-running or requesting more output]`;
|
|
28736
29405
|
const fixedBytes = Buffer.byteLength(marker + TOOL_OUTPUT_ARTIFACT_OMISSION, "utf8");
|
|
@@ -29279,7 +29948,7 @@ ${errorDetails}`,
|
|
|
29279
29948
|
}
|
|
29280
29949
|
|
|
29281
29950
|
// src/execution/tool-executor-runner.ts
|
|
29282
|
-
import { randomUUID as
|
|
29951
|
+
import { randomUUID as randomUUID18 } from "node:crypto";
|
|
29283
29952
|
|
|
29284
29953
|
// src/observability/process-telemetry.ts
|
|
29285
29954
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
@@ -29418,7 +30087,7 @@ async function runToolWithTimeout(tool, input, parentSignal, ctx, opts, config,
|
|
|
29418
30087
|
progressTailChars: config.progressTailChars,
|
|
29419
30088
|
progressHeadChars: config.progressHeadChars
|
|
29420
30089
|
}) : (async () => tool.execute(input, ctx, { signal: combined }))();
|
|
29421
|
-
const telemetryToolCallId = toolUseId ?? `nested-${
|
|
30090
|
+
const telemetryToolCallId = toolUseId ?? `nested-${randomUUID18()}`;
|
|
29422
30091
|
const toolPromise = opts.events ? runWithNetworkTelemetry(
|
|
29423
30092
|
{
|
|
29424
30093
|
events: opts.events,
|
|
@@ -29811,7 +30480,7 @@ ${post.additionalContext}`;
|
|
|
29811
30480
|
const bridge = async (toolName, input) => {
|
|
29812
30481
|
const nestedUse = {
|
|
29813
30482
|
type: "tool_use",
|
|
29814
|
-
id: `nested-${
|
|
30483
|
+
id: `nested-${randomUUID19()}`,
|
|
29815
30484
|
name: toolName,
|
|
29816
30485
|
input
|
|
29817
30486
|
};
|
|
@@ -30415,13 +31084,13 @@ import * as fs11 from "node:fs/promises";
|
|
|
30415
31084
|
import * as path37 from "node:path";
|
|
30416
31085
|
|
|
30417
31086
|
// src/types/mode-prompts.ts
|
|
30418
|
-
import { readFileSync as
|
|
31087
|
+
import { readFileSync as readFileSync11, statSync as statSync4 } from "node:fs";
|
|
30419
31088
|
import * as path36 from "node:path";
|
|
30420
31089
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
30421
31090
|
function modePrompt(id) {
|
|
30422
31091
|
for (const dir of modePromptDirCandidates()) {
|
|
30423
31092
|
try {
|
|
30424
|
-
return
|
|
31093
|
+
return readFileSync11(path36.join(dir, `${id}.md`), "utf8").trimEnd();
|
|
30425
31094
|
} catch {
|
|
30426
31095
|
}
|
|
30427
31096
|
}
|
|
@@ -31099,7 +31768,17 @@ function normalizeModelsDevModel(model) {
|
|
|
31099
31768
|
const reasoningConfig = {
|
|
31100
31769
|
default: disableSupported ? "enabled" : "always_on",
|
|
31101
31770
|
disableSupported,
|
|
31102
|
-
|
|
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 },
|
|
31103
31782
|
effortLevels,
|
|
31104
31783
|
preserveThinking: model.interleaved ? "always_on" : "unsupported"
|
|
31105
31784
|
};
|
|
@@ -31686,9 +32365,9 @@ async function startMetricsServer(opts) {
|
|
|
31686
32365
|
let server;
|
|
31687
32366
|
if (useHttps && tls) {
|
|
31688
32367
|
const { createServer } = await import("node:https");
|
|
31689
|
-
const { readFileSync:
|
|
32368
|
+
const { readFileSync: readFileSync13 } = await import("node:fs");
|
|
31690
32369
|
server = createServer(
|
|
31691
|
-
{ cert:
|
|
32370
|
+
{ cert: readFileSync13(tls.cert), key: readFileSync13(tls.key) },
|
|
31692
32371
|
listener
|
|
31693
32372
|
);
|
|
31694
32373
|
} else {
|
|
@@ -35441,7 +36120,7 @@ function deepFreeze(obj) {
|
|
|
35441
36120
|
}
|
|
35442
36121
|
|
|
35443
36122
|
// src/storage/plan-store.ts
|
|
35444
|
-
import { randomUUID as
|
|
36123
|
+
import { randomUUID as randomUUID20 } from "node:crypto";
|
|
35445
36124
|
import * as fsp30 from "node:fs/promises";
|
|
35446
36125
|
async function loadPlan(filePath, events) {
|
|
35447
36126
|
const t0 = Date.now();
|
|
@@ -35540,7 +36219,7 @@ function emptyPlan(sessionId, title) {
|
|
|
35540
36219
|
function addPlanItem(plan, title, details) {
|
|
35541
36220
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
35542
36221
|
const item = {
|
|
35543
|
-
id: `plan_${Date.now()}_${
|
|
36222
|
+
id: `plan_${Date.now()}_${randomUUID20().slice(0, 6)}`,
|
|
35544
36223
|
title,
|
|
35545
36224
|
details,
|
|
35546
36225
|
status: "open",
|
|
@@ -35612,7 +36291,7 @@ function deriveTodosFromPlanItem(plan, idOrIndex, subtasks) {
|
|
|
35612
36291
|
if (subtasks && subtasks.length > 0) {
|
|
35613
36292
|
for (const st of subtasks) {
|
|
35614
36293
|
todos.push({
|
|
35615
|
-
id: `todo_${Date.now()}_${
|
|
36294
|
+
id: `todo_${Date.now()}_${randomUUID20().slice(0, 6)}`,
|
|
35616
36295
|
content: st,
|
|
35617
36296
|
status: "pending",
|
|
35618
36297
|
promotedFromPlan: item.id
|