@mu-crew/mu 3.12.0 → 3.13.1

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 (58) hide show
  1. package/dist/chunk-3BARPJLY.js +44 -0
  2. package/dist/{chunk-R2SVBZAB.js → chunk-45KQLMEL.js} +193 -93
  3. package/dist/chunk-45KQLMEL.js.map +1 -0
  4. package/dist/{chunk-XV7J56GD.js → chunk-G434JKV2.js} +4 -3
  5. package/dist/{chunk-XV7J56GD.js.map → chunk-G434JKV2.js.map} +1 -1
  6. package/dist/{chunk-IRPTNVJC.js → chunk-KUGUFBWH.js} +8622 -6026
  7. package/dist/chunk-KUGUFBWH.js.map +1 -0
  8. package/dist/{chunk-77GBZ2KR.js → chunk-QITLNZEM.js} +3 -1
  9. package/dist/{chunk-77GBZ2KR.js.map → chunk-QITLNZEM.js.map} +1 -1
  10. package/dist/{chunk-TEGZOXML.js → chunk-VIEYQMQA.js} +12 -10
  11. package/dist/chunk-VIEYQMQA.js.map +1 -0
  12. package/dist/{chunk-L2ABBTYG.js → chunk-WI6AWCMK.js} +3 -1
  13. package/dist/{chunk-L2ABBTYG.js.map → chunk-WI6AWCMK.js.map} +1 -1
  14. package/dist/{cli-MDCJEU74.js → cli-CKOMHBXV.js} +3581 -107
  15. package/dist/cli-CKOMHBXV.js.map +1 -0
  16. package/dist/cli.js +4 -3
  17. package/dist/cli.js.map +1 -1
  18. package/dist/devtools-LS4ZEPWM.js +3772 -0
  19. package/dist/devtools-LS4ZEPWM.js.map +1 -0
  20. package/dist/extension/mu-pi.js +206 -61
  21. package/dist/extension/mu-pi.js.map +1 -1
  22. package/dist/index.d.ts +309 -228
  23. package/dist/index.js +12 -5
  24. package/dist/tui-LFJ5P4Y2.js +40956 -0
  25. package/dist/tui-LFJ5P4Y2.js.map +1 -0
  26. package/dist/tui-sync-worker.js +4 -3
  27. package/dist/tui-sync-worker.js.map +1 -1
  28. package/dist/yank-XDOONQSL.js +13 -0
  29. package/docs/ARCHITECTURE.md +4 -1
  30. package/docs/ROADMAP.md +43 -38
  31. package/docs/VISION.md +8 -0
  32. package/docs/VOCABULARY.md +3 -1
  33. package/docs/architecture/dag.md +4 -0
  34. package/docs/architecture/sync.md +8 -0
  35. package/docs/architecture/tui.md +11 -2
  36. package/docs/guide/delegate.md +6 -4
  37. package/docs/guide/recovery.md +4 -0
  38. package/docs/reference/env.md +2 -2
  39. package/package.json +2 -1
  40. package/skills/mu/SKILL.md +12 -2
  41. package/skills/mu/recipes/adversarial-review.md +3 -0
  42. package/skills/mu/recipes/backlog-triage.md +2 -0
  43. package/skills/mu/recipes/findings.md +7 -0
  44. package/skills/mu/recipes/long-run.md +3 -1
  45. package/skills/mu/recipes/loop-until-done.md +3 -1
  46. package/skills/mu/recipes/orchestrator-loop.md +4 -0
  47. package/skills/mu/recipes/ultrathink.md +3 -1
  48. package/skills/mu/recipes/watcher.md +6 -1
  49. package/dist/chunk-IRPTNVJC.js.map +0 -1
  50. package/dist/chunk-PZ5AY32C.js +0 -10
  51. package/dist/chunk-R2SVBZAB.js.map +0 -1
  52. package/dist/chunk-TEGZOXML.js.map +0 -1
  53. package/dist/cli-MDCJEU74.js.map +0 -1
  54. package/dist/tui-LO75XA3O.js +0 -6452
  55. package/dist/tui-LO75XA3O.js.map +0 -1
  56. package/dist/yank-T3AYQTVR.js +0 -12
  57. /package/dist/{chunk-PZ5AY32C.js.map → chunk-3BARPJLY.js.map} +0 -0
  58. /package/dist/{yank-T3AYQTVR.js.map → yank-XDOONQSL.js.map} +0 -0
