@mu-crew/mu 3.2.1 → 3.4.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.
Files changed (60) hide show
  1. package/AGENTS.md +10 -5
  2. package/README.md +83 -22
  3. package/dist/cli.js +294 -98
  4. package/dist/cli.js.map +1 -1
  5. package/dist/extension/mu-pi.js +346 -85
  6. package/dist/extension/mu-pi.js.map +1 -1
  7. package/dist/index.d.ts +68 -13
  8. package/dist/index.js +159 -71
  9. package/dist/index.js.map +1 -1
  10. package/docs/ARCHITECTURE.md +1 -0
  11. package/docs/VISION.md +3 -1
  12. package/docs/VOCABULARY.md +6 -1
  13. package/docs/architecture/control-socket.md +17 -3
  14. package/docs/architecture/dag.md +3 -3
  15. package/docs/architecture/mux.md +3 -4
  16. package/docs/guide/README.md +5 -0
  17. package/docs/guide/delegate.md +30 -3
  18. package/docs/guide/dispatch.md +11 -4
  19. package/docs/guide/getting-started.md +3 -0
  20. package/docs/guide/recovery.md +3 -1
  21. package/docs/guide/remote.md +1 -1
  22. package/docs/history/CHANGELOG-pre-3.md +1 -1
  23. package/docs/reference/env.md +2 -1
  24. package/docs/reference/naming.md +18 -0
  25. package/docs/specs/2026-09-29-task-substates.md +1 -1
  26. package/docs/specs/2026-10-03-keep-driving-nudge.md +142 -0
  27. package/package.json +2 -1
  28. package/prompts/mu-debug.md +8 -0
  29. package/prompts/mu-refute.md +8 -0
  30. package/prompts/mu-research.md +8 -0
  31. package/prompts/mu-review.md +8 -0
  32. package/prompts/mu-rules-audit.md +8 -0
  33. package/prompts/mu-sweep.md +8 -0
  34. package/prompts/mu-tournament.md +9 -0
  35. package/prompts/mu-until.md +8 -0
  36. package/prompts/ultrathink.md +10 -0
  37. package/skills/mu/SKILL.md +124 -145
  38. package/skills/mu/recipes/adversarial-review.md +91 -0
  39. package/skills/mu/recipes/backlog-triage.md +43 -0
  40. package/skills/mu/recipes/brief.md +53 -0
  41. package/skills/mu/recipes/codemode-driver.md +102 -0
  42. package/skills/mu/recipes/deep-research.md +67 -0
  43. package/skills/mu/recipes/fan-out.md +40 -0
  44. package/skills/mu/recipes/findings.md +103 -0
  45. package/skills/mu/recipes/hypothesis-panel.md +45 -0
  46. package/skills/mu/recipes/long-run.md +20 -0
  47. package/skills/mu/recipes/loop-until-done.md +42 -0
  48. package/skills/mu/recipes/orchestrator-loop.md +98 -0
  49. package/skills/mu/recipes/plan.md +59 -0
  50. package/skills/mu/recipes/recovery.md +28 -0
  51. package/skills/mu/recipes/refute.md +51 -0
  52. package/skills/mu/{REMOTE_WORKERS.md → recipes/remote-workers.md} +7 -2
  53. package/skills/mu/recipes/review-panel.md +62 -0
  54. package/skills/mu/recipes/rules-audit.md +83 -0
  55. package/skills/mu/recipes/tasks-or-calls.md +67 -0
  56. package/skills/mu/recipes/tournament.md +40 -0
  57. package/skills/mu/recipes/ultrathink.md +82 -0
  58. package/skills/mu/recipes/watcher.md +19 -0
  59. package/skills/mu/recipes/waves.md +20 -0
  60. package/skills/mu/recipes/worker.md +26 -0
package/dist/index.js CHANGED
@@ -19,6 +19,7 @@ import { createConnection } from "net";
19
19
 
