@mu-crew/mu 3.11.3 → 3.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -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-GJIO7GWD.js");
50
+ var { runCli } = await import("./cli-MDCJEU74.js");
51
51
  await runCli(process.argv);
52
52
  //# sourceMappingURL=cli.js.map
@@ -5,7 +5,7 @@ import {
5
5
  linkSync,
6
6
  mkdirSync as mkdirSync2,
7
7
  readdirSync,
8
- readFileSync as readFileSync2,
8
+ readFileSync as readFileSync3,
9
9
  realpathSync,
10
10
  rmSync,
11
11
  statSync as statSync2
@@ -44,7 +44,7 @@ var LineDecoder = class {
44
44
 
45
45
  // extension/delegate.ts
46
46
  import { execFile } from "child_process";
47
- import { existsSync, mkdirSync, statSync, writeFileSync } from "fs";
47
+ import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
48
48
  import { delimiter, join as join2 } from "path";
49
49
  import { fileURLToPath } from "url";
50
50
 
@@ -116,6 +116,37 @@ function defaultRunner() {
116
116
  );
117
117
  });
118
118
  }
119
+ function lastAssistantTextIn(sessionFile) {
120
+ let raw;
121
+ try {
122
+ raw = readFileSync(sessionFile, "utf8");
123
+ } catch {
124
+ return void 0;
125
+ }
126
+ const lines = raw.split("\n");
127
+ for (let i = lines.length - 1; i >= 0; i--) {
128
+ const line = lines[i];
129
+ if (!line?.includes('"assistant"')) continue;
130
+ try {
131
+ const e = JSON.parse(line);
132
+ if (e.message?.role !== "assistant") continue;
133
+ const c = e.message.content;
134
+ const text = typeof c === "string" ? c : Array.isArray(c) ? c.map((p) => {
135
+ const q = p;
136
+ return q.type === "text" && typeof q.text === "string" ? q.text : "";
137
+ }).join("") : "";
138
+ if (text.trim()) return text;
139
+ } catch {
140
+ }
141
+ }
142
+ return void 0;
143
+ }
144
+ function evidenceLines(x) {
145
+ return [
146
+ x.partial ? `Partial answer so far: ${x.partial}` : void 0,
147
+ x.sessionFile ? `Session: ${x.sessionFile}` : void 0
148
+ ].filter((v) => v !== void 0);
149
+ }
119
150
  function formatElapsed(ms) {
120
151
  const t = Math.max(0, Math.round(ms / 1e3));
121
152
  if (t < 60) return `${t}s`;
@@ -137,31 +168,36 @@ function delegateMessage(name, agent, pane, x = {}) {
137
168
  Pane tail:
138
169
  ${x.tail.trimEnd()}` : "";
139
170
  const ws = x.workspace ? ` Workspace: ${x.workspace}` : "";
171
+ const ev = evidenceLines(x);
172
+ const evBlock = ev.length > 0 ? `
173
+ ${ev.join("\n")}` : "";
140
174
  switch (agent?.outcome) {
141
175
  case "done":
142
176
  return `${who} finished${took}. ${fate}${ws}
143
177
 
144
178
  ${agent.lastText ?? ""}`;
145
179
  case "empty":
146
- return `${who} finished${took} without a text answer. ${fate}${ws}${tailBlock}`;
180
+ return `${who} finished${took} without a text answer. ${fate}${ws}${evBlock}${tailBlock}`;
147
181
  case "error":
148
182
  return [
149
183
  `${who} stopped on an API error${took}, after pi's own retries: ${(agent.lastError ?? "unknown error").replace(/[.\s]+$/, "")}. ${fate}${ws}`,
150
184
  "Decide: re-issue the call, or record the check as UNVERIFIED.",
185
+ ...ev,
151
186
  ...look,
152
187
  `Close: mu agent close ${name} -w ${W}`
153
188
  ].join("\n");
154
189
  case "died":
155
- return `${who} died${took} before answering. ${fate}${ws}${tailBlock}`;
190
+ return `${who} died${took} before answering. ${fate}${ws}${evBlock}${tailBlock}`;
156
191
  case "timeout":
157
192
  case "pending":
158
193
  return [
159
194
  `${who} is still running after ${timeoutS}s; stopped waiting, so its answer will not arrive here. ${fate}${ws}`,
195
+ ...ev,
160
196
  ...look,
161
197
  `Wait again: mu agent wait ${name} -w ${W} --json`
162
198
  ].join("\n");
163
199
  default:
164
- return `${who}: mu agent wait gave no result. ${fate}${ws}${tailBlock}`;
200
+ return `${who}: mu agent wait gave no result. ${fate}${ws}${evBlock}${tailBlock}`;
165
201
  }
166
202
  }