@@ -75,6 +75,7 @@ function delegateStatus(running, starting = 0, queued = 0, failed = 0, pending =
75
75
  var DELEGATE_TIMEOUT_S = 3600;
76
76
  var DELEGATE_MAX_DEFAULT = 16;
77
77
  var DELEGATE_QUEUE_FACTOR = 4;
78
+ var PRUNE_FAILED_MS = 3e4;
78
79
  function delegateMax(env = process.env) {
79
80
  const n = Number(env.MU_DELEGATE_MAX);
80
81
  return Number.isInteger(n) && n > 0 ? n : DELEGATE_MAX_DEFAULT;
@@ -188,13 +189,26 @@ ${agent.lastText ?? ""}`;
188
189
  ].join("\n");
189
190
  case "died":
190
191
  return `${who} died${took} before answering. ${fate}${ws}${evBlock}${tailBlock}`;
192
+ case "blocked": {
193
+ const b = agent.blocked;
194
+ const dialog = `${b?.kind ?? "dialog"}${b?.title ? ` "${b.title}"` : ""}`;
195
+ const lines = [
196
+ `${who} is blocked on a ${dialog} dialog${took} and has NOT finished; stopped waiting, so its answer will not arrive here. ${fate}${ws}`,
197
+ "Answer the dialog in its pane (attach), then wait again; or close it and record the check as UNVERIFIED.",
198
+ ...ev,
199
+ ...look,
200
+ `Wait again: mu agent wait ${name} -w ${W} --json${agent.afterRuns !== void 0 ? ` --after-runs ${agent.afterRuns}` : ""}`,
201
+ `Close: mu agent close ${name} -w ${W}`
202
+ ];
203
+ return `${lines.join("\n")}${tailBlock}`;
204
+ }
191
205
  case "timeout":
192
206
  case "pending":
193
207
  return [
194
208
  `${who} is still running after ${timeoutS}s; stopped waiting, so its answer will not arrive here. ${fate}${ws}`,
195
209
  ...ev,
196
210
  ...look,
197
- `Wait again: mu agent wait ${name} -w ${W} --json`
211
+ `Wait again: mu agent wait ${name} -w ${W} --json${agent.afterRuns !== void 0 ? ` --after-runs ${agent.afterRuns}` : ""}`
198
212
  ].join("\n");
199
213
  default:
200
214
  return `${who}: mu agent wait gave no result. ${fate}${ws}${evBlock}${tailBlock}`;
@@ -264,12 +278,13 @@ function modelCommand(cli, model, env = process.env) {
264
278
  return `${base} --model ${shellArg(model)}`;
265
279
  }
266
280
  function registerDelegate(pi, run = defaultRunner()) {
267
- if (!delegateEnabled()) return;
281
+ if (!delegateEnabled()) return { answerPending: () => false };
268
282
  const inflight = /* @__PURE__ */ new Map();
269
283
  let starting = 0;
270
284
  const queue = [];
271
285
  let queueSeq = 0;
272
286
  const failed = /* @__PURE__ */ new Set();
287
+ let lastPruneAt = 0;
273
288
  const arriving = /* @__PURE__ */ new Map();
274
289
  let deliverySeq = 0;
275
290
  let ui;
@@ -359,6 +374,9 @@ function registerDelegate(pi, run = defaultRunner()) {
359
374
  const failure = (what, r) => new Error(`${what} failed (exit ${r.code}): ${(r.stderr || r.stdout).trim()}`);
360
375
  function pruneFailed() {
361
376
  if (failed.size === 0) return;
377
+ const now = Date.now();
378
+ if (now - lastPruneAt < PRUNE_FAILED_MS) return;
379
+ lastPruneAt = now;
362
380
  void mu(["agent", "list", "-w", W, "--json"]).then((r) => {
363
381
  const agents = json(r)?.agents;
364
382
  if (!Array.isArray(agents)) return;
@@ -394,6 +412,12 @@ function registerDelegate(pi, run = defaultRunner()) {
394
412
  );
395
413
  }
396
414
  const sent = out.send;
415
+ if (sent?.ok !== true && sent?.error === "AgentSendIndeterminateError") {
416
+ const why = typeof sent.message === "string" ? sent.message : "no reply";
417
+ throw new Error(
418
+ `mu agent send to ${W}/${name}: delivery unknown (${why}). Pane kept; do NOT resend blindly. Check with: mu agent read ${name} -w ${W} -n 20 (or attach), then close it with: mu agent close ${name} -w ${W}`
419
+ );
420
+ }
397
421
  if (sent?.ok !== true) {
398
422
  const c = await mu(["agent", "close", name, "-w", W]);
399
423
  const pane = c.code === 0 ? "Pane closed." : `Pane kept; close it with: mu agent close ${name} -w ${W}`;
@@ -446,30 +470,32 @@ function registerDelegate(pi, run = defaultRunner()) {
446
470
  const outcome = agent?.outcome;
447
471
  const tail = outcome === "done" || outcome === "timeout" || outcome === "pending" || outcome === "error" ? void 0 : (await mu(["agent", "read", name, "-n", "50", "-w", W])).stdout;
448
472
  let pane = { closed: false, why: "keep: true" };
449
- if (outcome !== "done" && outcome !== "empty")
473
+ let closing;
474
+ if (outcome === "blocked") pane = { closed: false, why: "waiting on a dialog" };
475
+ else if (outcome !== "done" && outcome !== "empty")
450
476
  pane = { closed: false, why: "kept as evidence" };
451
- else if (!keep) {
452
- const c = await mu(["agent", "close", name, "-w", W]);
453
- pane = c.code === 0 ? { closed: true } : { closed: false, why: `close refused: ${(c.stderr || c.stdout).trim()}` };
454
- }
477
+ else if (!keep) closing = mu(["agent", "close", name, "-w", W]);
478
+ const recording = record ? writeRecord(
479
+ record.target,
480
+ name,
481
+ recordNote(record.label, name, outcome, agent?.lastText, extras.elapsedMs ?? 0)
482
+ ) : void 0;
483
+ const [closed, recorded] = await Promise.all([closing, recording]);
484
+ if (closed)
485
+ pane = closed.code === 0 ? { closed: true } : { closed: false, why: `close refused: ${(closed.stderr || closed.stdout).trim()}` };
455
486
  if (outcome === "error" && !pane.closed) {
456
487
  failed.add(name);
457
488
  showStatus();
458
489
  }
459
490
  if (outcome !== "done") {
460
491
  const sessionFile = agent?.sessionFile ?? extras.sessionFile;
461
- const partial = sessionFile ? partialCopy(name, sessionFile) : void 0;
492
+ const partial = sessionFile && outcome !== "blocked" ? partialCopy(name, sessionFile) : void 0;
462
493
  extras = {
463
494
  ...extras,
464
495
  ...sessionFile ? { sessionFile } : {},
465
496
  ...partial ? { partial } : {}
466
497
  };
467
498
  }
468
- const recorded = record ? await writeRecord(
469
- record.target,
470
- name,
471
- recordNote(record.label, name, outcome, agent?.lastText, extras.elapsedMs ?? 0)
472
- ) : void 0;
473
499
  const message = delegateMessage(name, agent, pane, { ...extras, ...tail ? { tail } : {} });
474
500
  const content = recorded ? `${message}
475
501
 
@@ -868,6 +894,7 @@ ${evidence.join("\n")}`;
868
894
  else process.stderr.write(`mu: ${msg}
869
895
  `);
870
896
  });
897
+ return { answerPending: () => arriving.size > 0 };
871
898
  }
872
899
 
873
900
  // extension/nudge.ts
@@ -881,6 +908,10 @@ var MARK_START = "<!-- mu:keep-driving -->";
881
908
  var MARK_END = "<!-- /mu:keep-driving -->";
882
909
  var WAIT_HINT_MAX = 8;
883
910
  var notNested = () => false;
911
+ var noHold = () => void 0;
912
+ function withQueuedInput(hold) {
913
+ return (ctx) => ctx?.hasPendingMessages?.() ? "answer" : hold(ctx);
914
+ }
884
915
  function noUI(ctx) {
885
916
  return ctx?.hasUI === false;
886
917
  }
@@ -1053,25 +1084,29 @@ function dispatchedTasks(command) {
1053
1084
  return out;
1054
1085
  }
1055
1086
  async function inProgress(run, workstreams) {
1056
- const named = workstreams.filter((w) => w !== "");
1057
- const args = ["state", "--json", "--events", "0"];
1058
- for (const w of named) args.push("-w", w);
1059
- const r = await run(args);
1060
- if (r.code !== 0) return void 0;
1061
- let json;
1062
- try {
1063
- json = JSON.parse(r.stdout);
1064
- } catch {
1065
- return void 0;
1066
- }
1067
- const cards = json.workstreams ?? [json];
1068
- const refs = [];
1069
- for (const ws of cards) {
1070
- for (const t of ws.inProgress ?? []) {
1071
- if (t.name) refs.push(`${ws.workstreamName ?? ""}/${t.name}`);
1072
- }
1073
- }
1074
- return refs;
1087
+ const results = await Promise.all(
1088
+ workstreams.map(async (w) => {
1089
+ const r = await run([
1090
+ "task",
1091
+ "list",
1092
+ "--status",
1093
+ "IN_PROGRESS",
1094
+ "--json",
1095
+ "--sort",
1096
+ "recency",
1097
+ ...w ? ["-w", w] : []
1098
+ ]);
1099
+ if (r.code !== 0) return void 0;
1100
+ try {
1101
+ const items = JSON.parse(r.stdout).items ?? [];
1102
+ return items.flatMap((t) => t.name ? [`${t.workstreamName ?? w}/${t.name}`] : []);
1103
+ } catch {
1104
+ return void 0;
1105
+ }
1106
+ })
1107
+ );
1108
+ if (results.some((r) => r === void 0)) return void 0;
1109
+ return [...new Set(results.flatMap((r) => r ?? []))];
1075
1110
  }
1076
1111
  function nudgeText(rule, refs) {
1077
1112
  const shown = refs.slice(0, WAIT_HINT_MAX).join(" ");
@@ -1086,7 +1121,7 @@ function breadcrumb(run, args) {
1086
1121
  void run(args).catch(() => {
1087
1122
  });
1088
1123
  }
1089
- function parallelSettle(pi) {
1124
+ function parallelSettle(pi, hold = noHold) {
1090
1125
  const handlers = [];
1091
1126
  const api = {
1092
1127
  on(event, handler) {
@@ -1111,7 +1146,15 @@ function parallelSettle(pi) {
1111
1146
  }
1112
1147
  })
1113
1148
  );
1114
- const fired = results.filter((r) => r !== void 0);
1149
+ const late = hold(ctx);
1150
+ const fired = results.flatMap((r) => {
1151
+ if (r === void 0) return [];
1152
+ if (late === "answer" || late === "blocked" && r.continue === true) {
1153
+ r.yielded?.();
1154
+ return [];
1155
+ }
1156
+ return [r];
1157
+ });
1115
1158
  if (fired.length === 0) return void 0;
1116
1159
  const prior = event?.entries ?? [];
1117
1160
  return {
@@ -1138,7 +1181,7 @@ function bashCommand(event) {
1138
1181
  const e = event;
1139
1182
  return e.toolName === "bash" && typeof e.input?.command === "string" ? e.input.command : void 0;
1140
1183
  }
1141
- function registerNudge(pi, run = defaultRunner(), skillMd = readSkill(), nested = notNested) {
1184
+ function registerNudge(pi, run = defaultRunner(), skillMd = readSkill(), nested = notNested, hold = noHold) {
1142
1185
  if (!nudgeEnabled()) return;
1143
1186
  const rule = skillMd === void 0 ? void 0 : keepDrivingRule(skillMd);
1144
1187
  if (rule === void 0) return;
@@ -1153,12 +1196,16 @@ function registerNudge(pi, run = defaultRunner(), skillMd = readSkill(), nested
1153
1196
  if (cmd === void 0) return;
1154
1197
  for (const ws of dispatchedWorkstreams(cmd) ?? []) armed.add(ws);
1155
1198
  });
1156
- pi.on("agent_before_settle", async (event) => {
1157
- if (fired || armed.size === 0 || nested()) return;
1199
+ pi.on("agent_before_settle", async (event, ctx) => {
1200
+ if (fired || armed.size === 0 || nested() || hold(ctx)) return;
1158
1201
  if (outcomeOf(event) !== "completed") return;
1159
1202
  fired = true;
1160
1203
  const refs = await inProgress(run, [...armed]);
1161
1204
  if (refs === void 0 || refs.length === 0) return;
1205
+ if (hold(ctx)) {
1206
+ fired = false;
1207
+ return;
1208
+ }
1162
1209
  const text = nudgeText(rule, refs);
1163
1210
  const logWs = [...armed].find((w) => w !== "");
1164
1211
  breadcrumb(run, [
@@ -1172,7 +1219,10 @@ function registerNudge(pi, run = defaultRunner(), skillMd = readSkill(), nested
1172
1219
  entries: [
1173
1220
  { type: "custom_message", customType: NUDGE_MESSAGE_TYPE, content: text, display: true }
1174
1221
  ],
1175
- continue: true
1222
+ continue: true,
1223
+ yielded: () => {
1224
+ fired = false;
1225
+ }
1176
1226
  };
1177
1227
  });
1178
1228
  }
@@ -1203,7 +1253,7 @@ function closeNudgeText(workstream, names) {
1203
1253
  "If you are blocked or need an answer, say so in one line and stop; the orchestrator will read your pane."
1204
1254
  ].join("\n");
1205
1255
  }
1206
- function registerCloseNudge(pi, run = defaultRunner(), env = process.env, nested = notNested) {
1256
+ function registerCloseNudge(pi, run = defaultRunner(), env = process.env, nested = notNested, hold = noHold) {
1207
1257
  if (!nudgeEnabled(env)) return;
1208
1258
  const me = workerIdentity(env);
1209
1259
  if (me === void 0) return;
@@ -1212,11 +1262,15 @@ function registerCloseNudge(pi, run = defaultRunner(), env = process.env, nested
1212
1262
  fired = false;
1213
1263
  });
1214
1264
  pi.on("agent_before_settle", async (event, ctx) => {
1215
- if (fired || nested() || noUI(ctx)) return;
1265
+ if (fired || nested() || noUI(ctx) || hold(ctx)) return;
1216
1266
  if (outcomeOf(event) !== "completed") return;
1217
1267
  fired = true;
1218
1268
  const names = await ownedInProgress(run, me.agent, me.workstream);
1219
1269
  if (names === void 0 || names.length === 0) return;
1270
+ if (hold(ctx)) {
1271
+ fired = false;
1272
+ return;
1273
+ }
1220
1274
  breadcrumb(run, [
1221
1275
  "log",
1222
1276
  "-w",
@@ -1234,7 +1288,10 @@ function registerCloseNudge(pi, run = defaultRunner(), env = process.env, nested
1234
1288
  display: true
1235
1289
  }
1236
1290
  ],
1237
- continue: true
1291
+ continue: true,
1292
+ yielded: () => {
1293
+ fired = false;
1294
+ }
1238
1295
  };
1239
1296
  });
1240
1297
  }
@@ -1252,8 +1309,9 @@ async function briefRefuted(run, ws, id) {
1252
1309
  function refuteNudgeText(refs) {
1253
1310
  return `[mu] dispatched ${refs.join(", ")} with an unrefuted brief: refute it (delegate, record) or note REFUTE-EXEMPT: <why>`;
1254
1311
  }
1255
- function registerRefuteNudge(pi, run = defaultRunner(), nested = notNested) {
1312
+ function registerRefuteNudge(pi, run = defaultRunner(), nested = notNested, hold = noHold) {
1256
1313
  if (!nudgeEnabled()) return;
1314
+ const answerDue = (ctx) => hold(ctx) === "answer";
1257
1315
  const armed = /* @__PURE__ */ new Map();
1258
1316
  let fired = false;
1259
1317
  pi.on("input", () => {
@@ -1268,13 +1326,17 @@ function registerRefuteNudge(pi, run = defaultRunner(), nested = notNested) {
1268
1326
  }
1269
1327
  });
1270
1328
  pi.on("agent_before_settle", async (event, ctx) => {
1271
- if (fired || armed.size === 0 || nested() || noUI(ctx)) return;
1329
+ if (fired || armed.size === 0 || nested() || noUI(ctx) || answerDue(ctx)) return;
1272
1330
  if (outcomeOf(event) !== "completed") return;
1273
1331
  fired = true;
1274
1332
  const tasks = [...armed];
1275
1333
  const refuted = await Promise.all(tasks.map(([, t]) => briefRefuted(run, t.ws, t.id)));
1276
1334
  const refs = tasks.flatMap(([ref], i) => refuted[i] === false ? [ref] : []);
1277
1335
  if (refs.length === 0) return;
1336
+ if (answerDue(ctx)) {
1337
+ fired = false;
1338
+ return;
1339
+ }
1278
1340
  const logWs = [...armed.values()].find((t) => t.ws !== "")?.ws;
1279
1341
  breadcrumb(run, [
1280
1342
  "log",
@@ -1291,13 +1353,16 @@ function registerRefuteNudge(pi, run = defaultRunner(), nested = notNested) {
1291
1353
  content: refuteNudgeText(refs),
1292
1354
  display: true
1293
1355
  }
1294
- ]
1356
+ ],
1357
+ yielded: () => {
1358
+ fired = false;
1359
+ }
1295
1360
  };
1296
1361
  });
1297
1362
  }
1298
1363
 
1299
1364
  // extension/mu-pi.ts
1300
- var EXT_VERSION = true ? "3.12.0" : void 0;
1365
+ var EXT_VERSION = true ? "3.13.1" : void 0;
1301
1366
  var FRESH_COMMAND = "mu-fresh";
1302
1367
  var FRESH_TIMEOUT_MS = 3e4;
1303
1368
  function commandName(name) {
@@ -1387,6 +1452,50 @@ function capText(text) {
1387
1452
  return `${cut.replace(/\uFFFD$/, "")}
1388
1453
  ${LAST_TEXT_TRUNCATED}`;
1389
1454
  }
1455
+ var UI_PROMPTS = /* @__PURE__ */ Symbol.for("mu.pi.uiPrompts");
1456
+ function uiPrompts() {
1457
+ const g = globalThis;
1458
+ g[UI_PROMPTS] ??= { open: [], since: 0 };
1459
+ return g[UI_PROMPTS];
1460
+ }
1461
+ function uiBlocked() {
1462
+ const u = uiPrompts();
1463
+ const top = u.open[u.open.length - 1];
1464
+ if (!top) return void 0;
1465
+ return {
1466
+ kind: top.kind,
1467
+ ...top.title !== void 0 ? { title: top.title } : {},
1468
+ since: u.since
1469
+ };
1470
+ }
1471
+ function isUiBlocked() {
1472
+ return uiPrompts().open.length > 0;
1473
+ }
1474
+ function promptOf(event) {
1475
+ const e = typeof event === "object" && event !== null ? event : {};
1476
+ const kind = typeof e.kind === "string" ? e.kind : "custom";
1477
+ const title = typeof e.title === "string" && e.title !== "" ? e.title : void 0;
1478
+ return { kind, ...title !== void 0 ? { title } : {} };
1479
+ }
1480
+ function trackUiPrompts(pi, onStart = () => {
1481
+ }) {
1482
+ pi.on("ui_prompt_start", (e) => {
1483
+ const u = uiPrompts();
1484
+ if (u.open.length === 0) u.since = Date.now();
1485
+ u.open.push(promptOf(e));
1486
+ onStart();
1487
+ });
1488
+ pi.on("session_start", () => {
1489
+ uiPrompts().open.length = 0;
1490
+ });
1491
+ pi.on("ui_prompt_end", (e) => {
1492
+ const u = uiPrompts();
1493
+ const { kind } = promptOf(e);
1494
+ let i = u.open.length - 1;
1495
+ while (i >= 0 && u.open[i]?.kind !== kind) i--;
1496
+ u.open.splice(i >= 0 ? i : u.open.length - 1, 1);
1497
+ });
1498
+ }
1390
1499
  function inodeOf(path) {
1391
1500
  try {
1392
1501
  return statSync2(path).ino;
@@ -1437,18 +1546,24 @@ function registerPrompts(pi) {
1437
1546
  });
1438
1547
  }
1439
1548
  function muPi(pi, run = defaultRunner()) {
1440
- registerDelegate(pi, run);
1549
+ const delegates = registerDelegate(pi, run);
1441
1550
  registerPrompts(pi);
1442
1551
  const nested = serveCtl(pi);
1443
- const settle = parallelSettle(pi);
1444
- registerNudge(settle.api, run, void 0, nested);
1445
- registerCloseNudge(settle.api, run, process.env, nested);
1446
- registerRefuteNudge(settle.api, run, nested);
1552
+ const hold = withQueuedInput(
1553
+ () => delegates.answerPending() ? "answer" : isUiBlocked() ? "blocked" : void 0
1554
+ );
1555
+ const settle = parallelSettle(pi, hold);
1556
+ registerNudge(settle.api, run, void 0, nested, hold);
1557
+ registerCloseNudge(settle.api, run, process.env, nested, hold);
1558
+ registerRefuteNudge(settle.api, run, nested, hold);
1447
1559
  settle.flush();
1448
1560
  }
1449
1561
  function serveCtl(pi) {
1450
1562
  const sock = process.env.MU_CTL_SOCK;
1451
- if (!sock) return () => false;
1563
+ if (!sock) {
1564
+ trackUiPrompts(pi);
1565
+ return () => false;
1566
+ }
1452
1567
  const sockPath = sock;
1453
1568
  const piVersion = detectPiVersion();
1454
1569
  const map = sharedMap();
@@ -1471,7 +1586,15 @@ function serveCtl(pi) {
1471
1586
  since: g.since,
1472
1587
  runs: g.runs,
1473
1588
  pending: g.ctx?.hasPendingMessages() ?? false,
1474
- ...sessionFileOf(g.ctx)
1589
+ ...sessionFileOf(g.ctx),
1590
+ ...blockedField()
1591
+ });
1592
+ const blockedField = () => {
1593
+ const b = uiBlocked();
1594
+ return b ? { blocked: b } : {};
1595
+ };
1596
+ trackUiPrompts(pi, () => {
1597
+ for (const w of [...g.waiters]) w("blocked");
1475
1598
  });
1476
1599
  const settleFresh = (r) => {
1477
1600
  const f = g.fresh;
@@ -1513,7 +1636,7 @@ function serveCtl(pi) {
1513
1636
  g.lastError = g.endError;
1514
1637
  g.endError = void 0;
1515
1638
  g.sessionFile = sessionFileOf(c).sessionFile;
1516
- for (const w of [...g.waiters]) w();
1639
+ for (const w of [...g.waiters]) w("settle");
1517
1640
  });
1518
1641
  pi.registerCommand(FRESH_COMMAND, {
1519
1642
  description: "mu internal: new session + the prompt mu queued (run by mu agent send --fresh)",
@@ -1643,11 +1766,19 @@ function serveCtl(pi) {
1643
1766
  case "command":
1644
1767
  return command(req);
1645
1768
  case "wait":
1646
- return wait(num(req, "afterRuns"), num(req, "timeoutMs"), conn);
1769
+ return wait(
1770
+ num(req, "afterRuns"),
1771
+ num(req, "timeoutMs"),
1772
+ conn,
1773
+ req.returnOnBlocked === true
1774
+ );
1647
1775
  case "abort":
1648
1776
  if (!g.ctx) return fail("no pi context yet");
1649
- g.ctx.abort();
1650
- return { v: V, ok: true, state: g.state };
1777
+ {
1778
+ const wasBusy = !g.ctx.isIdle();
1779
+ if (wasBusy) g.ctx.abort();
1780
+ return { v: V, ok: true, state: g.state, wasBusy };
1781
+ }
1651
1782
  case "interrupt":
1652
1783
  return interrupt(req, conn);
1653
1784
  default:
@@ -1675,10 +1806,18 @@ function serveCtl(pi) {
1675
1806
  ...g.lastError !== void 0 ? { lastError: g.lastError } : {},
1676
1807
  ...g.sessionFile !== void 0 ? { sessionFile: g.sessionFile } : {}
1677
1808
  });
1678
- function wait(afterRuns, timeoutMs, conn) {
1809
+ function wait(afterRuns, timeoutMs, conn, onBlocked = false) {
1679
1810
  if (afterRuns !== void 0 && g.runs > afterRuns && g.state === "idle") {
1680
- return Promise.resolve({ v: V, ok: true, ...status(), ...settledRun() });
1811
+ return Promise.resolve({
1812
+ v: V,
1813
+ ok: true,
1814
+ ...status(),
1815
+ ...settledRun(),
1816
+ ended: "settled"
1817
+ });
1681
1818
  }
1819
+ const blockedReply = () => ({ v: V, ok: true, ...status(), ended: "blocked" });
1820
+ if (onBlocked && isUiBlocked()) return Promise.resolve(blockedReply());
1682
1821
  return new Promise((resolve) => {
1683
1822
  let timer;
1684
1823
  const done = (r) => {
@@ -1687,9 +1826,13 @@ function serveCtl(pi) {
1687
1826
  conn.off("close", onClose);
1688
1827
  resolve(r);
1689
1828
  };
1690
- const waiter = () => {
1829
+ const waiter = (why) => {
1830
+ if (why === "blocked") {
1831
+ if (onBlocked) done(blockedReply());
1832
+ return;
1833
+ }
1691
1834
  if (afterRuns !== void 0 && g.runs <= afterRuns) return;
1692
- done({ v: V, ok: true, ...status(), ...settledRun() });
1835
+ done({ v: V, ok: true, ...status(), ...settledRun(), ended: "settled" });
1693
1836
  };
1694
1837
  const onClose = () => done(fail("client closed"));
1695
1838
  g.waiters.add(waiter);
@@ -1811,8 +1954,10 @@ export {
1811
1954
  LIVE_PROBE_MS,
1812
1955
  commandName,
1813
1956
  muPi as default,
1957
+ isUiBlocked,
1814
1958
  lastAssistantError,
1815
1959
  lastAssistantText,
1816
- promptsDir
1960
+ promptsDir,
1961
+ uiBlocked
1817
1962
  };
1818
1963
  //# sourceMappingURL=mu-pi.js.map