20
20
  // src/ctl/protocol.ts
21
21
  var CTL_PROTOCOL_VERSION = 1;
22
+ var CTL_COMMANDS = ["new", "reload", "compact"];
22
23
  var UNKNOWN_OP_PREFIX = "unknown op: ";
23
24
  function encode(msg) {
24
25
  return `${JSON.stringify(msg)}
@@ -66,7 +67,7 @@ var CtlUnknownOpError = class extends Error {
66
67
  };
67
68
  function clientTimeout(req, override) {
68
69
  if (override !== void 0) return override;
69
- if (req.op === "fresh") return CTL_FRESH_TIMEOUT_MS;
70
+ if (req.op === "fresh" || req.op === "command") return CTL_FRESH_TIMEOUT_MS;
70
71
  if (req.op !== "wait") return CTL_DEFAULT_TIMEOUT_MS;
71
72
  return req.timeoutMs === void 0 ? void 0 : req.timeoutMs + CTL_WAIT_SLACK_MS;
72
73
  }
@@ -358,7 +359,7 @@ function isTaskStatus(s) {
358
359
  }
359
360
  var TASK_STATUS_LIST = TASK_STATUSES.join(" | ");
360
361
  var TASK_SUBSTATES = {
361
- OPEN: ["todo", "parked"],
362
+ OPEN: ["todo", "parked", "triage"],
362
363
  IN_PROGRESS: ["active"],
363
364
  CLOSED: ["done", "rejected", "wontfix", "duplicate", "superseded"]
364
365
  };
@@ -589,7 +590,7 @@ CREATE VIEW ready AS
589
590
  SELECT t.*
590
591
  FROM tasks t
591
592
  WHERE t.status = 'OPEN'
592
- AND t.substate <> 'parked'
593
+ AND t.substate NOT IN ('parked', 'triage')
593
594
  AND NOT EXISTS (
594
595
  SELECT 1
595
596
  FROM task_edges e
@@ -1339,27 +1340,6 @@ async function awaitPaneQuiescence(paneId, budgetMs) {
1339
1340
  await currentSleep(Math.min(SEND_READINESS_POLL_MS, remaining));
1340
1341
  }
1341
1342
  }
1342
- async function awaitNewSession(paneId, baseline, budgetMs) {
1343
- if (budgetMs <= 0) return true;
1344
- const deadline = Date.now() + budgetMs;
1345
- let previous = baseline;
1346
- let stable = 0;
1347
- for (; ; ) {
1348
- const scrollback = await capturePane(paneId, { lines: 50 }).catch(() => void 0);
1349
- if (scrollback !== void 0 && scrollback !== baseline) {
1350
- if (scrollback === previous) {
1351
- stable++;
1352
- if (stable >= QUIESCENCE_CONFIRMATIONS) return true;
1353
- } else {
1354
- stable = 0;
1355
- }
1356
- previous = scrollback;
1357
- }
1358
- const remaining = deadline - Date.now();
1359
- if (remaining <= 0) return false;
1360
- await currentSleep(Math.min(SEND_READINESS_POLL_MS, remaining));
1361
- }
1362
- }
1363
1343
  function pasteProbe(text) {
1364
1344
  const firstLine = (text.split("\n")[0] ?? "").trim();
1365
1345
  return firstLine.slice(0, 24);
@@ -1387,7 +1367,6 @@ async function sendToPane(paneId, text, opts = {}) {
1387
1367
  assertValidPaneId(paneId);
1388
1368
  const readinessMs = opts.readinessMs ?? defaultSendReadinessMs();
1389
1369
  const quiesced = await awaitPaneQuiescence(paneId, readinessMs);
1390
- const newSessionBaseline = readinessMs > 0 && text.trim() === "/new" ? await capturePane(paneId, { lines: 50 }).catch(() => void 0) : void 0;
1391
1370
  const copyResult = await currentExecutor(["copy-mode", "-q", "-t", paneId]);
1392
1371
  if (copyResult.exitCode !== 0 && /can't find pane|no current target/i.test(copyResult.stderr)) {
1393
1372
  throw new TmuxError(
@@ -1410,15 +1389,6 @@ async function sendToPane(paneId, text, opts = {}) {
1410
1389
  if (delay2 > 0) await currentSleep(delay2);
1411
1390
  await tmux(["send-keys", "-t", paneId, "Enter"]);
1412
1391
  if (readinessMs <= 0) return;
1413
- if (newSessionBaseline !== void 0 && !await awaitNewSession(paneId, newSessionBaseline, readinessMs)) {
1414
- const warn3 = opts.onUndelivered ?? defaultUndeliveredWarning;
1415
- warn3({
1416
- paneId,
1417
- reason: "transition-unconfirmed",
1418
- message: `send to ${paneId} submitted /new, but the new session did not become ready within ${readinessMs}ms. Wait for the pane to settle before sending more work.`
1419
- });
1420
- return;
1421
- }
1422
1392
  const probe = pasteProbe(text);
1423
1393
  if (probe.length === 0) return;
1424
1394
  for (let attempt = 0; attempt <= SUBMIT_RETRIES; attempt++) {
@@ -4590,6 +4560,30 @@ var TaskParkStateError = class extends Error {
4590
4560
  ];
4591
4561
  }
4592
4562
  };
4563
+ var TaskInTriageError = class extends Error {
4564
+ constructor(taskId, workstream) {
4565
+ super(`task ${taskId} is in triage (OPEN/triage); accept it or claim with --force`);
4566
+ this.taskId = taskId;
4567
+ this.workstream = workstream;
4568
+ }
4569
+ taskId;
4570
+ workstream;
4571
+ name = "TaskInTriageError";
4572
+ errorNextSteps() {
4573
+ const ws = this.workstream;
4574
+ return [
4575
+ {
4576
+ intent: "Accept it as work, then claim",
4577
+ command: `mu task accept ${this.taskId} -w ${ws}`
4578
+ },
4579
+ {
4580
+ intent: "Decline it",
4581
+ command: `mu task close ${this.taskId} --as rejected --why "<reason>" -w ${ws}`
4582
+ },
4583
+ { intent: "Claim it anyway", command: `mu task claim ${this.taskId} --force -w ${ws}` }
4584
+ ];
4585
+ }
4586
+ };
4593
4587
  var TaskParkedError = class extends Error {
4594
4588
  constructor(taskId, workstream) {
4595
4589
  super(`task ${taskId} is parked (OPEN/parked); unpark it or claim with --force`);
@@ -5045,8 +5039,17 @@ function addTaskImpl(db, opts) {
5045
5039
  const now = (/* @__PURE__ */ new Date()).toISOString();
5046
5040
  const insertResult = db.prepare(
5047
5041
  `INSERT INTO tasks (workstream_id, local_id, title, status, substate, impact, effort_days, created_at, updated_at)
5048
- VALUES (?, ?, ?, 'OPEN', 'todo', ?, ?, ?, ?)`
5049
- ).run(wsId, opts.localId, opts.title, opts.impact, opts.effortDays, now, now);
5042
+ VALUES (?, ?, ?, 'OPEN', ?, ?, ?, ?, ?)`
5043
+ ).run(
5044
+ wsId,
5045
+ opts.localId,
5046
+ opts.title,
5047
+ opts.triage === true ? "triage" : "todo",
5048
+ opts.impact,
5049
+ opts.effortDays,
5050
+ now,
5051
+ now
5052
+ );
5050
5053
  const newTaskId = Number(insertResult.lastInsertRowid);