167
203
  function droppedAnswersDir() {
@@ -264,12 +300,12 @@ function registerDelegate(pi, run = defaultRunner()) {
264
300
  const a = arriving.get(delivery);
265
301
  if (a) a.queued = true;
266
302
  };
267
- const saveCopy = (name, content) => {
303
+ const saveCopy = (name, content, kind = "") => {
268
304
  try {
269
305
  const dir = droppedAnswersDir();
270
306
  mkdirSync(dir, { recursive: true });
271
307
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
272
- const path = join2(dir, `${stamp}_${name}.md`);
308
+ const path = join2(dir, `${stamp}_${name}${kind}.md`);
273
309
  writeFileSync(path, `${content}
274
310
  `);
275
311
  return path;
@@ -277,6 +313,10 @@ function registerDelegate(pi, run = defaultRunner()) {
277
313
  return void 0;
278
314
  }
279
315
  };
316
+ const partialCopy = (name, sessionFile) => {
317
+ const text = lastAssistantTextIn(sessionFile);
318
+ return text ? saveCopy(name, text, ".partial") : void 0;
319
+ };
280
320
  const forget = (name) => {
281
321
  inflight.delete(name);
282
322
  drain();
@@ -369,7 +409,8 @@ function registerDelegate(pi, run = defaultRunner()) {
369
409
  // extension's reply carries no runs.
370
410
  runs: typeof sent.runs === "number" ? sent.runs : 0,
371
411
  ...typeof attach === "string" ? { attach } : {},
372
- ...typeof wsPath === "string" ? { workspace: wsPath } : {}
412
+ ...typeof wsPath === "string" ? { workspace: wsPath } : {},
413
+ ...typeof sent.sessionFile === "string" ? { sessionFile: sent.sessionFile } : {}
373
414
  };
374
415
  }
375
416
  const recordLabel = (params, name) => typeof params.label === "string" && params.label.trim() ? params.label.trim() : name;
@@ -415,6 +456,15 @@ function registerDelegate(pi, run = defaultRunner()) {
415
456
  failed.add(name);
416
457
  showStatus();
417
458
  }
459
+ if (outcome !== "done") {
460
+ const sessionFile = agent?.sessionFile ?? extras.sessionFile;
461
+ const partial = sessionFile ? partialCopy(name, sessionFile) : void 0;
462
+ extras = {
463
+ ...extras,
464
+ ...sessionFile ? { sessionFile } : {},
465
+ ...partial ? { partial } : {}
466
+ };
467
+ }
418
468
  const recorded = record ? await writeRecord(
419
469
  record.target,
420
470
  name,
@@ -445,6 +495,8 @@ ${recorded}` : message;
445
495
  answer: outcome === "done" ? agent?.lastText ?? null : null,
446
496
  elapsedMs: extras.elapsedMs ?? null,
447
497
  workspace: extras.workspace ?? null,
498
+ sessionFile: agent?.sessionFile ?? extras.sessionFile ?? null,
499
+ partial: extras.partial ?? null,
448
500
  delivery
449
501
  }
450
502
  },
@@ -476,7 +528,7 @@ ${recorded}` : message;
476
528
  cwd = typeof params.cwd === "string" && params.cwd ? params.cwd : ctx?.cwd ?? process.cwd();
477
529
  if (!isDir(cwd)) throw new Error(`mu_delegate: cwd ${cwd} is not a directory`);
478
530
  }
479
- const { name, runs, attach, workspace } = await spawn(
531
+ const { name, runs, attach, workspace, sessionFile } = await spawn(
480
532
  params,
481
533
  brief ? `${brief}
482
534
 
@@ -487,6 +539,7 @@ ${task}` : task,
487
539
  const entry = {
488
540
  abort: new AbortController(),
489
541
  cancelling: false,
542
+ ...sessionFile ? { sessionFile } : {},
490
543
  ...record ? { record: { target: record, label: recordLabel(params, name) } } : {}
491
544
  };
492
545
  inflight.set(name, entry);
@@ -522,7 +575,8 @@ ${task}` : task,
522
575
  elapsedMs: Date.now() - started,
523
576
  ...queuedAs ? { queuedAs } : {},
524
577
  ...attach ? { attach } : {},
525
- ...workspace ? { workspace } : {}
578
+ ...workspace ? { workspace } : {},
579
+ ...entry.sessionFile ? { sessionFile: entry.sessionFile } : {}
526
580
  },
527
581
  delivery,
528
582
  entry.record
@@ -733,6 +787,12 @@ ${task}` : task,
733
787
  }
734
788
  failed.delete(name);
735
789
  let text = `Aborted ${W}/${name}.`;
790
+ const sessionFile = entry?.sessionFile;
791
+ const partial = sessionFile ? partialCopy(name, sessionFile) : void 0;
792
+ const evidence = evidenceLines({
793
+ ...sessionFile ? { sessionFile } : {},
794
+ ...partial ? { partial } : {}
795
+ });
736
796
  if (entry?.record)
737
797
  text += ` ${await writeRecord(entry.record.target, name, recordNote(entry.record.label, name, "cancelled", void 0, 0))}`;
738
798
  if (params.keep === true) text += " Pane kept.";
@@ -740,6 +800,8 @@ ${task}` : task,
740
800
  const c = await mu(["agent", "close", name, "-w", W]);
741
801
  text += c.code === 0 ? " Pane closed." : ` Pane kept: close refused: ${(c.stderr || c.stdout).trim()}`;
742
802
  }
803
+ if (evidence.length > 0) text += `
804
+ ${evidence.join("\n")}`;
743
805
  return { content: [{ type: "text", text }], details: { name, workstream: W } };
744
806
  }
745
807
  });
@@ -785,6 +847,9 @@ ${task}` : task,
785
847
  }
786
848
  if (inflight.size === 0 && queue.length === 0) return;
787
849
  const names = [...inflight.keys()];
850
+ const sessions = [...inflight].flatMap(
851
+ ([n, e]) => e.sessionFile ? [`${n}: ${e.sessionFile}`] : []
852
+ );
788
853
  const never = queue.splice(0).map((q) => q.handle);
789
854
  for (const e of inflight.values()) e.abort.abort();
790
855
  inflight.clear();
@@ -795,6 +860,7 @@ ${task}` : task,
795
860
  parts.push(
796
861
  `${names.length} delegate${names.length === 1 ? "" : "s"} still running: ${names.join(", ")}. Their answers will not come back here; use mu agent wait <name> -w ${W} --json.`
797
862
  );
863
+ if (sessions.length > 0) parts.push(`Sessions: ${sessions.join("; ")}.`);
798
864
  if (never.length > 0)
799
865
  parts.push(`${never.length} queued, never started: ${never.join(", ")}. Re-issue them.`);
800
866
  const msg = parts.join(" ");
@@ -805,7 +871,7 @@ ${task}` : task,
805
871
  }
806
872
 
807
873
  // extension/nudge.ts
808
- import { existsSync as existsSync2, readFileSync } from "fs";
874
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
809
875
  import { join as join3 } from "path";
810
876
  var NUDGE_MESSAGE_TYPE = "mu-keep-driving";
811
877
  var NUDGE_LOG_KIND = "nudge";
@@ -831,7 +897,7 @@ function keepDrivingRule(skillMd) {
831
897
  function readSkill() {
832
898
  for (const up of ["..", "../.."]) {
833
899
  const p = join3(import.meta.dirname ?? "", up, "skills", "mu", "SKILL.md");
834
- if (existsSync2(p)) return readFileSync(p, "utf8");
900
+ if (existsSync2(p)) return readFileSync2(p, "utf8");
835
901
  }
836
902
  return void 0;
837
903
  }
@@ -1231,7 +1297,7 @@ function registerRefuteNudge(pi, run = defaultRunner(), nested = notNested) {
1231
1297
  }
1232
1298
 
1233
1299
  // extension/mu-pi.ts
1234
- var EXT_VERSION = true ? "3.11.3" : void 0;
1300
+ var EXT_VERSION = true ? "3.12.0" : void 0;
1235
1301
  var FRESH_COMMAND = "mu-fresh";
1236
1302
  var FRESH_TIMEOUT_MS = 3e4;
1237
1303
  function commandName(name) {
@@ -1253,7 +1319,7 @@ function detectPiVersion() {
1253
1319
  for (let i = 0; i < 6; i++) {
1254
1320
  const pkgPath = join4(dir, "package.json");
1255
1321
  if (existsSync3(pkgPath)) {
1256
- const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
1322
+ const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
1257
1323
  if (typeof pkg === "object" && pkg !== null) {
1258
1324
  const { name, version } = pkg;
1259
1325
  if (typeof name === "string" && /pi/.test(name) && typeof version === "string") {
@@ -1404,7 +1470,8 @@ function serveCtl(pi) {
1404
1470
  state: g.state,
1405
1471
  since: g.since,
1406
1472
  runs: g.runs,
1407
- pending: g.ctx?.hasPendingMessages() ?? false
1473
+ pending: g.ctx?.hasPendingMessages() ?? false,
1474
+ ...sessionFileOf(g.ctx)
1408
1475
  });
1409
1476
  const settleFresh = (r) => {
1410
1477
  const f = g.fresh;
@@ -1428,6 +1495,14 @@ function serveCtl(pi) {
1428
1495
  g.endText = lastAssistantText(e);
1429
1496
  g.endError = lastAssistantError(e);
1430
1497
  });
1498
+ function sessionFileOf(c) {
1499
+ try {
1500
+ const f = c?.sessionManager?.getSessionFile();
1501
+ return f ? { sessionFile: f } : {};
1502
+ } catch {
1503
+ return {};
1504
+ }
1505
+ }
1431
1506
  pi.on("agent_settled", (_e, c) => {
1432
1507
  g.ctx = c;
1433
1508
  g.state = "idle";
@@ -1437,11 +1512,7 @@ function serveCtl(pi) {
1437
1512
  g.endText = void 0;
1438
1513
  g.lastError = g.endError;
1439
1514
  g.endError = void 0;
1440
- try {
1441
- g.sessionFile = c.sessionManager?.getSessionFile();
1442
- } catch {
1443
- g.sessionFile = void 0;
1444
- }
1515
+ g.sessionFile = sessionFileOf(c).sessionFile;
1445
1516
  for (const w of [...g.waiters]) w();
1446
1517
  });
1447
1518
  pi.registerCommand(FRESH_COMMAND, {