@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/index.js CHANGED
@@ -1171,7 +1171,7 @@ __export(review_report_store_exports, {
1171
1171
  REPORT_STORE_FILE: () => REPORT_STORE_FILE,
1172
1172
  resolveReportStorePath: () => resolveReportStorePath
1173
1173
  });
1174
- import { randomUUID as randomUUID31 } from "node:crypto";
1174
+ import { randomUUID as randomUUID32 } from "node:crypto";
1175
1175
  import * as fsp35 from "node:fs/promises";
1176
1176
  import * as path76 from "node:path";
1177
1177
  function resolveReportStorePath(projectDir) {
@@ -1238,7 +1238,7 @@ var init_review_report_store = __esm({
1238
1238
  ...input.evidenceChecks !== void 0 ? { evidenceChecks: input.evidenceChecks } : {}
1239
1239
  };
1240
1240
  const createdEvent = {
1241
- id: randomUUID31(),
1241
+ id: randomUUID32(),
1242
1242
  reportId: report.id,
1243
1243
  eventType: "created",
1244
1244
  fromLifecycle: null,
@@ -1261,7 +1261,7 @@ var init_review_report_store = __esm({
1261
1261
  const from = this._materialize(entry).lifecycle;
1262
1262
  validateReportTransition(from, to);
1263
1263
  const event = {
1264
- id: randomUUID31(),
1264
+ id: randomUUID32(),
1265
1265
  reportId,
1266
1266
  eventType: reportEventTypeFor(to),
1267
1267
  fromLifecycle: from,
@@ -1304,7 +1304,7 @@ var init_review_report_store = __esm({
1304
1304
  if (!entry) throw new Error(`Review report not found: ${reportId}`);
1305
1305
  const materialized = this._materialize(entry);
1306
1306
  const event = {
1307
- id: randomUUID31(),
1307
+ id: randomUUID32(),
1308
1308
  reportId,
1309
1309
  eventType: "note_added",
1310
1310
  fromLifecycle: materialized.lifecycle,
@@ -1527,7 +1527,7 @@ __export(review_finding_store_exports, {
1527
1527
  JsonlFindingStore: () => JsonlFindingStore,
1528
1528
  resolveFindingStorePath: () => resolveFindingStorePath
1529
1529
  });
1530
- import { randomUUID as randomUUID41 } from "node:crypto";
1530
+ import { randomUUID as randomUUID42 } from "node:crypto";
1531
1531
  import * as fsp48 from "node:fs/promises";
1532
1532
  import * as path105 from "node:path";
1533
1533
  function resolveFindingStorePath(projectDir) {
@@ -1616,7 +1616,7 @@ var init_review_finding_store = __esm({
1616
1616
  validateTransition(from, to);
1617
1617
  validateResolution(to, opts?.outcome);
1618
1618
  const event = {
1619
- id: randomUUID41(),
1619
+ id: randomUUID42(),
1620
1620
  findingId,
1621
1621
  eventType: to === "resolved" ? "resolved" : to === "ignored" ? "ignored" : this._eventTypeFor(to),
1622
1622
  fromStatus: from,
@@ -1800,7 +1800,7 @@ var init_review_finding_store = __esm({
1800
1800
  }
1801
1801
  _makeEvent(findingId, eventType, fromStatus, toStatus, context) {
1802
1802
  return {
1803
- id: randomUUID41(),
1803
+ id: randomUUID42(),
1804
1804
  findingId,
1805
1805
  eventType,
1806
1806
  fromStatus,
@@ -19762,9 +19762,68 @@ function resourceId3(kind, value) {
19762
19762
  return `${kind}_${createHash17("sha256").update(value).digest("hex").slice(0, 24)}`;
19763
19763
  }
19764
19764
 
19765
+ // src/core/btw.ts
19766
+ var META_KEY2 = "_btwNotes";
19767
+ var MAX_PENDING = 20;
19768
+ function readQueue(ctx) {
19769
+ const raw = ctx.meta[META_KEY2];
19770
+ return Array.isArray(raw) ? raw : [];
19771
+ }
19772
+ function setBtwNote(ctx, text2) {
19773
+ const trimmed = text2.trim();
19774
+ if (!trimmed) return readQueue(ctx).length;
19775
+ const next = [...readQueue(ctx), trimmed].slice(-MAX_PENDING);
19776
+ ctx.meta[META_KEY2] = next;
19777
+ return next.length;
19778
+ }
19779
+ function pendingBtwCount(ctx) {
19780
+ return readQueue(ctx).length;
19781
+ }
19782
+ function consumeBtwNotes(ctx) {
19783
+ const notes = readQueue(ctx);
19784
+ if (notes.length > 0) delete ctx.meta[META_KEY2];
19785
+ return notes;
19786
+ }
19787
+ function buildBtwBlock(notes) {
19788
+ const body = notes.map((n) => `- ${n}`).join("\n");
19789
+ return [
19790
+ "[BY THE WAY \u2014 the user added this while you were working. Fold it into",
19791
+ "your current task; do not restart from scratch unless it contradicts the",
19792
+ "goal:",
19793
+ "",
19794
+ body,
19795
+ "]"
19796
+ ].join("\n");
19797
+ }
19798
+
19765
19799
  // src/coordination/agent-subagent-runner.ts
19766
19800
  init_errors();
19767
19801
 
19802
+ // src/coordination/subagent-finish.ts
19803
+ var SUBAGENT_FINISH_REQUESTED_EVENT = "subagent.finish_requested";
19804
+ var DEFAULT_SUBAGENT_FINISH_GRACE_MS = 12e4;
19805
+ function resolveGracefulFinish(config) {
19806
+ const raw = config.gracefulFinish;
19807
+ if (raw === void 0 || raw === false) return void 0;
19808
+ if (raw === true) return { graceMs: DEFAULT_SUBAGENT_FINISH_GRACE_MS };
19809
+ const graceMs = typeof raw.graceMs === "number" && Number.isFinite(raw.graceMs) && raw.graceMs > 0 ? Math.floor(raw.graceMs) : DEFAULT_SUBAGENT_FINISH_GRACE_MS;
19810
+ return { graceMs };
19811
+ }
19812
+ function buildSubagentFinishNotice(input) {
19813
+ const localTime = new Date(input.deadlineMs).toISOString();
19814
+ const seconds = Math.max(1, Math.round(input.graceMs / 1e3));
19815
+ 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.`;
19816
+ return [
19817
+ "[SUBAGENT FINISH] The leader agent has finished its work.",
19818
+ `Reason: ${input.reason}`,
19819
+ timeLeft,
19820
+ "Finish your task now, in this turn: complete the thought you are working on, stop",
19821
+ "starting new tool calls unless one is strictly required to finish, and write your",
19822
+ "final answer or report as your final output, then end your turn.",
19823
+ "Do not restart the task and do not begin new work."
19824
+ ].join("\n");
19825
+ }
19826
+
19768
19827
  // src/coordination/subagent-budget.ts
19769
19828
  var TIMEOUT_PREEMPT_FRACTION = 0.85;
19770
19829
  var DECISION_TIMEOUT_MS = 6e4;
@@ -19822,6 +19881,82 @@ var SubagentBudget = class _SubagentBudget {
19822
19881
  this.limits.idleTimeoutMs = ext.idleTimeoutMs;
19823
19882
  }
19824
19883
  }
19884
+ /**
19885
+ * Graceful-finish state (see coordination/subagent-finish.ts).
19886
+ * `_finishNotified` guards the single in-band emission; `_grace` records a
19887
+ * granted working-time extension past the original wall-clock deadline.
19888
+ * They are separate because the two callers want different semantics:
19889
+ * the watchdog grants grace at the deadline crossing (notify + extend),
19890
+ * while an explicit leader-finished request only notifies — a subagent
19891
+ * well inside its budget keeps its full legitimate working time and simply
19892
+ * accelerates.
19893
+ */
19894
+ _finishNotified = false;
19895
+ _grace = null;
19896
+ /** True once the in-band finish notification has been emitted. */
19897
+ get finishNotified() {
19898
+ return this._finishNotified;
19899
+ }
19900
+ /** True once a grace window has been granted past the original deadline. */
19901
+ get graceGranted() {
19902
+ return this._grace !== null;
19903
+ }
19904
+ /**
19905
+ * Notify the subagent in-band to finish its task in its own turn:
19906
+ * `subagent.finish_requested` is emitted on the wired EventBus and the
19907
+ * agent loop folds the notice into the conversation between tool batches.
19908
+ * Nothing aborts — this is a notification, never an interrupt.
19909
+ *
19910
+ * `opts.graceMs` additionally extends the wall-clock ceiling by that window
19911
+ * (used by the watchdog at a deadline crossing, so the model gets working
19912
+ * time instead of a kill). Omit it to notify without touching the budget —
19913
+ * the subagent keeps its existing time budget and just accelerates.
19914
+ *
19915
+ * Returns `true` when this call did something (emitted the notification
19916
+ * and/or granted grace); `false` when there was nothing to do (already
19917
+ * notified, grace already granted, no EventBus wired, budget not started).
19918
+ */
19919
+ notifyFinish(reason, opts, now = Date.now) {
19920
+ if (!this._events) return false;
19921
+ if (this.startTime === null) return false;
19922
+ const shouldEmit = !this._finishNotified;
19923
+ const rawGrace = opts?.graceMs;
19924
+ const shouldGrant = rawGrace !== void 0 && this._grace === null;
19925
+ if (!shouldEmit && !shouldGrant) return false;
19926
+ let grantedGraceMs = 0;
19927
+ let graceDeadlineMs;
19928
+ if (shouldGrant && rawGrace !== void 0) {
19929
+ grantedGraceMs = Number.isFinite(rawGrace) && rawGrace > 0 ? Math.floor(rawGrace) : DEFAULT_SUBAGENT_FINISH_GRACE_MS;
19930
+ graceDeadlineMs = now() + grantedGraceMs;
19931
+ this._grace = { deadlineMs: graceDeadlineMs, graceMs: grantedGraceMs };
19932
+ this.patchLimits({ timeoutMs: graceDeadlineMs - this.startTime });
19933
+ }
19934
+ if (shouldEmit) {
19935
+ this._finishNotified = true;
19936
+ const effectiveDeadlineMs = graceDeadlineMs ?? (this.limits.timeoutMs !== void 0 ? this.startTime + this.limits.timeoutMs : now() + DEFAULT_SUBAGENT_FINISH_GRACE_MS);
19937
+ const effectiveGraceMs = Math.max(0, effectiveDeadlineMs - now());
19938
+ const subagentId = this._subagentId;
19939
+ this._events.emit(SUBAGENT_FINISH_REQUESTED_EVENT, {
19940
+ // Omitted entirely when the budget was built without an id — an
19941
+ // empty string is an address that matches nothing.
19942
+ ...subagentId !== void 0 ? { subagentId } : {},
19943
+ reason,
19944
+ deadlineMs: effectiveDeadlineMs,
19945
+ graceMs: effectiveGraceMs,
19946
+ notice: buildSubagentFinishNotice({
19947
+ reason,
19948
+ deadlineMs: effectiveDeadlineMs,
19949
+ graceMs: effectiveGraceMs
19950
+ })
19951
+ });
19952
+ }
19953
+ return true;
19954
+ }
19955
+ /** Epoch ms by which the subagent should have produced its final output,
19956
+ * once a grace window was granted. Undefined before that. */
19957
+ get finishDeadlineMs() {
19958
+ return this._grace?.deadlineMs;
19959
+ }
19825
19960
  iterations = 0;
19826
19961
  toolCalls = 0;
19827
19962
  tokenInput = 0;
@@ -19837,6 +19972,10 @@ var SubagentBudget = class _SubagentBudget {
19837
19972
  lastActivityTime = null;
19838
19973
  _onThreshold;
19839
19974
  _sessionId;
19975
+ /** Owning subagent id — used to address the graceful-finish event. */
19976
+ _subagentId;
19977
+ /** True when only the coordinator watchdog may enforce wall-clock limits. */
19978
+ _wallClockWatchdogOwned;
19840
19979
  /**
19841
19980
  * Hard cap on how long `_negotiateExtension` waits for the coordinator to
19842
19981
  * respond before defaulting to 'stop'. Without this fallback an absent
@@ -19908,6 +20047,8 @@ var SubagentBudget = class _SubagentBudget {
19908
20047
  constructor(limits = {}, mode = "auto", options = {}) {
19909
20048
  this._mode = mode;
19910
20049
  this._sessionId = options.sessionId;
20050
+ this._subagentId = options.subagentId;
20051
+ this._wallClockWatchdogOwned = options.wallClockWatchdogOwned === true;
19911
20052
  this.limits = { ...limits };
19912
20053
  }
19913
20054
  currentSessionId() {
@@ -19986,7 +20127,7 @@ var SubagentBudget = class _SubagentBudget {
19986
20127
  if (this.limits.idleTimeoutMs !== void 0 && idle > this.limits.idleTimeoutMs) {
19987
20128
  exceeded.push({ kind: "idle_timeout", used: idle, limit: this.limits.idleTimeoutMs });
19988
20129
  }
19989
- const wallOwnedByWatchdog = this._onThreshold !== void 0 && this._watchdogActive === this.limits.timeoutMs;
20130
+ const wallOwnedByWatchdog = this._wallClockWatchdogOwned || this._onThreshold !== void 0 && this._watchdogActive === this.limits.timeoutMs;
19990
20131
  if (this.limits.timeoutMs !== void 0 && elapsedMs2 > this.limits.timeoutMs && !wallOwnedByWatchdog) {
19991
20132
  exceeded.push({ kind: "timeout", used: elapsedMs2, limit: this.limits.timeoutMs });
19992
20133
  }
@@ -20185,7 +20326,7 @@ var SubagentBudget = class _SubagentBudget {
20185
20326
  if (timeoutMs === void 0 && idleTimeoutMs === void 0) return;
20186
20327
  const elapsed2 = Date.now() - this.startTime;
20187
20328
  const wallSkipped = this._onThreshold !== void 0 && this._watchdogActive !== void 0 && timeoutMs !== void 0 && this._watchdogActive === timeoutMs;
20188
- const wallTripped = wallSkipped ? false : timeoutMs !== void 0 && elapsed2 > timeoutMs;
20329
+ const wallTripped = this._wallClockWatchdogOwned || wallSkipped ? false : timeoutMs !== void 0 && elapsed2 > timeoutMs;
20189
20330
  const idleTripped = idleTimeoutMs !== void 0 && this.idleMs() > idleTimeoutMs;
20190
20331
  if (!wallTripped && !idleTripped) return;
20191
20332
  void this.checkLimits(elapsed2);
@@ -20666,6 +20807,14 @@ function makeAgentSubagentRunner(opts) {
20666
20807
  );
20667
20808
  const onParentAbort = () => aborter.abort();
20668
20809
  ctx.signal.addEventListener("abort", onParentAbort);
20810
+ if (resolveGracefulFinish(ctx.config)) {
20811
+ unsub.push(
20812
+ events.on("subagent.finish_requested", (e) => {
20813
+ if (e.subagentId && e.subagentId !== ctx.subagentId) return;
20814
+ setBtwNote(agent.ctx, e.notice);
20815
+ })
20816
+ );
20817
+ }
20669
20818
  let result;
20670
20819
  try {
20671
20820
  result = await agent.run(format(task, ctx.config), { signal: aborter.signal });
@@ -29007,7 +29156,7 @@ function attachDepWatcherBridge(opts) {
29007
29156
  }
29008
29157
 
29009
29158
  // src/coordination/director.ts
29010
- import { randomUUID as randomUUID19 } from "node:crypto";
29159
+ import { randomUUID as randomUUID20 } from "node:crypto";
29011
29160
  import * as fsp30 from "node:fs/promises";
29012
29161
 
29013
29162
  // src/storage/director-state.ts
@@ -30902,7 +31051,7 @@ ${JSON.stringify(result.result, null, 2)}
30902
31051
  };
30903
31052
 
30904
31053
  // src/coordination/director-tools.ts
30905
- import { randomUUID as randomUUID14 } from "node:crypto";
31054
+ import { randomUUID as randomUUID15 } from "node:crypto";
30906
31055
  import {
30907
31056
  completeKanbanDispatch,
30908
31057
  failKanbanDispatch,
@@ -32136,6 +32285,413 @@ function excerpt(text2, max) {
32136
32285
  ...(truncated)`;
32137
32286
  }
32138
32287
 
32288
+ // src/coordination/director-mutation-test-tool.ts
32289
+ import { randomUUID as randomUUID14 } from "node:crypto";
32290
+ import { readFileSync as readFileSync19 } from "node:fs";
32291
+ import { isAbsolute as isAbsolute9, join as join40 } from "node:path";
32292
+
32293
+ // src/coordination/mutation-engine.ts
32294
+ var TOKEN_PATTERNS = [
32295
+ {
32296
+ kind: "relax-boundary",
32297
+ // `>` not followed by `=` and not part of `=>` or `>>`; require code-ish
32298
+ // context on both sides so generic text (JSX, strings) is not touched.
32299
+ regex: /(?<=[\w\)\]\}'"`])\s*\x20?(?<op>>(?!=|>))/g,
32300
+ replace: () => ">="
32301
+ },
32302
+ {
32303
+ kind: "tighten-boundary",
32304
+ regex: /(?<=[\w\)\]\}'"`])\s*\x20?(?<op>>=)/g,
32305
+ replace: () => ">"
32306
+ },
32307
+ {
32308
+ kind: "arith-plus-to-minus",
32309
+ // `+` between operands (binary), not `++`, unary `+x`, or `+=`.
32310
+ regex: /(?<=[\w\)\]\}'"`])\s*\x20?(?<op>\+(?!\+|=))/g,
32311
+ replace: () => "-"
32312
+ },
32313
+ {
32314
+ kind: "arith-minus-to-plus",
32315
+ // Binary `-` between operands, not `--`, `-=` or negative-number literal.
32316
+ regex: /(?<=[\w\)\]\}'"`])\s*\x20?(?<op>-(?!-|=))/g,
32317
+ replace: () => "+"
32318
+ },
32319
+ {
32320
+ kind: "negate-boolean",
32321
+ // Standalone boolean literals used as values, not property names.
32322
+ regex: /(?<![.\w$])(?<op>true|false)(?![\w$])/g,
32323
+ replace: (m) => m === "true" ? "false" : "true"
32324
+ },
32325
+ {
32326
+ kind: "return-null",
32327
+ // `return <expr>;` where expr is not already null/undefined/void.
32328
+ regex: /(?<indent>\breturn\b)(?<expr>\s+[^;{}\n]+?)\s*;/g,
32329
+ replace: () => "return null;"
32330
+ }
32331
+ ];
32332
+ function planMutations(file, source, opts = {}) {
32333
+ const maxPerFile = opts.maxPerFile ?? 25;
32334
+ const out = [];
32335
+ const lines = source.split("\n");
32336
+ for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
32337
+ const line = lines[lineIdx];
32338
+ const t2 = line.trim();
32339
+ if (t2.startsWith("//") || t2.startsWith("*") || t2.startsWith("/*")) continue;
32340
+ for (const pattern of TOKEN_PATTERNS) {
32341
+ pattern.regex.lastIndex = 0;
32342
+ let m;
32343
+ while ((m = pattern.regex.exec(line)) !== null) {
32344
+ const token = m.groups?.["op"] ?? m[0];
32345
+ const tokenStart = m.index + m[0].indexOf(token);
32346
+ if (isMasked(line, tokenStart, token.length)) continue;
32347
+ const original = line.slice(tokenStart, tokenStart + token.length);
32348
+ const replacement = pattern.replace(token);
32349
+ if (replacement === original) continue;
32350
+ out.push({
32351
+ id: `${pattern.kind}#${lineIdx + 1}#${tokenStart + 1}`,
32352
+ kind: pattern.kind,
32353
+ file,
32354
+ line: lineIdx + 1,
32355
+ column: tokenStart + 1,
32356
+ original,
32357
+ replacement
32358
+ });
32359
+ }
32360
+ }
32361
+ if (out.length >= maxPerFile) break;
32362
+ }
32363
+ return out.slice(0, maxPerFile);
32364
+ }
32365
+ function isMasked(line, start, len) {
32366
+ let inSingle = false;
32367
+ let inDouble = false;
32368
+ for (let i = 0; i < start; i++) {
32369
+ const c = line[i];
32370
+ const prev = i > 0 ? line[i - 1] : void 0;
32371
+ if (c === "'" && prev !== "\\") inSingle = !inSingle;
32372
+ else if (c === '"' && prev !== "\\") inDouble = !inDouble;
32373
+ if (!inSingle && !inDouble && c === "/" && prev === "/") return true;
32374
+ }
32375
+ if (inSingle || inDouble) return true;
32376
+ const window = line.slice(start, start + len);
32377
+ return /['"]/.test(window);
32378
+ }
32379
+ function parseMutationReport(text2) {
32380
+ const candidates = [];
32381
+ const fence = text2.match(/```(?:json)?\s*([\s\S]*?)```/);
32382
+ if (fence?.[1]) candidates.push(fence[1].trim());
32383
+ const firstBrace = text2.indexOf("{");
32384
+ if (firstBrace >= 0) candidates.push(extractBalancedObject(text2, firstBrace));
32385
+ for (const candidate of candidates) {
32386
+ if (!candidate) continue;
32387
+ try {
32388
+ const parsed = JSON.parse(candidate);
32389
+ if (!Array.isArray(parsed.mutants)) continue;
32390
+ return {
32391
+ mutants: parsed.mutants.map(normalizeMutantEntry).filter((x) => Boolean(x)),
32392
+ summary: typeof parsed.summary === "string" ? parsed.summary : void 0
32393
+ };
32394
+ } catch {
32395
+ }
32396
+ }
32397
+ return void 0;
32398
+ }
32399
+ function extractBalancedObject(text2, start) {
32400
+ let depth = 0;
32401
+ let inString = false;
32402
+ let escaped = false;
32403
+ for (let i = start; i < text2.length; i++) {
32404
+ const c = text2[i];
32405
+ if (escaped) {
32406
+ escaped = false;
32407
+ continue;
32408
+ }
32409
+ if (c === "\\") {
32410
+ escaped = true;
32411
+ continue;
32412
+ }
32413
+ if (c === '"') inString = !inString;
32414
+ if (inString) continue;
32415
+ if (c === "{") depth++;
32416
+ else if (c === "}") {
32417
+ depth--;
32418
+ if (depth === 0) return text2.slice(start, i + 1);
32419
+ }
32420
+ }
32421
+ return text2.slice(start);
32422
+ }
32423
+ function normalizeMutantEntry(value) {
32424
+ if (typeof value !== "object" || value === null) return void 0;
32425
+ const rec = value;
32426
+ const id = typeof rec["id"] === "string" ? rec["id"] : void 0;
32427
+ const status = rec["status"];
32428
+ if (!id || status !== "killed" && status !== "survived" && status !== "skipped") {
32429
+ return void 0;
32430
+ }
32431
+ return {
32432
+ id,
32433
+ file: typeof rec["file"] === "string" ? rec["file"] : "",
32434
+ line: typeof rec["line"] === "number" ? rec["line"] : 0,
32435
+ kind: typeof rec["kind"] === "string" ? rec["kind"] : "",
32436
+ status,
32437
+ evidence: typeof rec["evidence"] === "string" ? rec["evidence"] : void 0
32438
+ };
32439
+ }
32440
+
32441
+ // src/coordination/director-mutation-test-tool.ts
32442
+ var DEFAULT_MAX_PER_FILE = 10;
32443
+ var DEFAULT_MAX_STRENGTHEN_ATTEMPTS = 2;
32444
+ var CHAOS_ROLE = "chaos-monkey";
32445
+ function makeMutationTestTool(director, roster, opts = {}) {
32446
+ return {
32447
+ name: "mutation_test",
32448
+ 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.",
32449
+ 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.",
32450
+ permission: "auto",
32451
+ mutating: false,
32452
+ capabilities: [ToolCapabilities.SUBAGENT_SPAWN],
32453
+ inputSchema: {
32454
+ type: "object",
32455
+ properties: {
32456
+ targets: {
32457
+ type: "array",
32458
+ items: { type: "string" },
32459
+ description: "Project-relative (or absolute) source files to mutate. Keep to files changed by the current task."
32460
+ },
32461
+ testCommand: {
32462
+ type: "string",
32463
+ description: 'Exact command that runs the relevant tests, e.g. "pnpm exec vitest run packages/core/tests/coordination/mutation-engine.test.ts".'
32464
+ },
32465
+ cwd: { type: "string", description: "Working directory for the test command." },
32466
+ maxPerFile: {
32467
+ type: "number",
32468
+ minimum: 1,
32469
+ maximum: 25,
32470
+ description: "Mutant cap per file per pass. Default 10."
32471
+ },
32472
+ maxStrengthenAttempts: {
32473
+ type: "number",
32474
+ minimum: 0,
32475
+ maximum: 5,
32476
+ description: "Strengthen\u2192re-verify rounds. Default 2 when repairSubagentId is set, else 0."
32477
+ },
32478
+ repairSubagentId: {
32479
+ type: "string",
32480
+ description: "Subagent that owns the tests. When set and mutants survive, it receives a strengthen-tests task and the survivors are re-verified."
32481
+ },
32482
+ chaosWorktree: {
32483
+ anyOf: [{ type: "boolean" }, { type: "string", enum: ["auto", "required", "off"] }],
32484
+ description: "Worktree override for the chaos agent. Use 'off' when targets are uncommitted \u2014 a worktree from HEAD would not contain them."
32485
+ },
32486
+ timeoutMs: { type: "number", minimum: 1, description: "Per-task timeout for chaos/strengthen/rerun tasks." },
32487
+ reportOnly: {
32488
+ type: "boolean",
32489
+ description: "Skip the strengthen loop even when survivors exist. Default false."
32490
+ }
32491
+ },
32492
+ required: ["targets", "testCommand"],
32493
+ additionalProperties: false
32494
+ },
32495
+ async execute(input, ctx) {
32496
+ const i = normalizeMutationTestInput(input);
32497
+ const root = opts.projectRoot ?? ctx.projectRoot;
32498
+ const plan = buildPlan(i, root);
32499
+ if (plan.length === 0) {
32500
+ return {
32501
+ verdict: "inconclusive",
32502
+ passed: false,
32503
+ error: "No mutable sites found in the given targets (after comment/string filtering)."
32504
+ };
32505
+ }
32506
+ const chaosSubagentId = await director.spawn(
32507
+ makeChaosConfig(roster, i.chaosWorktree ?? "off")
32508
+ );
32509
+ const chaosTaskId = await director.assign({
32510
+ id: randomUUID14(),
32511
+ subagentId: chaosSubagentId,
32512
+ description: buildChaosTask(plan, i, 1, []),
32513
+ timeoutMs: i.timeoutMs
32514
+ });
32515
+ const [chaosResult] = await director.awaitTasks([chaosTaskId]);
32516
+ const pass1 = collectOutcomes(chaosResult, plan);
32517
+ const survivors = pass1.filter((m) => m.status === "survived");
32518
+ const maxAttempts = clamp(
32519
+ i.maxStrengthenAttempts ?? (i.repairSubagentId && !i.reportOnly ? DEFAULT_MAX_STRENGTHEN_ATTEMPTS : 0),
32520
+ 0,
32521
+ 5
32522
+ );
32523
+ const attempts = [];
32524
+ let current = survivors;
32525
+ while (current.length > 0 && attempts.length < maxAttempts && i.repairSubagentId) {
32526
+ const attemptNo = attempts.length + 1;
32527
+ const strengthenTaskId = await director.assign({
32528
+ id: randomUUID14(),
32529
+ subagentId: i.repairSubagentId,
32530
+ description: buildStrengthenTask(current, i, attemptNo),
32531
+ timeoutMs: i.timeoutMs
32532
+ });
32533
+ const [strengthenResult] = await director.awaitTasks([strengthenTaskId]);
32534
+ if (strengthenResult?.status !== "success") {
32535
+ attempts.push({
32536
+ attempt: attemptNo,
32537
+ survivorsBefore: current,
32538
+ strengthenResult: strengthenResult ? { taskId: strengthenResult.taskId, status: strengthenResult.status } : void 0,
32539
+ survivorsAfter: current,
32540
+ suspectedEquivalent: []
32541
+ });
32542
+ break;
32543
+ }
32544
+ const survivorPlan = plan.filter((p) => current.some((s) => s.id === p.id));
32545
+ const rerunSubagentId = await director.spawn(
32546
+ makeChaosConfig(roster, i.chaosWorktree ?? "off")
32547
+ );
32548
+ const rerunTaskId = await director.assign({
32549
+ id: randomUUID14(),
32550
+ subagentId: rerunSubagentId,
32551
+ description: buildChaosTask(survivorPlan, i, attemptNo + 1, current),
32552
+ timeoutMs: i.timeoutMs
32553
+ });
32554
+ const [rerunResult] = await director.awaitTasks([rerunTaskId]);
32555
+ const passN = collectOutcomes(rerunResult, survivorPlan);
32556
+ const stillSurviving = passN.filter((m) => m.status === "survived" || m.status === "skipped");
32557
+ attempts.push({
32558
+ attempt: attemptNo,
32559
+ survivorsBefore: current,
32560
+ strengthenResult: { taskId: strengthenResult.taskId, status: strengthenResult.status },
32561
+ rerunResult: { taskId: rerunTaskId, status: rerunResult?.status ?? "unknown" },
32562
+ survivorsAfter: stillSurviving,
32563
+ suspectedEquivalent: stillSurviving.filter((m) => current.some((c) => c.id === m.id)).map((m) => m.id)
32564
+ });
32565
+ current = stillSurviving.filter((m) => m.status === "survived");
32566
+ if (passN.every((m) => m.status === "skipped")) break;
32567
+ }
32568
+ const finalSurvivors = current;
32569
+ const verifiedCount = pass1.filter((m) => m.status !== "skipped").length;
32570
+ const skippedCount = pass1.filter((m) => m.status === "skipped").length;
32571
+ const score = plan.length === 0 ? 0 : pass1.filter((m) => m.status === "killed").length / plan.length;
32572
+ const verdict = verifiedCount === 0 ? "inconclusive" : finalSurvivors.length === 0 ? skippedCount > 0 ? "partial" : "pass" : score >= 0.8 ? "partial" : "fail";
32573
+ return {
32574
+ verdict,
32575
+ passed: verdict === "pass",
32576
+ mutationScore: Number.parseFloat(score.toFixed(3)),
32577
+ planned: plan.length,
32578
+ killed: pass1.filter((m) => m.status === "killed").length,
32579
+ survived: pass1.filter((m) => m.status === "survived").length,
32580
+ skipped: pass1.filter((m) => m.status === "skipped").length,
32581
+ finalSurvivors: finalSurvivors.map((m) => ({ id: m.id, file: m.file, kind: m.kind })),
32582
+ suspectedEquivalent: attempts.flatMap((a) => a.suspectedEquivalent),
32583
+ strengthenAttempts: attempts.length,
32584
+ attempts,
32585
+ chaosTaskId,
32586
+ nextAction: finalSurvivors.length === 0 ? "accept" : attempts.length >= maxAttempts && i.repairSubagentId ? "manual_review_survivors" : "strengthen_tests"
32587
+ };
32588
+ }
32589
+ };
32590
+ }
32591
+ function normalizeMutationTestInput(input) {
32592
+ const raw = input ?? {};
32593
+ const targets = stringArray2(raw["targets"]) ?? [];
32594
+ const testCommand = typeof raw["testCommand"] === "string" ? raw["testCommand"].trim() : "";
32595
+ return {
32596
+ targets: targets.filter(Boolean),
32597
+ testCommand,
32598
+ cwd: typeof raw["cwd"] === "string" && raw["cwd"].trim() ? raw["cwd"].trim() : void 0,
32599
+ maxPerFile: typeof raw["maxPerFile"] === "number" ? raw["maxPerFile"] : void 0,
32600
+ maxStrengthenAttempts: typeof raw["maxStrengthenAttempts"] === "number" ? raw["maxStrengthenAttempts"] : void 0,
32601
+ repairSubagentId: typeof raw["repairSubagentId"] === "string" && raw["repairSubagentId"].trim() ? raw["repairSubagentId"].trim() : void 0,
32602
+ chaosWorktree: raw["chaosWorktree"] ?? void 0,
32603
+ timeoutMs: typeof raw["timeoutMs"] === "number" ? raw["timeoutMs"] : void 0,
32604
+ reportOnly: raw["reportOnly"] === true
32605
+ };
32606
+ }
32607
+ function clamp(n, lo, hi) {
32608
+ return Math.min(hi, Math.max(lo, n));
32609
+ }
32610
+ function buildPlan(i, projectRoot) {
32611
+ const plan = [];
32612
+ for (const target of i.targets) {
32613
+ const abs = isAbsolute9(target) ? target : join40(projectRoot ?? process.cwd(), target);
32614
+ let source;
32615
+ try {
32616
+ source = readFileSync19(abs, "utf8");
32617
+ } catch {
32618
+ continue;
32619
+ }
32620
+ plan.push(...planMutations(target, source, { maxPerFile: i.maxPerFile ?? DEFAULT_MAX_PER_FILE }));
32621
+ }
32622
+ return plan;
32623
+ }
32624
+ function makeChaosConfig(roster, worktree) {
32625
+ const base = roster?.[CHAOS_ROLE] ?? getAgentDefinition(CHAOS_ROLE)?.config ?? { name: "Chaos Monkey", role: CHAOS_ROLE };
32626
+ return { ...instantiateRosterConfig(CHAOS_ROLE, base), worktree };
32627
+ }
32628
+ function buildChaosTask(plan, i, pass, priorSurvivors) {
32629
+ const mutants = plan.map(
32630
+ (m) => `- ${m.id} | ${m.file}:${m.line}:${m.column} | ${m.kind} | "${m.original}" -> "${m.replacement}"`
32631
+ ).join("\n");
32632
+ const prior = priorSurvivors.length > 0 ? `
32633
+ These mutants survived a previous pass (pass ${pass - 1}) \u2014 re-verify them against the STRENGTHENED tests:
32634
+ ${priorSurvivors.map((s) => `- ${s.id} (${s.kind} @ ${s.file}:${s.line})`).join("\n")}` : "";
32635
+ return [
32636
+ "Execute this deterministic mutation plan against the current checkout.",
32637
+ "",
32638
+ "For each mutant, in order:",
32639
+ "1. Apply ONLY that mutation at its exact (file, line, column).",
32640
+ `2. Run the test command: ${i.testCommand}${i.cwd ? ` (cwd: ${i.cwd})` : ""}`,
32641
+ "3. Record killed (tests failed \u2014 quote first failing assertion) or survived (suite green).",
32642
+ "4. Restore the file byte-for-byte before the next mutant.",
32643
+ "",
32644
+ "Mutants:",
32645
+ mutants,
32646
+ prior,
32647
+ "",
32648
+ "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.",
32649
+ "Finish with submit_result, then repeat the same JSON as your final text."
32650
+ ].join("\n");
32651
+ }
32652
+ function buildStrengthenTask(survivors, i, attempt) {
32653
+ return [
32654
+ `Strengthen the tests so these SURVIVING mutants die (attempt ${attempt}).`,
32655
+ "",
32656
+ "Each survivor below was a deliberate sabotage of production code that the current suite did NOT catch:",
32657
+ ...survivors.map((s) => `- ${s.id} | ${s.file}:${s.line} | ${s.kind}${s.evidence ? ` | ${s.evidence}` : ""}`),
32658
+ "",
32659
+ `Test command that must fail under each mutant: ${i.testCommand}`,
32660
+ "",
32661
+ "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."
32662
+ ].join("\n");
32663
+ }
32664
+ function collectOutcomes(result, plan) {
32665
+ const fromText = parseTextOutcomes(result);
32666
+ if (fromText.length > 0) {
32667
+ const planned = new Set(plan.map((p) => p.id));
32668
+ const matched = fromText.filter((m) => planned.has(m.id));
32669
+ if (matched.length > 0) return matched;
32670
+ }
32671
+ return plan.map((p) => ({
32672
+ id: p.id,
32673
+ file: p.file,
32674
+ line: p.line,
32675
+ kind: p.kind,
32676
+ status: "skipped",
32677
+ evidence: result ? `chaos task ended ${result.status}` : "chaos task produced no result"
32678
+ }));
32679
+ }
32680
+ function parseTextOutcomes(result) {
32681
+ const text2 = typeof result?.result === "string" ? result.result : void 0;
32682
+ if (!text2) return [];
32683
+ const parsed = parseMutationReport(text2);
32684
+ if (!parsed) return [];
32685
+ return parsed.mutants.map((m) => ({
32686
+ id: m.id,
32687
+ file: m.file,
32688
+ line: m.line,
32689
+ kind: m.kind,
32690
+ status: m.status,
32691
+ evidence: m.evidence
32692
+ }));
32693
+ }
32694
+
32139
32695
  // src/coordination/director-tools.ts
32140
32696
  function makeSpawnTool(director, roster) {
32141
32697
  const dispatchCatalog = () => {
@@ -32428,7 +32984,7 @@ function makeKanbanQueueTool(director, roster) {
32428
32984
  try {
32429
32985
  const config = buildKanbanSubagentConfig(claim.task, i, roster, instantiateRosterConfig);
32430
32986
  subagentId = await director.spawn(config);
32431
- const dispatchTaskId = randomUUID14();
32987
+ const dispatchTaskId = randomUUID15();
32432
32988
  const taskSpec = {
32433
32989
  id: dispatchTaskId,
32434
32990
  subagentId,
@@ -32704,6 +33260,7 @@ function buildDirectorToolset(director, roster) {
32704
33260
  makeAskResultTool(director),
32705
33261
  makeRollUpTool(director),
32706
33262
  makeQualityGateTool(director, roster),
33263
+ makeMutationTestTool(director, roster),
32707
33264
  makeTerminateTool(director),
32708
33265
  makeTerminateAllTool(director),
32709
33266
  makeFleetTool(director),
@@ -32790,7 +33347,7 @@ import * as fsp29 from "node:fs/promises";
32790
33347
  import * as path63 from "node:path";
32791
33348
 
32792
33349
  // src/storage/session-store.ts
32793
- import { randomUUID as randomUUID16 } from "node:crypto";
33350
+ import { randomUUID as randomUUID17 } from "node:crypto";
32794
33351
  import * as fsp28 from "node:fs/promises";
32795
33352
  import * as path62 from "node:path";
32796
33353
  init_client();
@@ -33961,7 +34518,7 @@ var FileSessionWriter = class _FileSessionWriter {
33961
34518
  // src/storage/session-checkpoint-cas.ts
33962
34519
  init_atomic_write();
33963
34520
  import { spawn as spawn5 } from "node:child_process";
33964
- import { createHash as createHash18, randomUUID as randomUUID15 } from "node:crypto";
34521
+ import { createHash as createHash18, randomUUID as randomUUID16 } from "node:crypto";
33965
34522
  import * as fsp15 from "node:fs/promises";
33966
34523
  import * as path54 from "node:path";
33967
34524
  init_error();
@@ -34199,7 +34756,7 @@ var SessionCheckpointCas = class {
34199
34756
  }
34200
34757
  const temp = path54.join(
34201
34758
  path54.dirname(target),
34202
- `.${path54.basename(target)}.${process.pid}.${randomUUID15()}.tmp`
34759
+ `.${path54.basename(target)}.${process.pid}.${randomUUID16()}.tmp`
34203
34760
  );
34204
34761
  let handle;
34205
34762
  try {
@@ -36007,7 +36564,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
36007
36564
  onAppend;
36008
36565
  onAppendBatch;
36009
36566
  catalogClient;
36010
- maintenanceHolderId = randomUUID16();
36567
+ maintenanceHolderId = randomUUID17();
36011
36568
  _loadCache = /* @__PURE__ */ new Map();
36012
36569
  loadCache = new SessionLoadCache(this._loadCache);
36013
36570
  _indexCache = null;
@@ -36699,7 +37256,7 @@ async function readDirectorSubagentSession(args) {
36699
37256
  }
36700
37257
 
36701
37258
  // src/core/fallback-model.ts
36702
- import { randomUUID as randomUUID17 } from "node:crypto";
37259
+ import { randomUUID as randomUUID18 } from "node:crypto";
36703
37260
 
36704
37261
  // src/types/provider.ts
36705
37262
  init_errors();
@@ -36709,6 +37266,18 @@ var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not eno
36709
37266
  var ROUTE_SCOPED_QUOTA_RE = /\b(?:for|on)\s+(?:(?:this|the)\s+)?(?:route|model)\b|\b(?:route|model)(?:[-_\s]+[\w.-]+)?[-_\s]*(?:quota|limit)\b|\b(?:quota|limit).{0,24}\b(?:for|on)\s+(?:(?:this|the)\s+)?(?:route|model)\b/i;
36710
37267
 
36711
37268
  // src/types/provider.ts
37269
+ var REASONING_EFFORT_LEVELS = [
37270
+ "none",
37271
+ "minimal",
37272
+ "low",
37273
+ "medium",
37274
+ "high",
37275
+ "xhigh",
37276
+ "max"
37277
+ ];
37278
+ function isReasoningEffort(value) {
37279
+ return typeof value === "string" && REASONING_EFFORT_LEVELS.includes(value);
37280
+ }
36712
37281
  function effectiveInputTokens(usage) {
36713
37282
  return usage.input + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0);
36714
37283
  }
@@ -37731,7 +38300,7 @@ function createFallbackModelExtension(deps) {
37731
38300
  let gateRequestId;
37732
38301
  const configuredGateSeconds = cfg.fallbackGateSeconds ?? deps.fallbackGateSeconds;
37733
38302
  if (configuredGateSeconds !== 0 && deps.fallbackGate && usableChain.length > 0) {
37734
- gateRequestId = randomUUID17();
38303
+ gateRequestId = randomUUID18();
37735
38304
  const autoSwitchSeconds = Math.max(1, configuredGateSeconds ?? 7);
37736
38305
  const gateCandidates = usableChain.map((e) => ({
37737
38306
  providerId: e.providerId,
@@ -38703,7 +39272,7 @@ function hashStr(s) {
38703
39272
  }
38704
39273
 
38705
39274
  // src/coordination/multi-agent-coordinator.ts
38706
- import { randomUUID as randomUUID18 } from "node:crypto";
39275
+ import { randomUUID as randomUUID19 } from "node:crypto";
38707
39276
  import { EventEmitter as EventEmitter2 } from "node:events";
38708
39277
 
38709
39278
  // src/coordination/coordinator/error-classifier.ts
@@ -38828,6 +39397,20 @@ var EXPLORE_COMPANION_AGENT = {
38828
39397
  textStream: "silent",
38829
39398
  toolStream: "silent"
38830
39399
  };
39400
+ var CHAOS_MONKEY_AGENT = {
39401
+ ...defineAgent("chaos-monkey", "Chaos Monkey"),
39402
+ tools: [...TOOLS.build],
39403
+ skillNames: ["testing", "typescript-strict"],
39404
+ spawnBudgetExempt: true,
39405
+ // Follow fleet worktree policy (NOT 'required'): mutation targets are
39406
+ // often freshly written and uncommitted — a worktree spawned from HEAD
39407
+ // would not contain them and every mutant would drift. Callers pass
39408
+ // `worktree: 'off'` in the mutation_test input for uncommitted targets.
39409
+ worktree: "auto",
39410
+ // Report travels via submit_result + final text, not the leader's stream.
39411
+ textStream: "silent",
39412
+ toolStream: "silent"
39413
+ };
38831
39414
  var CRITIC_AGENT = defineAgent("critic", "Critic");
38832
39415
  var GENERIC_AGENT = defineAgent("generic", "Generic Project Agent");
38833
39416
  function withDispatchMetadata(definition) {
@@ -38847,6 +39430,7 @@ var FLEET_ROSTER = {
38847
39430
  generic: GENERIC_AGENT,
38848
39431
  "shadow-agent": SHADOW_AGENT,
38849
39432
  "explore-companion": EXPLORE_COMPANION_AGENT,
39433
+ "chaos-monkey": CHAOS_MONKEY_AGENT,
38850
39434
  ...Object.fromEntries(
38851
39435
  ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, withDispatchMetadata(d)])
38852
39436
  )
@@ -38877,6 +39461,16 @@ var FLEET_ROSTER_BUDGETS = {
38877
39461
  maxTokens: 96e3,
38878
39462
  maxCostUsd: 0.5
38879
39463
  },
39464
+ "chaos-monkey": {
39465
+ // A mutation pass is many short apply/run/restore cycles — per-mutant
39466
+ // work is tiny, but a large plan (25 mutants/file × N files) needs
39467
+ // headroom. Idle-based reaping covers a stalled pass.
39468
+ idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS,
39469
+ maxIterations: 2e3,
39470
+ maxToolCalls: 6e3,
39471
+ maxTokens: 96e3,
39472
+ maxCostUsd: 0.5
39473
+ },
38880
39474
  ...Object.fromEntries(
38881
39475
  ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.budget])
38882
39476
  )
@@ -38939,7 +39533,8 @@ async function executeSubagentWithTimeout({
38939
39533
  budget,
38940
39534
  preemptFraction = TIMEOUT_PREEMPT_FRACTION,
38941
39535
  abortSubagent,
38942
- currentSessionId
39536
+ currentSessionId,
39537
+ gracefulFinish
38943
39538
  }) {
38944
39539
  const initialTimeoutMs = budget.limits.timeoutMs;
38945
39540
  const idleLimitMs = budget.limits.idleTimeoutMs;
@@ -38970,9 +39565,17 @@ async function executeSubagentWithTimeout({
38970
39565
  const scheduleNext = () => {
38971
39566
  const wallLimit = budget.limits.timeoutMs ?? initialTimeoutMs;
38972
39567
  const wallRemaining = initialTimeoutMs === void 0 ? Number.POSITIVE_INFINITY : wallLimit - (Date.now() - start);
38973
- const idleRemaining = idleLimitMs === void 0 ? Number.POSITIVE_INFINITY : (budget.limits.idleTimeoutMs ?? idleLimitMs) - budget.idleMs();
38974
- const preemptRemaining = initialTimeoutMs === void 0 || preemptedCeiling === wallLimit ? Number.POSITIVE_INFINITY : wallLimit * preemptFraction - (Date.now() - start);
38975
- armFor(Math.max(25, Math.min(wallRemaining, idleRemaining, preemptRemaining)));
39568
+ const idleRemaining = idleLimitMs === void 0 || gracefulFinish !== void 0 && initialTimeoutMs !== void 0 ? Number.POSITIVE_INFINITY : (budget.limits.idleTimeoutMs ?? idleLimitMs) - budget.idleMs();
39569
+ const preemptRemaining = initialTimeoutMs === void 0 || preemptedCeiling === wallLimit || gracefulFinish !== void 0 ? Number.POSITIVE_INFINITY : wallLimit * preemptFraction - (Date.now() - start);
39570
+ const next = Math.min(wallRemaining, idleRemaining, preemptRemaining);
39571
+ if (!Number.isFinite(next)) {
39572
+ if (timer) {
39573
+ clearTimeout(timer);
39574
+ timer = null;
39575
+ }
39576
+ return;
39577
+ }
39578
+ armFor(Math.max(25, next));
38976
39579
  };
38977
39580
  const negotiateTimeout = async (used, limit) => {
38978
39581
  const handler = budget.onThreshold;
@@ -39021,6 +39624,10 @@ async function executeSubagentWithTimeout({
39021
39624
  const wallExceeded = wallLimit !== void 0 && elapsed2 >= wallLimit;
39022
39625
  const idleExceeded = idleLimit !== void 0 && budget.idleMs() >= idleLimit;
39023
39626
  if (idleExceeded && !wallExceeded) {
39627
+ if (gracefulFinish !== void 0 && initialTimeoutMs !== void 0) {
39628
+ scheduleNext();
39629
+ return;
39630
+ }
39024
39631
  const sessionId = currentSessionId();
39025
39632
  budget._events?.emit("budget.threshold_reached", {
39026
39633
  ...sessionId ? { sessionId } : {},
@@ -39037,7 +39644,7 @@ async function executeSubagentWithTimeout({
39037
39644
  reject(new BudgetExceededError("idle_timeout", idleLimit ?? 0, budget.idleMs()));
39038
39645
  return;
39039
39646
  }
39040
- if (wallLimit !== void 0 && !wallExceeded && budget.onThreshold && preemptState === "active" /* ACTIVE */ && elapsed2 >= wallLimit * preemptFraction) {
39647
+ if (wallLimit !== void 0 && !wallExceeded && gracefulFinish === void 0 && budget.onThreshold && preemptState === "active" /* ACTIVE */ && elapsed2 >= wallLimit * preemptFraction) {
39041
39648
  const activityTs = Date.now() - budget.idleMs();
39042
39649
  if (activityTs <= lastGrantActivityTs) {
39043
39650
  preemptState = "locked" /* LOCKED */;
@@ -39071,6 +39678,22 @@ async function executeSubagentWithTimeout({
39071
39678
  return;
39072
39679
  }
39073
39680
  const limit = wallLimit ?? 0;
39681
+ if (gracefulFinish !== void 0) {
39682
+ if (!budget.graceGranted) {
39683
+ const reason = `wall-clock budget of ${Math.round(limit / 1e3)}s reached`;
39684
+ if (budget.notifyFinish(reason, { graceMs: gracefulFinish.graceMs })) {
39685
+ scheduleNext();
39686
+ return;
39687
+ }
39688
+ abortSubagent(ctx.subagentId);
39689
+ reject(new BudgetExceededError("timeout", limit, elapsed2));
39690
+ return;
39691
+ } else {
39692
+ abortSubagent(ctx.subagentId);
39693
+ reject(new BudgetExceededError("timeout", limit, elapsed2));
39694
+ return;
39695
+ }
39696
+ }
39074
39697
  if (!budget.onThreshold) {
39075
39698
  abortSubagent(ctx.subagentId);
39076
39699
  reject(new BudgetExceededError("timeout", limit, elapsed2));
@@ -39218,7 +39841,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
39218
39841
  return { ...subagent, name: display };
39219
39842
  }
39220
39843
  async spawn(subagent) {
39221
- const id = subagent.id || randomUUID18();
39844
+ const id = subagent.id || randomUUID19();
39222
39845
  const cfg = this.withNickname(subagent, id);
39223
39846
  if (this.subagents.has(id)) {
39224
39847
  throw new Error(`Subagent id "${id}" already exists \u2014 refusing to overwrite`);
@@ -39455,6 +40078,32 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
39455
40078
  completeTask(result) {
39456
40079
  this.recordCompletion(result);
39457
40080
  }
40081
+ /**
40082
+ * Ask every RUNNING subagent that opted into `gracefulFinish` to finish its
40083
+ * task in its own turn (see coordination/subagent-finish.ts). This is the
40084
+ * leader-side entry point for "the leader agent has finished": it delivers
40085
+ * an in-band notification between tool batches — never an interrupt, never
40086
+ * an abort. Each notified subagent keeps its existing time budget and
40087
+ * accelerates; the watchdog still bounds the maximum lifetime.
40088
+ *
40089
+ * Subagents without the policy opted in are deliberately untouched — their
40090
+ * lifecycle remains the legacy watchdog contract.
40091
+ *
40092
+ * Returns the number of subagents actually notified.
40093
+ */
40094
+ requestFinish(reason) {
40095
+ let notified = 0;
40096
+ for (const subagent of this.subagents.values()) {
40097
+ if (subagent.status !== "running") continue;
40098
+ if (!resolveGracefulFinish(subagent.config)) continue;
40099
+ const budget = subagent.activeBudget;
40100
+ if (!budget) continue;
40101
+ const usage = budget.usage();
40102
+ if (usage.iterations === 0 && usage.toolCalls === 0) continue;
40103
+ if (budget.notifyFinish(reason)) notified++;
40104
+ }
40105
+ return notified;
40106
+ }
39458
40107
  // --- internal dispatching ---------------------------------------------
39459
40108
  tryDispatchNext() {
39460
40109
  while (this.canDispatch()) {
@@ -39628,7 +40277,14 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
39628
40277
  idleTimeoutMs: rawIdleTimeoutMs ?? this.config.defaultBudget?.idleTimeoutMs ?? configWithRosterDefaults.idleTimeoutMs
39629
40278
  },
39630
40279
  "auto",
39631
- { sessionId: () => this.currentSessionId() }
40280
+ {
40281
+ sessionId: () => this.currentSessionId(),
40282
+ subagentId,
40283
+ // Graceful-finish runs own wall-clock enforcement to the watchdog so
40284
+ // the notify-then-bound lifecycle cannot be raced by tool.progress
40285
+ // heartbeats calling checkTimeout() (see subagent-budget.ts).
40286
+ ...resolveGracefulFinish(subagent.config) ? { wallClockWatchdogOwned: true } : {}
40287
+ }
39632
40288
  );
39633
40289
  subagent.activeBudget = budget;
39634
40290
  if (!this.runner) {
@@ -39661,7 +40317,8 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
39661
40317
  task,
39662
40318
  runCtx,
39663
40319
  budget,
39664
- subagent.config.preemptFraction
40320
+ subagent.config.preemptFraction,
40321
+ resolveGracefulFinish(subagent.config)
39665
40322
  );
39666
40323
  result = {
39667
40324
  subagentId,
@@ -39691,13 +40348,14 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
39691
40348
  }
39692
40349
  this.recordCompletion(result);
39693
40350
  }
39694
- async executeWithTimeout(runner, task, ctx, budget, preemptFraction) {
40351
+ async executeWithTimeout(runner, task, ctx, budget, preemptFraction, gracefulFinish) {
39695
40352
  return executeSubagentWithTimeout({
39696
40353
  runner,
39697
40354
  task,
39698
40355
  ctx,
39699
40356
  budget,
39700
40357
  preemptFraction,
40358
+ gracefulFinish,
39701
40359
  abortSubagent: (subagentId) => this.subagents.get(subagentId)?.abortController.abort(),
39702
40360
  currentSessionId: () => this.currentSessionId()
39703
40361
  });
@@ -40104,7 +40762,7 @@ var Director = class _Director {
40104
40762
  sessionProvider;
40105
40763
  sessionModel;
40106
40764
  constructor(opts) {
40107
- this.id = opts.config.coordinatorId || randomUUID19();
40765
+ this.id = opts.config.coordinatorId || randomUUID20();
40108
40766
  this.manifestPath = opts.manifestPath;
40109
40767
  this.roster = opts.roster;
40110
40768
  this.directorPreamble = opts.directorPreamble ?? DEFAULT_DIRECTOR_PREAMBLE;
@@ -40311,6 +40969,17 @@ var Director = class _Director {
40311
40969
  isWorkComplete() {
40312
40970
  return this.workCompleteFlag;
40313
40971
  }
40972
+ /**
40973
+ * Ask every running background subagent that opted into `gracefulFinish`
40974
+ * to finish its task in its own turn. In-band notification between tool
40975
+ * batches — no interrupt, no abort; each subagent keeps its time budget and
40976
+ * accelerates. Session shutdown calls this before draining Chimera work so
40977
+ * the post-session reviewer is nudged to complete rather than killed.
40978
+ * Returns the number of subagents notified.
40979
+ */
40980
+ requestFinish(reason) {
40981
+ return this.coordinator.requestFinish(reason);
40982
+ }
40314
40983
  setLeaderBtwNote(note) {
40315
40984
  return this.btwNotes.add(note);
40316
40985
  }
@@ -40387,7 +41056,7 @@ var Director = class _Director {
40387
41056
  );
40388
41057
  }
40389
41058
  const msg = {
40390
- id: randomUUID19(),
41059
+ id: randomUUID20(),
40391
41060
  type: "task",
40392
41061
  from: this.id,
40393
41062
  to: subagentId,
@@ -40641,7 +41310,7 @@ var Director = class _Director {
40641
41310
  };
40642
41311
 
40643
41312
  // src/coordination/fleet-manager.ts
40644
- import { randomUUID as randomUUID20 } from "node:crypto";
41313
+ import { randomUUID as randomUUID21 } from "node:crypto";
40645
41314
  import * as fsp31 from "node:fs/promises";
40646
41315
  import * as path64 from "node:path";
40647
41316
  init_atomic_write();
@@ -40709,7 +41378,7 @@ var FleetManager = class {
40709
41378
  maxContext;
40710
41379
  constructor(opts = {}) {
40711
41380
  this.manifestPath = opts.manifestPath;
40712
- this.directorRunId = opts.directorRunId ?? randomUUID20();
41381
+ this.directorRunId = opts.directorRunId ?? randomUUID21();
40713
41382
  this.maxSpawns = opts.maxSpawns ?? Number.POSITIVE_INFINITY;
40714
41383
  this.maxSpawnDepth = resolveMaxSpawnDepth(opts.maxSpawnDepth);
40715
41384
  this.spawnDepth = opts.spawnDepth ?? 0;
@@ -42761,7 +43430,7 @@ function makeFleetStatusTool(opts = {}) {
42761
43430
  }
42762
43431
 
42763
43432
  // src/coordination/fleet-supervisor.ts
42764
- import { randomUUID as randomUUID21 } from "node:crypto";
43433
+ import { randomUUID as randomUUID22 } from "node:crypto";
42765
43434
  var COLLAB_ID_PREFIXES2 = ["bug-hunter-", "refactor-planner-", "critic-"];
42766
43435
  var DEFAULTS = {
42767
43436
  intervalMs: 2e4,
@@ -43039,7 +43708,7 @@ var FleetSupervisor = class {
43039
43708
  */
43040
43709
  async decide(question, context, options, risk) {
43041
43710
  const request = {
43042
- id: `fleetsup-${randomUUID21()}`,
43711
+ id: `fleetsup-${randomUUID22()}`,
43043
43712
  sessionId: this.opts.sessionId?.(),
43044
43713
  source: "system",
43045
43714
  question,
@@ -43435,7 +44104,7 @@ function attachAutoExtend(events, policy = {}) {
43435
44104
  }
43436
44105
 
43437
44106
  // src/coordination/delegate-tool.ts
43438
- import { randomUUID as randomUUID22 } from "node:crypto";
44107
+ import { randomUUID as randomUUID23 } from "node:crypto";
43439
44108
  import * as fsp32 from "node:fs/promises";
43440
44109
  import * as path69 from "node:path";
43441
44110
  init_error();
@@ -43642,7 +44311,7 @@ function createDelegateTool(opts) {
43642
44311
  }
43643
44312
  const description = delegatedTask;
43644
44313
  const taskId = await dir.assign({
43645
- id: randomUUID22(),
44314
+ id: randomUUID23(),
43646
44315
  description,
43647
44316
  subagentId
43648
44317
  });
@@ -43863,7 +44532,7 @@ async function awaitDelegateAttempt(director, subagentId, taskId, timeoutMs, abo
43863
44532
  function freshHandoffConfig(cfg, role, handoffCount) {
43864
44533
  return {
43865
44534
  ...cfg,
43866
- id: role ? `${role}-${randomUUID22().slice(0, 8)}` : `${cfg.name.toLowerCase().replace(/[^a-z0-9]+/g, "-") || "subagent"}-handoff-${handoffCount}-${randomUUID22().slice(0, 6)}`
44535
+ id: role ? `${role}-${randomUUID23().slice(0, 8)}` : `${cfg.name.toLowerCase().replace(/[^a-z0-9]+/g, "-") || "subagent"}-handoff-${handoffCount}-${randomUUID23().slice(0, 6)}`
43867
44536
  };
43868
44537
  }
43869
44538
  function continuationFor(result, partial, config) {
@@ -43923,7 +44592,7 @@ function instantiateRosterConfig2(role, base, requestedTimeoutMs, defaultTimeout
43923
44592
  timeoutMs: requestedTimeoutMs === void 0 ? rosterTimeoutMs ?? defaultTimeoutMs : void 0,
43924
44593
  // Give each spawn a fresh id so parallel or repeated delegates
43925
44594
  // can use the same role safely.
43926
- id: `${role}-${randomUUID22().slice(0, 8)}`
44595
+ id: `${role}-${randomUUID23().slice(0, 8)}`
43927
44596
  };
43928
44597
  }
43929
44598
  function hintForKind(kind, retryable, backoffMs, partial) {
@@ -44049,7 +44718,7 @@ async function readSubagentPartial(opts, subagentId) {
44049
44718
  }
44050
44719
 
44051
44720
  // src/coordination/explore-companion.ts
44052
- import { randomUUID as randomUUID23 } from "node:crypto";
44721
+ import { randomUUID as randomUUID24 } from "node:crypto";
44053
44722
  var DEFAULT_EXPLORE_COMPANION_AGENT_ID = "explore-companion";
44054
44723
  var DEFAULT_PROBE_COOLDOWN_MS = 12e4;
44055
44724
  var DEFAULT_MAX_PENDING_PROBES = 8;
@@ -44235,7 +44904,7 @@ var ExploreCompanion = class {
44235
44904
  if (e.ok && this.cfg.signals.editUnreadFile && this.cfg.fileEditTools.has(tool) && path131) {
44236
44905
  if (!this.readSet.has(path131)) {
44237
44906
  this.engage({
44238
- id: randomUUID23(),
44907
+ id: randomUUID24(),
44239
44908
  probe: `Map file ${path131}: role, exports, dependencies, and callers \u2014 the leader is about to edit it.`,
44240
44909
  hint: { file: path131 },
44241
44910
  context: `Leader edited ${path131} without reading it first.`,
@@ -44250,7 +44919,7 @@ var ExploreCompanion = class {
44250
44919
  if (!this.readSet.has(path131)) {
44251
44920
  this.readSet.add(path131);
44252
44921
  this.engage({
44253
- id: randomUUID23(),
44922
+ id: randomUUID24(),
44254
44923
  probe: `Skeleton + callers + dependents of ${path131}: what it exports, who imports it, and how it fits the feature flow.`,
44255
44924
  hint: { file: path131 },
44256
44925
  context: `Leader read unfamiliar file ${path131}.`,
@@ -44265,7 +44934,7 @@ var ExploreCompanion = class {
44265
44934
  const input = e.input ?? {};
44266
44935
  const query = typeof input["query"] === "string" ? input["query"] : typeof input["pattern"] === "string" ? input["pattern"] : "";
44267
44936
  this.engage({
44268
- id: randomUUID23(),
44937
+ id: randomUUID24(),
44269
44938
  probe: query ? `Locate "${query}" \u2014 the leader's ${e.name} returned no hits. Try synonyms, a refreshed index, and lexical fallbacks.` : `The leader's ${e.name} returned no results. Find where the concept actually lives.`,
44270
44939
  hint: query ? { symbol: query } : void 0,
44271
44940
  context: `${e.name} for "${query}" returned zero results.`,
@@ -44286,7 +44955,7 @@ var ExploreCompanion = class {
44286
44955
  const mentions = extractSubjectTokens(todo.content);
44287
44956
  const first = mentions[0];
44288
44957
  this.engage({
44289
- id: randomUUID23(),
44958
+ id: randomUUID24(),
44290
44959
  probe: `Pre-map the files/symbols behind this in-progress todo: "${todo.content.slice(0, 160)}".`,
44291
44960
  hint: first ? { [first.kind]: first.value } : void 0,
44292
44961
  context: `Todo "${todo.content.slice(0, 120)}" flipped to in_progress.`,
@@ -44302,7 +44971,7 @@ var ExploreCompanion = class {
44302
44971
  const tokens = extractSubjectTokens(err.message);
44303
44972
  for (const token of tokens.slice(0, 2)) {
44304
44973
  this.engage({
44305
- id: randomUUID23(),
44974
+ id: randomUUID24(),
44306
44975
  probe: `What is ${token.value}, where does it live, and who uses it? The leader hit an error naming it.`,
44307
44976
  hint: { [token.kind]: token.value },
44308
44977
  context: `Error: ${err.message.slice(0, 300)}`,
@@ -44325,7 +44994,7 @@ var ExploreCompanion = class {
44325
44994
  const fromLeader = isMailboxLeader(msg.from) || lsid != null && msg.senderSessionId === lsid;
44326
44995
  if (!fromLeader) continue;
44327
44996
  this.engage({
44328
- id: randomUUID23(),
44997
+ id: randomUUID24(),
44329
44998
  probe: msg.body.trim().slice(0, 2e3) || msg.subject,
44330
44999
  context: `Direct ask from ${msg.from}: ${msg.subject}`,
44331
45000
  source: "mailbox_ask",
@@ -44407,6 +45076,22 @@ var SEND_ALLOWED_FIELDS = /* @__PURE__ */ new Set([
44407
45076
  // receiver trusts the sender-asserted `sessionId`; the boundary must
44408
45077
  // refuse the field entirely.
44409
45078
  ]);
45079
+ var SEND_FORBIDDEN_FIELDS = /* @__PURE__ */ new Set([
45080
+ "from",
45081
+ "sessionAffinity"
45082
+ ]);
45083
+ function filterMailboxSendPayload(input) {
45084
+ const payload = {};
45085
+ const stripped = [];
45086
+ for (const key of Object.keys(input)) {
45087
+ if (SEND_ALLOWED_FIELDS.has(key) || SEND_FORBIDDEN_FIELDS.has(key)) {
45088
+ payload[key] = input[key];
45089
+ } else {
45090
+ stripped.push(key);
45091
+ }
45092
+ }
45093
+ return { payload, stripped };
45094
+ }
44410
45095
  var ACK_ALLOWED_FIELDS = /* @__PURE__ */ new Set([
44411
45096
  "messageId",
44412
45097
  "read",
@@ -44644,7 +45329,9 @@ function makeMailSendTool(opts = {}) {
44644
45329
  required: ["to", "subject", "body"]
44645
45330
  },
44646
45331
  async execute(input, ctx) {
44647
- const i = input ?? {};
45332
+ const { payload: i, stripped } = filterMailboxSendPayload(
45333
+ input ?? {}
45334
+ );
44648
45335
  const rawTo = i.to;
44649
45336
  const subject2 = i.subject;
44650
45337
  const body = i.body;
@@ -44667,15 +45354,13 @@ function makeMailSendTool(opts = {}) {
44667
45354
  recipientAliases: /* @__PURE__ */ new Set([codecIdentity.baseId]),
44668
45355
  sessionId: codecIdentity.sessionId
44669
45356
  };
45357
+ let parsed;
44670
45358
  try {
44671
- parseMailboxSendInput(i, codecActor);
45359
+ parsed = parseMailboxSendInput(i, codecActor);
44672
45360
  } catch (err) {
44673
45361
  return { ok: false, error: err instanceof Error ? err.message : String(err) };
44674
45362
  }
44675
- const audience = i.audience;
44676
- if (audience !== void 0 && audience !== "all" && audience !== "leaders") {
44677
- return { ok: false, error: '"audience" must be "all" or "leaders".' };
44678
- }
45363
+ const audience = parsed.audience;
44679
45364
  const mb = resolveMailbox(ctx);
44680
45365
  const identity2 = await register(mb, ctx);
44681
45366
  const requestedTo = normalizeRecipient(rawTo, identity2.sessionId);
@@ -44689,10 +45374,10 @@ function makeMailSendTool(opts = {}) {
44689
45374
  to: delivery.to,
44690
45375
  type: resolvedType,
44691
45376
  audience: delivery.audience,
44692
- subject: subject2,
44693
- body,
44694
- priority: i.priority ?? "normal",
44695
- replyTo: i.replyTo,
45377
+ subject: parsed.subject,
45378
+ body: parsed.body,
45379
+ priority: parsed.priority,
45380
+ replyTo: parsed.replyTo,
44696
45381
  senderSessionId: identity2.sessionId
44697
45382
  });
44698
45383
  return {
@@ -44700,7 +45385,9 @@ function makeMailSendTool(opts = {}) {
44700
45385
  messageId: msg.id,
44701
45386
  from: identity2.callerId,
44702
45387
  to: msg.to,
44703
- summary: `Mail sent to ${msg.to === "*" ? "all agents" : msg.to} as ${identity2.callerId}.`
45388
+ // Surfacing what was stripped keeps the send auditable without
45389
+ // re-introducing the clutter into the payload itself.
45390
+ ...stripped.length > 0 ? { strippedFields: stripped, summary: `Mail sent to ${msg.to === "*" ? "all agents" : msg.to} as ${identity2.callerId}. Ignored ${stripped.length} unrecognized field(s): ${stripped.join(", ")}.` } : { summary: `Mail sent to ${msg.to === "*" ? "all agents" : msg.to} as ${identity2.callerId}.` }
44704
45391
  };
44705
45392
  }
44706
45393
  };
@@ -48226,7 +48913,7 @@ function createAgentMonitorService(opts) {
48226
48913
  }
48227
48914
 
48228
48915
  // src/coordination/autonomous-brain.ts
48229
- import { randomUUID as randomUUID24 } from "node:crypto";
48916
+ import { randomUUID as randomUUID25 } from "node:crypto";
48230
48917
  var AutonomousBrain = class {
48231
48918
  graph;
48232
48919
  // Fleet bus for emitting decisions — null-safe, no-op if not provided
@@ -48332,7 +49019,7 @@ var AutonomousBrain = class {
48332
49019
  consequence: i === 0 ? `Spawn the most appropriate agent for: ${taskDescription.slice(0, 80)}` : `Spawn an alternative agent for the same task`
48333
49020
  }));
48334
49021
  return this.decideAuto({
48335
- id: randomUUID24(),
49022
+ id: randomUUID25(),
48336
49023
  source,
48337
49024
  decisionType: "spawn",
48338
49025
  question: `Should we spawn a subagent for this task?`,
@@ -48375,7 +49062,7 @@ var AutonomousBrain = class {
48375
49062
  }
48376
49063
  ];
48377
49064
  return this.decideAuto({
48378
- id: randomUUID24(),
49065
+ id: randomUUID25(),
48379
49066
  source,
48380
49067
  decisionType: "approve_change",
48381
49068
  question: `Should we approve the change "${change.title}"?`,
@@ -48434,7 +49121,7 @@ var AutonomousBrain = class {
48434
49121
  consequence: "Break the task into smaller sub-tasks"
48435
49122
  });
48436
49123
  return this.decideAuto({
48437
- id: randomUUID24(),
49124
+ id: randomUUID25(),
48438
49125
  source,
48439
49126
  decisionType: "escalate_task",
48440
49127
  question: `Task failed: ${error2.slice(0, 100)}. How should we proceed?`,
@@ -48568,12 +49255,12 @@ ${ctx.error}`);
48568
49255
  };
48569
49256
 
48570
49257
  // src/coordination/autonomous-coordinator.ts
48571
- import { randomUUID as randomUUID27 } from "node:crypto";
49258
+ import { randomUUID as randomUUID28 } from "node:crypto";
48572
49259
 
48573
49260
  // src/coordination/knowledge-graph.ts
48574
49261
  init_file_permissions();
48575
49262
  init_atomic_write();
48576
- import { randomUUID as randomUUID25 } from "node:crypto";
49263
+ import { randomUUID as randomUUID26 } from "node:crypto";
48577
49264
  import * as fsp34 from "node:fs/promises";
48578
49265
  import * as path73 from "node:path";
48579
49266
  var DEFAULT_MAX_NODES = 2e3;
@@ -48621,7 +49308,7 @@ var KnowledgeGraph = class _KnowledgeGraph {
48621
49308
  * Returns the node with its assigned id.
48622
49309
  */
48623
49310
  async add(node) {
48624
- const full = { id: randomUUID25(), ...node };
49311
+ const full = { id: randomUUID26(), ...node };
48625
49312
  this.nodes.set(full.id, full);
48626
49313
  this._trackSeq(full.id);
48627
49314
  this._addToIndex(full, this._indexKeys(full));
@@ -48750,8 +49437,8 @@ var KnowledgeGraph = class _KnowledgeGraph {
48750
49437
  if (this.subs.size >= MAX_SUBSCRIPTIONS) {
48751
49438
  throw new Error(`Knowledge graph subscription limit reached (${MAX_SUBSCRIPTIONS})`);
48752
49439
  }
48753
- const channel2 = randomUUID25();
48754
- const sub = { id: randomUUID25(), agentId, filter, channel: channel2 };
49440
+ const channel2 = randomUUID26();
49441
+ const sub = { id: randomUUID26(), agentId, filter, channel: channel2 };
48755
49442
  this.subs.set(channel2, sub);
48756
49443
  this.pendingDeliveries.set(channel2, []);
48757
49444
  return channel2;
@@ -49232,7 +49919,7 @@ var TaskDAG = class {
49232
49919
  };
49233
49920
 
49234
49921
  // src/coordination/task-auctioneer.ts
49235
- import { randomUUID as randomUUID26 } from "node:crypto";
49922
+ import { randomUUID as randomUUID27 } from "node:crypto";
49236
49923
  function isTerminalGoalStatus(status) {
49237
49924
  return status === "done" || status === "failed";
49238
49925
  }
@@ -49355,7 +50042,7 @@ var TaskAuctioneer = class {
49355
50042
  const score = dispatchResult.confidence * (dispatchResult.role === agent.agentRole ? 1.2 : 1);
49356
50043
  if (score < this.minConfidence) return false;
49357
50044
  const bid = {
49358
- id: randomUUID26(),
50045
+ id: randomUUID27(),
49359
50046
  taskId,
49360
50047
  agentId: agent.agentId,
49361
50048
  agentName: agent.agentName,
@@ -50292,7 +50979,7 @@ var AutonomousCoordinator = class _AutonomousCoordinator {
50292
50979
  break;
50293
50980
  }
50294
50981
  const decision = await this.brain.decideAuto({
50295
- id: randomUUID27(),
50982
+ id: randomUUID28(),
50296
50983
  source: "system",
50297
50984
  decisionType: "prioritize_goals",
50298
50985
  question: `What should we work on next? Open goals: ${dispatchable.map((g) => g.title).join(", ")}`,
@@ -51207,7 +51894,7 @@ var TOKENS = {
51207
51894
  init_errors();
51208
51895
 
51209
51896
  // src/hq/factory.ts
51210
- import { createHash as createHash24, randomUUID as randomUUID30 } from "node:crypto";
51897
+ import { createHash as createHash24, randomUUID as randomUUID31 } from "node:crypto";
51211
51898
  import * as fs30 from "node:fs";
51212
51899
  import { hostname as hostname3 } from "node:os";
51213
51900
  import { basename as basename15 } from "node:path";
@@ -51215,13 +51902,13 @@ import { basename as basename15 } from "node:path";
51215
51902
  // src/hq/auth-store.ts
51216
51903
  init_file_permissions();
51217
51904
  init_atomic_write();
51218
- import { createHash as createHash23, randomBytes as randomBytes4, randomUUID as randomUUID28, scrypt, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
51905
+ import { createHash as createHash23, randomBytes as randomBytes4, randomUUID as randomUUID29, scrypt, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
51219
51906
  import * as syncFs from "node:fs";
51220
51907
  import * as fs29 from "node:fs/promises";
51221
51908
  import * as path75 from "node:path";
51222
51909
 
51223
51910
  // src/hq/auth-audit.ts
51224
- import { appendFileSync as appendFileSync2, readFileSync as readFileSync20, mkdirSync as mkdirSync9 } from "node:fs";
51911
+ import { appendFileSync as appendFileSync2, readFileSync as readFileSync21, mkdirSync as mkdirSync9 } from "node:fs";
51225
51912
  import * as path74 from "node:path";
51226
51913
  function hqAuthAuditPath(dataDir) {
51227
51914
  return path74.join(dataDir, "auth-audit.jsonl");
@@ -51230,7 +51917,7 @@ function readHqAuthAuditTail(dataDir, maxEntries = 50) {
51230
51917
  const filePath = hqAuthAuditPath(dataDir);
51231
51918
  let content;
51232
51919
  try {
51233
- content = readFileSync20(filePath, "utf8");
51920
+ content = readFileSync21(filePath, "utf8");
51234
51921
  } catch {
51235
51922
  return [];
51236
51923
  }
@@ -51542,8 +52229,8 @@ function mintHqToken(labelOrOptions) {
51542
52229
  const at = opts.now ?? Date.now();
51543
52230
  const createdAtIso = new Date(at).toISOString();
51544
52231
  return {
51545
- id: randomUUID28(),
51546
- token: randomUUID28().replace(/-/g, "") + randomUUID28().replace(/-/g, ""),
52232
+ id: randomUUID29(),
52233
+ token: randomUUID29().replace(/-/g, "") + randomUUID29().replace(/-/g, ""),
51547
52234
  createdAt: createdAtIso,
51548
52235
  ...opts.label ? { label: opts.label } : {},
51549
52236
  ...opts.ttlMs !== void 0 && Number.isFinite(opts.ttlMs) && opts.ttlMs > 0 ? { expiresAt: new Date(at + opts.ttlMs).toISOString() } : {}
@@ -51603,7 +52290,7 @@ function watchHqAuthFile(dataDir, onChange, opts = {}) {
51603
52290
  }
51604
52291
 
51605
52292
  // src/hq/publisher.ts
51606
- import { randomUUID as randomUUID29 } from "node:crypto";
52293
+ import { randomUUID as randomUUID30 } from "node:crypto";
51607
52294
  import * as v82 from "node:v8";
51608
52295
 
51609
52296
  // src/hq/protocol/governance.ts
@@ -52677,7 +53364,7 @@ var HqPublisher = class {
52677
53364
  this.options = options;
52678
53365
  this.socketFactory = options.socketFactory ?? defaultSocketFactory;
52679
53366
  this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
52680
- this.idFactory = options.idFactory ?? randomUUID29;
53367
+ this.idFactory = options.idFactory ?? randomUUID30;
52681
53368
  this.capabilities = options.capabilities ?? [
52682
53369
  "telemetry.publish",
52683
53370
  "mailbox.summary",
@@ -53261,7 +53948,7 @@ function createHqPublisherFromEnv(options) {
53261
53948
  const projectAlias = config.projectAlias?.trim() || void 0;
53262
53949
  const projectName = projectAlias ?? options.projectName ?? (basename15(options.projectRoot) || "unknown");
53263
53950
  const client = {
53264
- clientId: `${machineId}:${options.clientKind}:${process.pid}:${randomUUID30().slice(0, 8)}`,
53951
+ clientId: `${machineId}:${options.clientKind}:${process.pid}:${randomUUID31().slice(0, 8)}`,
53265
53952
  kind: options.clientKind,
53266
53953
  machineId,
53267
53954
  ...host ? { hostname: host } : {},
@@ -53530,40 +54217,6 @@ async function injectPendingMailboxMessages(checkMailbox2, foldFn, a, deliveryMo
53530
54217
  return interruptMsg ? { interrupt: true, interruptReason: interruptMsg.body || interruptMsg.subject || "operator interrupt" } : { interrupt: false };
53531
54218
  }
53532
54219
 
53533
- // src/core/btw.ts
53534
- var META_KEY2 = "_btwNotes";
53535
- var MAX_PENDING = 20;
53536
- function readQueue(ctx) {
53537
- const raw = ctx.meta[META_KEY2];
53538
- return Array.isArray(raw) ? raw : [];
53539
- }
53540
- function setBtwNote(ctx, text2) {
53541
- const trimmed = text2.trim();
53542
- if (!trimmed) return readQueue(ctx).length;
53543
- const next = [...readQueue(ctx), trimmed].slice(-MAX_PENDING);
53544
- ctx.meta[META_KEY2] = next;
53545
- return next.length;
53546
- }
53547
- function pendingBtwCount(ctx) {
53548
- return readQueue(ctx).length;
53549
- }
53550
- function consumeBtwNotes(ctx) {
53551
- const notes = readQueue(ctx);
53552
- if (notes.length > 0) delete ctx.meta[META_KEY2];
53553
- return notes;
53554
- }
53555
- function buildBtwBlock(notes) {
53556
- const body = notes.map((n) => `- ${n}`).join("\n");
53557
- return [
53558
- "[BY THE WAY \u2014 the user added this while you were working. Fold it into",
53559
- "your current task; do not restart from scratch unless it contradicts the",
53560
- "goal:",
53561
- "",
53562
- body,
53563
- "]"
53564
- ].join("\n");
53565
- }
53566
-
53567
54220
  // src/core/fleet-pulse.ts
53568
54221
  var DEFAULT_MAX_AGENTS = 15;
53569
54222
  var DEFAULT_MAX_CHARS = 900;
@@ -53571,9 +54224,17 @@ var TASK_SNIPPET_CHARS2 = 60;
53571
54224
  function fleetPulseSignature(statuses) {
53572
54225
  return statuses.map((s) => `${s.agentId}|${s.status}|${s.currentTask ?? ""}`).sort().join("\n");
53573
54226
  }
53574
- function peerLine(s) {
54227
+ function visibleLineKey(s) {
54228
+ const role = s.role && s.role !== s.name ? s.role : "";
54229
+ const task = s.currentTask && s.currentTask.length > TASK_SNIPPET_CHARS2 ? `${s.currentTask.slice(0, TASK_SNIPPET_CHARS2)}\u2026` : s.currentTask ?? "";
54230
+ const tool = s.currentTool || "";
54231
+ const toolCalls = s.toolCalls > 0 ? String(s.toolCalls) : "";
54232
+ return [s.name, role, s.status, task, tool, toolCalls].join("\0");
54233
+ }
54234
+ function peerLine(s, count = 1) {
53575
54235
  const role = s.role && s.role !== s.name ? ` (${s.role})` : "";
53576
- const parts = [`\u2022 ${s.name}${role} \u2014 ${s.status}`];
54236
+ const grouped = count > 1 ? ` \xD7${count}` : "";
54237
+ const parts = [`\u2022 ${s.name}${role}${grouped} \u2014 ${s.status}`];
53577
54238
  if (s.currentTask) {
53578
54239
  const task = s.currentTask.length > TASK_SNIPPET_CHARS2 ? `${s.currentTask.slice(0, TASK_SNIPPET_CHARS2)}\u2026` : s.currentTask;
53579
54240
  parts.push(`"${task}"`);
@@ -53589,13 +54250,20 @@ function buildFleetPulseBlock(statuses, opts) {
53589
54250
  if (peers.length === 0) return null;
53590
54251
  const order = { running: 0, streaming: 0, waiting_user: 1, idle: 2, error: 3, offline: 4 };
53591
54252
  const sorted = [...peers].sort(
53592
- (x, y) => (order[x.status] ?? 5) - (order[y.status] ?? 5) || x.name.localeCompare(y.name)
54253
+ (x, y) => (order[x.status] ?? 5) - (order[y.status] ?? 5) || visibleLineKey(x).localeCompare(visibleLineKey(y))
53593
54254
  );
53594
54255
  const shown = sorted.slice(0, maxAgents);
53595
54256
  const hidden = sorted.length - shown.length;
53596
54257
  const parts = [];
53597
54258
  parts.push(`[FLEET PULSE] ${peers.length} peer${peers.length === 1 ? "" : "s"} online:`);
53598
- for (const s of shown) parts.push(peerLine(s));
54259
+ for (let i = 0; i < shown.length; ) {
54260
+ let run = 1;
54261
+ while (i + run < shown.length && visibleLineKey(shown[i]) === visibleLineKey(shown[i + run])) {
54262
+ run++;
54263
+ }
54264
+ parts.push(peerLine(shown[i], run));
54265
+ i += run;
54266
+ }
53599
54267
  if (hidden > 0) parts.push(`\u2026 +${hidden} more`);
53600
54268
  parts.push(
53601
54269
  "[END FLEET PULSE] (FYI \u2014 coordinate via mail_send; avoid duplicating peers' work)"
@@ -54546,7 +55214,7 @@ async function getPromptJournalEntries(projectRoot, filter = {}) {
54546
55214
  init_errors();
54547
55215
 
54548
55216
  // src/core/streaming-response-builder.ts
54549
- import { randomUUID as randomUUID32 } from "node:crypto";
55217
+ import { randomUUID as randomUUID33 } from "node:crypto";
54550
55218
  var STREAM_DRAIN_TIMEOUT_MS = 500;
54551
55219
  function buildResponse(state) {
54552
55220
  const content = [];
@@ -54606,7 +55274,7 @@ function handleContentBlockStart(state, ev) {
54606
55274
  state.textBuffers.push("");
54607
55275
  state.blockOrder.push({ kind: "text", idx: state.currentTextIndex });
54608
55276
  } else if (kind === "tool_use") {
54609
- const id = ev.id ?? randomUUID32();
55277
+ const id = ev.id ?? randomUUID33();
54610
55278
  state.tools.set(id, { name: ev.name ?? "unknown", partial: "" });
54611
55279
  state.blockOrder.push({ kind: "tool", id });
54612
55280
  state.currentTextIndex = -1;
@@ -54841,7 +55509,7 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
54841
55509
 
54842
55510
  // src/observability/network-telemetry.ts
54843
55511
  import { AsyncLocalStorage } from "node:async_hooks";
54844
- import { createHash as createHash25, randomUUID as randomUUID33 } from "node:crypto";
55512
+ import { createHash as createHash25, randomUUID as randomUUID34 } from "node:crypto";
54845
55513
  import { channel } from "node:diagnostics_channel";
54846
55514
  var storage = new AsyncLocalStorage();
54847
55515
  var requests = /* @__PURE__ */ new WeakMap();
@@ -54878,7 +55546,7 @@ function subscribe() {
54878
55546
  const requestBytes = numberField2(request, "contentLength");
54879
55547
  const state = {
54880
55548
  ...context,
54881
- requestId: randomUUID33(),
55549
+ requestId: randomUUID34(),
54882
55550
  ...target,
54883
55551
  ...requestBytes !== void 0 ? { requestBytes } : {},
54884
55552
  startedAt,
@@ -55013,7 +55681,7 @@ function hash3(value) {
55013
55681
  }
55014
55682
 
55015
55683
  // src/core/provider-runner.ts
55016
- import { randomUUID as randomUUID34 } from "node:crypto";
55684
+ import { randomUUID as randomUUID35 } from "node:crypto";
55017
55685
  function scrubProviderBody(body) {
55018
55686
  if (!body) return void 0;
55019
55687
  return {
@@ -55035,11 +55703,11 @@ function providerLogCtx(p, r) {
55035
55703
  }
55036
55704
  async function runProviderWithRetry(opts) {
55037
55705
  const { provider, request, signal, ctx, events, retry, logger, tracer } = opts;
55038
- const logicalRequestId = randomUUID34();
55706
+ const logicalRequestId = randomUUID35();
55039
55707
  const promptManifest = createChroniclePromptManifest(request);
55040
55708
  let attempt = 0;
55041
55709
  for (; ; ) {
55042
- const attemptId = randomUUID34();
55710
+ const attemptId = randomUUID35();
55043
55711
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
55044
55712
  const startedNs = process.hrtime.bigint();
55045
55713
  const correlation = {
@@ -59708,7 +60376,7 @@ function _resetDesignRulesCache() {
59708
60376
  }
59709
60377
 
59710
60378
  // src/execution/design-color.ts
59711
- function clamp(n, lo, hi) {
60379
+ function clamp2(n, lo, hi) {
59712
60380
  return n < lo ? lo : n > hi ? hi : n;
59713
60381
  }
59714
60382
  function parseOklch(value) {
@@ -59724,9 +60392,9 @@ function parseOklch(value) {
59724
60392
  let a = 1;
59725
60393
  if (alphaPart !== void 0) {
59726
60394
  const av = parseComponent(alphaPart.trim(), true);
59727
- if (av !== null) a = clamp(av, 0, 1);
60395
+ if (av !== null) a = clamp2(av, 0, 1);
59728
60396
  }
59729
- return [clamp(L, 0, 1), Math.max(0, C), H, a];
60397
+ return [clamp2(L, 0, 1), Math.max(0, C), H, a];
59730
60398
  }
59731
60399
  function parseComponent(s, percentIsFraction) {
59732
60400
  s = s.trim();
@@ -59745,7 +60413,7 @@ function parseAngle(s) {
59745
60413
  }
59746
60414
  function linearToSrgb(c) {
59747
60415
  const v = c <= 31308e-7 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - 0.055;
59748
- return clamp(v, 0, 1);
60416
+ return clamp2(v, 0, 1);
59749
60417
  }
59750
60418
  function toHex2(n) {
59751
60419
  return Math.round(n * 255).toString(16).padStart(2, "0");
@@ -62492,7 +63160,7 @@ ${summaryText}` : summaryText;
62492
63160
 
62493
63161
  // src/execution/parallel-eternal-engine.ts
62494
63162
  init_error();
62495
- import { randomUUID as randomUUID35 } from "node:crypto";
63163
+ import { randomUUID as randomUUID36 } from "node:crypto";
62496
63164
  var GOAL_COMPLETE_MARKER2 = /^\s*\[goal[_\s-]?complete\]\s*$/im;
62497
63165
  var ParallelEternalEngine = class {
62498
63166
  constructor(opts) {
@@ -62569,7 +63237,7 @@ var ParallelEternalEngine = class {
62569
63237
  this.state = "running";
62570
63238
  await this.persistState("running");
62571
63239
  const config = {
62572
- coordinatorId: `parallel-${randomUUID35().slice(0, 8)}`,
63240
+ coordinatorId: `parallel-${randomUUID36().slice(0, 8)}`,
62573
63241
  maxConcurrent: this.slots,
62574
63242
  doneCondition: { type: "all_tasks_done" }
62575
63243
  };
@@ -62623,7 +63291,7 @@ var ParallelEternalEngine = class {
62623
63291
  }
62624
63292
  if (!this.coordinator) {
62625
63293
  const config = {
62626
- coordinatorId: `parallel-${randomUUID35().slice(0, 8)}`,
63294
+ coordinatorId: `parallel-${randomUUID36().slice(0, 8)}`,
62627
63295
  maxConcurrent: this.slots,
62628
63296
  doneCondition: { type: "all_tasks_done" }
62629
63297
  };
@@ -62709,7 +63377,7 @@ ${recentJournal}` : "No prior iterations.",
62709
63377
  const task = expectDefined(tasks[i]);
62710
63378
  const route = routes[i] ?? null;
62711
63379
  const subagentId = `parallel-${this.iterations}-${i}`;
62712
- const taskId = randomUUID35();
63380
+ const taskId = randomUUID36();
62713
63381
  const personaLine = route ? `Acting agent: ${route.definition.config.name} \u2014 ${route.definition.capability.summary}
62714
63382
  ` : "";
62715
63383
  const spec = {
@@ -64337,7 +65005,7 @@ function readPolicy(ctx) {
64337
65005
  }
64338
65006
 
64339
65007
  // src/execution/tool-executor.ts
64340
- import { randomUUID as randomUUID38 } from "node:crypto";
65008
+ import { randomUUID as randomUUID39 } from "node:crypto";
64341
65009
  import * as fs38 from "node:fs/promises";
64342
65010
  import * as path85 from "node:path";
64343
65011
  init_errors();
@@ -64359,7 +65027,7 @@ var ToolErrorCategory = /* @__PURE__ */ ((ToolErrorCategory2) => {
64359
65027
  })(ToolErrorCategory || {});
64360
65028
 
64361
65029
  // src/execution/tool-executor-support.ts
64362
- import { createHash as createHash29, randomUUID as randomUUID36 } from "node:crypto";
65030
+ import { createHash as createHash29, randomUUID as randomUUID37 } from "node:crypto";
64363
65031
  import * as fs37 from "node:fs/promises";
64364
65032
  import * as path83 from "node:path";
64365
65033
  init_errors();
@@ -64491,7 +65159,7 @@ async function maybePersistLargeToolOutput(toolName, content, budget) {
64491
65159
  await fs37.mkdir(dir, { recursive: true });
64492
65160
  const safeTool = toolName.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 40) || "tool";
64493
65161
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
64494
- const filePath = path83.join(dir, `${stamp}-${safeTool}-${randomUUID36()}.log`);
65162
+ const filePath = path83.join(dir, `${stamp}-${safeTool}-${randomUUID37()}.log`);
64495
65163
  await fs37.writeFile(filePath, content, "utf8");
64496
65164
  const marker = `[full tool output: ${bytes} bytes at ${filePath}; read/grep that file selectively instead of re-running or requesting more output]`;
64497
65165
  const fixedBytes = Buffer.byteLength(marker + TOOL_OUTPUT_ARTIFACT_OMISSION, "utf8");
@@ -65041,7 +65709,7 @@ ${errorDetails}`,
65041
65709
  }
65042
65710
 
65043
65711
  // src/execution/tool-executor-runner.ts
65044
- import { randomUUID as randomUUID37 } from "node:crypto";
65712
+ import { randomUUID as randomUUID38 } from "node:crypto";
65045
65713
 
65046
65714
  // src/observability/process-telemetry.ts
65047
65715
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
@@ -65265,7 +65933,7 @@ async function runToolWithTimeout(tool, input, parentSignal, ctx, opts, config,
65265
65933
  progressTailChars: config.progressTailChars,
65266
65934
  progressHeadChars: config.progressHeadChars
65267
65935
  }) : (async () => tool.execute(input, ctx, { signal: combined }))();
65268
- const telemetryToolCallId = toolUseId ?? `nested-${randomUUID37()}`;
65936
+ const telemetryToolCallId = toolUseId ?? `nested-${randomUUID38()}`;
65269
65937
  const toolPromise = opts.events ? runWithNetworkTelemetry(
65270
65938
  {
65271
65939
  events: opts.events,
@@ -65658,7 +66326,7 @@ ${post.additionalContext}`;
65658
66326
  const bridge = async (toolName, input) => {
65659
66327
  const nestedUse = {
65660
66328
  type: "tool_use",
65661
- id: `nested-${randomUUID38()}`,
66329
+ id: `nested-${randomUUID39()}`,
65662
66330
  name: toolName,
65663
66331
  input
65664
66332
  };
@@ -66263,13 +66931,13 @@ import * as fs39 from "node:fs/promises";
66263
66931
  import * as path87 from "node:path";
66264
66932
 
66265
66933
  // src/types/mode-prompts.ts
66266
- import { readFileSync as readFileSync23, statSync as statSync9 } from "node:fs";
66934
+ import { readFileSync as readFileSync24, statSync as statSync9 } from "node:fs";
66267
66935
  import * as path86 from "node:path";
66268
66936
  import { fileURLToPath as fileURLToPath9 } from "node:url";
66269
66937
  function modePrompt(id) {
66270
66938
  for (const dir of modePromptDirCandidates()) {
66271
66939
  try {
66272
- return readFileSync23(path86.join(dir, `${id}.md`), "utf8").trimEnd();
66940
+ return readFileSync24(path86.join(dir, `${id}.md`), "utf8").trimEnd();
66273
66941
  } catch {
66274
66942
  }
66275
66943
  }
@@ -66950,7 +67618,17 @@ function normalizeModelsDevModel(model) {
66950
67618
  const reasoningConfig = {
66951
67619
  default: disableSupported ? "enabled" : "always_on",
66952
67620
  disableSupported,
66953
- effortSupported: effortLevels.length > 0,
67621
+ // Tri-state (see ReasoningConfig.effortSupported):
67622
+ // options present → documented answer (true when effort values exist;
67623
+ // an explicitly EMPTY array is a documented "no
67624
+ // effort control", not an absent field).
67625
+ // field ABSENT → the model is known to reason but its vocabulary is
67626
+ // undocumented → `undefined`, so the resolver forwards
67627
+ // the request and each wire adapter applies its own
67628
+ // transport gating. Sending `false` here would make
67629
+ // the resolver claim "does not support effort" — an
67630
+ // assertion the catalog never made.
67631
+ ...raw === void 0 ? {} : { effortSupported: effortLevels.length > 0 },
66954
67632
  effortLevels,
66955
67633
  preserveThinking: model.interleaved ? "always_on" : "unsupported"
66956
67634
  };
@@ -67539,9 +68217,9 @@ async function startMetricsServer(opts) {
67539
68217
  let server;
67540
68218
  if (useHttps && tls) {
67541
68219
  const { createServer } = await import("node:https");
67542
- const { readFileSync: readFileSync25 } = await import("node:fs");
68220
+ const { readFileSync: readFileSync26 } = await import("node:fs");
67543
68221
  server = createServer(
67544
- { cert: readFileSync25(tls.cert), key: readFileSync25(tls.key) },
68222
+ { cert: readFileSync26(tls.cert), key: readFileSync26(tls.key) },
67545
68223
  listener
67546
68224
  );
67547
68225
  } else {
@@ -69507,7 +70185,7 @@ function deepFreeze(obj) {
69507
70185
  init_errors();
69508
70186
  init_atomic_write();
69509
70187
  init_error();
69510
- import { randomUUID as randomUUID39 } from "node:crypto";
70188
+ import { randomUUID as randomUUID40 } from "node:crypto";
69511
70189
  import * as fsp38 from "node:fs/promises";
69512
70190
  function assertPlanMutationInvariants(previous, updated) {
69513
70191
  const ids = updated.items.map((item) => item.id);
@@ -69629,7 +70307,7 @@ function emptyPlan(sessionId, title) {
69629
70307
  function addPlanItem(plan, title, details) {
69630
70308
  const now = (/* @__PURE__ */ new Date()).toISOString();
69631
70309
  const item = {
69632
- id: `plan_${Date.now()}_${randomUUID39().slice(0, 6)}`,
70310
+ id: `plan_${Date.now()}_${randomUUID40().slice(0, 6)}`,
69633
70311
  title,
69634
70312
  details,
69635
70313
  status: "open",
@@ -69701,7 +70379,7 @@ function deriveTodosFromPlanItem(plan, idOrIndex, subtasks) {
69701
70379
  if (subtasks && subtasks.length > 0) {
69702
70380
  for (const st of subtasks) {
69703
70381
  todos.push({
69704
- id: `todo_${Date.now()}_${randomUUID39().slice(0, 6)}`,
70382
+ id: `todo_${Date.now()}_${randomUUID40().slice(0, 6)}`,
69705
70383
  content: st,
69706
70384
  status: "pending",
69707
70385
  promotedFromPlan: item.id
@@ -73698,7 +74376,7 @@ function resolveReasoningForRequest(settings, rc, warnings) {
73698
74376
  const cfg = settings.reasoning;
73699
74377
  if (!cfg) return void 0;
73700
74378
  const capKnown = rc !== void 0;
73701
- const supportsReasoning = rc ? rc.default !== "disabled" || rc.disableSupported || rc.effortSupported : false;
74379
+ const supportsReasoning = rc ? rc.default !== "disabled" || rc.disableSupported || rc.effortSupported !== false : false;
73702
74380
  const out = {};
73703
74381
  if (cfg.mode === "off") {
73704
74382
  if (capKnown && rc?.disableSupported) {
@@ -73720,14 +74398,17 @@ function resolveReasoningForRequest(settings, rc, warnings) {
73720
74398
  }
73721
74399
  const effort = cfg.effort;
73722
74400
  if (effort !== void 0) {
73723
- if (capKnown && rc?.effortSupported && rc.effortLevels.includes(effort)) {
73724
- out.effort = effort;
73725
- } else if (capKnown && rc?.effortSupported) {
74401
+ if (!capKnown) {
74402
+ } else if (rc?.effortSupported === false) {
74403
+ warnings.push(
74404
+ `reasoning effort "${effort}" requested, but this model does not support effort control; the setting was omitted.`
74405
+ );
74406
+ } else if (rc?.effortSupported === true && rc.effortLevels.length > 0 && !rc.effortLevels.includes(effort)) {
73726
74407
  warnings.push(
73727
74408
  `reasoning effort "${effort}" not supported by this model (supported: ${rc.effortLevels.join(", ")}); the setting was omitted.`
73728
74409
  );
73729
- } else if (capKnown) {
73730
- warnings.push(`reasoning effort "${effort}" requested, but this model does not support effort; the setting was omitted.`);
74410
+ } else {
74411
+ out.effort = effort;
73731
74412
  }
73732
74413
  }
73733
74414
  if (cfg.preserve !== void 0) {
@@ -82152,7 +82833,7 @@ async function snapshotChangedFiles(cwd) {
82152
82833
  }
82153
82834
 
82154
82835
  // src/plugins/review-claim-registry.ts
82155
- import { createHash as createHash33, randomUUID as randomUUID40 } from "node:crypto";
82836
+ import { createHash as createHash33, randomUUID as randomUUID41 } from "node:crypto";
82156
82837
  import * as fsp46 from "node:fs/promises";
82157
82838
  import { hostname as hostname5 } from "node:os";
82158
82839
  import * as path103 from "node:path";
@@ -82215,7 +82896,7 @@ async function breakStaleLock(lockPath) {
82215
82896
  return false;
82216
82897
  }
82217
82898
  async function breakLockAtomically(lockPath) {
82218
- const tombstone = `${lockPath}.stale-${randomUUID40()}.tmp`;
82899
+ const tombstone = `${lockPath}.stale-${randomUUID41()}.tmp`;
82219
82900
  try {
82220
82901
  await fsp46.rename(lockPath, tombstone);
82221
82902
  } catch {
@@ -82275,7 +82956,7 @@ async function withStoreLock(storeDir, fn, waitMs = LOCK_WAIT_MS) {
82275
82956
  }
82276
82957
  }
82277
82958
  var MAX_LEDGER_LINES = 1e4;
82278
- var HOST_SID = randomUUID40();
82959
+ var HOST_SID = randomUUID41();
82279
82960
  var claimsByEventBus = /* @__PURE__ */ new WeakMap();
82280
82961
  var startedReviews = /* @__PURE__ */ new WeakMap();
82281
82962
  var pendingStartedReviews = /* @__PURE__ */ new WeakMap();
@@ -82356,7 +83037,7 @@ async function compactLedger(storeDir, active) {
82356
83037
  );
82357
83038
  }
82358
83039
  }
82359
- const tmp = `${claimsFilePath(storeDir)}.tmp-${randomUUID40()}`;
83040
+ const tmp = `${claimsFilePath(storeDir)}.tmp-${randomUUID41()}`;
82360
83041
  await fsp46.writeFile(tmp, lines.length > 0 ? `${lines.join("\n")}
82361
83042
  ` : "", "utf8");
82362
83043
  let replaced = false;
@@ -83285,7 +83966,7 @@ init_review_finding_store();
83285
83966
 
83286
83967
  // src/plugins/review-finding-parser.ts
83287
83968
  init_review_finding_types();
83288
- import { randomUUID as randomUUID42 } from "node:crypto";
83969
+ import { randomUUID as randomUUID43 } from "node:crypto";
83289
83970
  var SEVERITIES = /* @__PURE__ */ new Set(["critical", "high", "medium", "low"]);
83290
83971
  var CATEGORIES = /* @__PURE__ */ new Set([
83291
83972
  "bug",
@@ -83357,7 +84038,7 @@ function parseChimeraReviewReport(reportText, context = {}) {
83357
84038
  }
83358
84039
  const structured = extractStructuredFindingsBlock(reportText);
83359
84040
  if (structured) {
83360
- const reportId2 = context.reportId ?? randomUUID42();
84041
+ const reportId2 = context.reportId ?? randomUUID43();
83361
84042
  const findings2 = structured.findings.map(
83362
84043
  (item) => buildFindingFromStructuredItem(item, { ...context, reportId: reportId2 })
83363
84044
  );
@@ -83369,7 +84050,7 @@ function parseChimeraReviewReport(reportText, context = {}) {
83369
84050
  };
83370
84051
  }
83371
84052
  const findings = [];
83372
- const reportId = context.reportId ?? randomUUID42();
84053
+ const reportId = context.reportId ?? randomUUID43();
83373
84054
  let unparseableCount = 0;
83374
84055
  let durationSeconds;
83375
84056
  let currentSeverity = null;
@@ -83470,7 +84151,7 @@ function parseFindingSegment(segment, severity, context) {
83470
84151
  const fullDesc = suggestions.length > 0 ? cleanDesc + "\n" + suggestions.map((s) => " \u2192 " + s).join("\n") : cleanDesc;
83471
84152
  const suggestedFix = suggestions.length > 0 ? suggestions.join("\n") : void 0;
83472
84153
  return {
83473
- id: randomUUID42(),
84154
+ id: randomUUID43(),
83474
84155
  fingerprint: computeFindingFingerprint(file ?? "", line ?? null, title),
83475
84156
  severity,
83476
84157
  source: normalizeFindingSource(context.reviewType),
@@ -83481,7 +84162,7 @@ function parseFindingSegment(segment, severity, context) {
83481
84162
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
83482
84163
  status: "active",
83483
84164
  originReport: {
83484
- reportId: context.reportId ?? randomUUID42(),
84165
+ reportId: context.reportId ?? randomUUID43(),
83485
84166
  sessionId: context.sessionId ?? "",
83486
84167
  agentId: context.agentId ?? "",
83487
84168
  reviewerModel: context.reviewerModel ?? ""
@@ -83505,7 +84186,7 @@ function buildFindingFromStructuredItem(item, context) {
83505
84186
  const title = item.title;
83506
84187
  const description = item.description ?? title;
83507
84188
  return {
83508
- id: randomUUID42(),
84189
+ id: randomUUID43(),
83509
84190
  fingerprint: computeFindingFingerprint(file ?? "", line ?? null, title),
83510
84191
  severity: item.severity,
83511
84192
  source: normalizeFindingSource(context.reviewType),
@@ -83518,7 +84199,7 @@ function buildFindingFromStructuredItem(item, context) {
83518
84199
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
83519
84200
  status: "active",
83520
84201
  originReport: {
83521
- reportId: context.reportId ?? randomUUID42(),
84202
+ reportId: context.reportId ?? randomUUID43(),
83522
84203
  sessionId: context.sessionId ?? "",
83523
84204
  agentId: context.agentId ?? "",
83524
84205
  reviewerModel: context.reviewerModel ?? ""
@@ -89169,7 +89850,7 @@ var ReplayProviderRunner = class {
89169
89850
  };
89170
89851
 
89171
89852
  // src/session-catalog/store.ts
89172
- import { randomBytes as randomBytes8, randomUUID as randomUUID43 } from "node:crypto";
89853
+ import { randomBytes as randomBytes8, randomUUID as randomUUID44 } from "node:crypto";
89173
89854
  import * as fs59 from "node:fs";
89174
89855
  import * as path119 from "node:path";
89175
89856
  init_atomic_write();
@@ -89520,7 +90201,7 @@ var SessionCatalogStore = class {
89520
90201
  if (!Number.isSafeInteger(entry.pid) || entry.pid <= 0)
89521
90202
  throw new TypeError("Invalid owner pid");
89522
90203
  const now = Date.now();
89523
- const leaseId = randomUUID43();
90204
+ const leaseId = randomUUID44();
89524
90205
  const leaseSecret = randomBytes8(32).toString("hex");
89525
90206
  const expiresAt = now + boundedMs(leaseMs, SESSION_CATALOG_DEFAULT_LEASE_MS, MAX_LEASE_MS);
89526
90207
  this.db.prepare(`INSERT INTO session_leases(
@@ -89592,7 +90273,7 @@ var SessionCatalogStore = class {
89592
90273
  const catalog = this.db.prepare("SELECT 1 AS yes FROM sessions WHERE session_id=?").get(targetSessionId);
89593
90274
  if (!catalog && !fs59.existsSync(this.containedPath(`${targetSessionId}.jsonl`)))
89594
90275
  throw new Error(`Session not found: ${targetSessionId}`);
89595
- const reservationId = randomUUID43();
90276
+ const reservationId = randomUUID44();
89596
90277
  const now = Date.now();
89597
90278
  const expiresAt = now + boundedMs(reservationMs, SESSION_CATALOG_DEFAULT_RESERVATION_MS, MAX_RESERVATION_MS);
89598
90279
  try {
@@ -89888,7 +90569,7 @@ var SessionCatalogStore = class {
89888
90569
  "SELECT 1 AS yes FROM resume_reservations WHERE target_session_id=? AND expires_at>?"
89889
90570
  ).get(sessionId, Date.now());
89890
90571
  if (reservation) throw conflict(`Session ${sessionId} is reserved for resume`);
89891
- const leaseId = randomUUID43();
90572
+ const leaseId = randomUUID44();
89892
90573
  const now = Date.now();
89893
90574
  const expiresAt = now + boundedMs(leaseMs, 6e4, MAX_MAINTENANCE_MS);
89894
90575
  try {
@@ -90846,7 +91527,7 @@ import * as fs62 from "node:fs/promises";
90846
91527
  import * as path122 from "node:path";
90847
91528
 
90848
91529
  // src/session-registry-atomic-file.ts
90849
- import { randomUUID as randomUUID44 } from "node:crypto";
91530
+ import { randomUUID as randomUUID45 } from "node:crypto";
90850
91531
  import * as fs61 from "node:fs/promises";
90851
91532
  import { hostname as hostname6 } from "node:os";
90852
91533
  import * as path121 from "node:path";
@@ -90919,7 +91600,7 @@ async function breakStaleLockVerified2(lockPath, verify) {
90919
91600
  return await breakLockAtomically2(lockPath) === true;
90920
91601
  }
90921
91602
  async function breakLockAtomically2(lockPath) {
90922
- const tombstone = `${lockPath}.stale-${randomUUID44()}.tmp`;
91603
+ const tombstone = `${lockPath}.stale-${randomUUID45()}.tmp`;
90923
91604
  try {
90924
91605
  await fs61.rename(lockPath, tombstone);
90925
91606
  } catch {
@@ -90931,7 +91612,7 @@ async function breakLockAtomically2(lockPath) {
90931
91612
  async function writeAtomicFile(filePath, registry2) {
90932
91613
  const tmp = path121.join(
90933
91614
  path121.dirname(filePath),
90934
- `.${path121.basename(filePath)}.${randomUUID44().slice(0, 8)}.tmp`
91615
+ `.${path121.basename(filePath)}.${randomUUID45().slice(0, 8)}.tmp`
90935
91616
  );
90936
91617
  let tmpPersisted = false;
90937
91618
  try {
@@ -91494,7 +92175,7 @@ var SessionRegistry = class {
91494
92175
  init_errors();
91495
92176
  init_atomic_write();
91496
92177
  init_error();
91497
- import { randomUUID as randomUUID45 } from "node:crypto";
92178
+ import { randomUUID as randomUUID46 } from "node:crypto";
91498
92179
  import * as fs63 from "node:fs/promises";
91499
92180
  var FILE_VERSION = 1;
91500
92181
  var MAX_TEXT_LENGTH = 2e3;
@@ -91592,7 +92273,7 @@ var AnnotationsStore = class {
91592
92273
  });
91593
92274
  }
91594
92275
  const annotation = {
91595
- id: randomUUID45(),
92276
+ id: randomUUID46(),
91596
92277
  sessionId: input.sessionId,
91597
92278
  atEventIndex: input.atEventIndex,
91598
92279
  authorId: input.authorId,
@@ -91945,7 +92626,7 @@ var InputHistoryStore = class {
91945
92626
 
91946
92627
  // src/storage/memory-backend.ts
91947
92628
  init_file_permissions();
91948
- import { randomUUID as randomUUID46 } from "node:crypto";
92629
+ import { randomUUID as randomUUID47 } from "node:crypto";
91949
92630
  import * as fs65 from "node:fs/promises";
91950
92631
  import * as path124 from "node:path";
91951
92632
 
@@ -92173,7 +92854,7 @@ var FileMemoryBackend = class {
92173
92854
  }
92174
92855
  async remember(scope, entry, filePath) {
92175
92856
  const file = this.resolveFile(filePath, scope);
92176
- const id = `mem_${Date.now()}_${randomUUID46().slice(0, 8)}`;
92857
+ const id = `mem_${Date.now()}_${randomUUID47().slice(0, 8)}`;
92177
92858
  const meta = formatMetadata(entry);
92178
92859
  const line = `- [${entry.ts}] ${id}${meta} ${entry.text.replace(/\n/g, " ")}
92179
92860
  `;
@@ -94187,7 +94868,7 @@ async function mutateTasks(filePath, sessionId, fn, events, traceId) {
94187
94868
  init_file_permissions();
94188
94869
  init_atomic_write();
94189
94870
  init_error();
94190
- import { createHash as createHash39, randomUUID as randomUUID47 } from "node:crypto";
94871
+ import { createHash as createHash39, randomUUID as randomUUID48 } from "node:crypto";
94191
94872
  import * as fs70 from "node:fs/promises";
94192
94873
  var GENESIS_PREV = "0".repeat(64);
94193
94874
  var DEFAULT_FSYNC_EVERY = 100;
@@ -94232,7 +94913,7 @@ var ToolAuditLog = class {
94232
94913
  const tip = await this._resolveChainTip(input.sessionId, fp);
94233
94914
  const prevHash = tip.prevHash;
94234
94915
  const index = tip.nextIndex;
94235
- const id = randomUUID47();
94916
+ const id = randomUUID48();
94236
94917
  const ts = (/* @__PURE__ */ new Date()).toISOString();
94237
94918
  const content = {
94238
94919
  id,
@@ -96773,7 +97454,7 @@ var DEFAULT_SPEC_TEMPLATE = {
96773
97454
  // src/worktree/worktree-manager.ts
96774
97455
  init_error();
96775
97456
  import { mkdir as mkdir35, readFile as readFile73 } from "node:fs/promises";
96776
- import { join as join102, resolve as resolve56, sep as sep9 } from "node:path";
97457
+ import { join as join103, resolve as resolve56, sep as sep9 } from "node:path";
96777
97458
 
96778
97459
  // src/worktree/worktree-git.ts
96779
97460
  import { spawn as spawn13 } from "node:child_process";
@@ -97005,7 +97686,7 @@ var WorktreeManager = class {
97005
97686
  }
97006
97687
  const slug = this.makeSlug(opts.slugHint ?? ownerId);
97007
97688
  const branch = `wstack/ap/${slug}`;
97008
- const dir = join102(this.worktreesRoot(), slug);
97689
+ const dir = join103(this.worktreesRoot(), slug);
97009
97690
  const absDir = resolve56(dir);
97010
97691
  const absRoot = resolve56(this.projectRoot);
97011
97692
  if (!absDir.startsWith(absRoot + sep9)) {
@@ -97396,7 +98077,7 @@ ${merged.stderr}`);
97396
98077
  const startMarker = /^(?:<{7,}(?: |$)|\|{7,}(?: |$))/m;
97397
98078
  for (const rel of paths) {
97398
98079
  try {
97399
- const content = (await readFile73(join102(this.projectRoot, rel), "utf8")).replace(/\r/g, "");
98080
+ const content = (await readFile73(join103(this.projectRoot, rel), "utf8")).replace(/\r/g, "");
97400
98081
  const lines = content.split("\n");
97401
98082
  let seenStart = false;
97402
98083
  for (const line of lines) {
@@ -97440,7 +98121,7 @@ ${merged.stderr}`);
97440
98121
  }
97441
98122
  // ── internals ────────────────────────────────────────────────────────────
97442
98123
  worktreesRoot() {
97443
- return join102(this.projectRoot, ".wrongstack", "worktrees");
98124
+ return join103(this.projectRoot, ".wrongstack", "worktrees");
97444
98125
  }
97445
98126
  async detectBaseBranch() {
97446
98127
  const head = await this.runGit(["rev-parse", "--abbrev-ref", "HEAD"], this.projectRoot);
@@ -97858,6 +98539,7 @@ export {
97858
98539
  QUEUE_MAX_ITEMS,
97859
98540
  QUEUE_MAX_ITEM_BYTES,
97860
98541
  QueueStore,
98542
+ REASONING_EFFORT_LEVELS,
97861
98543
  REFACTOR_PLANNER_AGENT,
97862
98544
  REPORT_STORE_FILE,
97863
98545
  REVIEW_AGENTS,
@@ -98321,6 +99003,7 @@ export {
98321
99003
  isPrivateIPv6,
98322
99004
  isProjectId,
98323
99005
  isProvenDirective,
99006
+ isReasoningEffort,
98324
99007
  isRetryableKind,
98325
99008
  isSafePathSegment,
98326
99009
  isSddError,
@@ -98395,6 +99078,7 @@ export {
98395
99078
  makeMailInboxTool,
98396
99079
  makeMailSendTool,
98397
99080
  makeMailboxTool,
99081
+ makeMutationTestTool,
98398
99082
  makeQualityGateTool,
98399
99083
  makeRollUpTool,
98400
99084
  makeSpawnTool,