5051
5054
  if (opts.blockedBy && opts.blockedBy.length > 0) {
5052
5055
  const blockerLookupSameWs = db.prepare(
@@ -5321,6 +5324,28 @@ function parkTask(db, localId, opts) {
5321
5324
  })()
5322
5325
  );
5323
5326
  }
5327
+ function acceptTask(db, localId, opts) {
5328
+ return withOpContext(
5329
+ db,
5330
+ { intent: "task.accept", group: "new" },
5331
+ () => db.transaction(() => {
5332
+ const before = getTask(db, localId, opts.workstream);
5333
+ if (!before) throw new TaskNotFoundError(localId);
5334
+ if (before.status !== "OPEN" || before.substate !== "triage") {
5335
+ return {
5336
+ previousStatus: before.status,
5337
+ status: before.status,
5338
+ previousSubstate: before.substate,
5339
+ substate: before.substate,
5340
+ changed: false
5341
+ };
5342
+ }
5343
+ const r = setTaskStatus(db, localId, "OPEN", { workstream: opts.workstream });
5344
+ recordEvidenceNote(db, localId, before.workstreamName, "ACCEPT", opts);
5345
+ return r;
5346
+ })()
5347
+ );
5348
+ }
5324
5349
  function unparkTask(db, localId, opts) {
5325
5350
  return withOpContext(
5326
5351
  db,
@@ -5451,6 +5476,9 @@ function assertNotParked(task, opts) {
5451
5476
  if (task.status === "OPEN" && task.substate === "parked" && opts.force !== true) {
5452
5477
  throw new TaskParkedError(task.name, task.workstreamName);
5453
5478
  }
5479
+ if (task.status === "OPEN" && task.substate === "triage" && opts.force !== true) {
5480
+ throw new TaskInTriageError(task.name, task.workstreamName);
5481
+ }
5454
5482
  }
5455
5483
  async function resolveActorIdentity() {
5456
5484
  const worker = await resolveWorkerIdentity();
@@ -6485,7 +6513,7 @@ async function spawnAgent(db, opts) {
6485
6513
  agent = created.row;
6486
6514
  const startAgent = (await activeMux()).startAgentInPane;
6487
6515
  if (startAgent === void 0) {
6488
- await awaitSpawnLiveness(paneId, opts.name, opts.workstream, command);
6516
+ await awaitSpawnLiveness(paneId, opts.name, command);
6489
6517
  } else {
6490
6518
  await startAgent({ paneId, name: opts.name, cli, command, commandSource });
6491
6519
  }
@@ -6625,7 +6653,7 @@ function detectSpawnStartupError(scrollback) {
6625
6653
  }
6626
6654
  return void 0;
6627
6655
  }
6628
- async function awaitSpawnLiveness(paneId, agentName, workstreamName, command) {
6656
+ async function awaitSpawnLiveness(paneId, agentName, command) {
6629
6657
  const ms = defaultSpawnLivenessMs();
6630
6658
  if (ms === 0) return;
6631
6659
  await sleep(ms);
@@ -6640,31 +6668,6 @@ async function awaitSpawnLiveness(paneId, agentName, workstreamName, command) {
6640
6668
  throw new AgentSpawnStartupError(agentName, paneId, matchedLine, scrollback);
6641
6669
  }
6642
6670
  }
6643
- await awaitSpawnReadiness(paneId, agentName, workstreamName, command);
6644
- }
6645
- function defaultSpawnReadinessMs() {
6646
- const raw = process.env.MU_SPAWN_READINESS_MS;
6647
- if (raw === void 0) return 1e4;
6648
- const parsed = Number.parseInt(raw, 10);
6649
- if (Number.isNaN(parsed) || parsed < 0) return 1e4;
6650
- return parsed;
6651
- }
6652
- var READINESS_POLL_INTERVAL_MS = 250;
6653
- async function awaitSpawnReadiness(paneId, agentName, workstreamName, command) {
6654
- const budgetMs = defaultSpawnReadinessMs();
6655
- if (budgetMs === 0 || !murmurAvailable()) return;
6656
- const mux = await activeMux();
6657
- const deadline = Date.now() + budgetMs;
6658
- const agent = { name: agentName, workstreamName, paneId };
6659
- while (Date.now() < deadline) {
6660
- if (!await mux.paneExists(paneId)) {
6661
- const scrollback = await mux.capturePane(paneId, { lines: 50 }).catch(() => void 0);
6662
- throw new AgentDiedOnSpawnError(agentName, paneId, scrollback, command);
6663
- }
6664
- const reading = (await readAgentStates([agent])).get(agentKey(agent));
6665
- if (reading?.source === "murmur") return;
6666
- await sleep(Math.min(READINESS_POLL_INTERVAL_MS, Math.max(0, deadline - Date.now())));
6667
- }
6668
6671
  }
6669
6672
  async function createOrReusePane(opts) {
6670
6673
  const mux = await activeMux();
@@ -7087,23 +7090,47 @@ var AgentExtensionOutdatedError = class extends Error {
7087
7090
  }
7088
7091
  };
7089
7092
  var AgentBusyError = class extends Error {
7090
- constructor(agentName, workstream) {
7091
- super(`agent ${agentName} is busy: --fresh would abandon its running turn`);
7093
+ constructor(agentName, workstream, command) {
7094
+ super(`agent ${agentName} is busy: ${command ?? "--fresh"} would abandon its running turn`);
7092
7095
  this.agentName = agentName;
7093
7096
  this.workstream = workstream;
7097
+ this.command = command;
7094
7098
  }
7095
7099
  agentName;
7096
7100
  workstream;
7101
+ command;
7097
7102
  name = "AgentBusyError";
7098
7103
  errorNextSteps() {
7104
+ const retry = this.command === void 0 ? `mu agent send ${this.agentName} --fresh --force '...' -w ${this.workstream}` : `mu agent send ${this.agentName} '${this.command}' --force -w ${this.workstream}`;
7099
7105
  return [
7100
7106
  {
7101
7107
  intent: "Stop the turn first, then retry",
7102
7108
  command: `mu agent abort ${this.agentName} -w ${this.workstream}`
7103
7109
  },
7110
+ { intent: "Or abandon the turn in one step", command: retry }
7111
+ ];
7112
+ }
7113
+ };
7114
+ var AgentSlashCommandUnsupportedError = class extends Error {
7115
+ constructor(agentName, workstream, text, supported) {
7116
+ super(
7117
+ `agent ${agentName} runs pi: over the control socket mu runs only ${supported.join(", ")}; ${JSON.stringify(text.split("\n")[0])} is not one of them. Pass --via mux to type it into the pane`
7118
+ );
7119
+ this.agentName = agentName;
7120
+ this.workstream = workstream;
7121
+ this.text = text;
7122
+ this.supported = supported;
7123
+ }
7124
+ agentName;
7125
+ workstream;
7126
+ text;
7127
+ supported;
7128
+ name = "AgentSlashCommandUnsupportedError";
7129
+ errorNextSteps() {
7130
+ return [
7104
7131
  {
7105
- intent: "Or abandon the turn in one step",
7106
- command: `mu agent send ${this.agentName} --fresh --force '...' -w ${this.workstream}`
7132
+ intent: "Type it into the pane on purpose (tmux paste; unconfirmed)",
7133
+ command: `mu agent send ${this.agentName} '${this.text.split("\n")[0]}' --via mux -w ${this.workstream}`
7107
7134
  }
7108
7135
  ];
7109
7136
  }
@@ -7116,10 +7143,22 @@ function agentCtlSocket(db, agent) {
7116
7143
  function expectsCtl(agent, sock = ctlSocketPath(agent.workstreamName, agent.name)) {
7117
7144
  return speaksMuCtl(agent.cli, resolveCliCommand(agent.cli)) || existsSync8(sock);
7118
7145
  }
7119
- function chooseTransport(agent, text, sock) {
7120
- if (text.startsWith("/")) return "mux";
7146
+ function chooseTransport(agent, sock) {
7121
7147
  return expectsCtl(agent, sock) ? "ctl" : "mux";
7122
7148
  }
7149
+ var CTL_SLASH_COMMANDS = CTL_COMMANDS.map((n) => `/${n}`);
7150
+ function parseSessionCommand(text) {
7151
+ const t = text.trim();
7152
+ if (t === "/new") return { name: "new" };
7153
+ if (t === "/reload") return { name: "reload" };
7154
+ const m = /^\/compact(?:\s+([\s\S]*))?$/.exec(t);
7155
+ if (!m) return void 0;
7156
+ const instructions = m[1]?.trim();
7157
+ return instructions ? { name: "compact", instructions } : { name: "compact" };
7158
+ }
7159
+ function isSlashCommand(text) {
7160
+ return /^\/[A-Za-z][\w:-]*(?:\s|$)/.test(text.trimStart());
7161
+ }
7123
7162
  async function extensionOutdated(agent, sock, e) {
7124
7163
  let extVersion;
7125
7164
  try {
@@ -7148,11 +7187,23 @@ async function sendViaTransport(agent, text, opts) {
7148
7187
  if (opts.fresh && (opts.via === "mux" || !expectsCtl(agent, sock))) {
7149
7188
  throw new AgentFreshNeedsCtlError(agent.name, agent.workstreamName, agent.cli);
7150
7189
  }
7151
- const transport = opts.fresh ? "ctl" : opts.via ?? chooseTransport(agent, text, sock);
7190
+ const transport = opts.fresh ? "ctl" : opts.via ?? chooseTransport(agent, sock);
7152
7191
  if (transport === "mux") {
7153
7192
  await (await activeMux()).sendToPane(agent.paneId, text, opts);
7154
7193
  return { transport };
7155
7194
  }
7195
+ if (!opts.fresh) {
7196
+ const cmd = parseSessionCommand(text);
7197
+ if (cmd) return runSessionCommand(agent, sock, cmd, opts.force === true);
7198
+ if (isSlashCommand(text)) {
7199
+ throw new AgentSlashCommandUnsupportedError(
7200
+ agent.name,
7201
+ agent.workstreamName,
7202
+ text,
7203
+ CTL_SLASH_COMMANDS
7204
+ );
7205
+ }
7206
+ }
7156
7207
  const reply = await ctlRequestFor(
7157
7208
  agent,
7158
7209
  sock,
@@ -7166,6 +7217,25 @@ async function sendViaTransport(agent, text, opts) {
7166
7217
  }
7167
7218
  return reply.state === void 0 ? { transport } : { transport, state: reply.state };
7168
7219
  }
7220
+ async function runSessionCommand(agent, sock, cmd, force) {
7221
+ const reply = await ctlRequestFor(agent, sock, {
7222
+ op: "command",
7223
+ name: cmd.name,
7224
+ ...cmd.instructions !== void 0 ? { instructions: cmd.instructions } : {},
7225
+ ...force ? { force: true } : {}
7226
+ });
7227
+ if (!reply.ok) {
7228
+ if (reply.error === "busy") {
7229
+ throw new AgentBusyError(agent.name, agent.workstreamName, `/${cmd.name}`);
7230
+ }
7231
+ throw new Error(`pi refused /${cmd.name}: ${reply.error}`);
7232
+ }
7233
+ return {
7234
+ transport: "ctl",
7235
+ command: cmd.name,
7236
+ ...reply.state !== void 0 ? { state: reply.state } : {}
7237
+ };
7238
+ }
7169
7239
 
7170
7240
  // src/agent-state.ts
7171
7241
  var CTL_STATE_PROBE_MS = 1e3;
@@ -8948,6 +9018,15 @@ function murmurDoctorCheck(agentStateSource, opts = {}) {
8948
9018
  };
8949
9019
  }
8950
9020
  var RELINK = "run mu link pi";
9021
+ function extFeatures(env) {
9022
+ const features = [
9023
+ ["mu_delegate", "MU_DELEGATE"],
9024
+ ["nudge", "MU_NUDGE"]
9025
+ ];
9026
+ const on = ["ctl", ...features.filter(([, v]) => env[v] !== "0").map(([f]) => f)];
9027
+ const off = features.filter(([, v]) => env[v] === "0").map(([f, v]) => `${f} disabled by ${v}=0`);
9028
+ return `(${on.join(", ")}${off.length ? `; ${off.join("; ")}` : ""})`;
9029
+ }
8951
9030
  function ctlExtensionDoctorCheck(opts) {
8952
9031
  const { extension } = inspectLinks(opts);
8953
9032
  switch (extension.state) {
@@ -8955,7 +9034,7 @@ function ctlExtensionDoctorCheck(opts) {
8955
9034
  return {
8956
9035
  name: "mu ext",
8957
9036
  status: "ok",
8958
- detail: process.env.MU_DELEGATE === "0" ? "(ctl; mu_delegate disabled by MU_DELEGATE=0)" : "(ctl, mu_delegate)"
9037
+ detail: extFeatures(process.env)
8959
9038
  };
8960
9039
  case "stale-copy":
8961
9040
  return { name: "mu ext", status: "warn", detail: `stale copy: ${RELINK}` };
@@ -10001,6 +10080,10 @@ async function loadWorkstreamSnapshotFast(db, workstream, opts = {}) {
10001
10080
  blocked: listBlocked(db, workstream),
10002
10081
  recentClosed: listRecentClosed(db, workstream),
10003
10082
  parkedCount: listTasks(db, workstream, { status: "OPEN", substate: "parked" }).length,
10083
+ triage: listTasks(db, workstream, { status: "OPEN", substate: "triage" }),
10084
+ taskCount: db.prepare(
10085
+ "SELECT count(*) AS n FROM tasks t JOIN workstreams w ON w.id = t.workstream_id WHERE w.name = ?"
10086
+ ).get(workstream).n,
10004
10087
  allTasks: opts.withAllTasks === true ? listTasks(db, workstream) : [],
10005
10088
  workspaces: listWorkspaces2(db, workstream),
10006
10089
  workspaceOrphans: listWorkspaceOrphans(db, workstream),
@@ -10074,6 +10157,8 @@ function minimalSnapshot(workstream) {
10074
10157
  blocked: [],
10075
10158
  recentClosed: [],
10076
10159
  parkedCount: 0,
10160
+ triage: [],
10161
+ taskCount: 0,
10077
10162
  allTasks: [],
10078
10163
  workspaces: [],
10079
10164
  workspaceOrphans: [],
@@ -10919,6 +11004,7 @@ export {
10919
11004
  AgentFreshNeedsCtlError,
10920
11005
  AgentNotFoundError,
10921
11006
  AgentNotInWorkstreamError,
11007
+ AgentSlashCommandUnsupportedError,
10922
11008
  AgentSpawnCliNotFoundError,
10923
11009
  AgentSpawnStartupError,
10924
11010
  CAPTURE_TRIGGER_DDL,
@@ -10969,6 +11055,7 @@ export {
10969
11055
  TASK_SUBSTATE_ROWS,
10970
11056
  TaskAlreadyOwnedError,
10971
11057
  TaskExistsError,
11058
+ TaskInTriageError,
10972
11059
  TaskNotFoundError,
10973
11060
  TaskNotInWorkstreamError,
10974
11061
  TaskParkStateError,
@@ -10985,6 +11072,7 @@ export {
10985
11072
  WorkstreamExistsError,
10986
11073
  WorkstreamNameInvalidError,
10987
11074
  abortAgent,
11075
+ acceptTask,
10988
11076
  activeMux,
10989
11077
  addBlockEdge,
10990
11078
  addNote,