@wrongstack/core 0.282.0 → 0.282.2

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.
Files changed (84) hide show
  1. package/README.md +1 -1
  2. package/dist/{agent-subagent-runner-Dksf0QVU.d.ts → agent-subagent-runner-DCczSoQj.d.ts} +6 -6
  3. package/dist/{config-BCAEJicb.d.ts → config-DkCbA3Bw.d.ts} +22 -0
  4. package/dist/coordination/index.d.ts +15 -15
  5. package/dist/coordination/index.js +802 -139
  6. package/dist/coordination/index.js.map +1 -1
  7. package/dist/{default-config-azFprTB3.d.ts → default-config-Buho8yge.d.ts} +1 -0
  8. package/dist/defaults/index.d.ts +18 -18
  9. package/dist/defaults/index.js +811 -119
  10. package/dist/defaults/index.js.map +1 -1
  11. package/dist/{events-CovDylaH.d.ts → events-BOv8h6I1.d.ts} +1 -1
  12. package/dist/execution/index.d.ts +9 -9
  13. package/dist/execution/index.js +8 -1
  14. package/dist/execution/index.js.map +1 -1
  15. package/dist/extension/index.d.ts +4 -4
  16. package/dist/{global-mailbox-CNfgvRw3.d.ts → global-mailbox-MDDFhLYh.d.ts} +1 -1
  17. package/dist/{goal-store-CBd749M4.d.ts → goal-store-BEmDmSKF.d.ts} +1 -1
  18. package/dist/hq/index.d.ts +4 -4
  19. package/dist/{index-D5HB7WkV.d.ts → index-Dd-PJJ8A.d.ts} +3 -3
  20. package/dist/index.d.ts +209 -30
  21. package/dist/index.js +29562 -28136
  22. package/dist/index.js.map +1 -1
  23. package/dist/infrastructure/index.d.ts +3 -3
  24. package/dist/infrastructure/index.js +8 -0
  25. package/dist/infrastructure/index.js.map +1 -1
  26. package/dist/kernel/index.d.ts +7 -7
  27. package/dist/kernel/index.js.map +1 -1
  28. package/dist/{mcp-servers-DJYwZXyH.d.ts → mcp-servers-BUx9A_AQ.d.ts} +1 -1
  29. package/dist/models/index.d.ts +4 -4
  30. package/dist/models/index.js +18 -20
  31. package/dist/models/index.js.map +1 -1
  32. package/dist/{multi-agent-coordinator-4DC31QWu.d.ts → multi-agent-coordinator-BRqtpn-a.d.ts} +1 -1
  33. package/dist/{null-fleet-bus-DuvA8PIR.d.ts → null-fleet-bus-BkptvIVo.d.ts} +7 -6
  34. package/dist/observability/index.d.ts +1 -1
  35. package/dist/{parallel-eternal-engine-BwcA0bAt.d.ts → parallel-eternal-engine-BXECuOVG.d.ts} +4 -4
  36. package/dist/{pipeline-DM-CPTUs.d.ts → pipeline-ixSFe5jg.d.ts} +2 -2
  37. package/dist/{provider-model-resolve-hNUbewMd.d.ts → provider-model-resolve-DIxKL_T-.d.ts} +1 -1
  38. package/dist/{provider-runner-Bz4GyUqo.d.ts → provider-runner-CJtCs1Rw.d.ts} +1 -1
  39. package/dist/sdd/index.d.ts +7 -7
  40. package/dist/sdd/index.js.map +1 -1
  41. package/dist/security/index.js +6 -0
  42. package/dist/security/index.js.map +1 -1
  43. package/dist/storage/index.d.ts +9 -9
  44. package/dist/storage/index.js +10 -1
  45. package/dist/storage/index.js.map +1 -1
  46. package/dist/{task-format-vGOIftmK.d.ts → task-format-GX0B1mMC.d.ts} +1 -1
  47. package/dist/{task-graph-u1q9Jkyk.d.ts → task-graph-CH3acNQ7.d.ts} +10 -1
  48. package/dist/{todos-checkpoint-addCdQ9f.d.ts → todos-checkpoint-Bw83WMh9.d.ts} +2 -2
  49. package/dist/{tool-executor-B3spV_Q6.d.ts → tool-executor-4mWN2vHW.d.ts} +7 -7
  50. package/dist/tools/index.d.ts +1 -1
  51. package/dist/tools/index.js +8 -0
  52. package/dist/tools/index.js.map +1 -1
  53. package/dist/types/index.d.ts +12 -12
  54. package/dist/types/index.js +22 -1
  55. package/dist/types/index.js.map +1 -1
  56. package/dist/utils/index.d.ts +5 -5
  57. package/dist/utils/index.js.map +1 -1
  58. package/dist/{worktree-manager-BBXdJq-Y.d.ts → worktree-manager-BrtjUFYk.d.ts} +1 -1
  59. package/instructions/leader-after-task.md +4 -4
  60. package/instructions/system.md +11 -0
  61. package/package.json +1 -1
  62. package/skills/api-design/SKILL.md +1 -1
  63. package/skills/audit-log/SKILL.md +3 -3
  64. package/skills/bug-hunter/SKILL.md +3 -3
  65. package/skills/chimera/SKILL.md +3 -3
  66. package/skills/docker-deploy/SKILL.md +1 -1
  67. package/skills/git-flow/SKILL.md +3 -3
  68. package/skills/mailbox-bridge/SKILL.md +1 -1
  69. package/skills/multi-agent/SKILL.md +3 -3
  70. package/skills/node-modern/SKILL.md +1 -1
  71. package/skills/observability/SKILL.md +1 -1
  72. package/skills/output-standards/SKILL.md +31 -30
  73. package/skills/output-standards/SKILL.save.md +5 -5
  74. package/skills/plugin-author/SKILL.md +1 -1
  75. package/skills/prompt-engineering/SKILL.md +1 -1
  76. package/skills/react-modern/SKILL.md +1 -1
  77. package/skills/refactor-planner/SKILL.md +3 -3
  78. package/skills/research-web/SKILL.md +1 -1
  79. package/skills/sdd/SKILL.md +1 -1
  80. package/skills/security-scanner/SKILL.md +3 -3
  81. package/skills/skill-creator/SKILL.md +1 -1
  82. package/skills/tech-stack/SKILL.md +3 -3
  83. package/skills/testing/SKILL.md +1 -1
  84. package/skills/typescript-strict/SKILL.md +1 -1
@@ -6,7 +6,7 @@ import * as path4 from 'path';
6
6
  import { isAbsolute, join, resolve, sep } from 'path';
7
7
  import * as os from 'os';
8
8
  import { hostname } from 'os';
9
- import * as fs12 from 'fs';
9
+ import * as fs13 from 'fs';
10
10
  import { readFileSync, statSync, createReadStream, existsSync } from 'fs';
11
11
  import { fileURLToPath } from 'url';
12
12
  import { EventEmitter } from 'events';
@@ -70,8 +70,8 @@ async function atomicWrite(targetPath, content, opts = {}) {
70
70
  }
71
71
  let mode;
72
72
  try {
73
- const stat11 = await fsp7.stat(targetPath);
74
- mode = stat11.mode & 511;
73
+ const stat12 = await fsp7.stat(targetPath);
74
+ mode = stat12.mode & 511;
75
75
  } catch {
76
76
  mode = opts.mode;
77
77
  }
@@ -117,8 +117,8 @@ async function withFileLock(targetPath, fn, opts = {}) {
117
117
  }
118
118
  if (code !== "EEXIST") throw err;
119
119
  try {
120
- const stat11 = await fsp7.stat(lockPath);
121
- if (Date.now() - stat11.mtimeMs > staleMs) {
120
+ const stat12 = await fsp7.stat(lockPath);
121
+ if (Date.now() - stat12.mtimeMs > staleMs) {
122
122
  await fsp7.unlink(lockPath);
123
123
  continue;
124
124
  }
@@ -133,7 +133,7 @@ async function withFileLock(targetPath, fn, opts = {}) {
133
133
  context: { timeoutMs }
134
134
  });
135
135
  }
136
- await new Promise((resolve12) => setTimeout(resolve12, 25));
136
+ await new Promise((resolve13) => setTimeout(resolve13, 25));
137
137
  }
138
138
  }
139
139
  try {
@@ -166,7 +166,7 @@ async function renameWithRetry(from, to) {
166
166
  if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
167
167
  throw err;
168
168
  }
169
- await new Promise((resolve12) => setTimeout(resolve12, delays[i]));
169
+ await new Promise((resolve13) => setTimeout(resolve13, delays[i]));
170
170
  }
171
171
  }
172
172
  throw lastErr;
