@mu-crew/mu 3.11.1 → 3.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  defaultStateDir
4
- } from "./chunk-4PACTXAB.js";
4
+ } from "./chunk-L2ABBTYG.js";
5
5
  import "./chunk-PZ5AY32C.js";
6
6
 
7
7
  // src/main.ts
@@ -47,6 +47,6 @@ function enableCompileCacheIn(dir, enable = nodeEnable ?? null) {
47
47
 
48
48
  // src/main.ts
49
49
  enableCompileCacheIn(join(defaultStateDir(), "compile-cache"));
50
- var { runCli } = await import("./cli-GRAPF5VC.js");
50
+ var { runCli } = await import("./cli-GJIO7GWD.js");
51
51
  await runCli(process.argv);
52
52
  //# sourceMappingURL=cli.js.map
@@ -3,7 +3,7 @@ import {
3
3
  chmodSync,
4
4
  existsSync as existsSync3,
5
5
  linkSync,
6
- mkdirSync,
6
+ mkdirSync as mkdirSync2,
7
7
  readdirSync,
8
8
  readFileSync as readFileSync2,
9
9
  realpathSync,
@@ -11,7 +11,7 @@ import {
11
11
  statSync as statSync2
12
12
  } from "fs";
13
13
  import { connect, createServer } from "net";
14
- import { dirname, join as join3 } from "path";
14
+ import { dirname, join as join4 } from "path";
15
15
  import { fileURLToPath as fileURLToPath2 } from "url";
16
16
 
17
17
  // src/ctl/protocol.ts
@@ -44,9 +44,19 @@ var LineDecoder = class {
44
44
 
45
45
  // extension/delegate.ts
46
46
  import { execFile } from "child_process";
47
- import { existsSync, statSync } from "fs";
48
- import { delimiter, join } from "path";
47
+ import { existsSync, mkdirSync, statSync, writeFileSync } from "fs";
48
+ import { delimiter, join as join2 } from "path";
49
49
  import { fileURLToPath } from "url";
50
+
51
+ // src/state-dir.ts
52
+ import { homedir } from "os";
53
+ import { isAbsolute, join } from "path";
54
+ function defaultCacheDir() {
55
+ const xdg = process.env.XDG_CACHE_HOME;
56
+ return join(xdg !== void 0 && isAbsolute(xdg) ? xdg : join(homedir(), ".cache"), "mu");
57
+ }
58
+
59
+ // extension/delegate.ts
50
60
  var DELEGATE_TOOL = "mu_delegate";
51
61
  var DELEGATE_CANCEL_TOOL = "mu_delegate_cancel";
52
62
  var DELEGATE_MESSAGE_TYPE = "mu-delegate";
@@ -84,7 +94,7 @@ function isDir(p) {
84
94
  }
85
95
  }
86
96
  function onPath(bin, env) {
87
- return (env.PATH ?? "").split(delimiter).some((d) => d !== "" && existsSync(join(d, bin)));
97
+ return (env.PATH ?? "").split(delimiter).some((d) => d !== "" && existsSync(join2(d, bin)));
88
98
  }
89
99
  function defaultRunner() {
90
100
  let cli;
@@ -154,6 +164,29 @@ ${agent.lastText ?? ""}`;
154
164
  return `${who}: mu agent wait gave no result. ${fate}${ws}${tailBlock}`;
155
165
  }
156
166
  }
167
+ function droppedAnswersDir() {
168
+ return join2(defaultCacheDir(), "delegate-answers");
169
+ }
170
+ function droppedNote(dropped) {
171
+ const n = dropped.length;
172
+ const lines = dropped.map((d) => {
173
+ const how = [
174
+ d.outcome ?? "no result",
175
+ d.elapsedMs !== void 0 ? formatElapsed(d.elapsedMs) : void 0
176
+ ].filter((x) => x !== void 0).join(", ");
177
+ const where = [
178
+ d.copy ? `answer: ${d.copy}` : void 0,
179
+ d.sessionFile ? `session: ${d.sessionFile}` : void 0
180
+ ].filter((x) => x !== void 0);
181
+ if (where.length === 0) where.push(`read: mu agent read ${d.name} -n 50 -w ${W}`);
182
+ return `- ${W}/${d.name} (${how}) ${where.join(" \xB7 ")}`;
183
+ });
184
+ return [
185
+ `The interrupt dropped ${n} delegate answer${n === 1 ? "" : "s"} before ${n === 1 ? "it" : "they"} arrived:`,
186
+ ...lines,
187
+ "The answer file holds the message that would have arrived; in a session file the answer is the last assistant message."
188
+ ].join("\n");
189
+ }
157
190
  var RECORD_NOTE_MAX = 4e3;
158
191
  var RECORD_FALLBACK_TAIL = 1500;
159
192
  var VERDICT_LINE = /^[\s>*_`-]*VERDICT:/;
@@ -176,11 +209,23 @@ function labelStem(label) {
176
209
  const stem = label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 20).replace(/-+$/, "");
177
210
  return stem || void 0;
178
211
  }
179
- var MODEL_RE = /^[A-Za-z0-9._:/@+-]+$/;
212
+ var MODEL_BARE_RE = /^[A-Za-z0-9._:/@+=-]+$/;
213
+ function normalizeModel(raw) {
214
+ if (typeof raw !== "string") return void 0;
215
+ let m = raw.trim();
216
+ for (let prev = ""; prev !== m; ) {
217
+ prev = m;
218
+ m = m.replace(/^--model(?:=|\s+)/, "").replace(/^(["'`])(.*)\1$/s, "$2").trim();
219
+ }
220
+ return m && !/\s/.test(m) ? m : void 0;
221
+ }
222
+ function shellArg(s) {
223
+ return MODEL_BARE_RE.test(s) ? s : `'${s.replace(/'/g, "'\\''")}'`;
224
+ }
180
225
  function modelCommand(cli, model, env = process.env) {
181
226
  const override = env[`MU_${cli.toUpperCase().replace(/-/g, "_")}_COMMAND`];
182
227
  const base = override && override.trim() !== "" ? override.trim() : cli;
183
- return `${base} --model ${model}`;
228
+ return `${base} --model ${shellArg(model)}`;
184
229
  }
185
230
  function registerDelegate(pi, run = defaultRunner()) {
186
231
  if (!delegateEnabled()) return;
@@ -215,6 +260,23 @@ function registerDelegate(pi, run = defaultRunner()) {
215
260
  const track = (ctx) => {
216
261
  if (ctx?.hasUI !== false && ctx?.ui) ui = ctx.ui;
217
262
  };
263
+ const markQueued = (delivery) => {
264
+ const a = arriving.get(delivery);
265
+ if (a) a.queued = true;
266
+ };
267
+ const saveCopy = (name, content) => {
268
+ try {
269
+ const dir = droppedAnswersDir();
270
+ mkdirSync(dir, { recursive: true });
271
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
272
+ const path = join2(dir, `${stamp}_${name}.md`);
273
+ writeFileSync(path, `${content}
274
+ `);
275
+ return path;
276
+ } catch {
277
+ return void 0;
278
+ }
279
+ };
218
280
  const forget = (name) => {
219
281
  inflight.delete(name);
220
282
  drain();
@@ -359,12 +421,21 @@ function registerDelegate(pi, run = defaultRunner()) {
359
421
  recordNote(record.label, name, outcome, agent?.lastText, extras.elapsedMs ?? 0)
360
422
  ) : void 0;
361
423
  const message = delegateMessage(name, agent, pane, { ...extras, ...tail ? { tail } : {} });
424
+ const content = recorded ? `${message}
425
+
426
+ ${recorded}` : message;
427
+ const a = arriving.get(delivery);
428
+ if (a)
429
+ Object.assign(a, {
430
+ content,
431
+ ...outcome ? { outcome } : {},
432
+ ...extras.elapsedMs !== void 0 ? { elapsedMs: extras.elapsedMs } : {},
433
+ ...agent?.sessionFile ? { sessionFile: agent.sessionFile } : {}
434
+ });
362
435
  await pi.sendMessage(
363
436
  {
364
437
  customType: DELEGATE_MESSAGE_TYPE,
365
- content: recorded ? `${message}
366
-
367
- ${recorded}` : message,
438
+ content,
368
439
  display: true,
369
440
  details: {
370
441
  name,
@@ -439,7 +510,7 @@ ${task}` : task,
439
510
  );
440
511
  const settle = async (r) => {
441
512
  const delivery = ++deliverySeq;
442
- arriving.set(delivery, false);
513
+ arriving.set(delivery, { queued: false, name });
443
514
  forget(name);
444
515
  try {
445
516
  await deliver(
@@ -456,22 +527,28 @@ ${task}` : task,
456
527
  delivery,
457
528
  entry.record
458
529
  );
459
- if (arriving.has(delivery)) arriving.set(delivery, true);
530
+ markQueued(delivery);
460
531
  } catch (e) {
532
+ const content = `Delegate ${W}/${name} settled, but delivering its answer failed: ${e instanceof Error ? e.message : String(e)}. Read it with: mu agent read ${name} -n 50 -w ${W}`;
533
+ const a = arriving.get(delivery);
534
+ if (a) a.content = content;
461
535
  await Promise.resolve(
462
536
  pi.sendMessage(
463
537
  {
464
538
  customType: DELEGATE_MESSAGE_TYPE,
465
- content: `Delegate ${W}/${name} settled, but delivering its answer failed: ${e instanceof Error ? e.message : String(e)}. Read it with: mu agent read ${name} -n 50 -w ${W}`,
539
+ content,
466
540
  display: true,
467
541
  details: { name, workstream: W, outcome: null, closed: false, delivery }
468
542
  },
469
543
  { deliverAs: "followUp", triggerTurn: true }
470
544
  )
471
- ).catch(() => {
472
- arriving.delete(delivery);
473
- showStatus();
474
- });
545
+ ).then(
546
+ () => markQueued(delivery),
547
+ () => {
548
+ arriving.delete(delivery);
549
+ showStatus();
550
+ }
551
+ );
475
552
  }
476
553
  };
477
554
  void waiting.then(async (r) => {
@@ -569,11 +646,14 @@ ${task}` : task,
569
646
  const cwd = typeof params.cwd === "string" && params.cwd ? params.cwd : ctx?.cwd ?? process.cwd();
570
647
  if (!isDir(cwd)) throw new Error(`mu_delegate: cwd ${cwd} is not a directory`);
571
648
  }
572
- const m = params.model;
573
- if (m !== void 0 && !(typeof m === "string" && MODEL_RE.test(m)))
574
- throw new Error(
575
- `mu_delegate: model must be a pi model id like 'provider/id:thinking' (got ${JSON.stringify(m)})`
576
- );
649
+ if (params.model !== void 0 && params.model !== "") {
650
+ const m = normalizeModel(params.model);
651
+ if (!m)
652
+ throw new Error(
653
+ `mu_delegate: model must be one pi model id like 'provider/id:thinking', as listed by \`pi --list-models\` (got ${JSON.stringify(params.model)}); omit it for the cli's default`
654
+ );
655
+ params = { ...params, model: m };
656
+ }
577
657
  const record = await resolveRecord(params.record);
578
658
  const max = delegateMax();
579
659
  if (inflight.size + starting < max) {
@@ -671,13 +751,32 @@ ${task}` : task,
671
751
  });
672
752
  pi.on("agent_settled", (e) => {
673
753
  if (e.aborted !== true) return;
674
- let dropped = false;
675
- for (const [id, queued] of arriving)
676
- if (queued) {
677
- arriving.delete(id);
678
- dropped = true;
679
- }
680
- if (dropped) showStatus();
754
+ const dropped = [];
755
+ for (const [id, a] of arriving) {
756
+ if (!a.queued) continue;
757
+ arriving.delete(id);
758
+ dropped.push({
759
+ name: a.name,
760
+ ...a.outcome ? { outcome: a.outcome } : {},
761
+ ...a.elapsedMs !== void 0 ? { elapsedMs: a.elapsedMs } : {},
762
+ ...a.sessionFile ? { sessionFile: a.sessionFile } : {},
763
+ ...a.content ? { copy: saveCopy(a.name, a.content) } : {}
764
+ });
765
+ }
766
+ if (dropped.length === 0) return;
767
+ showStatus();
768
+ void Promise.resolve(
769
+ pi.sendMessage(
770
+ {
771
+ customType: DELEGATE_MESSAGE_TYPE,
772
+ content: droppedNote(dropped),
773
+ display: true,
774
+ details: { dropped }
775
+ },
776
+ { triggerTurn: false }
777
+ )
778
+ ).catch(() => {
779
+ });
681
780
  });
682
781
  pi.on("session_shutdown", (_e, ctx) => {
683
782
  if (arriving.size > 0) {
@@ -707,7 +806,7 @@ ${task}` : task,
707
806
 
708
807
  // extension/nudge.ts
709
808
  import { existsSync as existsSync2, readFileSync } from "fs";
710
- import { join as join2 } from "path";
809
+ import { join as join3 } from "path";
711
810
  var NUDGE_MESSAGE_TYPE = "mu-keep-driving";
712
811
  var NUDGE_LOG_KIND = "nudge";
713
812
  var CLOSE_NUDGE_MESSAGE_TYPE = "mu-close-task";
@@ -731,7 +830,7 @@ function keepDrivingRule(skillMd) {
731
830
  }
732
831
  function readSkill() {
733
832
  for (const up of ["..", "../.."]) {
734
- const p = join2(import.meta.dirname ?? "", up, "skills", "mu", "SKILL.md");
833
+ const p = join3(import.meta.dirname ?? "", up, "skills", "mu", "SKILL.md");
735
834
  if (existsSync2(p)) return readFileSync(p, "utf8");
736
835
  }
737
836
  return void 0;
@@ -1132,7 +1231,7 @@ function registerRefuteNudge(pi, run = defaultRunner(), nested = notNested) {
1132
1231
  }
1133
1232
 
1134
1233
  // extension/mu-pi.ts
1135
- var EXT_VERSION = true ? "3.11.1" : void 0;
1234
+ var EXT_VERSION = true ? "3.11.3" : void 0;
1136
1235
  var FRESH_COMMAND = "mu-fresh";
1137
1236
  var FRESH_TIMEOUT_MS = 3e4;
1138
1237
  function commandName(name) {
@@ -1152,7 +1251,7 @@ function detectPiVersion() {
1152
1251
  if (!entry) return void 0;
1153
1252
  let dir = dirname(realpathSync(entry));
1154
1253
  for (let i = 0; i < 6; i++) {
1155
- const pkgPath = join3(dir, "package.json");
1254
+ const pkgPath = join4(dir, "package.json");
1156
1255
  if (existsSync3(pkgPath)) {
1157
1256
  const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
1158
1257
  if (typeof pkg === "object" && pkg !== null) {
@@ -1258,8 +1357,8 @@ function promptsDir(from = import.meta.url) {
1258
1357
  return void 0;
1259
1358
  }
1260
1359
  for (const up of ["..", "../.."]) {
1261
- const d = join3(here, up, "prompts");
1262
- if (existsSync3(join3(d, "ultrathink.md"))) return d;
1360
+ const d = join4(here, up, "prompts");
1361
+ if (existsSync3(join4(d, "ultrathink.md"))) return d;
1263
1362
  }
1264
1363
  return void 0;
1265
1364
  }
@@ -1267,7 +1366,7 @@ function registerPrompts(pi) {
1267
1366
  pi.on("resources_discover", () => {
1268
1367
  const d = promptsDir();
1269
1368
  if (!d) return {};
1270
- const paths = readdirSync(d).filter((f) => f.endsWith(".md")).sort().map((f) => join3(d, f));
1369
+ const paths = readdirSync(d).filter((f) => f.endsWith(".md")).sort().map((f) => join4(d, f));
1271
1370
  return { promptPaths: paths };
1272
1371
  });
1273
1372
  }
@@ -1338,6 +1437,11 @@ function serveCtl(pi) {
1338
1437
  g.endText = void 0;
1339
1438
  g.lastError = g.endError;
1340
1439
  g.endError = void 0;
1440
+ try {
1441
+ g.sessionFile = c.sessionManager?.getSessionFile();
1442
+ } catch {
1443
+ g.sessionFile = void 0;
1444
+ }
1341
1445
  for (const w of [...g.waiters]) w();
1342
1446
  });
1343
1447
  pi.registerCommand(FRESH_COMMAND, {
@@ -1497,7 +1601,8 @@ function serveCtl(pi) {
1497
1601
  }
1498
1602
  const settledRun = () => ({
1499
1603
  lastText: g.lastText,
1500
- ...g.lastError !== void 0 ? { lastError: g.lastError } : {}
1604
+ ...g.lastError !== void 0 ? { lastError: g.lastError } : {},
1605
+ ...g.sessionFile !== void 0 ? { sessionFile: g.sessionFile } : {}
1501
1606
  });
1502
1607
  function wait(afterRuns, timeoutMs, conn) {
1503
1608
  if (afterRuns !== void 0 && g.runs > afterRuns && g.state === "idle") {
@@ -1555,7 +1660,7 @@ function serveCtl(pi) {
1555
1660
  });
1556
1661
  }
1557
1662
  async function bind() {
1558
- mkdirSync(dirname(sockPath), { recursive: true, mode: 448 });
1663
+ mkdirSync2(dirname(sockPath), { recursive: true, mode: 448 });
1559
1664
  if (await socketIsLive(sockPath)) {
1560
1665
  g.nested = true;
1561
1666
  process.stderr.write(
@@ -1565,7 +1670,7 @@ function serveCtl(pi) {
1565
1670
  return;
1566
1671
  }
1567
1672
  rmSync(sockPath, { force: true });
1568
- const tmp = join3(dirname(sockPath), `.${process.pid.toString(36)}${(++bindSeq).toString(36)}`);
1673
+ const tmp = join4(dirname(sockPath), `.${process.pid.toString(36)}${(++bindSeq).toString(36)}`);
1569
1674
  rmSync(tmp, { force: true });
1570
1675
  const s = createServer(onConnection);
1571
1676
  await new Promise((resolve, reject) => {