mu-agent 0.1.10 → 0.1.13

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 (41) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +6 -6
  3. package/dist/bundle/chunks/{chunk-O7IPTASE.js → chunk-3J3HSHLX.js} +22 -21
  4. package/dist/bundle/chunks/{chunk-DNNHZBQR.js → chunk-6Z2347ML.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-X234222Q.js → chunk-DYTENXHX.js} +2 -2
  6. package/dist/bundle/chunks/{chunk-RAQ6TU76.js → chunk-FLHKNMLQ.js} +1 -1
  7. package/dist/bundle/chunks/chunk-LHRPMXVI.js +2 -0
  8. package/dist/bundle/chunks/{chunk-HZNN636Z.js → chunk-LT2C56HV.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-SGOTYFMT.js → chunk-MF3VB62R.js} +1 -1
  10. package/dist/bundle/chunks/{chunk-QOEEJKS4.js → chunk-URBBMV3M.js} +1 -1
  11. package/dist/bundle/chunks/{chunk-VJLOQZKR.js → chunk-WCJNJQO5.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-FN6OEZYR.js → chunk-WDURT4ZM.js} +1 -1
  13. package/dist/bundle/chunks/{cli-722OL3K5.js → cli-M4YBZZ6Q.js} +1 -1
  14. package/dist/bundle/chunks/{easter-egg-3d-6H34EDGX.js → easter-egg-3d-VFY3YRQR.js} +1 -1
  15. package/dist/bundle/chunks/{execute-KVSEIDWL.js → execute-FKI7Y4XR.js} +1 -1
  16. package/dist/bundle/chunks/{runtime-O3ELDOM7.js → runtime-IKE5OQIW.js} +1 -1
  17. package/dist/bundle/chunks/{virtual-modules-IKEARUOZ.js → virtual-modules-TJPHLTRT.js} +1 -1
  18. package/dist/bundle/cli-runtime.js +1 -1
  19. package/dist/bundle/index.js +1 -1
  20. package/dist/bundle/rpc-entry.js +1 -1
  21. package/docs/settings.md +1 -1
  22. package/judge/dist/auth.js +51 -26
  23. package/judge/dist/chrome.js +2 -2
  24. package/judge/dist/import.js +833 -161
  25. package/judge/dist/setup.js +129 -46
  26. package/judge/manifest.json +1935 -349
  27. package/judge/mu/chunk-3NQ5BIBI.js +4773 -0
  28. package/judge/mu/chunk-SKGYWHW6.js +51 -0
  29. package/judge/mu/chunk-WQM4MV6L.js +58 -0
  30. package/judge/mu/cloud-code-V7FOGB4J.js +22802 -0
  31. package/judge/mu/dist-75PS4OC4.js +1283 -0
  32. package/judge/mu/index.js +4509 -32801
  33. package/judge/mu/multipart-parser-ZDXVIOOP.js +372 -0
  34. package/judge/mu/src-TVVA5ZK2.js +1409 -0
  35. package/kyrn/bin/kyrn-doctor +28 -3
  36. package/kyrn/bin/kyrn-judge-local +42 -13
  37. package/kyrn/bin/kyrn-ledger +16 -1
  38. package/kyrn/bin/mu.mjs +77 -23
  39. package/kyrn/local-judge/server.py +26 -14
  40. package/package.json +6 -3
  41. package/dist/bundle/chunks/chunk-BSS6T6KQ.js +0 -2
@@ -330,12 +330,37 @@ row(
330
330
  "browse",
331
331
  [browseTiers].flat().some(canRelate)
332
332
  ? `driven by ${[browseTiers].flat().join(" > ")}`
333
- : 'needs a judge that can match a goal to a page. In mu.json: "routes": { "browser.step": ["llm:openai-codex/gpt-5.6-luna"] }',
333
+ : 'needs a judge that can match a goal to a page. In mu.json: "routes": { "browser.step": ["llm:openai/gpt-5.6-luna"] }',
334
334
  );
335
335
 
336
336
  const marks = { ok: "\x1b[32m ok\x1b[0m", fix: "\x1b[33mfix\x1b[0m", "--": "\x1b[2m ·\x1b[0m" };
337
337
  const plain = !process.stdout.isTTY;
338
+ /** Where a row's text starts: the mark, two spaces, the name's column. */
339
+ const TEXT_COLUMN = 18;
340
+ /** `text` broken between words into lines at most `size` wide; without a terminal, one line. */
341
+ function wrap(text, size) {
342
+ if (!size || size < 20) return [text];
343
+ const lines = [];
344
+ let line = "";
345
+ for (const word of text.split(" ")) {
346
+ if (line && line.length + 1 + word.length > size) {
347
+ lines.push(line);
348
+ line = word;
349
+ } else line = line ? `${line} ${word}` : word;
350
+ }
351
+ return [...lines, line];
352
+ }
353
+ // In a terminal, what needs doing comes first, as in /doctor inside mu; piped, every line is a row.
354
+ const fixes = rows.filter(([state]) => state === "fix").length;
355
+ if (!plain) {
356
+ console.log(
357
+ fixes > 0 ? `mu doctor found ${fixes} ${fixes === 1 ? "thing" : "things"} to fix` : "mu doctor: everything is set up",
358
+ );
359
+ }
338
360
  for (const [state, name, text] of rows) {
339
- console.log(`${plain ? state.padStart(3) : marks[state]} ${name.padEnd(13)}${text}`);
361
+ // In a terminal, a long row continues under its own text rather than at the left edge.
362
+ const [first, ...rest] = wrap(text, plain ? undefined : process.stdout.columns - TEXT_COLUMN - 1);
363
+ console.log(`${plain ? state.padStart(3) : marks[state]} ${name.padEnd(13)}${first}`);
364
+ for (const line of rest) console.log(`${" ".repeat(TEXT_COLUMN)}${line}`);
340
365
  }
341
- process.exit(rows.some(([state]) => state === "fix") ? 1 : 0);
366
+ process.exit(fixes > 0 ? 1 : 0);
@@ -34,17 +34,17 @@ is_up() { [[ "$(health)" == *'"status"'* ]]; }
34
34
 
35
35
  require_setup() {
36
36
  if [[ ! -x "$PYTHON" || ! -f "$MODEL_DIR/coreml_config.json" ]]; then
37
- echo "The local judge is not installed. Run: mu judge setup" >&2
37
+ echo "The local judge isn't installed yet. Run: mu judge setup" >&2
38
38
  exit 1
39
39
  fi
40
40
  }
41
41
 
42
42
  cmd_setup() {
43
43
  if [[ "$(uname -s)-$(uname -m)" != "Darwin-arm64" ]]; then
44
- echo "laya-coreml needs Apple silicon and macOS 15+." >&2
44
+ echo "The local judge needs a Mac with Apple silicon and macOS 15 or later." >&2
45
45
  exit 1
46
46
  fi
47
- command -v uv >/dev/null || { echo "uv is required: https://docs.astral.sh/uv/" >&2; exit 1; }
47
+ command -v uv >/dev/null || { echo "mu judge setup needs uv: https://docs.astral.sh/uv/" >&2; exit 1; }
48
48
  mkdir -p "$STATE_DIR"
49
49
  [[ -x "$PYTHON" ]] || uv venv --python 3.12 "$STATE_DIR/.venv"
50
50
  # The official index on purpose: mirrors lag behind new releases.
@@ -72,7 +72,7 @@ from laya_coreml.artifacts import verify_files
72
72
  directory = Path(sys.argv[1])
73
73
  manifest = json.loads((directory / "coreml_config.json").read_text())
74
74
  verify_files(directory, manifest["files"])
75
- print(f"weights verified against the bundle manifest ({len(manifest['files'])} files)")
75
+ print(f"Weights checked against the bundle's manifest ({len(manifest['files'])} files).")
76
76
  PY
77
77
  }
78
78
 