@@ -629,8 +629,8 @@ async function expandGlob(pattern) {
629
629
  for (const e of entries) {
630
630
  const full = `${dir}${SEP}${e}`;
631
631
  try {
632
- const stat11 = await fsp7.stat(full);
633
- if (stat11.isDirectory()) await walk3(full, rest);
632
+ const stat12 = await fsp7.stat(full);
633
+ if (stat12.isDirectory()) await walk3(full, rest);
634
634
  } catch {
635
635
  }
636
636
  }
@@ -647,8 +647,8 @@ async function expandGlob(pattern) {
647
647
  if (entries.includes(seg)) {
648
648
  const full = `${dir}${SEP}${seg}`;
649
649
  try {
650
- const stat11 = await fsp7.stat(full);
651
- if (stat11.isDirectory()) await walk3(full, rest);
650
+ const stat12 = await fsp7.stat(full);
651
+ if (stat12.isDirectory()) await walk3(full, rest);
652
652
  } catch {
653
653
  }
654
654
  }
@@ -859,10 +859,10 @@ function validateAgainstSchema(value, schema) {
859
859
  walk(value, schema, "", errors, 0);
860
860
  return { ok: errors.length === 0, errors };
861
861
  }
862
- function walk(value, schema, path32, errors, depth) {
862
+ function walk(value, schema, path33, errors, depth) {
863
863
  if (depth > MAX_SCHEMA_DEPTH) {
864
864
  errors.push({
865
- path: path32 || "<root>",
865
+ path: path33 || "<root>",
866
866
  message: `schema nesting exceeds maximum depth (${MAX_SCHEMA_DEPTH})`
867
867
  });
868
868
  return;
@@ -870,7 +870,7 @@ function walk(value, schema, path32, errors, depth) {
870
870
  if (schema.enum !== void 0) {
871
871
  if (!enumIncludes(schema.enum, value)) {
872
872
  errors.push({
873
- path: path32 || "<root>",
873
+ path: path33 || "<root>",
874
874
  message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
875
875
  });
876
876
  return;
@@ -879,7 +879,7 @@ function walk(value, schema, path32, errors, depth) {
879
879
  if (typeof schema.type === "string") {
880
880
  if (!checkType(value, schema.type)) {
881
881
  errors.push({
882
- path: path32 || "<root>",
882
+ path: path33 || "<root>",
883
883
  message: `expected ${schema.type}, got ${describeType(value)}`
884
884
  });
885
885
  return;
@@ -889,20 +889,20 @@ function walk(value, schema, path32, errors, depth) {
889
889
  const obj = value;
890
890
  for (const req of schema.required ?? []) {
891
891
  if (!(req in obj)) {
892
- errors.push({ path: joinPath(path32, req), message: "required property missing" });
892
+ errors.push({ path: joinPath(path33, req), message: "required property missing" });
893
893
  }
894
894
  }
895
895
  if (schema.properties) {
896
896
  for (const [key, subSchema] of Object.entries(schema.properties)) {
897
897
  if (key in obj) {
898
- walk(obj[key], subSchema, joinPath(path32, key), errors, depth + 1);
898
+ walk(obj[key], subSchema, joinPath(path33, key), errors, depth + 1);
899
899
  }
900
900
  }
901
901
  }
902
902
  }
903
903
  if (schema.type === "array" && Array.isArray(value) && schema.items) {
904
904
  for (let i = 0; i < value.length; i++) {
905
- walk(value[i], schema.items, `${path32}[${i}]`, errors, depth + 1);
905
+ walk(value[i], schema.items, `${path33}[${i}]`, errors, depth + 1);
906
906
  }
907
907
  }
908
908
  }
@@ -1283,7 +1283,7 @@ var init_ulid = __esm({
1283
1283
 
1284
1284
  // src/utils/sleep.ts
1285
1285
  function sleep(ms) {
1286
- return new Promise((resolve12) => setTimeout(resolve12, ms));
1286
+ return new Promise((resolve13) => setTimeout(resolve13, ms));
1287
1287
  }
1288
1288
  var init_sleep = __esm({
1289
1289
  "src/utils/sleep.ts"() {
@@ -2697,7 +2697,7 @@ var InMemoryAgentBridge = class {
2697
2697
  });
2698
2698
  }
2699
2699
  this.inflightGuards.add(correlationId);
2700
- return new Promise((resolve12, reject) => {
2700
+ return new Promise((resolve13, reject) => {
2701
2701
  const timer = setTimeout(() => {
2702
2702
  this.inflightGuards.delete(correlationId);
2703
2703
  this.pendingRequests.delete(correlationId);
@@ -2716,7 +2716,7 @@ var InMemoryAgentBridge = class {
2716
2716
  return;
2717
2717
  }
2718
2718
  this.pendingRequests.set(correlationId, {
2719
- resolve: resolve12,
2719
+ resolve: resolve13,
2720
2720
  reject,
2721
2721
  timer
2722
2722
  });
@@ -3068,13 +3068,13 @@ var SubagentBudget = class _SubagentBudget {
3068
3068
  if (!bus?.hasListenerFor("budget.threshold_reached")) {
3069
3069
  return Promise.resolve("stop");
3070
3070
  }
3071
- return new Promise((resolve12) => {
3071
+ return new Promise((resolve13) => {
3072
3072
  let resolved = false;
3073
3073
  const respond = (d) => {
3074
3074
  if (resolved) return;
3075
3075
  resolved = true;
3076
3076
  clearTimeout(fallback);
3077
- resolve12(d);
3077
+ resolve13(d);
3078
3078
  };
3079
3079
  const fallback = setTimeout(() => respond("stop"), _SubagentBudget.DECISION_TIMEOUT_MS);
3080
3080
  const sessionId = this.currentSessionId();
@@ -5448,7 +5448,7 @@ function createDelegateTool(opts) {
5448
5448
  subagentId
5449
5449
  });
5450
5450
  const dir = director;
5451
- const result = await new Promise((resolve12) => {
5451
+ const result = await new Promise((resolve13) => {
5452
5452
  let settled = false;
5453
5453
  let timer;
5454
5454
  const finish = (value) => {
@@ -5458,7 +5458,7 @@ function createDelegateTool(opts) {
5458
5458
  offTool();
5459
5459
  offIter();
5460
5460
  offProgress();
5461
- resolve12(value);
5461
+ resolve13(value);
5462
5462
  };
5463
5463
  const arm = () => {
5464
5464
  if (timer) clearTimeout(timer);
@@ -5902,7 +5902,14 @@ var DirectorStateCheckpoint = class {
5902
5902
  mode: 384
5903
5903
  });
5904
5904
  } catch (err) {
5905
- console.warn("[director-state] checkpoint write failed:", toErrorMessage(err));
5905
+ console.warn(
5906
+ JSON.stringify({
5907
+ level: "warn",
5908
+ event: "director_state.checkpoint_write_failed",
5909
+ message: toErrorMessage(err),
5910
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
5911
+ })
5912
+ );
5906
5913
  } finally {
5907
5914
  this.writing = false;
5908
5915
  if (this.rewriteRequested) {
@@ -6008,7 +6015,7 @@ var CollabSession = class extends EventEmitter {
6008
6015
  }
6009
6016
  for (const filePath of allFiles) {
6010
6017
  try {
6011
- const [content, stat11] = await Promise.all([
6018
+ const [content, stat12] = await Promise.all([
6012
6019
  fsp7.readFile(filePath, "utf8"),
6013
6020
  fsp7.stat(filePath)
6014
6021
  ]);
@@ -6018,8 +6025,8 @@ var CollabSession = class extends EventEmitter {
6018
6025
  path: filePath,
6019
6026
  content,
6020
6027
  language,
6021
- snapshotMtimeMs: stat11.mtimeMs,
6022
- snapshotSizeBytes: stat11.size
6028
+ snapshotMtimeMs: stat12.mtimeMs,
6029
+ snapshotSizeBytes: stat12.size
6023
6030
  });
6024
6031
  } catch {
6025
6032
  this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
@@ -6432,9 +6439,9 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
6432
6439
  for (const file of this.snapshot.files) {
6433
6440
  if (file.snapshotMtimeMs === void 0 && file.snapshotSizeBytes === void 0) continue;
6434
6441
  try {
6435
- const stat11 = await fsp7.stat(file.path);
6436
- const mtimeChanged = file.snapshotMtimeMs !== void 0 && stat11.mtimeMs > file.snapshotMtimeMs + 1;
6437
- const sizeChanged = file.snapshotSizeBytes !== void 0 && stat11.size !== file.snapshotSizeBytes;
6442
+ const stat12 = await fsp7.stat(file.path);
6443
+ const mtimeChanged = file.snapshotMtimeMs !== void 0 && stat12.mtimeMs > file.snapshotMtimeMs + 1;
6444
+ const sizeChanged = file.snapshotSizeBytes !== void 0 && stat12.size !== file.snapshotSizeBytes;
6438
6445
  if (mtimeChanged || sizeChanged) {
6439
6446
  warnings.push(`${file.path} changed after the collab snapshot was captured.`);
6440
6447
  }
@@ -6652,6 +6659,366 @@ function rosterSummaryFromConfigs(roster) {
6652
6659
  }
6653
6660
  return lines.join("\n");
6654
6661
  }
6662
+
6663
+ // src/kanban/storage.ts
6664
+ init_atomic_write();
6665
+
6666
+ // src/kanban/types.ts
6667
+ var DEFAULT_COLUMNS = [
6668
+ { id: "backlog", title: "Backlog", order: 0, wipLimit: 0, color: "#64748b" },
6669
+ { id: "todo", title: "To Do", order: 1, wipLimit: 0, color: "#2563eb" },
6670
+ { id: "in-progress", title: "In Progress", order: 2, wipLimit: 5, color: "#d97706" },
6671
+ { id: "review", title: "Review", order: 3, wipLimit: 0, color: "#7c3aed" },
6672
+ { id: "done", title: "Done", order: 4, wipLimit: 0, color: "#16a34a" }
6673
+ ];
6674
+ var CURRENT_KANBAN_VERSION = 1;
6675
+
6676
+ // src/kanban/storage.ts
6677
+ var KANBANS_DIR = "kanbans";
6678
+ var BOARD_ID_RE = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
6679
+ function getKanbanDir(projectRoot) {
6680
+ return path4.join(projectRoot, ".wrongstack", KANBANS_DIR);
6681
+ }
6682
+ function getKanbanPath(projectRoot, boardId) {
6683
+ assertValidBoardId(boardId);
6684
+ const dir = path4.resolve(getKanbanDir(projectRoot));
6685
+ const resolved = path4.resolve(dir, `${boardId}.json`);
6686
+ const rel = path4.relative(dir, resolved);
6687
+ if (rel.startsWith("..") || path4.isAbsolute(rel)) {
6688
+ throw invalidBoardId(boardId);
6689
+ }
6690
+ return resolved;
6691
+ }
6692
+ function isValidBoardId(boardId) {
6693
+ return BOARD_ID_RE.test(boardId) && !boardId.includes("..");
6694
+ }
6695
+ function assertValidBoardId(boardId) {
6696
+ if (!isValidBoardId(boardId)) throw invalidBoardId(boardId);
6697
+ }
6698
+ function invalidBoardId(boardId) {
6699
+ return new Error(`Invalid kanban board id: ${boardId}`);
6700
+ }
6701
+ function isEnoent(err) {
6702
+ return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
6703
+ }
6704
+ async function pathExists(filePath) {
6705
+ try {
6706
+ await fsp7.stat(filePath);
6707
+ return true;
6708
+ } catch (err) {
6709
+ if (isEnoent(err)) return false;
6710
+ throw err;
6711
+ }
6712
+ }
6713
+ async function listBoardIds(projectRoot) {
6714
+ const dir = getKanbanDir(projectRoot);
6715
+ try {
6716
+ const entries = await fsp7.readdir(dir, { withFileTypes: true });
6717
+ return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -5)).filter(isValidBoardId).sort();
6718
+ } catch (err) {
6719
+ if (isEnoent(err)) return [];
6720
+ throw err;
6721
+ }
6722
+ }
6723
+ async function resolveBoardRef(projectRoot, boardRef) {
6724
+ assertValidBoardId(boardRef);
6725
+ if (await pathExists(getKanbanPath(projectRoot, boardRef))) return boardRef;
6726
+ const matches = (await listBoardIds(projectRoot)).filter((id) => id.startsWith(boardRef));
6727
+ if (matches.length === 0) return null;
6728
+ if (matches.length > 1) {
6729
+ throw new Error(`Ambiguous kanban board id "${boardRef}": ${matches.slice(0, 5).join(", ")}`);
6730
+ }
6731
+ return matches[0] ?? null;
6732
+ }
6733
+ async function readBoard(projectRoot, boardRef) {
6734
+ const boardId = await resolveBoardRef(projectRoot, boardRef);
6735
+ if (!boardId) return null;
6736
+ try {
6737
+ const raw = await fsp7.readFile(getKanbanPath(projectRoot, boardId), "utf8");
6738
+ return normalizeBoard(JSON.parse(raw));
6739
+ } catch (err) {
6740
+ if (isEnoent(err)) return null;
6741
+ throw err;
6742
+ }
6743
+ }
6744
+ async function mutateBoard(projectRoot, boardRef, mutator) {
6745
+ const boardId = await resolveBoardRef(projectRoot, boardRef);
6746
+ if (!boardId) return null;
6747
+ const filePath = getKanbanPath(projectRoot, boardId);
6748
+ return withFileLock(filePath, async () => {
6749
+ let board;
6750
+ try {
6751
+ const raw = await fsp7.readFile(filePath, "utf8");
6752
+ board = normalizeBoard(JSON.parse(raw));
6753
+ } catch (err) {
6754
+ if (isEnoent(err)) return null;
6755
+ throw err;
6756
+ }
6757
+ const result = await mutator(board);
6758
+ await writeBoardUnlocked(filePath, normalizeBoard(board));
6759
+ return { board, result };
6760
+ });
6761
+ }
6762
+ function summarizeBoard(board) {
6763
+ const total = board.tasks.length;
6764
+ const completed = board.tasks.filter((task) => task.status === "completed").length;
6765
+ const lastActivity = latestActivity(board);
6766
+ return {
6767
+ id: board.id,
6768
+ title: board.title,
6769
+ createdAt: board.createdAt,
6770
+ updatedAt: board.updatedAt,
6771
+ columnCount: board.columns.length,
6772
+ taskCount: total,
6773
+ completedTaskCount: completed,
6774
+ ...board.description !== void 0 ? { description: board.description } : {},
6775
+ ...board.tags !== void 0 ? { tags: board.tags } : {},
6776
+ ...lastActivity !== void 0 ? { lastActivity } : {}
6777
+ };
6778
+ }
6779
+ async function listBoardSummaries(projectRoot) {
6780
+ const summaries = [];
6781
+ for (const id of await listBoardIds(projectRoot)) {
6782
+ const board = await readBoard(projectRoot, id);
6783
+ if (board) summaries.push(summarizeBoard(board));
6784
+ }
6785
+ return summaries.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
6786
+ }
6787
+ function normalizeBoard(board) {
6788
+ const now = (/* @__PURE__ */ new Date()).toISOString();
6789
+ const columns = [...board.columns?.length ? board.columns : DEFAULT_COLUMNS].map((column, index) => ({
6790
+ ...column,
6791
+ order: column.order ?? index
6792
+ })).sort((a, b) => a.order - b.order).map((column, index) => ({ ...column, order: index }));
6793
+ const columnIds = new Set(columns.map((column) => column.id));
6794
+ const fallbackColumnId = columns[0]?.id ?? "backlog";
6795
+ const tasks = normalizeTaskOrders(
6796
+ (board.tasks ?? []).map((task, index) => ({
6797
+ ...task,
6798
+ columnId: columnIds.has(task.columnId) ? task.columnId : fallbackColumnId,
6799
+ order: task.order ?? index,
6800
+ priority: task.priority ?? "medium",
6801
+ status: task.status ?? "pending",
6802
+ createdAt: task.createdAt ?? now,
6803
+ updatedAt: task.updatedAt ?? now
6804
+ }))
6805
+ );
6806
+ return {
6807
+ ...board,
6808
+ version: board.version ?? CURRENT_KANBAN_VERSION,
6809
+ createdAt: board.createdAt ?? now,
6810
+ updatedAt: board.updatedAt ?? now,
6811
+ columns,
6812
+ tasks
6813
+ };
6814
+ }
6815
+ function latestActivity(board) {
6816
+ return [board.updatedAt, ...board.tasks.map((task) => task.updatedAt)].filter(Boolean).sort().reverse()[0];
6817
+ }
6818
+ async function writeBoardUnlocked(filePath, board) {
6819
+ await atomicWrite(filePath, `${JSON.stringify(board, null, 2)}
6820
+ `, { encoding: "utf8" });
6821
+ }
6822
+ function normalizeTaskOrders(tasks) {
6823
+ const byColumn = /* @__PURE__ */ new Map();
6824
+ for (const task of tasks) {
6825
+ const columnTasks = byColumn.get(task.columnId) ?? [];
6826
+ columnTasks.push(task);
6827
+ byColumn.set(task.columnId, columnTasks);
6828
+ }
6829
+ for (const columnTasks of byColumn.values()) {
6830
+ columnTasks.sort((a, b) => a.order - b.order || a.createdAt.localeCompare(b.createdAt)).forEach((task, index) => {
6831
+ task.order = index;
6832
+ });
6833
+ }
6834
+ return tasks;
6835
+ }
6836
+
6837
+ // src/kanban/manager.ts
6838
+ async function listBoards(projectRoot) {
6839
+ return listBoardSummaries(projectRoot);
6840
+ }
6841
+ async function updateTaskAssignment(projectRoot, boardId, taskId, patch) {
6842
+ const updated = await mutateBoard(projectRoot, boardId, (board) => {
6843
+ const task = findTask(board, taskId);
6844
+ if (!task) return null;
6845
+ const nextAssignment = {
6846
+ ...task.assignment ?? { status: "assigned" }
6847
+ };
6848
+ for (const [key, value] of Object.entries(patch)) {
6849
+ if (value !== void 0) {
6850
+ nextAssignment[key] = value;
6851
+ }
6852
+ }
6853
+ task.assignment = nextAssignment;
6854
+ if (task.assignment.agentId) task.assignedAgent = task.assignment.agentId;
6855
+ if (task.assignment.status === "completed") {
6856
+ task.assignment.completedAt = task.assignment.completedAt ?? nowIso();
6857
+ task.status = "completed";
6858
+ task.completedAt = task.assignment.completedAt;
6859
+ if (patch.error === void 0) delete task.assignment.error;
6860
+ } else if (task.assignment.status === "running") {
6861
+ delete task.assignment.completedAt;
6862
+ if (patch.error === void 0) delete task.assignment.error;
6863
+ task.status = "in_progress";
6864
+ delete task.completedAt;
6865
+ } else if (task.assignment.status === "failed") {
6866
+ delete task.assignment.completedAt;
6867
+ task.status = "failed";
6868
+ delete task.completedAt;
6869
+ } else if (task.assignment.status === "cancelled") {
6870
+ delete task.assignment.completedAt;
6871
+ if (patch.error === void 0) delete task.assignment.error;
6872
+ task.status = "blocked";
6873
+ delete task.completedAt;
6874
+ } else if (task.assignment.status === "queued" || task.assignment.status === "assigned") {
6875
+ delete task.assignment.completedAt;
6876
+ if (patch.error === void 0) delete task.assignment.error;
6877
+ if (task.status === "completed" || task.status === "failed") {
6878
+ task.status = task.assignment.status === "queued" ? "ready" : "pending";
6879
+ }
6880
+ delete task.completedAt;
6881
+ }
6882
+ task.updatedAt = nowIso();
6883
+ board.updatedAt = task.updatedAt;
6884
+ return task;
6885
+ });
6886
+ return updated?.result ? updated.board : null;
6887
+ }
6888
+ async function claimReadyTask(projectRoot, input = {}) {
6889
+ if (input.boardId) return claimReadyTaskOnBoard(projectRoot, input.boardId, input);
6890
+ for (const board of await listBoards(projectRoot)) {
6891
+ const claimed = await claimReadyTaskOnBoard(projectRoot, board.id, input);
6892
+ if (claimed) return claimed;
6893
+ }
6894
+ return null;
6895
+ }
6896
+ async function searchKanban(projectRoot, input = {}) {
6897
+ const boardIds = input.boardId ? [input.boardId] : (await listBoards(projectRoot)).map((b) => b.id);
6898
+ const results = [];
6899
+ for (const boardId of boardIds) {
6900
+ const board = await readBoard(projectRoot, boardId);
6901
+ if (!board) continue;
6902
+ for (const task of board.tasks) {
6903
+ if (!matchesKanbanSearch(board, task, input)) continue;
6904
+ results.push({ board: summarizeBoard(board), task });
6905
+ }
6906
+ }
6907
+ return results;
6908
+ }
6909
+ async function listReadyTasks(projectRoot, input = {}) {
6910
+ const results = await searchKanban(projectRoot, { ...input, readyOnly: true });
6911
+ return input.limit && input.limit > 0 ? results.slice(0, input.limit) : results;
6912
+ }
6913
+ function areDependenciesMet(board, taskId) {
6914
+ const task = findTask(board, taskId);
6915
+ if (!task?.dependsOn?.length) return true;
6916
+ return task.dependsOn.every((depId) => {
6917
+ const depTask = board.tasks.find((candidate) => candidate.id === depId);
6918
+ return depTask?.status === "completed";
6919
+ });
6920
+ }
6921
+ function buildAssignment(input) {
6922
+ return {
6923
+ status: input.status ?? "assigned",
6924
+ ...input.agentId !== void 0 ? { agentId: input.agentId } : {},
6925
+ ...input.name !== void 0 ? { name: input.name } : {},
6926
+ ...input.role !== void 0 ? { role: input.role } : {},
6927
+ ...input.provider !== void 0 ? { provider: input.provider } : {},
6928
+ ...input.model !== void 0 ? { model: input.model } : {},
6929
+ ...input.fallbackProfile !== void 0 ? { fallbackProfile: input.fallbackProfile } : {},
6930
+ ...input.fallbackModels !== void 0 ? { fallbackModels: input.fallbackModels } : {},
6931
+ ...input.tools !== void 0 ? { tools: input.tools } : {},
6932
+ ...input.allowedCapabilities !== void 0 ? { allowedCapabilities: input.allowedCapabilities } : {}
6933
+ };
6934
+ }
6935
+ async function claimReadyTaskOnBoard(projectRoot, boardId, input) {
6936
+ const updated = await mutateBoard(projectRoot, boardId, (board) => {
6937
+ const candidates = input.taskId ? [findTask(board, input.taskId)].filter((task2) => Boolean(task2)) : board.tasks.filter((task2) => isTaskReadyForWork(board, task2)).sort(compareTasksForWork);
6938
+ const task = candidates.find((candidate) => isTaskReadyForWork(board, candidate));
6939
+ if (!task) return null;
6940
+ const current = task.assignment;
6941
+ const assignment = buildAssignment({
6942
+ ...current?.agentId !== void 0 ? { agentId: current.agentId } : {},
6943
+ ...current?.name !== void 0 ? { name: current.name } : {},
6944
+ ...current?.role !== void 0 ? { role: current.role } : {},
6945
+ ...current?.provider !== void 0 ? { provider: current.provider } : {},
6946
+ ...current?.model !== void 0 ? { model: current.model } : {},
6947
+ ...current?.fallbackProfile !== void 0 ? { fallbackProfile: current.fallbackProfile } : {},
6948
+ ...current?.fallbackModels !== void 0 ? { fallbackModels: current.fallbackModels } : {},
6949
+ ...current?.tools !== void 0 ? { tools: current.tools } : {},
6950
+ ...current?.allowedCapabilities !== void 0 ? { allowedCapabilities: current.allowedCapabilities } : {},
6951
+ ...input.agentId !== void 0 ? { agentId: input.agentId } : {},
6952
+ ...input.name !== void 0 ? { name: input.name } : {},
6953
+ ...input.role !== void 0 ? { role: input.role } : {},
6954
+ ...input.provider !== void 0 ? { provider: input.provider } : {},
6955
+ ...input.model !== void 0 ? { model: input.model } : {},
6956
+ ...input.fallbackProfile !== void 0 ? { fallbackProfile: input.fallbackProfile } : {},
6957
+ ...input.fallbackModels !== void 0 ? { fallbackModels: input.fallbackModels } : {},
6958
+ ...input.tools !== void 0 ? { tools: input.tools } : {},
6959
+ ...input.allowedCapabilities !== void 0 ? { allowedCapabilities: input.allowedCapabilities } : {},
6960
+ ...input.assignee !== void 0 ? { assignee: input.assignee } : {},
6961
+ status: input.status ?? "queued"
6962
+ });
6963
+ assignment.dispatchedAt = assignment.dispatchedAt ?? nowIso();
6964
+ task.assignment = assignment;
6965
+ if (assignment.agentId ?? assignment.role ?? assignment.name) {
6966
+ task.assignedAgent = assignment.agentId ?? assignment.role ?? assignment.name;
6967
+ }
6968
+ if (input.assignee ?? assignment.name ?? assignment.agentId) {
6969
+ task.assignee = input.assignee ?? assignment.name ?? assignment.agentId;
6970
+ }
6971
+ task.status = assignment.status === "running" ? "in_progress" : "ready";
6972
+ delete task.completedAt;
6973
+ task.updatedAt = nowIso();
6974
+ board.updatedAt = task.updatedAt;
6975
+ return task;
6976
+ });
6977
+ return updated?.result ? { board: updated.board, task: updated.result } : null;
6978
+ }
6979
+ function compareTasksForWork(a, b) {
6980
+ const priorityRank = {
6981
+ critical: 0,
6982
+ high: 1,
6983
+ medium: 2,
6984
+ low: 3
6985
+ };
6986
+ return priorityRank[a.priority] - priorityRank[b.priority] || a.columnId.localeCompare(b.columnId) || a.order - b.order || a.createdAt.localeCompare(b.createdAt);
6987
+ }
6988
+ function matchesKanbanSearch(board, task, input) {
6989
+ const query = input.query?.trim().toLowerCase();
6990
+ if (input.assignedAgent && task.assignedAgent !== input.assignedAgent) return false;
6991
+ if (input.status && task.status !== input.status) return false;
6992
+ if (input.priority && task.priority !== input.priority) return false;
6993
+ if (input.label && !task.labels?.includes(input.label)) return false;
6994
+ if (input.chainId && task.chain?.chainId !== input.chainId) return false;
6995
+ if (input.readyOnly && !isTaskReadyForWork(board, task)) return false;
6996
+ if (!query) return true;
6997
+ return [task.title, task.description, task.assignedAgent, task.assignee, ...task.labels ?? []].filter(Boolean).some((value) => String(value).toLowerCase().includes(query));
6998
+ }
6999
+ function findTask(board, taskId) {
7000
+ const exact = board.tasks.find((task) => task.id === taskId);
7001
+ if (exact) return exact;
7002
+ const matches = board.tasks.filter((task) => task.id.startsWith(taskId));
7003
+ if (matches.length > 1) {
7004
+ throw new Error(
7005
+ `Ambiguous kanban task id "${taskId}": ${matches.slice(0, 5).map((task) => task.id).join(", ")}`
7006
+ );
7007
+ }
7008
+ return matches[0];
7009
+ }
7010
+ function isTaskReadyForWork(board, task) {
7011
+ if (!["pending", "ready"].includes(task.status)) return false;
7012
+ if (task.assignment && ["queued", "running"].includes(task.assignment.status)) return false;
7013
+ if (task.mergedIntoTaskId) return false;
7014
+ if (!areDependenciesMet(board, task.id)) return false;
7015
+ return true;
7016
+ }
7017
+ function nowIso() {
7018
+ return (/* @__PURE__ */ new Date()).toISOString();
7019
+ }
7020
+
7021
+ // src/coordination/director-tools.ts
6655
7022
  init_error();
6656
7023
 
6657
7024
  // src/coordination/dispatcher.ts
@@ -7332,6 +7699,298 @@ function makeAssignTool(director) {
7332
7699
  }
7333
7700
  };
7334
7701
  }
7702
+ function makeKanbanQueueTool(director, roster) {
7703
+ return {
7704
+ name: "kanban_queue",
7705
+ description: "Claim dependency-ready Kanban tasks and dispatch them into the Director fleet. Preserves per-task provider/model/role/tool routing metadata, assigns each claimed task to a spawned subagent, and can await results while writing completion back to Kanban.",
7706
+ usageHint: 'Use action:"dispatch_ready" with optional boardId/taskId/maxTasks. Set awaitCompletion:true when you want this call to update Kanban to completed/failed before returning; otherwise use await_tasks and kanban mark_assignment later.',
7707
+ permission: "auto",
7708
+ mutating: true,
7709
+ capabilities: [ToolCapabilities.SUBAGENT_SPAWN, ToolCapabilities.FS_WRITE],
7710
+ inputSchema: {
7711
+ type: "object",
7712
+ properties: {
7713
+ action: { type: "string", enum: ["dispatch_ready"] },
7714
+ boardId: { type: "string", description: "Optional board id/prefix to claim from." },
7715
+ taskId: { type: "string", description: "Optional exact task id/prefix to claim." },
7716
+ query: { type: "string", description: "Optional natural-language filter for ready tasks." },
7717
+ maxTasks: {
7718
+ type: "number",
7719
+ minimum: 1,
7720
+ maximum: 20,
7721
+ description: "Maximum number of ready tasks to claim and dispatch. Default 1."
7722
+ },
7723
+ awaitCompletion: {
7724
+ type: "boolean",
7725
+ description: "When true, waits for assigned fleet task results and writes completed/failed back to Kanban."
7726
+ },
7727
+ timeoutMs: {
7728
+ type: "number",
7729
+ minimum: 1,
7730
+ description: "Optional per-assigned-task timeout in ms."
7731
+ },
7732
+ maxToolCalls: {
7733
+ type: "number",
7734
+ minimum: 1,
7735
+ description: "Optional per-assigned-task tool-call cap."
7736
+ },
7737
+ agentId: { type: "string" },
7738
+ name: { type: "string" },
7739
+ role: { type: "string" },
7740
+ provider: { type: "string" },
7741
+ model: { type: "string" },
7742
+ fallbackModels: { type: "array", items: { type: "string" } },
7743
+ tools: { type: "array", items: { type: "string" } },
7744
+ allowedCapabilities: { type: "array", items: { type: "string" } },
7745
+ worktree: {
7746
+ anyOf: [{ type: "boolean" }, { type: "string", enum: ["auto", "required", "off"] }]
7747
+ }
7748
+ }
7749
+ },
7750
+ async execute(input, ctx) {
7751
+ const i = normalizeKanbanQueueInput(input);
7752
+ const rawAction = input?.action;
7753
+ if (rawAction !== void 0 && rawAction !== "dispatch_ready") {
7754
+ return { error: `Unknown kanban_queue action: ${String(rawAction)}` };
7755
+ }
7756
+ const projectRoot = ctx.projectRoot;
7757
+ if (!projectRoot) return { error: "kanban_queue requires ctx.projectRoot." };
7758
+ const maxTasks = Math.max(1, Math.min(20, Math.floor(i.maxTasks ?? 1)));
7759
+ const candidateTaskIds = i.taskId !== void 0 ? [i.taskId] : i.query ? (await listReadyTasks(projectRoot, {
7760
+ ...i.boardId !== void 0 ? { boardId: i.boardId } : {}
7761
+ })).filter((candidate) => matchesKanbanQueueQuery(candidate.task, i.query ?? "")).slice(0, maxTasks).map((candidate) => candidate.task.id) : void 0;
7762
+ const dispatches = [];
7763
+ const errors = [];
7764
+ const resultFailures = [];
7765
+ for (let index = 0; index < maxTasks; index++) {
7766
+ const candidateTaskId = candidateTaskIds?.[index];
7767
+ if (candidateTaskIds && !candidateTaskId) break;
7768
+ const claim = await claimReadyTask(projectRoot, {
7769
+ ...i.boardId !== void 0 ? { boardId: i.boardId } : {},
7770
+ ...candidateTaskId !== void 0 ? { taskId: candidateTaskId } : {},
7771
+ ...i.agentId !== void 0 ? { agentId: i.agentId } : {},
7772
+ ...i.name !== void 0 ? { name: i.name } : {},
7773
+ ...i.role !== void 0 ? { role: i.role } : {},
7774
+ ...i.provider !== void 0 ? { provider: i.provider } : {},
7775
+ ...i.model !== void 0 ? { model: i.model } : {},
7776
+ ...i.fallbackModels !== void 0 ? { fallbackModels: i.fallbackModels } : {},
7777
+ ...i.tools !== void 0 ? { tools: i.tools } : {},
7778
+ ...i.allowedCapabilities !== void 0 ? { allowedCapabilities: i.allowedCapabilities } : {},
7779
+ status: "queued"
7780
+ });
7781
+ if (!claim) {
7782
+ if (candidateTaskIds) continue;
7783
+ break;
7784
+ }
7785
+ let subagentId;
7786
+ let runTaskId;
7787
+ try {
7788
+ const config = buildKanbanSubagentConfig(claim.task, i, roster);
7789
+ subagentId = await director.spawn(config);
7790
+ runTaskId = await director.assign({
7791
+ id: randomUUID(),
7792
+ subagentId,
7793
+ description: buildKanbanFleetTaskPrompt(claim.board, claim.task),
7794
+ ...i.maxToolCalls !== void 0 ? { maxToolCalls: i.maxToolCalls } : {},
7795
+ ...i.timeoutMs !== void 0 ? { timeoutMs: i.timeoutMs } : {},
7796
+ context: {
7797
+ kanban: {
7798
+ boardId: claim.board.id,
7799
+ taskId: claim.task.id,
7800
+ origin: claim.task.origin
7801
+ }
7802
+ }
7803
+ });
7804
+ await updateTaskAssignment(projectRoot, claim.board.id, claim.task.id, {
7805
+ ...claim.task.assignment ?? {},
7806
+ status: "running",
7807
+ subagentId,
7808
+ runTaskId
7809
+ });
7810
+ dispatches.push({
7811
+ boardId: claim.board.id,
7812
+ taskId: claim.task.id,
7813
+ title: claim.task.title,
7814
+ subagentId,
7815
+ runTaskId,
7816
+ ...config.provider !== void 0 ? { provider: config.provider } : {},
7817
+ ...config.model !== void 0 ? { model: config.model } : {}
7818
+ });
7819
+ } catch (err) {
7820
+ let message = toErrorMessage(err);
7821
+ if (subagentId && !runTaskId) {
7822
+ try {
7823
+ await director.terminate(subagentId);
7824
+ } catch (cleanupErr) {
7825
+ message = `${message}; cleanup failed for spawned subagent ${subagentId}: ${toErrorMessage(cleanupErr)}`;
7826
+ }
7827
+ }
7828
+ await updateTaskAssignment(projectRoot, claim.board.id, claim.task.id, {
7829
+ ...claim.task.assignment ?? {},
7830
+ status: "failed",
7831
+ ...subagentId !== void 0 ? { subagentId } : {},
7832
+ ...runTaskId !== void 0 ? { runTaskId } : {},
7833
+ error: message
7834
+ });
7835
+ errors.push({ taskId: claim.task.id, error: message });
7836
+ }
7837
+ }
7838
+ let results;
7839
+ if (i.awaitCompletion && dispatches.length > 0) {
7840
+ results = await director.awaitTasks(dispatches.map((dispatch) => dispatch.runTaskId));
7841
+ for (const result of results) {
7842
+ const dispatch = dispatches.find((candidate) => candidate.runTaskId === result.taskId);
7843
+ if (!dispatch) continue;
7844
+ await updateTaskAssignment(projectRoot, dispatch.boardId, dispatch.taskId, {
7845
+ status: result.status === "success" ? "completed" : "failed",
7846
+ subagentId: result.subagentId,
7847
+ runTaskId: result.taskId,
7848
+ ...result.status === "success" ? { lastResult: resultToText(result) } : { error: resultErrorText(result) }
7849
+ });
7850
+ if (result.status !== "success") {
7851
+ resultFailures.push({
7852
+ taskId: dispatch.taskId,
7853
+ runTaskId: result.taskId,
7854
+ status: result.status,
7855
+ error: resultErrorText(result)
7856
+ });
7857
+ }
7858
+ }
7859
+ }
7860
+ return {
7861
+ ok: errors.length === 0 && resultFailures.length === 0,
7862
+ dispatched: dispatches,
7863
+ count: dispatches.length,
7864
+ ...results !== void 0 ? { results } : {},
7865
+ ...errors.length > 0 ? { errors } : {},
7866
+ ...resultFailures.length > 0 ? { resultFailures } : {},
7867
+ message: dispatches.length > 0 ? `Dispatched ${dispatches.length} kanban task(s).` : "No ready kanban task matched."
7868
+ };
7869
+ }
7870
+ };
7871
+ }
7872
+ function normalizeKanbanQueueInput(input) {
7873
+ const raw = input ?? {};
7874
+ return {
7875
+ action: raw.action === "dispatch_ready" ? "dispatch_ready" : void 0,
7876
+ boardId: typeof raw.boardId === "string" ? raw.boardId : void 0,
7877
+ taskId: typeof raw.taskId === "string" ? raw.taskId : void 0,
7878
+ query: typeof raw.query === "string" ? raw.query : void 0,
7879
+ maxTasks: typeof raw.maxTasks === "number" ? raw.maxTasks : void 0,
7880
+ awaitCompletion: typeof raw.awaitCompletion === "boolean" ? raw.awaitCompletion : void 0,
7881
+ timeoutMs: typeof raw.timeoutMs === "number" ? raw.timeoutMs : void 0,
7882
+ maxToolCalls: typeof raw.maxToolCalls === "number" ? raw.maxToolCalls : void 0,
7883
+ agentId: typeof raw.agentId === "string" ? raw.agentId : void 0,
7884
+ name: typeof raw.name === "string" ? raw.name : void 0,
7885
+ role: typeof raw.role === "string" ? raw.role : void 0,
7886
+ provider: typeof raw.provider === "string" ? raw.provider : void 0,
7887
+ model: typeof raw.model === "string" ? raw.model : void 0,
7888
+ fallbackModels: stringArray(raw.fallbackModels),
7889
+ tools: stringArray(raw.tools),
7890
+ allowedCapabilities: stringArray(raw.allowedCapabilities),
7891
+ worktree: normalizeWorktreeOverride(raw.worktree)
7892
+ };
7893
+ }
7894
+ function buildKanbanSubagentConfig(task, input, roster) {
7895
+ const assignment = task.assignment;
7896
+ const role = input.role ?? assignment?.role;
7897
+ const base = role && roster?.[role] ? instantiateRosterConfig2(role, roster[role] ?? {}) : void 0;
7898
+ const tools = input.tools ?? assignment?.tools ?? base?.tools;
7899
+ const name = input.name ?? assignment?.name ?? input.agentId ?? assignment?.agentId ?? task.assignedAgent ?? role ?? "kanban-agent";
7900
+ return {
7901
+ ...base ?? { name },
7902
+ name: base?.name ?? name,
7903
+ ...role !== void 0 ? { role } : {},
7904
+ ...input.provider ?? assignment?.provider ? { provider: input.provider ?? assignment?.provider } : {},
7905
+ ...input.model ?? assignment?.model ? { model: input.model ?? assignment?.model } : {},
7906
+ ...input.fallbackModels ?? assignment?.fallbackModels ? { fallbackModels: input.fallbackModels ?? assignment?.fallbackModels } : {},
7907
+ ...tools ? { tools: ensureKanbanTool(tools) } : {},
7908
+ ...input.allowedCapabilities ?? assignment?.allowedCapabilities ? { allowedCapabilities: input.allowedCapabilities ?? assignment?.allowedCapabilities } : {},
7909
+ ...input.worktree !== void 0 ? { worktree: input.worktree } : {}
7910
+ };
7911
+ }
7912
+ function ensureKanbanTool(tools) {
7913
+ return tools.includes("kanban") ? [...tools] : [...tools, "kanban"];
7914
+ }
7915
+ function matchesKanbanQueueQuery(task, query) {
7916
+ const normalized = query.trim().toLowerCase();
7917
+ if (!normalized) return true;
7918
+ return [
7919
+ task.title,
7920
+ task.description,
7921
+ task.assignedAgent,
7922
+ task.assignee,
7923
+ task.origin?.system,
7924
+ task.origin?.graphId,
7925
+ task.origin?.phaseId,
7926
+ ...task.labels ?? []
7927
+ ].filter(Boolean).some((value) => String(value).toLowerCase().includes(normalized));
7928
+ }
7929
+ function buildKanbanFleetTaskPrompt(board, task) {
7930
+ const dependencyLines = (task.dependsOn ?? []).map((depId) => board.tasks.find((candidate) => candidate.id === depId)).filter((dep) => Boolean(dep)).map((dep) => `- ${dep.title} [${dep.status}] (${dep.id})`);
7931
+ const checks = task.successCriteria?.map((check) => `- ${check.description}`).join("\n");
7932
+ const metrics = task.goalMetrics?.map(
7933
+ (metric) => `- ${metric.name}: ${metric.current ?? "n/a"}${metric.target !== void 0 ? ` / ${metric.target}` : ""}${metric.unit ? ` ${metric.unit}` : ""} [${metric.status}]`
7934
+ ).join("\n");
7935
+ const chain = task.chain ? [
7936
+ `chainId: ${task.chain.chainId}`,
7937
+ `order: ${task.chain.order}`,
7938
+ task.chain.previousTaskId ? `previous: ${task.chain.previousTaskId}` : "",
7939
+ task.chain.nextTaskId ? `next: ${task.chain.nextTaskId}` : ""
7940
+ ].filter(Boolean).join("\n") : "";
7941
+ const routing = [
7942
+ task.assignment?.role ? `role: ${task.assignment.role}` : "",
7943
+ task.assignment?.provider ? `provider: ${task.assignment.provider}` : "",
7944
+ task.assignment?.model ? `model: ${task.assignment.model}` : "",
7945
+ task.assignment?.fallbackProfile ? `fallbackProfile: ${task.assignment.fallbackProfile}` : "",
7946
+ task.assignment?.fallbackModels?.length ? `fallbackModels: ${task.assignment.fallbackModels.join(", ")}` : ""
7947
+ ].filter(Boolean);
7948
+ const origin = task.origin ? [
7949
+ `system: ${task.origin.system}`,
7950
+ task.origin.graphId ? `graphId: ${task.origin.graphId}` : "",
7951
+ task.origin.phaseId ? `phaseId: ${task.origin.phaseId}` : "",
7952
+ task.origin.taskId ? `sourceTaskId: ${task.origin.taskId}` : ""
7953
+ ].filter(Boolean).join("\n") : "";
7954
+ return [
7955
+ "You are processing a claimed WrongStack Kanban task.",
7956
+ "",
7957
+ `Board: ${board.title} (${board.id})`,
7958
+ `Task: ${task.title} (${task.id})`,
7959
+ `Status: ${task.status}`,
7960
+ `Priority: ${task.priority}`,
7961
+ task.description ? `Description:
7962
+ ${task.description}` : "",
7963
+ origin ? `Origin:
7964
+ ${origin}` : "",
7965
+ routing.length ? `Routing hints:
7966
+ ${routing.join("\n")}` : "",
7967
+ chain ? `Task chain:
7968
+ ${chain}` : "",
7969
+ dependencyLines.length ? `Dependencies:
7970
+ ${dependencyLines.join("\n")}` : "",
7971
+ checks ? `Success criteria:
7972
+ ${checks}` : "",
7973
+ metrics ? `Goal metrics:
7974
+ ${metrics}` : "",
7975
+ task.labels?.length ? `Labels: ${task.labels.join(", ")}` : "",
7976
+ "",
7977
+ "Work this task end-to-end. If scope is too broad or too small, use the kanban tool to split_task or merge_tasks instead of losing traceability.",
7978
+ `When you start or finish, call kanban with action "mark_assignment", boardId "${board.id}", taskId "${task.id}", and assignmentStatus "running", "completed", or "failed". Include lastResult or error when you finish.`,
7979
+ "When finished, report what changed, what you verified, and any remaining blockers."
7980
+ ].filter(Boolean).join("\n");
7981
+ }
7982
+ function resultToText(result) {
7983
+ if (typeof result.result === "string") return result.result;
7984
+ if (result.result === void 0) return "";
7985
+ try {
7986
+ return JSON.stringify(result.result);
7987
+ } catch {
7988
+ return String(result.result);
7989
+ }
7990
+ }
7991
+ function resultErrorText(result) {
7992
+ return result.error ? `${result.error.kind}: ${result.error.message}` : result.status;
7993
+ }
7335
7994
  function makeAwaitTasksTool(director) {
7336
7995
  return {
7337
7996
  name: "await_tasks",
@@ -8557,7 +9216,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
8557
9216
  taskIds.map((id) => {
8558
9217
  const cached = this.completedResults.find((r) => r.taskId === id);
8559
9218
  if (cached) return cached;
8560
- return new Promise((resolve12, reject) => {
9219
+ return new Promise((resolve13, reject) => {
8561
9220
  const timeout = setTimeout(() => {
8562
9221
  this.off("task.completed", handler);
8563
9222
  reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
@@ -8566,7 +9225,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
8566
9225
  if (result.taskId === id) {
8567
9226
  clearTimeout(timeout);
8568
9227
  this.off("task.completed", handler);
8569
- resolve12(result);
9228
+ resolve13(result);
8570
9229
  }
8571
9230
  };
8572
9231
  this.on("task.completed", handler);
@@ -8591,13 +9250,13 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
8591
9250
  const done = new Set(completed.map((r) => r.taskId));
8592
9251
  return { completed, pending: taskIds.filter((id) => !done.has(id)) };
8593
9252
  }
8594
- return new Promise((resolve12) => {
9253
+ return new Promise((resolve13) => {
8595
9254
  let timer;
8596
9255
  const handler = ({ result }) => {
8597
9256
  if (!ids.has(result.taskId)) return;
8598
9257
  if (timer) clearTimeout(timer);
8599
9258
  this.off("task.completed", handler);
8600
- resolve12({
9259
+ resolve13({
8601
9260
  completed: [result],
8602
9261
  pending: taskIds.filter((id) => id !== result.taskId)
8603
9262
  });
@@ -8605,7 +9264,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
8605
9264
  if (opts?.timeoutMs !== void 0) {
8606
9265
  timer = setTimeout(() => {
8607
9266
  this.off("task.completed", handler);
8608
- resolve12({ completed: [], pending: [...taskIds], timedOut: true });
9267
+ resolve13({ completed: [], pending: [...taskIds], timedOut: true });
8609
9268
  }, opts.timeoutMs);
8610
9269
  }
8611
9270
  this.on("task.completed", handler);
@@ -8875,12 +9534,12 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
8875
9534
  }
8876
9535
  return new Promise((resolveDecision) => {
8877
9536
  let settled = false;
8878
- const resolve12 = (d) => {
9537
+ const resolve13 = (d) => {
8879
9538
  if (settled) return;
8880
9539
  settled = true;
8881
9540
  resolveDecision(d);
8882
9541
  };
8883
- const fallback = setTimeout(() => resolve12("stop"), DECISION_TIMEOUT_MS);
9542
+ const fallback = setTimeout(() => resolve13("stop"), DECISION_TIMEOUT_MS);
8884
9543
  const sessionId = this.currentSessionId();
8885
9544
  budget._events?.emit("budget.threshold_reached", {
8886
9545
  ...sessionId ? { sessionId } : {},
@@ -8898,11 +9557,11 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
8898
9557
  // disagreeing, resolves as a stop). Async grants still resolve.
8899
9558
  extend: (extra) => {
8900
9559
  clearTimeout(fallback);
8901
- queueMicrotask(() => resolve12({ extend: extra }));
9560
+ queueMicrotask(() => resolve13({ extend: extra }));
8902
9561
  },
8903
9562
  deny: () => {
8904
9563
  clearTimeout(fallback);
8905
- resolve12("stop");
9564
+ resolve13("stop");
8906
9565
  }
8907
9566
  });
8908
9567
  });
@@ -10367,11 +11026,11 @@ var Director = class _Director {
10367
11026
  if (cached) return cached;
10368
11027
  const existing = this.taskWaiters.get(id);
10369
11028
  if (existing) return existing.promise;
10370
- let resolve12;
11029
+ let resolve13;
10371
11030
  const promise = new Promise((res) => {
10372
- resolve12 = res;
11031
+ resolve13 = res;
10373
11032
  });
10374
- this.taskWaiters.set(id, { promise, resolve: resolve12 });
11033
+ this.taskWaiters.set(id, { promise, resolve: resolve13 });
10375
11034
  return promise;
10376
11035
  })
10377
11036
  );
@@ -10400,10 +11059,10 @@ var Director = class _Director {
10400
11059
  pending: taskIds.filter((id) => !done.has(id))
10401
11060
  });
10402
11061
  }
10403
- return new Promise((resolve12) => {
11062
+ return new Promise((resolve13) => {
10404
11063
  const entry = {
10405
11064
  ids: new Set(taskIds),
10406
- resolve: (r) => resolve12({
11065
+ resolve: (r) => resolve13({
10407
11066
  completed: [r],
10408
11067
  pending: taskIds.filter((id) => id !== r.taskId)
10409
11068
  })
@@ -10411,7 +11070,7 @@ var Director = class _Director {
10411
11070
  if (opts?.timeoutMs !== void 0) {
10412
11071
  entry.timer = setTimeout(() => {
10413
11072
  this.anyWaiters.delete(entry);
10414
- resolve12({ completed: [], pending: [...taskIds], timedOut: true });
11073
+ resolve13({ completed: [], pending: [...taskIds], timedOut: true });
10415
11074
  }, opts.timeoutMs);
10416
11075
  }
10417
11076
  this.anyWaiters.add(entry);
@@ -10643,6 +11302,7 @@ var Director = class _Director {
10643
11302
  const t = [
10644
11303
  makeSpawnTool(this, effectiveRoster),
10645
11304
  makeAssignTool(this),
11305
+ makeKanbanQueueTool(this, effectiveRoster),
10646
11306
  makeAwaitTasksTool(this),
10647
11307
  makeAskTool(this),
10648
11308
  makeAskResultTool(this),
@@ -11600,9 +12260,9 @@ var DefaultSessionStore = class _DefaultSessionStore {
11600
12260
  let cacheHit = false;
11601
12261
  try {
11602
12262
  const s = await fsp7.stat(file);
11603
- const stat11 = { mtimeMs: s.mtimeMs, size: s.size };
12263
+ const stat12 = { mtimeMs: s.mtimeMs, size: s.size };
11604
12264
  const cached = this._loadCache.get(id);
11605
- if (cached && cached.mtimeMs === stat11.mtimeMs && cached.size === stat11.size) {
12265
+ if (cached && cached.mtimeMs === stat12.mtimeMs && cached.size === stat12.size) {
11606
12266
  cacheHit = true;
11607
12267
  this._loadCache.delete(id);
11608
12268
  this._loadCache.set(id, cached);
@@ -11725,7 +12385,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
11725
12385
  this._loadCache.delete(oldest);
11726
12386
  }
11727
12387
  }
11728
- this._loadCache.set(id, { mtimeMs: stat11.mtimeMs, size: stat11.size, data });
12388
+ this._loadCache.set(id, { mtimeMs: stat12.mtimeMs, size: stat12.size, data });
11729
12389
  }
11730
12390
  return data;
11731
12391
  } catch (err) {
@@ -11767,14 +12427,14 @@ var DefaultSessionStore = class _DefaultSessionStore {
11767
12427
  const limit = opts?.limit;
11768
12428
  const signal = opts?.signal;
11769
12429
  const out = [];
11770
- let stat11;
12430
+ let stat12;
11771
12431
  try {
11772
- stat11 = await fsp7.stat(file);
12432
+ stat12 = await fsp7.stat(file);
11773
12433
  } catch (err) {
11774
12434
  if (err.code === "ENOENT") return [];
11775
12435
  throw err;
11776
12436
  }
11777
- if (stat11.size === 0) return [];
12437
+ if (stat12.size === 0) return [];
11778
12438
  let fh;
11779
12439
  try {
11780
12440
  fh = await fsp7.open(file, "r");
@@ -11957,15 +12617,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
11957
12617
  * Returns empty array when the index doesn't exist or is corrupt.
11958
12618
  */
11959
12619
  async readIndex() {
11960
- let stat11;
12620
+ let stat12;
11961
12621
  try {
11962
12622
  const s = await fsp7.stat(this.indexFile);
11963
- stat11 = { mtimeMs: s.mtimeMs, size: s.size };
12623
+ stat12 = { mtimeMs: s.mtimeMs, size: s.size };
11964
12624
  } catch {
11965
12625
  this._indexCache = null;
11966
12626
  return [];
11967
12627
  }
11968
- if (this._indexCache !== null && this._indexCache.mtimeMs === stat11.mtimeMs && this._indexCache.size === stat11.size) {
12628
+ if (this._indexCache !== null && this._indexCache.mtimeMs === stat12.mtimeMs && this._indexCache.size === stat12.size) {
11969
12629
  return [...this._indexCache.summaries];
11970
12630
  }
11971
12631
  let raw;
@@ -11998,7 +12658,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
11998
12658
  if (a.startedAt > b.startedAt) return -1;
11999
12659
  return a.id.localeCompare(b.id);
12000
12660
  });
12001
- this._indexCache = { ...stat11, summaries };
12661
+ this._indexCache = { ...stat12, summaries };
12002
12662
  return [...summaries];
12003
12663
  }
12004
12664
  /**
@@ -12165,8 +12825,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
12165
12825
  if (fromManifest) return fromManifest;
12166
12826
  try {
12167
12827
  const full = this.sessionPath(id, ".jsonl");
12168
- const stat11 = await fsp7.stat(full);
12169
- const summary = await this.summarize(id, stat11.mtime.toISOString());
12828
+ const stat12 = await fsp7.stat(full);
12829
+ const summary = await this.summarize(id, stat12.mtime.toISOString());
12170
12830
  await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
12171
12831
  const msg = toErrorMessage(err);
12172
12832
  this.emitError(id, manifest, "summary_fallback", msg, true);
@@ -12209,18 +12869,18 @@ var DefaultSessionStore = class _DefaultSessionStore {
12209
12869
  async summaryHeaderFor(ref) {
12210
12870
  let mtime = (/* @__PURE__ */ new Date(0)).toISOString();
12211
12871
  try {
12212
- const stat11 = await fsp7.stat(ref.filePath);
12213
- if (!stat11.isFile()) {
12872
+ const stat12 = await fsp7.stat(ref.filePath);
12873
+ if (!stat12.isFile()) {
12214
12874
  return {
12215
12875
  id: ref.id,
12216
12876
  title: "(damaged)",
12217
- startedAt: stat11.mtime.toISOString(),
12877
+ startedAt: stat12.mtime.toISOString(),
12218
12878
  model: "unknown",
12219
12879
  provider: "unknown",
12220
12880
  tokenTotal: 0
12221
12881
  };
12222
12882
  }
12223
- mtime = stat11.mtime.toISOString();
12883
+ mtime = stat12.mtime.toISOString();
12224
12884
  } catch {
12225
12885
  return null;
12226
12886
  }
@@ -12376,8 +13036,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
12376
13036
  const pruneFile = async (dir, name, prefix) => {
12377
13037
  const jsonlPath = path4.join(dir, name);
12378
13038
  try {
12379
- const stat11 = await fsp7.stat(jsonlPath);
12380
- if (stat11.mtimeMs >= cutoff) return;
13039
+ const stat12 = await fsp7.stat(jsonlPath);
13040
+ if (stat12.mtimeMs >= cutoff) return;
12381
13041
  } catch {
12382
13042
  return;
12383
13043
  }
@@ -13712,6 +14372,7 @@ var DEFAULT_TOOLS_CONFIG = Object.freeze({
13712
14372
  defaultExecutionStrategy: "smart",
13713
14373
  maxIterations: 100,
13714
14374
  iterationTimeoutMs: 3e5,
14375
+ maxToolTimeoutMs: 3e5,
13715
14376
  sessionTimeoutMs: 18e5,
13716
14377
  perIterationOutputCapBytes: 1e5,
13717
14378
  descriptionMode: Object.freeze({}),
@@ -14355,12 +15016,12 @@ function verifyFiles(tokens, files) {
14355
15016
  const violations = [];
14356
15017
  let onPalette = 0;
14357
15018
  let offPalette = 0;
14358
- for (const { path: path32, text } of files) {
15019
+ for (const { path: path33, text } of files) {
14359
15020
  const lines = text.split("\n");
14360
15021
  lines.forEach((lineText, i) => {
14361
15022
  const lineNo = i + 1;
14362
15023
  const flag = (snippet, reason) => {
14363
- violations.push({ file: path32, line: lineNo, snippet: snippet.slice(0, 80), reason });
15024
+ violations.push({ file: path33, line: lineNo, snippet: snippet.slice(0, 80), reason });
14364
15025
  };
14365
15026
  for (const re of [HEX_RE, FUNC_COLOR_RE]) {
14366
15027
  re.lastIndex = 0;
@@ -14412,14 +15073,14 @@ var SKIP_DIR = /* @__PURE__ */ new Set([
14412
15073
  ".design"
14413
15074
  ]);
14414
15075
  async function walkUiFiles(root, max = 200) {
14415
- const { default: fs16 } = await import('fs/promises');
15076
+ const { default: fs17 } = await import('fs/promises');
14416
15077
  const { default: nodePath } = await import('path');
14417
15078
  const found = [];
14418
15079
  async function rec(dir, depth) {
14419
15080
  if (found.length >= max || depth > 8) return;
14420
15081
  let entries;
14421
15082
  try {
14422
- entries = await fs16.readdir(dir, { withFileTypes: true });
15083
+ entries = await fs17.readdir(dir, { withFileTypes: true });
14423
15084
  } catch {
14424
15085
  return;
14425
15086
  }
@@ -14437,13 +15098,13 @@ async function walkUiFiles(root, max = 200) {
14437
15098
  return found;
14438
15099
  }
14439
15100
  async function runDesignVerify(projectRoot, tokens, explicitFiles) {
14440
- const { default: fs16 } = await import('fs/promises');
15101
+ const { default: fs17 } = await import('fs/promises');
14441
15102
  const { default: nodePath } = await import('path');
14442
15103
  const abs = explicitFiles && explicitFiles.length > 0 ? explicitFiles.map((f) => nodePath.isAbsolute(f) ? f : nodePath.join(projectRoot, f)) : await walkUiFiles(projectRoot);
14443
15104
  const files = [];
14444
15105
  for (const a of abs) {
14445
15106
  try {
14446
- files.push({ path: nodePath.relative(projectRoot, a), text: await fs16.readFile(a, "utf8") });
15107
+ files.push({ path: nodePath.relative(projectRoot, a), text: await fs17.readFile(a, "utf8") });
14447
15108
  } catch {
14448
15109
  }
14449
15110
  }
@@ -14574,10 +15235,10 @@ function makeDesignVerifyToolCallMiddleware() {
14574
15235
  const p = typeof input?.path === "string" ? input.path : "";
14575
15236
  if (!p || !detectFrontendFile(p)) return out;
14576
15237
  const ctx = out.ctx;
14577
- const { default: fs16 } = await import('fs/promises');
15238
+ const { default: fs17 } = await import('fs/promises');
14578
15239
  const { default: nodePath } = await import('path');
14579
15240
  const abs = nodePath.isAbsolute(p) ? p : nodePath.join(ctx.projectRoot, p);
14580
- const text = await fs16.readFile(abs, "utf8").catch(() => "");
15241
+ const text = await fs17.readFile(abs, "utf8").catch(() => "");
14581
15242
  if (!text) return out;
14582
15243
  const loader = getDesignKitLoader(ctx.projectRoot);
14583
15244
  const rawTokens = await loader.readTokens(state.activeKit);
@@ -15529,8 +16190,8 @@ ${recentJournal}` : "No prior iterations.",
15529
16190
  await saveGoal(this.goalPath, abandoned, this.opts.events);
15530
16191
  }
15531
16192
  try {
15532
- const { unlink: unlink15 } = await import('fs/promises');
15533
- await unlink15(this.goalPath);
16193
+ const { unlink: unlink16 } = await import('fs/promises');
16194
+ await unlink16(this.goalPath);
15534
16195
  } catch {
15535
16196
  }
15536
16197
  this.opts.onEternalStop?.();
@@ -16472,8 +17133,8 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
16472
17133
  });
16473
17134
  await Promise.race([
16474
17135
  drainPromise,
16475
- new Promise((resolve12) => {
16476
- drainTimer = setTimeout(resolve12, STREAM_DRAIN_TIMEOUT_MS);
17136
+ new Promise((resolve13) => {
17137
+ drainTimer = setTimeout(resolve13, STREAM_DRAIN_TIMEOUT_MS);
16477
17138
  })
16478
17139
  ]);
16479
17140
  } finally {
@@ -16584,7 +17245,7 @@ async function runProviderWithRetry(opts) {
16584
17245
  description
16585
17246
  });
16586
17247
  }
16587
- await new Promise((resolve12, reject) => {
17248
+ await new Promise((resolve13, reject) => {
16588
17249
  let settled = false;
16589
17250
  const cleanup = () => {
16590
17251
  clearTimeout(t);
@@ -16600,7 +17261,7 @@ async function runProviderWithRetry(opts) {
16600
17261
  if (settled) return;
16601
17262
  settled = true;
16602
17263
  cleanup();
16603
- resolve12();
17264
+ resolve13();
16604
17265
  }, delay);
16605
17266
  if (signal.aborted) {
16606
17267
  onAbort();
@@ -16750,7 +17411,14 @@ IMPORTANT: Total conversation (${totalTokens} tokens) exceeds budget (${effectiv
16750
17411
  raw = textBlocks.map((b) => b.text).join("\n").trim();
16751
17412
  } catch (err) {
16752
17413
  if (err instanceof Error) {
16753
- console.warn("[LLMSelector] selector call failed, using recency fallback:", err.message);
17414
+ console.warn(
17415
+ JSON.stringify({
17416
+ level: "warn",
17417
+ event: "llm_selector.call_failed",
17418
+ message: `selector call failed, using recency fallback: ${err.message}`,
17419
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
17420
+ })
17421
+ );
16754
17422
  }
16755
17423
  return this.fallbackSelect(messages, effectiveBudget);
16756
17424
  } finally {
@@ -19017,6 +19685,7 @@ var githubServer = () => ({
19017
19685
  transport: "stdio",
19018
19686
  command: "npx",
19019
19687
  args: ["-y", "@modelcontextprotocol/server-github"],
19688
+ passthroughEnv: ["GITHUB_PERSONAL_ACCESS_TOKEN", "GITHUB_TOKEN"],
19020
19689
  permission: "confirm"
19021
19690
  });
19022
19691
  var context7Server = () => ({
@@ -19032,6 +19701,7 @@ var braveSearchServer = () => ({
19032
19701
  transport: "stdio",
19033
19702
  command: "npx",
19034
19703
  args: ["-y", "@modelcontextprotocol/server-brave-search"],
19704
+ passthroughEnv: ["BRAVE_SEARCH_API_KEY"],
19035
19705
  permission: "confirm"
19036
19706
  });
19037
19707
  var blockServer = () => ({
@@ -19048,6 +19718,7 @@ var everArtServer = () => ({
19048
19718
  transport: "stdio",
19049
19719
  command: "npx",
19050
19720
  args: ["-y", "@modelcontextprotocol/server-everart"],
19721
+ passthroughEnv: ["EVERART_API_KEY"],
19051
19722
  permission: "confirm"
19052
19723
  });
19053
19724
  var slackServer = () => ({
@@ -19056,6 +19727,7 @@ var slackServer = () => ({
19056
19727
  transport: "stdio",
19057
19728
  command: "npx",
19058
19729
  args: ["-y", "@modelcontextprotocol/server-slack"],
19730
+ passthroughEnv: ["SLACK_BOT_TOKEN", "SLACK_TEAM_ID"],
19059
19731
  permission: "confirm"
19060
19732
  });
19061
19733
  var awsServer = () => ({
@@ -19064,6 +19736,7 @@ var awsServer = () => ({
19064
19736
  transport: "stdio",
19065
19737
  command: "npx",
19066
19738
  args: ["-y", "@modelcontextprotocol/server-aws"],
19739
+ passthroughEnv: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION", "AWS_SESSION_TOKEN"],
19067
19740
  permission: "confirm"
19068
19741
  });
19069
19742
  var googleMapsServer = () => ({
@@ -19072,6 +19745,7 @@ var googleMapsServer = () => ({
19072
19745
  transport: "stdio",
19073
19746
  command: "npx",
19074
19747
  args: ["-y", "@modelcontextprotocol/server-google-maps"],
19748
+ passthroughEnv: ["GOOGLE_MAPS_API_KEY"],
19075
19749
  permission: "confirm"
19076
19750
  });
19077
19751
  var sentinelServer = () => ({
@@ -19089,6 +19763,7 @@ var zaiVisionServer = () => ({
19089
19763
  command: "npx",
19090
19764
  args: ["-y", "@z_ai/mcp-server@latest"],
19091
19765
  env: { Z_AI_MODE: "ZAI" },
19766
+ passthroughEnv: ["Z_AI_API_KEY"],
19092
19767
  allowedTools: [
19093
19768
  "image_analysis",
19094
19769
  "extract_text_from_screenshot",
@@ -19118,6 +19793,7 @@ var miniMaxVisionServer = () => ({
19118
19793
  MINIMAX_API_HOST: "https://api.minimax.io",
19119
19794
  MINIMAX_API_RESOURCE_MODE: "url"
19120
19795
  },
19796
+ passthroughEnv: ["MINIMAX_API_KEY"],
19121
19797
  allowedTools: ["understand_image"],
19122
19798
  permission: "auto"
19123
19799
  });
@@ -19392,8 +20068,8 @@ async function loadProjectModes(modesDir) {
19392
20068
  for (const entry of entries) {
19393
20069
  if (!entry.endsWith(".md") && !entry.endsWith(".txt")) continue;
19394
20070
  const filePath = path4.join(modesDir, entry);
19395
- const stat11 = await fsp7.stat(filePath);
19396
- if (!stat11.isFile()) continue;
20071
+ const stat12 = await fsp7.stat(filePath);
20072
+ if (!stat12.isFile()) continue;
19397
20073
  const content = await fsp7.readFile(filePath, "utf8");
19398
20074
  const id = path4.basename(entry, path4.extname(entry));
19399
20075
  modes.push({
@@ -19452,10 +20128,19 @@ var FAMILY_BY_NPM = {
19452
20128
  "@ai-sdk/deepseek": "openai-compatible",
19453
20129
  "@ai-sdk/google": "google"
19454
20130
  };
20131
+ var FAMILY_BY_PROVIDER_ID = {
20132
+ "anthropic-oauth": "anthropic-oauth",
20133
+ "github-copilot": "github-copilot",
20134
+ "openai-codex": "openai-codex"
20135
+ };
19455
20136
  function classifyFamily(npm) {
19456
20137
  if (!npm) return "unsupported";
19457
20138
  return FAMILY_BY_NPM[npm] ?? "unsupported";
19458
20139
  }
20140
+ function classifyProviderFamily(p) {
20141
+ const byNpm = classifyFamily(p.npm);
20142
+ return byNpm !== "unsupported" ? byNpm : FAMILY_BY_PROVIDER_ID[p.id] ?? "unsupported";
20143
+ }
19459
20144
  var DefaultModelsRegistry = class {
19460
20145
  /** Merged (base + overlay) payload — what every reader sees. */
19461
20146
  payload;
@@ -19721,7 +20406,7 @@ var DefaultModelsRegistry = class {
19721
20406
  return {
19722
20407
  id: p.id,
19723
20408
  name: p.name,
19724
- family: classifyFamily(p.npm),
20409
+ family: classifyProviderFamily(p),
19725
20410
  apiBase: p.api,
19726
20411
  envVars: p.env ?? [],
19727
20412
  doc: p.doc,
@@ -19968,9 +20653,9 @@ var DefaultHealthRegistry = class {
19968
20653
  }
19969
20654
  async runOne(check) {
19970
20655
  let timer = null;
19971
- const timeout = new Promise((resolve12) => {
20656
+ const timeout = new Promise((resolve13) => {
19972
20657
  timer = setTimeout(
19973
- () => resolve12({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
20658
+ () => resolve13({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
19974
20659
  this.timeoutMs
19975
20660
  );
19976
20661
  });
@@ -20154,7 +20839,7 @@ async function startMetricsServer(opts) {
20154
20839
  const tls = opts.tls;
20155
20840
  const useHttps = !!(tls?.cert && tls?.key);
20156
20841
  const host = opts.host ?? "127.0.0.1";
20157
- const path32 = opts.path ?? "/metrics";
20842
+ const path33 = opts.path ?? "/metrics";
20158
20843
  const healthPath = opts.healthPath ?? "/healthz";
20159
20844
  const healthRegistry = opts.healthRegistry;
20160
20845
  const listener = (req, res) => {
@@ -20164,7 +20849,7 @@ async function startMetricsServer(opts) {
20164
20849
  return;
20165
20850
  }
20166
20851
  const url = req.url.split("?")[0];
20167
- if (url === path32) {
20852
+ if (url === path33) {
20168
20853
  let body;
20169
20854
  try {
20170
20855
  body = renderPrometheus(opts.sink.snapshot());
@@ -20210,14 +20895,14 @@ async function startMetricsServer(opts) {
20210
20895
  const { createServer } = await import('http');
20211
20896
  server = createServer(listener);
20212
20897
  }
20213
- await new Promise((resolve12, reject) => {
20898
+ await new Promise((resolve13, reject) => {
20214
20899
  const onError = (err) => {
20215
20900
  server.off("listening", onListening);
20216
20901
  reject(err);
20217
20902
  };
20218
20903
  const onListening = () => {
20219
20904
  server.off("error", onError);
20220
- resolve12();
20905
+ resolve13();
20221
20906
  };
20222
20907
  server.once("error", onError);
20223
20908
  server.once("listening", onListening);
@@ -20228,9 +20913,9 @@ async function startMetricsServer(opts) {
20228
20913
  const protocol = useHttps ? "https" : "http";
20229
20914
  return {
20230
20915
  port: boundPort,
20231
- url: `${protocol}://${host}:${boundPort}${path32}`,
20232
- close: () => new Promise((resolve12, reject) => {
20233
- server.close((err) => err ? reject(err) : resolve12());
20916
+ url: `${protocol}://${host}:${boundPort}${path33}`,
20917
+ close: () => new Promise((resolve13, reject) => {
20918
+ server.close((err) => err ? reject(err) : resolve13());
20234
20919
  })
20235
20920
  };
20236
20921
  }
@@ -20699,15 +21384,15 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
20699
21384
  maxId = id;
20700
21385
  }
20701
21386
  }
20702
- const path32 = [];
21387
+ const path33 = [];
20703
21388
  let current = maxId;
20704
21389
  const visited = /* @__PURE__ */ new Set();
20705
21390
  while (current && !visited.has(current)) {
20706
21391
  visited.add(current);
20707
- path32.unshift(current);
21392
+ path33.unshift(current);
20708
21393
  current = prev.get(current) ?? null;
20709
21394
  }
20710
- return path32;
21395
+ return path33;
20711
21396
  }
20712
21397
  function computeParallelGroups(graph, blockedByMap) {
20713
21398
  const groups = [];
@@ -21773,9 +22458,9 @@ var AISpecBuilder = class {
21773
22458
  if (!this.sessionPath) return;
21774
22459
  try {
21775
22460
  const fsp20 = await import('fs/promises');
21776
- const path32 = await import('path');
22461
+ const path33 = await import('path');
21777
22462
  const { atomicWrite: atomicWrite2 } = await Promise.resolve().then(() => (init_atomic_write(), atomic_write_exports));
21778
- await fsp20.mkdir(path32.dirname(this.sessionPath), { recursive: true });
22463
+ await fsp20.mkdir(path33.dirname(this.sessionPath), { recursive: true });
21779
22464
  await atomicWrite2(this.sessionPath, JSON.stringify(this.session, null, 2));
21780
22465
  } catch {
21781
22466
  }
@@ -25984,7 +26669,7 @@ function makeCommandVerifier(options = {}) {
25984
26669
  return async function verifyTask(info) {
25985
26670
  const cmd = info.task.metadata?.[metadataKey];
25986
26671
  if (typeof cmd !== "string" || !cmd.trim()) return { ok: true };
25987
- return await new Promise((resolve12) => {
26672
+ return await new Promise((resolve13) => {
25988
26673
  const isWindows = process.platform === "win32";
25989
26674
  const [shell, ...shellArgs] = isWindows ? ["cmd", "/d", "/c"] : ["sh", "-c"];
25990
26675
  const child = spawn(shell, [...shellArgs, cmd], {
@@ -25997,19 +26682,19 @@ function makeCommandVerifier(options = {}) {
25997
26682
  const timer = setTimeout(() => {
25998
26683
  timedOut = true;
25999
26684
  child.kill();
26000
- resolve12({ ok: false, reason: `verification timed out: ${cmd}` });
26685
+ resolve13({ ok: false, reason: `verification timed out: ${cmd}` });
26001
26686
  }, timeoutMs);
26002
26687
  child.on("exit", (code) => {
26003
26688
  clearTimeout(timer);
26004
26689
  if (timedOut) return;
26005
- resolve12(
26690
+ resolve13(
26006
26691
  code === 0 ? { ok: true } : { ok: false, reason: `verification failed (exit ${code}): ${cmd}` }
26007
26692
  );
26008
26693
  });
26009
26694
  child.on("error", (err) => {
26010
26695
  clearTimeout(timer);
26011
26696
  if (timedOut) return;
26012
- resolve12({ ok: false, reason: `verification spawn error: ${String(err)}` });
26697
+ resolve13({ ok: false, reason: `verification spawn error: ${String(err)}` });
26013
26698
  });
26014
26699
  });
26015
26700
  };
@@ -26110,8 +26795,8 @@ function pathLooksInsideProject(rawPath, projectRoot) {
26110
26795
  if (!projectRoot) return false;
26111
26796
  if (rawPath === "~" || rawPath.startsWith("~/") || rawPath.startsWith("~\\")) return false;
26112
26797
  const resolved = path4.resolve(projectRoot, rawPath);
26113
- const relative4 = path4.relative(projectRoot, resolved);
26114
- return !!relative4 && !relative4.startsWith("..") && !path4.isAbsolute(relative4);
26798
+ const relative5 = path4.relative(projectRoot, resolved);
26799
+ return !!relative5 && !relative5.startsWith("..") && !path4.isAbsolute(relative5);
26115
26800
  }
26116
26801
  function tokenizeShell(command) {
26117
26802
  return command.match(/"[^"]*"|'[^']*'|\S+/g)?.map((token) => token.replace(/^['"]|['"]$/g, "")) ?? [];
@@ -26427,6 +27112,12 @@ var DefaultPermissionPolicy = class {
26427
27112
  return false;
26428
27113
  }
26429
27114
  isDestructiveYoloCall(tool, input, ctx) {
27115
+ if (tool.name === "mcp_control") {
27116
+ const action = getInputString(input, "action");
27117
+ if (action === "list" || action === "search" || action === "activate" || action === "deactivate") {
27118
+ return false;
27119
+ }
27120
+ }
26430
27121
  if (this.isDestructiveByCapability(tool)) {
26431
27122
  const caps = tool.capabilities ?? [];
26432
27123
  if (caps.includes(ToolCapabilities.SHELL_ARBITRARY) || caps.includes(ToolCapabilities.SHELL_RESTRICTED) || caps.includes(ToolCapabilities.SHELL_EXEC)) {
@@ -26833,8 +27524,8 @@ function unwrapDataKey(buf, keyFile) {
26833
27524
  function checkKeyFilePermissions(keyFile) {
26834
27525
  if (process.platform === "win32") return;
26835
27526
  try {
26836
- const stat11 = fs12.statSync(keyFile);
26837
- const actualMode = stat11.mode & 511;
27527
+ const stat12 = fs13.statSync(keyFile);
27528
+ const actualMode = stat12.mode & 511;
26838
27529
  if (actualMode !== KEY_FILE_MODE) {
26839
27530
  console.warn(JSON.stringify({
26840
27531
  level: "warn",
@@ -26922,16 +27613,16 @@ var DefaultSecretVault = class {
26922
27613
  const oldVersion = this._keyVersion;
26923
27614
  const newKey = randomBytes(KEY_BYTES);
26924
27615
  const newVersion = oldVersion + 1;
26925
- fs12.mkdirSync(path4.dirname(this.keyFile), { recursive: true });
27616
+ fs13.mkdirSync(path4.dirname(this.keyFile), { recursive: true });
26926
27617
  const passphrase = getVaultPassphrase();
26927
27618
  if (passphrase) {
26928
- fs12.writeFileSync(this.keyFile, wrapDataKey(newKey, newVersion, passphrase), { mode: 384 });
27619
+ fs13.writeFileSync(this.keyFile, wrapDataKey(newKey, newVersion, passphrase), { mode: 384 });
26929
27620
  } else {
26930
27621
  const keyFileBuf = Buffer.alloc(VERSIONED_KEY_FILE_SIZE);
26931
27622
  KEY_FILE_MAGIC.copy(keyFileBuf, 0);
26932
27623
  keyFileBuf[KEY_FILE_MAGIC.length] = newVersion;
26933
27624
  newKey.copy(keyFileBuf, KEY_FILE_MAGIC.length + 1);
26934
- fs12.writeFileSync(this.keyFile, keyFileBuf, { mode: 384 });
27625
+ fs13.writeFileSync(this.keyFile, keyFileBuf, { mode: 384 });
26935
27626
  }
26936
27627
  checkKeyFilePermissions(this.keyFile);
26937
27628
  this.key = newKey;
@@ -26949,7 +27640,7 @@ var DefaultSecretVault = class {
26949
27640
  const passphrase = getVaultPassphrase();
26950
27641
  if (!passphrase || !this.key) return;
26951
27642
  try {
26952
- fs12.writeFileSync(this.keyFile, wrapDataKey(this.key, this._keyVersion, passphrase), {
27643
+ fs13.writeFileSync(this.keyFile, wrapDataKey(this.key, this._keyVersion, passphrase), {
26953
27644
  mode: 384
26954
27645
  });
26955
27646
  checkKeyFilePermissions(this.keyFile);
@@ -26959,7 +27650,7 @@ var DefaultSecretVault = class {
26959
27650
  loadOrCreateKey() {
26960
27651
  if (this.key) return this.key;
26961
27652
  try {
26962
- const buf = fs12.readFileSync(this.keyFile);
27653
+ const buf = fs13.readFileSync(this.keyFile);
26963
27654
  if (isWrappedKeyFile(buf)) {
26964
27655
  const { key: key2, version } = unwrapDataKey(buf, this.keyFile);
26965
27656
  this.key = key2;
@@ -27006,15 +27697,15 @@ var DefaultSecretVault = class {
27006
27697
  } catch (err) {
27007
27698
  if (err.code !== "ENOENT") throw err;
27008
27699
  }
27009
- fs12.mkdirSync(path4.dirname(this.keyFile), { recursive: true });
27700
+ fs13.mkdirSync(path4.dirname(this.keyFile), { recursive: true });
27010
27701
  const key = randomBytes(KEY_BYTES);
27011
27702
  const passphrase = getVaultPassphrase();
27012
27703
  const initialBytes = passphrase ? wrapDataKey(key, 1, passphrase) : key;
27013
27704
  try {
27014
- fs12.writeFileSync(this.keyFile, initialBytes, { mode: 384, flag: "wx" });
27705
+ fs13.writeFileSync(this.keyFile, initialBytes, { mode: 384, flag: "wx" });
27015
27706
  } catch (err) {
27016
27707
  if (err.code !== "EEXIST") throw err;
27017
- const buf = fs12.readFileSync(this.keyFile);
27708
+ const buf = fs13.readFileSync(this.keyFile);
27018
27709
  if (isWrappedKeyFile(buf)) {
27019
27710
  const { key: winnerKey, version } = unwrapDataKey(buf, this.keyFile);
27020
27711
  this.key = winnerKey;
@@ -27409,6 +28100,7 @@ var BEHAVIOR_DEFAULTS = {
27409
28100
  defaultExecutionStrategy: DEFAULT_TOOLS_CONFIG.defaultExecutionStrategy,
27410
28101
  maxIterations: DEFAULT_TOOLS_CONFIG.maxIterations,
27411
28102
  iterationTimeoutMs: DEFAULT_TOOLS_CONFIG.iterationTimeoutMs,
28103
+ maxToolTimeoutMs: DEFAULT_TOOLS_CONFIG.maxToolTimeoutMs,
27412
28104
  sessionTimeoutMs: DEFAULT_TOOLS_CONFIG.sessionTimeoutMs,
27413
28105
  perIterationOutputCapBytes: DEFAULT_TOOLS_CONFIG.perIterationOutputCapBytes,
27414
28106
  descriptionMode: DEFAULT_TOOLS_CONFIG.descriptionMode,
@@ -28057,8 +28749,8 @@ var DefaultConfigLoader = class {
28057
28749
  const t0 = Date.now();
28058
28750
  let mtimeMs = null;
28059
28751
  try {
28060
- const stat11 = await fsp7.stat(file);
28061
- mtimeMs = stat11.mtimeMs;
28752
+ const stat12 = await fsp7.stat(file);
28753
+ mtimeMs = stat12.mtimeMs;
28062
28754
  const cached = this.jsonCache.get(file);
28063
28755
  if (cached && cached.mtimeMs === mtimeMs) {
28064
28756
  return structuredClone(cached.value);
@@ -28501,8 +29193,8 @@ var FileMemoryBackend = class {
28501
29193
  }
28502
29194
  async getMtime(file) {
28503
29195
  try {
28504
- const stat11 = await fsp7.stat(file);
28505
- return stat11.mtimeMs;
29196
+ const stat12 = await fsp7.stat(file);
29197
+ return stat12.mtimeMs;
28506
29198
  } catch {
28507
29199
  return 0;
28508
29200
  }
@@ -30104,8 +30796,8 @@ var DefaultSessionReader = class _DefaultSessionReader {
30104
30796
  const sessionPath = sessionScopedPath(rootDir, sessionId, ".jsonl");
30105
30797
  let mtimeMs = null;
30106
30798
  try {
30107
- const stat11 = await fsp7.stat(sessionPath);
30108
- mtimeMs = stat11.mtimeMs;
30799
+ const stat12 = await fsp7.stat(sessionPath);
30800
+ mtimeMs = stat12.mtimeMs;
30109
30801
  } catch {
30110
30802
  this.eventCache.delete(sessionId);
30111
30803
  this.eventCacheMtimes.delete(sessionId);