@@ -84,7 +84,7 @@ cmd_run() {
84
84
  cmd_start() {
85
85
  require_setup
86
86
  if is_up; then
87
- echo "local judge already running at $URL"
87
+ echo "The local judge is already running at $URL."
88
88
  return 0
89
89
  fi
90
90
  mkdir -p "$RUN_DIR"
@@ -92,25 +92,25 @@ cmd_start() {
92
92
  echo $! >"$PID_FILE"
93
93
  for _ in $(seq 1 120); do
94
94
  if is_ready; then
95
- echo "local judge ready at $URL (pid $(cat "$PID_FILE"), log $LOG_FILE)"
95
+ echo "The local judge is ready at $URL (pid $(cat "$PID_FILE"), log $LOG_FILE)."
96
96
  return 0
97
97
  fi
98
98
  if ! kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
99
- echo "local judge exited during startup; see $LOG_FILE" >&2
99
+ echo "The local judge stopped while starting; see $LOG_FILE" >&2
100
100
  tail -5 "$LOG_FILE" >&2 || true
101
101
  return 1
102
102
  fi
103
103
  sleep 0.5
104
104
  done
105
- echo "local judge is still warming up; see $LOG_FILE" >&2
105
+ echo "The local judge is still warming up; see $LOG_FILE" >&2
106
106
  }
107
107
 
108
108
  cmd_stop() {
109
109
  if [[ -f "$PID_FILE" ]] && kill -0 "$(cat "$PID_FILE")" 2>/dev/null; then
110
110
  kill "$(cat "$PID_FILE")"
111
- echo "stopped local judge (pid $(cat "$PID_FILE"))"
111
+ echo "Stopped the local judge (pid $(cat "$PID_FILE"))."
112
112
  else
113
- echo "local judge is not running"
113
+ echo "The local judge isn't running."
114
114
  fi
115
115
  rm -f "$PID_FILE"
116
116
  }
@@ -118,7 +118,35 @@ cmd_stop() {
118
118
  cmd_status() {
119
119
  local body
120
120
  body="$(health)"
121
- if [[ -n "$body" ]]; then echo "$body"; else echo "local judge is not running at $URL"; return 1; fi
121
+ if [[ -z "$body" ]]; then echo "The local judge isn't running at $URL."; return 1; fi
122
+ # --json: the server's own answer, for scripts.
123
+ if [[ "${1:-}" == "--json" ]]; then echo "$body"; return 0; fi
124
+ URL="$URL" node -e '
125
+ let text = "";
126
+ process.stdin.on("data", (chunk) => (text += chunk)).on("end", () => {
127
+ let health;
128
+ try { health = JSON.parse(text); } catch { console.log(text.trim()); return; }
129
+ const up = Math.round((health.uptimeSeconds ?? 0) / 60);
130
+ const uptime = up < 60 ? `${up} min` : `${Math.floor(up / 60)} h ${up % 60} min`;
131
+ const state = health.status === "ok" ? "running" : `starting (${health.status})`;
132
+ const model = String(health.model ?? "").split("/").pop();
133
+ console.log(`The local judge is ${state} at ${process.env.URL}: ${model} on ${health.computeUnits}, pid ${health.pid}, up ${uptime}, ${health.requests} requests.`);
134
+ });' <<<"$body"
135
+ }
136
+
137
+ usage() {
138
+ cat <<'USAGE'
139
+ mu judge: the local judge, Laya, on Core ML (macOS on Apple silicon).
140
+
141
+ mu judge setup install it: a Python environment and the weights (about 680 MB)
142
+ mu judge start start it in the background, unless it's already running
143
+ mu judge stop stop it
144
+ mu judge status [--json] whether it's running, and what it serves
145
+ mu judge run run it in the foreground
146
+
147
+ One local judge serves every mu session on this machine. To have mu ask it, add "laya" to "tiers" in
148
+ mu.json, or start mu with MU_JUDGE=laya.
149
+ USAGE
122
150
  }
123
151
 
124
152
  case "${1:-status}" in
@@ -126,6 +154,7 @@ case "${1:-status}" in
126
154
  run) shift; cmd_run "$@" ;;
127
155
  start) cmd_start ;;
128
156
  stop) cmd_stop ;;
129
- status) cmd_status ;;
130
- *) echo "usage: mu judge setup|start|stop|status|run" >&2; exit 2 ;;
157
+ status) shift; cmd_status "$@" ;;
158
+ help | -h | --help) usage ;;
159
+ *) usage >&2; exit 2 ;;
131
160
  esac
@@ -12,6 +12,20 @@ import { homedir } from "node:os";
12
12
  import { join } from "node:path";
13
13
 
14
14
  const args = process.argv.slice(2);
15
+ if (args.includes("--help") || args.includes("-h")) {
16
+ console.log(
17
+ [
18
+ "mu ledger: the judge's verdicts from your recent sessions, each with the message it judged.",
19
+ "",
20
+ " mu ledger verdicts from the 5 most recent sessions",
21
+ " mu ledger <n> from the n most recent",
22
+ " mu ledger --json one JSON object per verdict, for scripts",
23
+ "",
24
+ "It reads the session files mu keeps on this machine; nothing is sent anywhere.",
25
+ ].join("\n"),
26
+ );
27
+ process.exit(0);
28
+ }
15
29
  const asJson = args.includes("--json");
16
30
  const limit = Number(args.find((arg) => /^\d+$/.test(arg)) ?? 5);
17
31
  /** `MU_<name>`, or the `KYRN_<name>` spelling from before the rename. */
@@ -87,4 +101,5 @@ for (const file of files) {
87
101
  }
88
102
  }
89
103
 
90
- if (!asJson) console.log(count === 0 ? `No verdicts found under ${sessionsDir}` : `\n${count} ${count === 1 ? "verdict" : "verdicts"} from ${files.length} ${files.length === 1 ? "session" : "sessions"}`);
104
+ const tilde = (path) => (path.startsWith(homedir()) ? `~${path.slice(homedir().length)}` : path);
105
+ if (!asJson) console.log(count === 0 ? `No verdicts found under ${tilde(sessionsDir)}` : `\n${count} ${count === 1 ? "verdict" : "verdicts"} from ${files.length} ${files.length === 1 ? "session" : "sessions"}`);
package/kyrn/bin/mu.mjs CHANGED
@@ -104,7 +104,7 @@ export function usage(platform) {
104
104
  " (see mu setup --help for scripting, reading a key from stdin and the supported services)",
105
105
  " mu judge <cmd> manage the local judge, Laya (macOS on Apple silicon): setup | start | stop | status | run",
106
106
  " mu ledger [n] [--json] print the judge's verdicts from the last n sessions",
107
- " mu import --list | <file>... import Claude Code and Codex conversations (mu import --help)",
107
+ " mu import <file>... import Claude Code, Codex and ZCode conversations; --list shows them (mu import --help)",
108
108
  " mu doctor check the installation, the judges and the connections",
109
109
  " mu auth <cmd> sign-in helper for the desktop app (JSON lines):",
110
110
  " status | login <provider> | logout <provider>",
@@ -431,7 +431,7 @@ export function localJudgeSupport({ platform, env, wsl = false }) {
431
431
  url,
432
432
  message: [
433
433
  `The local judge (Laya) is a Core ML sidecar that runs only on macOS on Apple silicon, not on ${where}.`,
434
- "mu works without it: Jev answers by default (free on OpenCode Zen, or with your own key: mu setup), or set an LLM judge in mu.json.",
434
+ "mu works without it: Jev, the default hosted judge, answers instead (free on OpenCode Zen, or with your own key via mu setup); you can also set an LLM judge in mu.json.",
435
435
  "A local judge for the desktop app is in progress. Without a judge, decision points use their fallbacks.",
436
436
  "To use a server that implements the same API (GET /health, POST /evaluate), set:",
437
437
  " MU_LOCAL_JUDGE_URL=http://host:port",
@@ -451,12 +451,14 @@ export function launchStrategy({ platform, env, canExec }) {
451
451
  * The judgment layer never runs for them, so its extension stays out of their way. */
452
452
  function judgeArgs({ argv, layout, root, platform, agentDir, readFile }) {
453
453
  if (PI_COMMANDS.has(argv[0])) return [];
454
- const path = pathFor(platform);
455
- const extension =
456
- layout === "package"
457
- ? ["-e", packageEntries({ root, platform }).extension]
458
- : ["-e", path.join(root, "packages", "kyrn-judge", "src", "extension", "kyrn-judge.ts")];
459
- return [...extension, ...themeArgs({ argv, layout, root, platform, agentDir, readFile })];
454
+ return ["-e", judgeExtension({ layout, root, platform }), ...themeArgs({ argv, layout, root, platform, agentDir, readFile })];
455
+ }
456
+
457
+ /** The judgment layer's extension file: the package's build, or a checkout's source. */
458
+ function judgeExtension({ layout, root, platform }) {
459
+ return layout === "package"
460
+ ? packageEntries({ root, platform }).extension
461
+ : pathFor(platform).join(root, "packages", "kyrn-judge", "src", "extension", "kyrn-judge.ts");
460
462
  }
461
463
 
462
464
  /** mu's own terminal themes, beside the judgment layer's files. */
@@ -538,6 +540,8 @@ export function planLaunch({
538
540
  if (typeof value === "string") childEnv[key] = value;
539
541
  }
540
542
  childEnv.PI_PACKAGE_DIR = appDir;
543
+ // The judgment layer is part of mu, not an extension loaded for this run: pi shows its commands without [t].
544
+ childEnv.PI_APP_EXTENSIONS = judgeExtension({ layout, root, platform });
541
545
  childEnv.MU_CODING_AGENT_DIR = agentDir;
542
546
  childEnv.KYRN_CODING_AGENT_DIR = agentDir;
543
547
  childEnv.PI_CODING_AGENT_DIR = agentDir;
@@ -553,7 +557,7 @@ export function planLaunch({
553
557
  if (support.runs) startJudge = true;
554
558
  else if (!support.url) {
555
559
  notes.push(
556
- `mu: the configuration asks for the local judge (Laya), which runs on macOS only; on ${platformName({ platform, wsl })} its decision points use the next judge or their fallbacks (mu judge status)`,
560
+ `mu: the configuration asks for the local judge (Laya), which runs on macOS only; on ${platformName({ platform, wsl })}, the decision points it would answer use the next judge or their fallbacks (mu judge status)`,
557
561
  );
558
562
  }
559
563
  }
@@ -579,6 +583,9 @@ export function planLaunch({
579
583
  * Node flags that process starts with, pi's arguments (the judgment layer, then `argv`) and its whole environment.
580
584
  * The app's pi and the command line's are then one: the same files, settings, sign-ins and keys.
581
585
  *
586
+ * `cli` is the script the command line runs pi with. pi starts its sub-agents (swarm, hive, delegate) by running
587
+ * `process.argv[1]` again, which inside the host is the host's own file: the host hands pi this script there instead.
588
+ *
582
589
  * A checkout runs there as it runs on the command line (sourceRuntime). Returns { error } when pi cannot run.
583
590
  */
584
591
  export function planHost({ platform, env, argv, root, home, fs, wsl = false, stripsTypes = true }) {
@@ -598,6 +605,7 @@ export function planHost({ platform, env, argv, root, home, fs, wsl = false, str
598
605
  return {
599
606
  ...common,
600
607
  module: path.join(root, "dist", "bundle", "index.js"),
608
+ cli: packageEntries({ root, platform }).cli,
601
609
  execArgv: [],
602
610
  args: [...judgeArgs({ argv, layout: launch.layout, root, platform, agentDir: launch.agentDir, readFile: fs.readFile }), ...argv],
603
611
  };
@@ -607,6 +615,7 @@ export function planHost({ platform, env, argv, root, home, fs, wsl = false, str
607
615
  return {
608
616
  ...common,
609
617
  module: path.join(root, "packages", "coding-agent", "src", "index.ts"),
618
+ cli: path.join(root, "packages", "coding-agent", "src", "cli.ts"),
610
619
  execArgv: runtime.args,
611
620
  args: [...judgeArgs({ argv, layout: launch.layout, root, platform, agentDir: launch.agentDir, readFile: fs.readFile }), ...argv],
612
621
  };
@@ -668,6 +677,29 @@ export async function prepareLaunchAsync({ plan, platform, root, bin, err }) {
668
677
  /** What `mu auth` answers itself. Every other `auth` command (check, print-api-key, ...) is pi's own. */
669
678
  export const AUTH_COMMANDS = ["status", "login", "logout"];
670
679
 
680
+ /** `mu link --help` and `mu unlink --help`. */
681
+ export const LINK_USAGE = [
682
+ "mu link: put this mu on your PATH, as a link in ~/.local/bin (on Windows, a small mu.cmd there). MU_LINK_DIR",
683
+ "names another folder.",
684
+ "",
685
+ " mu link link this mu, unless another mu is already on your PATH",
686
+ " mu link --force link it anyway (it comes first only if its folder comes first on your PATH)",
687
+ " mu unlink remove the link mu link made",
688
+ "",
689
+ "A mu installed with npm is already on your PATH and needs neither.",
690
+ ].join("\n");
691
+
692
+ /** What `mu auth --help` (or a bare `mu auth`) says before pi's own auth commands. */
693
+ export const AUTH_USAGE = [
694
+ "mu's sign-in, which the desktop app runs (JSON lines on stdout):",
695
+ " mu auth status",
696
+ " mu auth login <provider>",
697
+ " mu auth logout <provider>",
698
+ "In a terminal, /login inside mu signs in the same way.",
699
+ "",
700
+ "pi's auth commands:",
701
+ ].join("\n");
702
+
671
703
  /**
672
704
  * `mu auth status | login <provider> | logout <provider>`: the sign-in the desktop app runs, with pi's own OAuth
673
705
  * flows and credential store (packages/kyrn-judge/src/auth). It is pi's code, so it runs the way pi does here:
@@ -715,7 +747,7 @@ export function planJudge({ platform, env, argv, bin, wsl = false }) {
715
747
  // ---------------------------------------------------------------------------------------------------------
716
748
 
717
749
  /**
718
- * `mu import`: Claude Code and Codex conversations into mu's sessions (packages/kyrn-judge/src/import). The importer
750
+ * `mu import`: Claude Code, Codex and ZCode conversations into mu's sessions (packages/kyrn-judge/src/import). The importer
719
751
  * needs nothing but Node: the npm package runs its build, judge/dist/import.js, and a checkout its TypeScript with
720
752
  * Node's own type stripping (on by default from 22.18).
721
753
  */
@@ -753,6 +785,17 @@ export const SETUP_DECLINED_FILE = "setup-declined";
753
785
  /** How `mu setup` says: set up, and start a session now. */
754
786
  export const SETUP_EXIT_START = 3;
755
787
 
788
+ /** How `mu setup` says: a subscription was chosen, so start a session that opens on /login. */
789
+ export const SETUP_EXIT_SIGN_IN = 4;
790
+
791
+ /**
792
+ * What the session is started with after the wizard ended with `code`: /login after a subscription was chosen, unless
793
+ * the command line already says what to do.
794
+ */
795
+ export function afterSetup(code, argv) {
796
+ return code === SETUP_EXIT_SIGN_IN && argv.length === 0 ? ["/login"] : argv;
797
+ }
798
+
756
799
  /** How `mu setup` says it was cancelled (ctrl+c): a first start then goes no further. */
757
800
  export const SETUP_EXIT_CANCELLED = 130;
758
801
 
@@ -1073,12 +1116,12 @@ export function planUnlink({ platform, env, home, fs }) {
1073
1116
  text = fs.readFile(link);
1074
1117
  } catch {}
1075
1118
  return text?.includes(SHIM_MARK)
1076
- ? { remove: link, message: `removed ${link}` }
1077
- : { message: `${link} is not a shim written by mu link; nothing removed` };
1119
+ ? { remove: link, message: `Removed ${link}.` }
1120
+ : { message: `${link} is not a shim written by mu link; nothing removed.` };
1078
1121
  }
1079
1122
  return fs.isLink(link)
1080
- ? { remove: link, message: `removed ${link}` }
1081
- : { message: `${link} is not a link; nothing removed` };
1123
+ ? { remove: link, message: `Removed ${link}.` }
1124
+ : { message: `${link} is not a link; nothing removed.` };
1082
1125
  }
1083
1126
 
1084
1127
  /** What `mu doctor` says about the command. */
@@ -1249,7 +1292,7 @@ export function migrate({ platform, home, argv, fs, run, alive, self, out, err }
1249
1292
  }
1250
1293
  if (found.browser.length > 0) busy.push(` the mu browser (pid ${list(found.browser)}). Close it.`);
1251
1294
  if (found.desktop.length > 0) {
1252
- busy.push(` the desktop app's backend (pid ${list(found.desktop)}). Quit the desktop app.`);
1295
+ busy.push(` the desktop app's local service (pid ${list(found.desktop)}). Quit the desktop app.`);
1253
1296
  }
1254
1297
  if (busy.length > 0) {
1255
1298
  err(`Nothing was moved, because these are still using ${old}:`);
@@ -1390,6 +1433,8 @@ export async function main(argv = process.argv.slice(2)) {
1390
1433
  out(usage(platform));
1391
1434
  return 0;
1392
1435
  }
1436
+ // mu's own auth commands first; pi prints its own after them.
1437
+ if (command === "auth" && (rest.length === 0 || rest[0] === "--help" || rest[0] === "-h")) out(AUTH_USAGE);
1393
1438
  if (command === "auth" && AUTH_COMMANDS.includes(rest[0])) {
1394
1439
  const plan = planAuth({
1395
1440
  platform,
@@ -1498,8 +1543,8 @@ export async function main(argv = process.argv.slice(2)) {
1498
1543
  if (plan.keyGiven) process.title = "mu setup";
1499
1544
  mkdirSync(plan.agentDir, { recursive: true });
1500
1545
  const code = await handOver(plan);
1501
- if (code !== SETUP_EXIT_START) return code;
1502
- sessionArgv = [];
1546
+ if (code !== SETUP_EXIT_START && code !== SETUP_EXIT_SIGN_IN) return code;
1547
+ sessionArgv = afterSetup(code, []);
1503
1548
  }
1504
1549
  if (command === "doctor") {
1505
1550
  const link = linkPath({ platform, env, home });
@@ -1510,8 +1555,12 @@ export async function main(argv = process.argv.slice(2)) {
1510
1555
  strategy,
1511
1556
  });
1512
1557
  }
1558
+ if ((command === "link" || command === "unlink") && (rest[0] === "--help" || rest[0] === "-h")) {
1559
+ out(LINK_USAGE);
1560
+ return 0;
1561
+ }
1513
1562
  if ((command === "link" || command === "unlink") && layout === "package") {
1514
- out("This mu was installed with npm, which already put `mu` on your PATH (npm i -g mu-agent); nothing to link.");
1563
+ out("This mu was installed with npm, which already put `mu` on your PATH (npm i -g mu-agent); nothing to do.");
1515
1564
  return 0;
1516
1565
  }
1517
1566
  if (command === "link") {
@@ -1544,9 +1593,10 @@ export async function main(argv = process.argv.slice(2)) {
1544
1593
  return 0;
1545
1594
  }
1546
1595
 
1547
- if (layout === "package" && command === "version") {
1596
+ // --version is the bare number, in the npm package as in a checkout (below).
1597
+ if (layout === "package" && (command === "version" || command === "--version" || command === "-v")) {
1548
1598
  const pkg = JSON.parse(readText(path.join(root, "package.json")));
1549
- out(`mu ${pkg.version} (pi ${pkg.muBuild?.pi ?? "?"}, npm package ${pkg.name})`);
1599
+ out(command === "version" ? `mu ${pkg.version} (pi ${pkg.muBuild?.pi ?? "?"}, npm package ${pkg.name})` : pkg.version);
1550
1600
  return 0;
1551
1601
  }
1552
1602
  // Asked before `version` too, as it always was: a checkout without its dependencies says so at the first command.
@@ -1554,10 +1604,12 @@ export async function main(argv = process.argv.slice(2)) {
1554
1604
  err(installHint({ root, platform }));
1555
1605
  return 1;
1556
1606
  }
1557
- if (command === "version") {
1607
+ if (command === "version" || command === "--version" || command === "-v") {
1558
1608
  // mu's version is the one this tree is released as; the judgment layer's own package.json is never released.
1559
1609
  const version = (file) => JSON.parse(readText(path.join(root, ...file.split("/")))).version;
1560
- out(`mu ${version("kyrn/npm/package.template.json")} (pi ${version("packages/coding-agent/package.json")})`);
1610
+ const mu = version("kyrn/npm/package.template.json");
1611
+ // --version is the bare number, as the npm package's pi bundle prints it; pi's own here would be pi's version.
1612
+ out(command === "version" ? `mu ${mu} (pi ${version("packages/coding-agent/package.json")})` : mu);
1561
1613
  return 0;
1562
1614
  }
1563
1615
 
@@ -1591,7 +1643,9 @@ export async function main(argv = process.argv.slice(2)) {
1591
1643
  if (!setup.error) {
1592
1644
  mkdirSync(setup.agentDir, { recursive: true });
1593
1645
  // Whatever the wizard ends with, the session follows (pi says itself when there is no model), unless it was cancelled.
1594
- if ((await handOver(setup)) === SETUP_EXIT_CANCELLED) return SETUP_EXIT_CANCELLED;
1646
+ const code = await handOver(setup);
1647
+ if (code === SETUP_EXIT_CANCELLED) return SETUP_EXIT_CANCELLED;
1648
+ sessionArgv = afterSetup(code, sessionArgv);
1595
1649
  }
1596
1650
  }
1597
1651
 
@@ -3,7 +3,7 @@
3
3
 
4
4
  Serves a Laya Core ML checkpoint (https://github.com/mizorewww/laya-coreml) over
5
5
  the same wire contract as the AI Gateway `evaluation-model` endpoint, so the
6
- mu kernel can swap Jev for a local model without translating anything:
6
+ mu kernel can swap the hosted judge model for a local one without translating anything:
7
7
 
8
8
  POST /evaluate {"state": ..., "questions": {id: {type, instructions, criteria?}}}
9
9
  -> {"answers": {...}, "usage": {...}, "warnings": [...], "providerMetadata": {"laya": {...}}}
@@ -27,9 +27,16 @@ from pathlib import Path
27
27
  DEFAULT_HOST = "127.0.0.1"
28
28
  DEFAULT_PORT = 47823
29
29
  MAX_BODY_BYTES = 1024 * 1024
30
- # Measured on an M3 Max with 10-question preflights: no floor p50 411 ms (one shape switch per request),
31
- # 256 -> 124 ms, 384 -> 155 ms. Requests longer than the floor still work; they just pay the switch.
32
- DEFAULT_MIN_LENGTH = 256
30
+ # Every request is padded to at least this many tokens; by default (None) to the checkpoint's whole window, so that
31
+ # after warm-up the input length never changes. A change of length is what crashed the sidecar: Core ML re-specializes
32
+ # the GPU program from its compiled copy of the model in ~/Library/Caches/<process>/com.apple.e5rt.e5bundlecache,
33
+ # which it marks APFS-purgeable, and macOS deletes those files whenever the disk runs low. The next change of length
34
+ # then recompiles the model (3-36 s, 644 MB written again) or, when the purge lands mid-load, aborts the process with
35
+ # "MPSGraphTensorData.mm: failed assertion `shape.count = 0 != strides.count = 2'". Measured on an M6 under load:
36
+ # 1024 tokens cost ~34 ms a question at a steady length against ~23-32 ms for 384-768, while a change of length
37
+ # cost 0.4-1.1 s at p50 and up to 18 s at p90. A lower floor (MU_LOCAL_JUDGE_MIN_LENGTH=256) is faster for short
38
+ # requests and brings the changes of length back.
39
+ DEFAULT_MIN_LENGTH = None
33
40
  DEFAULT_MODEL_DIR = Path(__file__).resolve().parent / "models" / "laya-multilingual-coreml"
34
41
 
35
42
 
@@ -88,11 +95,10 @@ def from_laya_answer(answer):
88
95
 
89
96
 
90
97
  class LocalJudge:
91
- def __init__(self, model_dir, compute_units=None, min_length=0):
98
+ def __init__(self, model_dir, compute_units=None, min_length=DEFAULT_MIN_LENGTH):
92
99
  import laya_coreml as laya
93
100
 
94
101
  self.model_dir = Path(model_dir)
95
- self.min_length = min_length
96
102
  started = time.perf_counter()
97
103
  self.agent = laya.load(str(self.model_dir), local_files_only=True, compute_units=compute_units)
98
104
  self.load_seconds = time.perf_counter() - started
@@ -103,15 +109,19 @@ class LocalJudge:
103
109
  manifest = getattr(self.agent, "manifest", {})
104
110
  self.model_id = "laya:" + str(manifest.get("repository") or self.model_dir.name)
105
111
  self.shape = getattr(self.agent, "shape", {})
112
+ if min_length is None:
113
+ min_length = getattr(self.agent, "cfg", {}).get("max_len") or self.shape.get("max_length") or 0
114
+ self.min_length = int(min_length)
106
115
 
107
116
  def uniform_shape(self, state, laya_questions):
108
117
  """Pad every question of a request to one input length.
109
118
 
110
119
  Core ML re-specializes the GPU program whenever the enumerated length
111
- changes (about 350 ms on an M3 Max, against 40 ms for a steady shape),
112
- and the questions of one request differ in length because their
113
- options do. One bucket per request removes those switches; the floor
114
- keeps consecutive requests on the same bucket too.
120
+ changes (0.3-1 s, against ~30 ms for a steady shape), reading the
121
+ compiled model back from a cache macOS may have purged (see
122
+ DEFAULT_MIN_LENGTH). The questions of one request differ in length
123
+ because their options do, so one bucket per request removes the
124
+ changes within it; the floor keeps every request in the same bucket.
115
125
  """
116
126
  lengths = self.agent.shape.get("lengths")
117
127
  if not lengths:
@@ -199,9 +209,10 @@ class LocalJudge:
199
209
  def warm_up(self):
200
210
  """Core ML compiles each enumerated input length on first use (seconds). Pay that before real traffic."""
201
211
  lengths = list(self.shape.get("lengths") or [self.shape.get("default_length", 128)])
202
- # Finish on the bucket real traffic starts in, so the first request does not pay a shape switch.
212
+ # Requests are padded to at least the floor's bucket, so no request uses a shorter one: warm only the lengths
213
+ # traffic can reach, and finish on the floor's bucket, so the first request does not pay a shape switch.
203
214
  resting = next((n for n in lengths if n >= self.min_length), lengths[-1])
204
- lengths = [n for n in lengths if n != resting] + [resting]
215
+ lengths = [n for n in lengths if n > resting] + [resting]
205
216
  question = {"warm": {"type": "noul", "instructions": "Is this a warm-up request?"}}
206
217
  started = time.perf_counter()
207
218
  for length in lengths:
@@ -299,8 +310,9 @@ def main():
299
310
  parser.add_argument(
300
311
  "--min-length",
301
312
  type=int,
302
- default=int(setting("LOCAL_JUDGE_MIN_LENGTH", DEFAULT_MIN_LENGTH)),
303
- help="Smallest input length to pad to. A floor above the usual request size avoids shape switches between requests.",
313
+ default=setting("LOCAL_JUDGE_MIN_LENGTH", DEFAULT_MIN_LENGTH),
314
+ help="Smallest input length to pad to. Default: the checkpoint's window, so the length never changes after "
315
+ "warm-up; a change of length is slow and, when macOS has purged Core ML's cache, can crash the sidecar.",
304
316
  )
305
317
  args = parser.parse_args()
306
318
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mu-agent",
3
- "version": "0.1.10",
3
+ "version": "0.1.13",
4
4
  "description": "mu: a coding agent that hands its routine decisions to a small, fast judge model, so the main model can keep its attention on the work. Built on pi.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,13 +12,16 @@
12
12
  },
13
13
  "keywords": [
14
14
  "coding-agent",
15
+ "pi-coding-agent",
15
16
  "ai",
16
17
  "llm",
17
18
  "cli",
18
19
  "terminal",
19
20
  "agent",
20
21
  "judge",
21
- "pi"
22
+ "pi",
23
+ "mcp",
24
+ "prompt-injection"
22
25
  ],
23
26
  "homepage": "https://github.com/qybaihe/mu",
24
27
  "repository": {
@@ -39,6 +42,6 @@
39
42
  "muBuild": {
40
43
  "pi": "1.3.0",
41
44
  "judge": "0.1.0",
42
- "commit": "84e66744e"
45
+ "commit": "47bdfac19"
43
46
  }
44
47
  }
@@ -1,2 +0,0 @@
1
- import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url);
2
- import{__commonJS,__require,__toESM}from"./chunk-JXWJOIH4.js";var require_windows=__commonJS({"node_modules/isexe/windows.js"(exports,module){module.exports=isexe;isexe.sync=sync;var fs=__require("fs");function checkPathExt(path,options){var pathext=options.pathExt!==void 0?options.pathExt:process.env.PATHEXT;if(!pathext||(pathext=pathext.split(";"),pathext.indexOf("")!==-1))return!0;for(var i=0;i<pathext.length;i++){var p=pathext[i].toLowerCase();if(p&&path.substr(-p.length).toLowerCase()===p)return!0}return!1}function checkStat(stat,path,options){return!stat.isSymbolicLink()&&!stat.isFile()?!1:checkPathExt(path,options)}function isexe(path,options,cb){fs.stat(path,function(er,stat){cb(er,er?!1:checkStat(stat,path,options))})}function sync(path,options){return checkStat(fs.statSync(path),path,options)}}});var require_mode=__commonJS({"node_modules/isexe/mode.js"(exports,module){module.exports=isexe;isexe.sync=sync;var fs=__require("fs");function isexe(path,options,cb){fs.stat(path,function(er,stat){cb(er,er?!1:checkStat(stat,options))})}function sync(path,options){return checkStat(fs.statSync(path),options)}function checkStat(stat,options){return stat.isFile()&&checkMode(stat,options)}function checkMode(stat,options){var mod=stat.mode,uid=stat.uid,gid=stat.gid,myUid=options.uid!==void 0?options.uid:process.getuid&&process.getuid(),myGid=options.gid!==void 0?options.gid:process.getgid&&process.getgid(),u=parseInt("100",8),g=parseInt("010",8),o=parseInt("001",8),ug=u|g,ret=mod&o||mod&g&&gid===myGid||mod&u&&uid===myUid||mod&ug&&myUid===0;return ret}}});var require_isexe=__commonJS({"node_modules/isexe/index.js"(exports,module){var fs=__require("fs"),core;process.platform==="win32"||global.TESTING_WINDOWS?core=require_windows():core=require_mode();module.exports=isexe;isexe.sync=sync;function isexe(path,options,cb){if(typeof options=="function"&&(cb=options,options={}),!cb){if(typeof Promise!="function")throw new TypeError("callback not provided");return new Promise(function(resolve2,reject){isexe(path,options||{},function(er,is){er?reject(er):resolve2(is)})})}core(path,options||{},function(er,is){er&&(er.code==="EACCES"||options&&options.ignoreErrors)&&(er=null,is=!1),cb(er,is)})}function sync(path,options){try{return core.sync(path,options||{})}catch(er){if(options&&options.ignoreErrors||er.code==="EACCES")return!1;throw er}}}});var require_which=__commonJS({"node_modules/which/which.js"(exports,module){var isWindows=process.platform==="win32"||process.env.OSTYPE==="cygwin"||process.env.OSTYPE==="msys",path=__require("path"),COLON=isWindows?";":":",isexe=require_isexe(),getNotFoundError=cmd=>Object.assign(new Error(`not found: ${cmd}`),{code:"ENOENT"}),getPathInfo=(cmd,opt)=>{let colon=opt.colon||COLON,pathEnv=cmd.match(/\//)||isWindows&&cmd.match(/\\/)?[""]:[...isWindows?[process.cwd()]:[],...(opt.path||process.env.PATH||"").split(colon)],pathExtExe=isWindows?opt.pathExt||process.env.PATHEXT||".EXE;.CMD;.BAT;.COM":"",pathExt=isWindows?pathExtExe.split(colon):[""];return isWindows&&cmd.indexOf(".")!==-1&&pathExt[0]!==""&&pathExt.unshift(""),{pathEnv,pathExt,pathExtExe}},which=(cmd,opt,cb)=>{typeof opt=="function"&&(cb=opt,opt={}),opt||(opt={});let{pathEnv,pathExt,pathExtExe}=getPathInfo(cmd,opt),found=[],step=i=>new Promise((resolve2,reject)=>{if(i===pathEnv.length)return opt.all&&found.length?resolve2(found):reject(getNotFoundError(cmd));let ppRaw=pathEnv[i],pathPart=/^".*"$/.test(ppRaw)?ppRaw.slice(1,-1):ppRaw,pCmd=path.join(pathPart,cmd),p=!pathPart&&/^\.[\\\/]/.test(cmd)?cmd.slice(0,2)+pCmd:pCmd;resolve2(subStep(p,i,0))}),subStep=(p,i,ii)=>new Promise((resolve2,reject)=>{if(ii===pathExt.length)return resolve2(step(i+1));let ext=pathExt[ii];isexe(p+ext,{pathExt:pathExtExe},(er,is)=>{if(!er&&is)if(opt.all)found.push(p+ext);else return resolve2(p+ext);return resolve2(subStep(p,i,ii+1))})});return cb?step(0).then(res=>cb(null,res),cb):step(0)},whichSync=(cmd,opt)=>{opt=opt||{};let{pathEnv,pathExt,pathExtExe}=getPathInfo(cmd,opt),found=[];for(let i=0;i<pathEnv.length;i++){let ppRaw=pathEnv[i],pathPart=/^".*"$/.test(ppRaw)?ppRaw.slice(1,-1):ppRaw,pCmd=path.join(pathPart,cmd),p=!pathPart&&/^\.[\\\/]/.test(cmd)?cmd.slice(0,2)+pCmd:pCmd;for(let j=0;j<pathExt.length;j++){let cur=p+pathExt[j];try{if(isexe.sync(cur,{pathExt:pathExtExe}))if(opt.all)found.push(cur);else return cur}catch{}}}if(opt.all&&found.length)return found;if(opt.nothrow)return null;throw getNotFoundError(cmd)};module.exports=which;which.sync=whichSync}});var require_path_key=__commonJS({"node_modules/path-key/index.js"(exports,module){"use strict";var pathKey=(options={})=>{let environment=options.env||process.env;return(options.platform||process.platform)!=="win32"?"PATH":Object.keys(environment).reverse().find(key=>key.toUpperCase()==="PATH")||"Path"};module.exports=pathKey;module.exports.default=pathKey}});var require_resolveCommand=__commonJS({"node_modules/cross-spawn/lib/util/resolveCommand.js"(exports,module){"use strict";var path=__require("path"),which=require_which(),getPathKey=require_path_key();function resolveCommandAttempt(parsed,withoutPathExt){let env=parsed.options.env||process.env,cwd=process.cwd(),hasCustomCwd=parsed.options.cwd!=null,shouldSwitchCwd=hasCustomCwd&&process.chdir!==void 0&&!process.chdir.disabled;if(shouldSwitchCwd)try{process.chdir(parsed.options.cwd)}catch{}let resolved;try{resolved=which.sync(parsed.command,{path:env[getPathKey({env})],pathExt:withoutPathExt?path.delimiter:void 0})}catch{}finally{shouldSwitchCwd&&process.chdir(cwd)}return resolved&&(resolved=path.resolve(hasCustomCwd?parsed.options.cwd:"",resolved)),resolved}function resolveCommand(parsed){return resolveCommandAttempt(parsed)||resolveCommandAttempt(parsed,!0)}module.exports=resolveCommand}});var require_escape=__commonJS({"node_modules/cross-spawn/lib/util/escape.js"(exports,module){"use strict";var metaCharsRegExp=/([()\][%!^"`<>&|;, *?])/g;function escapeCommand(arg){return arg=arg.replace(metaCharsRegExp,"^$1"),arg}function escapeArgument(arg,doubleEscapeMetaChars){return arg=`${arg}`,arg=arg.replace(/(?=(\\+?)?)\1"/g,'$1$1\\"'),arg=arg.replace(/(?=(\\+?)?)\1$/,"$1$1"),arg=`"${arg}"`,arg=arg.replace(metaCharsRegExp,"^$1"),doubleEscapeMetaChars&&(arg=arg.replace(metaCharsRegExp,"^$1")),arg}module.exports.command=escapeCommand;module.exports.argument=escapeArgument}});var require_shebang_regex=__commonJS({"node_modules/shebang-regex/index.js"(exports,module){"use strict";module.exports=/^#!(.*)/}});var require_shebang_command=__commonJS({"node_modules/shebang-command/index.js"(exports,module){"use strict";var shebangRegex=require_shebang_regex();module.exports=(string="")=>{let match=string.match(shebangRegex);if(!match)return null;let[path,argument]=match[0].replace(/#! ?/,"").split(" "),binary=path.split("/").pop();return binary==="env"?argument:argument?`${binary} ${argument}`:binary}}});var require_readShebang=__commonJS({"node_modules/cross-spawn/lib/util/readShebang.js"(exports,module){"use strict";var fs=__require("fs"),shebangCommand=require_shebang_command();function readShebang(command){let buffer=Buffer.alloc(150),fd;try{fd=fs.openSync(command,"r"),fs.readSync(fd,buffer,0,150,0),fs.closeSync(fd)}catch{}return shebangCommand(buffer.toString())}module.exports=readShebang}});var require_parse=__commonJS({"node_modules/cross-spawn/lib/parse.js"(exports,module){"use strict";var path=__require("path"),resolveCommand=require_resolveCommand(),escape=require_escape(),readShebang=require_readShebang(),isWin=process.platform==="win32",isExecutableRegExp=/\.(?:com|exe)$/i,isCmdShimRegExp=/node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;function detectShebang(parsed){parsed.file=resolveCommand(parsed);let shebang=parsed.file&&readShebang(parsed.file);return shebang?(parsed.args.unshift(parsed.file),parsed.command=shebang,resolveCommand(parsed)):parsed.file}function parseNonShell(parsed){if(!isWin)return parsed;let commandFile=detectShebang(parsed),needsShell=!isExecutableRegExp.test(commandFile);if(parsed.options.forceShell||needsShell){let needsDoubleEscapeMetaChars=isCmdShimRegExp.test(commandFile);parsed.command=path.normalize(parsed.command),parsed.command=escape.command(parsed.command),parsed.args=parsed.args.map(arg=>escape.argument(arg,needsDoubleEscapeMetaChars));let shellCommand=[parsed.command].concat(parsed.args).join(" ");parsed.args=["/d","/s","/c",`"${shellCommand}"`],parsed.command=process.env.comspec||"cmd.exe",parsed.options.windowsVerbatimArguments=!0}return parsed}function parse(command,args,options){args&&!Array.isArray(args)&&(options=args,args=null),args=args?args.slice(0):[],options=Object.assign({},options);let parsed={command,args,options,file:void 0,original:{command,args}};return options.shell?parsed:parseNonShell(parsed)}module.exports=parse}});var require_enoent=__commonJS({"node_modules/cross-spawn/lib/enoent.js"(exports,module){"use strict";var isWin=process.platform==="win32";function notFoundError(original,syscall){return Object.assign(new Error(`${syscall} ${original.command} ENOENT`),{code:"ENOENT",errno:"ENOENT",syscall:`${syscall} ${original.command}`,path:original.command,spawnargs:original.args})}function hookChildProcess(cp,parsed){if(!isWin)return;let originalEmit=cp.emit;cp.emit=function(name,arg1){if(name==="exit"){let err=verifyENOENT(arg1,parsed);if(err)return originalEmit.call(cp,"error",err)}return originalEmit.apply(cp,arguments)}}function verifyENOENT(status,parsed){return isWin&&status===1&&!parsed.file?notFoundError(parsed.original,"spawn"):null}function verifyENOENTSync(status,parsed){return isWin&&status===1&&!parsed.file?notFoundError(parsed.original,"spawnSync"):null}module.exports={hookChildProcess,verifyENOENT,verifyENOENTSync,notFoundError}}});var require_cross_spawn=__commonJS({"node_modules/cross-spawn/index.js"(exports,module){"use strict";var cp=__require("child_process"),parse=require_parse(),enoent=require_enoent();function spawn(command,args,options){let parsed=parse(command,args,options),spawned=cp.spawn(parsed.command,parsed.args,parsed.options);return enoent.hookChildProcess(spawned,parsed),spawned}function spawnSync(command,args,options){let parsed=parse(command,args,options),result=cp.spawnSync(parsed.command,parsed.args,parsed.options);return result.error=result.error||enoent.verifyENOENTSync(result.status,parsed),result}module.exports=spawn;module.exports.spawn=spawn;module.exports.sync=spawnSync;module.exports._parse=parse;module.exports._enoent=enoent}});import{accessSync,constants,existsSync,readFileSync,realpathSync as realpathSync2}from"fs";import{createRequire}from"module";import{homedir as homedir2}from"os";import{basename,dirname,join as join2,resolve,sep as sep2,win32}from"path";import{fileURLToPath as fileURLToPath2}from"url";var import_cross_spawn=__toESM(require_cross_spawn(),1);import{spawn as nodeSpawn,spawnSync as nodeSpawnSync}from"node:child_process";var EXIT_STDIO_GRACE_MS=100;function spawnProcess(command,args,options){return process.platform==="win32"?(0,import_cross_spawn.default)(command,args,options):nodeSpawn(command,args,options)}function spawnProcessSync(command,args,options){return process.platform==="win32"?import_cross_spawn.default.sync(command,args,options):nodeSpawnSync(command,args,options)}function waitForChildProcess(child){return new Promise((resolve2,reject)=>{let settled=!1,exited=!1,exitCode=null,postExitTimer,stdoutEnded=child.stdout===null,stderrEnded=child.stderr===null,cleanup=()=>{postExitTimer&&(clearTimeout(postExitTimer),postExitTimer=void 0),child.removeListener("error",onError),child.removeListener("exit",onExit),child.removeListener("close",onClose),child.stdout?.removeListener("end",onStdoutEnd),child.stderr?.removeListener("end",onStderrEnd),child.stdout?.removeListener("data",onData),child.stderr?.removeListener("data",onData)},finalize=code=>{settled||(settled=!0,cleanup(),child.stdout?.destroy(),child.stderr?.destroy(),resolve2(code))},maybeFinalizeAfterExit=()=>{!exited||settled||stdoutEnded&&stderrEnded&&finalize(exitCode)},armIdleTimer=()=>{postExitTimer&&clearTimeout(postExitTimer),postExitTimer=setTimeout(()=>finalize(exitCode),EXIT_STDIO_GRACE_MS)},onData=()=>{exited&&!settled&&armIdleTimer()},onStdoutEnd=()=>{stdoutEnded=!0,maybeFinalizeAfterExit()},onStderrEnd=()=>{stderrEnded=!0,maybeFinalizeAfterExit()},onError=err=>{settled||(settled=!0,cleanup(),reject(err))},onExit=code=>{exited=!0,exitCode=code,maybeFinalizeAfterExit(),settled||armIdleTimer()},onClose=code=>{finalize(code)};child.stdout?.once("end",onStdoutEnd),child.stderr?.once("end",onStderrEnd),child.stdout?.on("data",onData),child.stderr?.on("data",onData),child.once("error",onError),child.once("exit",onExit),child.once("close",onClose)})}import{realpathSync,statSync}from"node:fs";import{homedir}from"node:os";import{isAbsolute,join,resolve as nodeResolvePath,relative,sep}from"node:path";import{fileURLToPath}from"node:url";var UNICODE_SPACES=/[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;function canonicalizePath(path){try{return realpathSync(path)}catch{return path}}function getFileRevision(path){try{let stats=statSync(path,{bigint:!0});return`${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeNs}:${stats.ctimeNs}`}catch{return}}function isLocalPath(value){let trimmed=value.trim();return!(trimmed.startsWith("npm:")||trimmed.startsWith("git:")||trimmed.startsWith("github:")||trimmed.startsWith("http:")||trimmed.startsWith("https:")||trimmed.startsWith("ssh:")||trimmed.startsWith("builtin:"))}function normalizeWindowsShellPath(filePath){if(!filePath.startsWith("/")||filePath.startsWith("//")||filePath.includes("\\"))return filePath;let match=filePath.match(/^\/(?:mnt\/|cygdrive\/)?([a-z])(?:\/(.*))?$/i);if(!match)return filePath;let suffix=match[2]?.replaceAll("/","\\");return`${match[1].toUpperCase()}:\\${suffix??""}`}function normalizePath(input,options={}){let normalized=options.trim?input.trim():input;if(options.normalizeUnicodeSpaces&&(normalized=normalized.replace(UNICODE_SPACES," ")),options.stripAtPrefix&&normalized.startsWith("@")&&(normalized=normalized.slice(1)),process.platform==="win32"&&(normalized=normalizeWindowsShellPath(normalized)),options.expandTilde??!0){let home=options.homeDir??homedir();if(normalized==="~")return home;if(normalized.startsWith("~/")||process.platform==="win32"&&normalized.startsWith("~\\"))return join(home,normalized.slice(2))}return/^file:\/\//.test(normalized)?fileURLToPath(normalized):normalized}function resolvePath(input,baseDir=process.cwd(),options={}){let normalized=normalizePath(input,options),normalizedBaseDir=normalizePath(baseDir);return isAbsolute(normalized)?nodeResolvePath(normalized):nodeResolvePath(normalizedBaseDir,normalized)}function getCwdRelativePath(filePath,cwd){let resolvedCwd=resolvePath(cwd),resolvedPath=resolvePath(filePath,resolvedCwd),relativePath=relative(resolvedCwd,resolvedPath);return relativePath===""||relativePath!==".."&&!relativePath.startsWith(`..${sep}`)&&!isAbsolute(relativePath)?relativePath||".":void 0}function formatPathRelativeToCwdOrAbsolute(filePath,cwd){let absolutePath=resolvePath(filePath,cwd);return(getCwdRelativePath(absolutePath,cwd)??absolutePath).split(sep).join("/")}function markPathIgnoredByCloudSync(path){let attrs=process.platform==="darwin"?["com.dropbox.ignored","com.apple.fileprovider.ignore#P"]:process.platform==="linux"?["user.com.dropbox.ignored"]:[];for(let attr of attrs)process.platform==="darwin"?spawnProcessSync("xattr",["-w",attr,"1",path],{encoding:"utf-8",stdio:"ignore"}):spawnProcessSync("setfattr",["-n",attr,"-v","1",path],{encoding:"utf-8",stdio:"ignore"})}function splitBom(content){return content.startsWith("\uFEFF")?{bom:"\uFEFF",text:content.slice(1)}:{bom:"",text:content}}function stripBom(content){return splitBom(content).text}var __filename=fileURLToPath2(import.meta.url),__dirname=dirname(__filename),isBunBinary=import.meta.url.includes("$bunfs")||import.meta.url.includes("~BUN")||import.meta.url.includes("%7EBUN"),isBunRuntime=!!process.versions.bun,isBundledNode=!0;function normalizeSelfUpdatePackageTarget(target){return typeof target=="string"?{packageName:target,installSpec:target}:{packageName:target.packageName,installSpec:target.installSpec??target.packageName}}function makeSelfUpdateCommand(installStep,uninstallStep){return uninstallStep?{...installStep,display:`${uninstallStep.display} && ${installStep.display}`,steps:[uninstallStep,installStep]}:installStep}function makeSelfUpdateCommandStep(command,args){return{command,args,display:[command,...args].map(arg=>/\s/.test(arg)?`"${arg}"`:arg).join(" ")}}function detectInstallMethod(){if(isBunBinary)return"bun-binary";let resolvedPath=`${__dirname}\0${process.execPath||""}`.toLowerCase().replace(/\\/g,"/");return resolvedPath.includes("/pnpm/")||resolvedPath.includes("/.pnpm/")?"pnpm":resolvedPath.includes("/yarn/")||resolvedPath.includes("/.yarn/")?"yarn":isBunRuntime||resolvedPath.includes("/install/global/node_modules/")?"bun":resolvedPath.includes("/npm/")||resolvedPath.includes("/node_modules/")?"npm":"unknown"}function getInferredNpmInstall(){let packageDir=getPackageDir(),path=process.platform==="win32"||packageDir.includes("\\")?win32:{basename,dirname},parent=path.dirname(packageDir),root;if(path.basename(parent).startsWith("@")&&path.basename(path.dirname(parent))==="node_modules"?root=path.dirname(parent):path.basename(parent)==="node_modules"&&(root=parent),!root)return;let rootParent=path.dirname(root);if(path.basename(rootParent)==="lib")return{root,prefix:path.dirname(rootParent)}}function getSelfUpdateCommandForMethod(method,installedPackageName,updatePackageTarget=installedPackageName,npmCommand){let target=normalizeSelfUpdatePackageTarget(updatePackageTarget);switch(method){case"bun-binary":return;case"pnpm":{let match=readCommandOutput("pnpm",["root","-g"])?void 0:/^(.*[\\/]global[\\/][^\\/]+)[\\/]\.pnpm[\\/]/.exec(getPackageDir()),binDirArgs=match?[`--config.global-bin-dir=${process.env.PNPM_HOME||dirname(dirname(match[1]))}`]:[];return makeSelfUpdateCommand(makeSelfUpdateCommandStep("pnpm",["install","-g","--ignore-scripts","--config.minimumReleaseAge=0",...binDirArgs,target.installSpec]),target.packageName===installedPackageName?void 0:makeSelfUpdateCommandStep("pnpm",["remove","-g",...binDirArgs,installedPackageName]))}case"yarn":return makeSelfUpdateCommand(makeSelfUpdateCommandStep("yarn",["global","add","--ignore-scripts",target.installSpec]),target.packageName===installedPackageName?void 0:makeSelfUpdateCommandStep("yarn",["global","remove",installedPackageName]));case"bun":return makeSelfUpdateCommand(makeSelfUpdateCommandStep("bun",["install","-g","--ignore-scripts","--minimum-release-age=0",target.installSpec]),target.packageName===installedPackageName?void 0:makeSelfUpdateCommandStep("bun",["uninstall","-g",installedPackageName]));case"npm":{let[command="npm",...npmArgs]=npmCommand??[],inferred=npmCommand?.length?void 0:getInferredNpmInstall(),prefixArgs=[...npmArgs,...inferred?["--prefix",inferred.prefix]:[]],installStep=makeSelfUpdateCommandStep(command,[...prefixArgs,"install","-g","--ignore-scripts","--min-release-age=0",target.installSpec]),uninstallStep=target.packageName===installedPackageName?void 0:makeSelfUpdateCommandStep(command,[...prefixArgs,"uninstall","-g",installedPackageName]);return makeSelfUpdateCommand(installStep,uninstallStep)}case"unknown":return}}function readCommandOutput(command,args,options={}){let result=spawnProcessSync(command,args,{encoding:"utf-8",stdio:["ignore","pipe","pipe"]});if(result.status===0)return result.stdout.trim()||void 0;if(options.requireSuccess){let reason=result.error?.message||result.stderr.trim()||`exit code ${result.status??"unknown"}`;throw new Error(`Failed to run ${[command,...args].join(" ")}: ${reason}`)}}function getGlobalPackageRoots(method,_packageName,npmCommand){switch(method){case"npm":{let configured=!!npmCommand?.length,[command="npm",...npmArgs]=npmCommand??[];if(configured&&command==="bun"){let bunBin=readCommandOutput(command,[...npmArgs,"pm","bin","-g"],{requireSuccess:!0}),roots=[join2(homedir2(),".bun","install","global","node_modules")];return bunBin&&roots.push(join2(dirname(bunBin),"install","global","node_modules")),roots}let root=readCommandOutput(command,[...npmArgs,"root","-g"],{requireSuccess:configured}),inferred=configured?void 0:getInferredNpmInstall();return[root,inferred?.root].filter(x=>!!x)}case"pnpm":{let root=readCommandOutput("pnpm",["root","-g"]);if(root)return[root,dirname(root)];let match=/^(.*[\\/]global[\\/][^\\/]+)[\\/]\.pnpm[\\/]/.exec(getPackageDir());return match?[match[1]]:[]}case"yarn":{let dir=readCommandOutput("yarn",["global","dir"]);return dir?[dir,join2(dir,"node_modules")]:[]}case"bun":{let bunBin=readCommandOutput("bun",["pm","bin","-g"]),roots=[join2(homedir2(),".bun","install","global","node_modules")];return bunBin&&roots.push(join2(dirname(bunBin),"install","global","node_modules")),roots}case"bun-binary":case"unknown":return[]}}function normalizeExistingPathForComparison(path,resolveSymlinks){let resolvedPath=resolve(path);if(!existsSync(resolvedPath))return;let normalizedPath=resolvedPath;if(resolveSymlinks)try{normalizedPath=realpathSync2(resolvedPath)}catch{return}return process.platform==="win32"&&(normalizedPath=normalizedPath.toLowerCase()),normalizedPath}function getPathComparisonCandidates(path){return Array.from(new Set([normalizeExistingPathForComparison(path,!1),normalizeExistingPathForComparison(path,!0)].filter(candidate=>!!candidate)))}function getEntrypointPackageDir(){let entrypoint=process.argv[1];if(!entrypoint)return;let dir=dirname(entrypoint);for(;dir!==dirname(dir);){if(existsSync(join2(dir,"package.json")))return dir;dir=dirname(dir)}}function isSelfUpdatePathWritable(){let packageDir=getPackageDir();try{return accessSync(packageDir,constants.W_OK),accessSync(dirname(packageDir),constants.W_OK),!0}catch{return!1}}function isManagedByGlobalPackageManager(method,packageName,npmCommand){let packageDirCandidates=[getPackageDir(),getEntrypointPackageDir()].filter(dir=>!!dir).flatMap(dir=>getPathComparisonCandidates(dir));return getGlobalPackageRoots(method,packageName,npmCommand).some(root=>getPathComparisonCandidates(root).some(normalizedRoot=>{let rootPrefix=normalizedRoot.endsWith(sep2)?normalizedRoot:`${normalizedRoot}${sep2}`;return packageDirCandidates.some(packageDir=>packageDir.startsWith(rootPrefix))}))}function getSelfUpdateCommand(packageName,npmCommand,updatePackageTarget=packageName){let method=detectInstallMethod(),command=getSelfUpdateCommandForMethod(method,packageName,updatePackageTarget,npmCommand);if(!(!command||!isManagedByGlobalPackageManager(method,packageName,npmCommand)||!isSelfUpdatePathWritable()))return command}function getSelfUpdateUnavailableInstruction(packageName,npmCommand,updatePackageTarget=packageName){let method=detectInstallMethod(),target=normalizeSelfUpdatePackageTarget(updatePackageTarget);if(method==="bun-binary")return"Download from: https://github.com/earendil-works/pi/releases/latest";let command=getSelfUpdateCommandForMethod(method,packageName,target,npmCommand);return command?isManagedByGlobalPackageManager(method,packageName,npmCommand)&&!isSelfUpdatePathWritable()?`This installation is managed by a global ${method} install, but the install path is not writable. Update it yourself with: ${command.display}`:`This installation is not managed by a global ${method} install. Update it with the package manager, wrapper, or source checkout that provides it.`:`Update ${target.installSpec} using the package manager, wrapper, or source checkout that provides this installation.`}function findNodePackageDir(startDir){let dir=startDir;for(;dir!==dirname(dir);){if(existsSync(join2(dir,"package.json"))){let parent=dirname(dir);return basename(dir)==="dist"&&existsSync(join2(parent,"package.json"))?parent:dir}dir=dirname(dir)}return startDir}function getPackageDir(){let envDir=process.env.PI_PACKAGE_DIR;return envDir?normalizePath(envDir):isBunBinary?dirname(process.execPath):findNodePackageDir(__dirname)}function getThemesDir(){if(isBunBinary)return join2(getPackageDir(),"theme");let packageDir=getPackageDir(),srcOrDist=existsSync(join2(packageDir,"src"))?"src":"dist";return join2(packageDir,srcOrDist,"modes","interactive","theme")}function getExportTemplateDir(){if(isBunBinary)return join2(getPackageDir(),"export-html");let packageDir=getPackageDir(),srcOrDist=existsSync(join2(packageDir,"src"))?"src":"dist";return join2(packageDir,srcOrDist,"core","export-html")}function getPackageJsonPath(){return join2(getPackageDir(),"package.json")}function getReadmePath(){return resolve(join2(getPackageDir(),"README.md"))}function getDocsPath(){return resolve(join2(getPackageDir(),"docs"))}function getExamplesPath(){return resolve(join2(getPackageDir(),"examples"))}function getChangelogPath(){return resolve(join2(getPackageDir(),"CHANGELOG.md"))}function getInteractiveAssetsDir(){if(isBunBinary)return join2(getPackageDir(),"assets");let packageDir=getPackageDir(),srcOrDist=existsSync(join2(packageDir,"src"))?"src":"dist";return join2(packageDir,srcOrDist,"modes","interactive","assets")}function getBundledInteractiveAssetPath(name){return join2(getInteractiveAssetsDir(),name)}var quickJSWasmPath;function getQuickJSWasmPath(){return quickJSWasmPath??=createRequire(import.meta.url).resolve("quickjs-wasi/quickjs.wasm"),quickJSWasmPath}function resolveCodemodeWorkerSpecifier(runtime,moduleUrl){if(runtime==="bun-binary")return"./src/extensions/codemode/worker.ts";if(runtime==="bundled-node")return new URL("./codemode-worker.js",moduleUrl)}var codemodeWorkerDataUrl;function getCodemodeWorkerSpecifier(){let runtime=isBunBinary?"bun-binary":isBundledNode?"bundled-node":"unbundled",specifier=resolveCodemodeWorkerSpecifier(runtime,import.meta.url);return runtime!=="bundled-node"||!(specifier instanceof URL)?specifier:(codemodeWorkerDataUrl??=new URL(`data:text/javascript;base64,${readFileSync(specifier).toString("base64")}`),codemodeWorkerDataUrl)}function detectInstallChange(packageJsonPath=startupPackageJsonPath){if(isBunBinary||!packageJsonPath)return;let installed;try{installed=JSON.parse(stripBom(readFileSync(packageJsonPath,"utf-8")))}catch(error){return error.code==="ENOENT"?{kind:"removed"}:void 0}return installed.version&&installed.version!==VERSION?{kind:"updated",version:installed.version}:void 0}var pkg={},startupPackageJsonPath;try{let packageJsonPath=getPackageJsonPath();pkg=JSON.parse(stripBom(readFileSync(packageJsonPath,"utf-8"))),startupPackageJsonPath=packageJsonPath}catch(e){if(e.code!=="ENOENT")throw e}var piConfigName=pkg.piConfig?.name,PACKAGE_NAME=pkg.name||"@earendil-works/pi-coding-agent",APP_NAME=piConfigName||"pi",APP_TITLE=piConfigName?APP_NAME:"\u03C0",CONFIG_DIR_NAME=pkg.piConfig?.configDir||".pi",VERSION=pkg.version||"0.0.0",ENV_AGENT_DIR=`${APP_NAME.toUpperCase()}_CODING_AGENT_DIR`,ENV_SESSION_DIR=`${APP_NAME.toUpperCase()}_CODING_AGENT_SESSION_DIR`;function expandTildePath(path){return normalizePath(path)}var DEFAULT_SHARE_VIEWER_URL="https://pi.dev/session/";function getShareViewerUrl(gistId){return`${process.env.PI_SHARE_VIEWER_URL||DEFAULT_SHARE_VIEWER_URL}#${gistId}`}function getAgentDir(){let envDir=process.env[ENV_AGENT_DIR];return envDir?expandTildePath(envDir):join2(homedir2(),CONFIG_DIR_NAME,"agent")}function getCustomThemesDir(){return join2(getAgentDir(),"themes")}function getAuthPath(){return join2(getAgentDir(),"auth.json")}function getSettingsPath(){return join2(getAgentDir(),"settings.json")}function getBinDir(){return join2(getAgentDir(),"bin")}function getSessionsDir(){return join2(getAgentDir(),"sessions")}function getDebugLogPath(){return join2(getAgentDir(),`${APP_NAME}-debug.log`)}export{require_cross_spawn,spawnProcess,spawnProcessSync,waitForChildProcess,canonicalizePath,getFileRevision,isLocalPath,normalizePath,resolvePath,getCwdRelativePath,formatPathRelativeToCwdOrAbsolute,markPathIgnoredByCloudSync,splitBom,stripBom,isBunBinary,isBundledNode,detectInstallMethod,getSelfUpdateCommand,getSelfUpdateUnavailableInstruction,getPackageDir,getThemesDir,getExportTemplateDir,getReadmePath,getDocsPath,getExamplesPath,getChangelogPath,getBundledInteractiveAssetPath,getQuickJSWasmPath,getCodemodeWorkerSpecifier,detectInstallChange,PACKAGE_NAME,APP_NAME,APP_TITLE,CONFIG_DIR_NAME,VERSION,ENV_AGENT_DIR,ENV_SESSION_DIR,expandTildePath,getShareViewerUrl,getAgentDir,getCustomThemesDir,getAuthPath,getSettingsPath,getBinDir,getSessionsDir,getDebugLogPath};