mu-agent 0.1.8 → 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.
- package/CHANGELOG.md +121 -0
- package/README.md +34 -30
- package/dist/bundle/chunks/anthropic-messages-4PAJY4R6.js +6 -0
- package/dist/bundle/chunks/anthropic.js +1 -1
- package/dist/bundle/chunks/azure-openai-responses-Y4BBWQ3N.js +2 -0
- package/dist/bundle/chunks/bedrock-converse-stream.js +12 -9
- package/dist/bundle/chunks/{chunk-NG4PO5FP.js → chunk-3J3HSHLX.js} +128 -110
- package/dist/bundle/chunks/{chunk-TRDNDS6A.js → chunk-3V45WARH.js} +6 -3
- package/dist/bundle/chunks/{chunk-S3GGRYIO.js → chunk-5AGIVGSV.js} +1 -1
- package/dist/bundle/chunks/chunk-6QGV3IWD.js +6 -0
- package/dist/bundle/chunks/{chunk-4OBXYPH4.js → chunk-6Z2347ML.js} +1 -1
- package/dist/bundle/chunks/{chunk-F5ZABL6Z.js → chunk-7CBJINYJ.js} +2 -2
- package/dist/bundle/chunks/{chunk-DRQDKNXA.js → chunk-7DDKXS2Q.js} +1 -1
- package/dist/bundle/chunks/{chunk-H3QXYIF7.js → chunk-CF5GWZHW.js} +1 -1
- package/dist/bundle/chunks/{chunk-RLZVFGW4.js → chunk-DYTENXHX.js} +29 -92
- package/dist/bundle/chunks/{chunk-NTSWBG4X.js → chunk-FLHKNMLQ.js} +1 -1
- package/dist/bundle/chunks/chunk-FTJT2X6E.js +2 -0
- package/dist/bundle/chunks/{chunk-4L3WN2XY.js → chunk-JXWJOIH4.js} +1 -1
- package/dist/bundle/chunks/{chunk-TAOL3JFA.js → chunk-KIDDRTZP.js} +4 -4
- package/dist/bundle/chunks/chunk-LHRPMXVI.js +2 -0
- package/dist/bundle/chunks/chunk-LMNWTX2L.js +11 -0
- package/dist/bundle/chunks/chunk-LPXNMA2W.js +2 -0
- package/dist/bundle/chunks/chunk-LT2C56HV.js +130 -0
- package/dist/bundle/chunks/{chunk-6CYCI3EJ.js → chunk-LVVZS2NO.js} +1 -1
- package/dist/bundle/chunks/chunk-MF3VB62R.js +91 -0
- package/dist/bundle/chunks/chunk-NN4BEPYX.js +2 -0
- package/dist/bundle/chunks/{chunk-LD2P6SFZ.js → chunk-PMRZO46Y.js} +1 -1
- package/dist/bundle/chunks/chunk-R4VPBBDS.js +2 -0
- package/dist/bundle/chunks/chunk-RDZVAGPR.js +2 -0
- package/dist/bundle/chunks/chunk-TMTOQ7H7.js +2 -0
- package/dist/bundle/chunks/chunk-UKVNB6C4.js +2 -0
- package/dist/bundle/chunks/chunk-URBBMV3M.js +9 -0
- package/dist/bundle/chunks/chunk-WCGU75WO.js +11 -0
- package/dist/bundle/chunks/chunk-WCJNJQO5.js +9 -0
- package/dist/bundle/chunks/chunk-WDURT4ZM.js +2 -0
- package/dist/bundle/chunks/{cli-AOVD3SSZ.js → cli-M4YBZZ6Q.js} +4 -4
- package/dist/bundle/chunks/cloudflare-workers-ai-system-one-MEZHQVNP.js +2 -0
- package/dist/bundle/chunks/codemode-worker.js +117 -13
- package/dist/bundle/chunks/easter-egg-3d-VFY3YRQR.js +2 -0
- package/dist/bundle/chunks/execute-FKI7Y4XR.js +499 -0
- package/dist/bundle/chunks/github-copilot.js +1 -1
- package/dist/bundle/chunks/{google-generative-ai-XMFHGQC5.js → google-generative-ai-D2VJQBTK.js} +1 -1
- package/dist/bundle/chunks/{google-vertex-4XRRNP74.js → google-vertex-SQG2BO6C.js} +1 -1
- package/dist/bundle/chunks/https-proxy-agent-LVOO5TPL.js +13 -0
- package/dist/bundle/chunks/image-resize-worker.js +1 -1
- package/dist/bundle/chunks/{jiti-loader-C3NU2SDH.js → jiti-loader-GL4HJFAD.js} +1 -1
- package/dist/bundle/chunks/{jiti-static-loader-E6ANQD5T.js → jiti-static-loader-2RCDI6B4.js} +1 -1
- package/dist/bundle/chunks/{lib-TEDK3E5I.js → lib-4AVRLBHM.js} +1 -1
- package/dist/bundle/chunks/{llama-cpp-classify-XT44PI3C.js → llama-cpp-classify-5YHLY3RC.js} +2 -2
- package/dist/bundle/chunks/mistral-conversations-BDARVJTT.js +5 -0
- package/dist/bundle/chunks/{multipart-parser-SQ6GDJL7.js → multipart-parser-FMGQ7342.js} +1 -1
- package/dist/bundle/chunks/{node-TWVVAGPO.js → node-AUOGOBCN.js} +1 -1
- package/dist/bundle/chunks/{node-N5BSDOA5.js → node-KCMANYC2.js} +1 -1
- package/dist/bundle/chunks/openai-chatgpt.js +1 -1
- package/dist/bundle/chunks/openai-codex-responses-CECGTDYD.js +10 -0
- package/dist/bundle/chunks/openai-codex.js +1 -1
- package/dist/bundle/chunks/openai-completions-UOA7MELD.js +7 -0
- package/dist/bundle/chunks/openai-decisions-HLM372N6.js +5 -0
- package/dist/bundle/chunks/openai-responses-TII7ZY5X.js +3 -0
- package/dist/bundle/chunks/{openrouter-images-ZVXWCSIM.js → openrouter-images-7T452AS5.js} +1 -1
- package/dist/bundle/chunks/{pi-messages-TM5SOOIW.js → pi-messages-2UDKLKOO.js} +1 -1
- package/dist/bundle/chunks/runtime-IKE5OQIW.js +2 -0
- package/dist/bundle/chunks/{src-UK77SYZE.js → src-QKGKDFSU.js} +1 -1
- package/dist/bundle/chunks/typesafe-system-one-KNCICMO5.js +2 -0
- package/dist/bundle/chunks/virtual-modules-TJPHLTRT.js +2 -0
- package/dist/bundle/cli-runtime.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/core/export-html/template.js +21 -16
- package/dist/modes/interactive/theme/dark.json +1 -1
- package/dist/modes/interactive/theme/light.json +1 -1
- package/dist/modes/interactive/theme/theme-schema.d.ts +28 -0
- package/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -0
- package/dist/modes/interactive/theme/theme-schema.js +105 -0
- package/dist/modes/interactive/theme/theme-schema.js.map +1 -0
- package/dist/modes/interactive/theme/theme-tokens.d.ts +257 -0
- package/dist/modes/interactive/theme/theme-tokens.d.ts.map +1 -0
- package/dist/modes/interactive/theme/theme-tokens.js +94 -0
- package/dist/modes/interactive/theme/theme-tokens.js.map +1 -0
- package/dist/modes/interactive/theme/theme.d.ts +13 -16
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +22 -29
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/docs/cli-integration.md +1 -1
- package/docs/cli.md +22 -10
- package/docs/codemode.md +29 -6
- package/docs/compaction.md +5 -5
- package/docs/custom-provider.md +53 -1
- package/docs/docs.json +8 -0
- package/docs/environment-variables.md +3 -1
- package/docs/extensions.md +122 -13
- package/docs/json.md +3 -3
- package/docs/keybindings.md +5 -4
- package/docs/llama-cpp.md +9 -2
- package/docs/mcp.md +4 -2
- package/docs/message-types.md +22 -4
- package/docs/models-configuration.md +187 -0
- package/docs/models.md +18 -2
- package/docs/nvidia-rtx-llama-cpp.md +96 -0
- package/docs/providers.md +47 -0
- package/docs/sessions.md +1 -1
- package/docs/settings.md +9 -6
- package/docs/terminal-setup.md +14 -0
- package/docs/themes.md +4 -2
- package/docs/tui.md +71 -1
- package/docs/usage.md +1 -1
- package/examples/README.md +0 -3
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/dynamic-resources/dynamic.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/minimal-mode.ts +4 -5
- package/examples/extensions/overlay-test.ts +2 -2
- package/examples/extensions/plan-mode/index.ts +6 -2
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/subagent/index.ts +4 -6
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/judge/dist/auth.js +91 -35
- package/judge/dist/chrome.js +2 -2
- package/judge/dist/import.js +852 -175
- package/judge/dist/setup.js +240 -153
- package/judge/manifest.json +2512 -546
- package/judge/mu/chunk-3NQ5BIBI.js +4773 -0
- package/judge/mu/chunk-SKGYWHW6.js +51 -0
- package/judge/mu/chunk-WQM4MV6L.js +58 -0
- package/judge/mu/cloud-code-V7FOGB4J.js +22802 -0
- package/judge/mu/dist-75PS4OC4.js +1283 -0
- package/judge/{dist/kyrn-judge.js → mu/index.js} +5365 -32809
- package/judge/mu/multipart-parser-ZDXVIOOP.js +372 -0
- package/judge/mu/src-TVVA5ZK2.js +1409 -0
- package/judge/themes/mu-dark.json +80 -0
- package/judge/themes/mu-light.json +80 -0
- package/kyrn/bin/kyrn-doctor +44 -19
- package/kyrn/bin/kyrn-judge-local +43 -14
- package/kyrn/bin/kyrn-ledger +16 -1
- package/kyrn/bin/mu.mjs +142 -61
- package/kyrn/local-judge/server.py +26 -14
- package/package.json +8 -6
- package/dist/bundle/chunks/anthropic-messages-MI4QHRXW.js +0 -6
- package/dist/bundle/chunks/azure-openai-responses-E3QGN4VN.js +0 -2
- package/dist/bundle/chunks/chunk-33KL6IIQ.js +0 -20
- package/dist/bundle/chunks/chunk-A3JYRWB6.js +0 -2
- package/dist/bundle/chunks/chunk-C47CLQZK.js +0 -2
- package/dist/bundle/chunks/chunk-HQHD6PZI.js +0 -2
- package/dist/bundle/chunks/chunk-KJUDFE6Y.js +0 -11
- package/dist/bundle/chunks/chunk-TE3GUV6H.js +0 -9
- package/dist/bundle/chunks/chunk-VIV3CWXG.js +0 -2
- package/dist/bundle/chunks/chunk-WE4MXUQS.js +0 -11
- package/dist/bundle/chunks/chunk-Y2PP32XV.js +0 -2
- package/dist/bundle/chunks/chunk-YLY6CKPK.js +0 -132
- package/dist/bundle/chunks/chunk-ZE5FMF3V.js +0 -99
- package/dist/bundle/chunks/cloudflare-workers-ai-system-one-NM3JRRWQ.js +0 -2
- package/dist/bundle/chunks/easter-egg-3d-G2LUBEMZ.js +0 -2
- package/dist/bundle/chunks/execute-MZVUC6BB.js +0 -388
- package/dist/bundle/chunks/https-proxy-agent-2VXB7436.js +0 -13
- package/dist/bundle/chunks/mistral-conversations-Q22M3IBG.js +0 -5
- package/dist/bundle/chunks/openai-codex-responses-4MYQSO5E.js +0 -10
- package/dist/bundle/chunks/openai-completions-OSRCWAAA.js +0 -7
- package/dist/bundle/chunks/openai-responses-UHF3DHNG.js +0 -3
- package/dist/bundle/chunks/runtime-VJMMDLVH.js +0 -2
- package/dist/bundle/chunks/typesafe-system-one-PRR5K6CI.js +0 -2
- package/dist/bundle/chunks/virtual-modules-RJXJGCPI.js +0 -2
- package/dist/modes/interactive/theme/theme-json.d.ts +0 -85
- package/dist/modes/interactive/theme/theme-json.d.ts.map +0 -1
- package/dist/modes/interactive/theme/theme-json.js +0 -132
- package/dist/modes/interactive/theme/theme-json.js.map +0 -1
- package/dist/modes/interactive/theme/theme-schema.json +0 -361
- package/examples/plugins/pi-example-plugin/README.md +0 -38
- package/examples/plugins/pi-example-plugin/package.json +0 -10
- package/examples/plugins/pi-example-plugin/src/contract.ts +0 -13
- package/examples/plugins/pi-example-plugin/src/session.ts +0 -24
- package/examples/plugins/pi-example-plugin/src/tui.ts +0 -31
package/kyrn/bin/mu.mjs
CHANGED
|
@@ -94,26 +94,28 @@ export function platformName({ platform, wsl }) {
|
|
|
94
94
|
export function usage(platform) {
|
|
95
95
|
const linkDir = platform === "win32" ? "%USERPROFILE%\\.local\\bin" : "~/.local/bin";
|
|
96
96
|
return [
|
|
97
|
-
"mu
|
|
97
|
+
"mu: a coding agent with a judgment kernel, built on pi.",
|
|
98
98
|
"",
|
|
99
|
-
" mu
|
|
100
|
-
' mu "prompt"
|
|
101
|
-
' mu -p "prompt" one
|
|
102
|
-
" mu -c | -r continue the last session | pick
|
|
103
|
-
" mu setup connect a model (an API key
|
|
104
|
-
" (mu setup --help
|
|
105
|
-
" mu judge <cmd> the local judge (
|
|
106
|
-
" mu ledger [n] [--json]
|
|
107
|
-
" mu import
|
|
108
|
-
" mu doctor check the installation",
|
|
109
|
-
" mu auth <cmd>
|
|
99
|
+
" mu start a session in the current directory",
|
|
100
|
+
' mu "prompt" start a session with this prompt',
|
|
101
|
+
' mu -p "prompt" run one prompt, print the answer and exit',
|
|
102
|
+
" mu -c | -r continue the last session | pick a session to resume",
|
|
103
|
+
" mu setup connect a model (paste an API key or pick a service) and choose a judge",
|
|
104
|
+
" (see mu setup --help for scripting, reading a key from stdin and the supported services)",
|
|
105
|
+
" mu judge <cmd> manage the local judge, Laya (macOS on Apple silicon): setup | start | stop | status | run",
|
|
106
|
+
" mu ledger [n] [--json] print the judge's verdicts from the last n sessions",
|
|
107
|
+
" mu import <file>... import Claude Code, Codex and ZCode conversations; --list shows them (mu import --help)",
|
|
108
|
+
" mu doctor check the installation, the judges and the connections",
|
|
109
|
+
" mu auth <cmd> sign-in helper for the desktop app (JSON lines):",
|
|
110
110
|
" status | login <provider> | logout <provider>",
|
|
111
|
-
` mu link | unlink put the
|
|
112
|
-
" mu migrate [--dry-run]
|
|
113
|
-
" mu help | version",
|
|
111
|
+
` mu link | unlink put the mu command on your PATH (${linkDir}) | remove it`,
|
|
112
|
+
" mu migrate [--dry-run] move the old home folder, ~/.kyrn, to ~/.mu (once)",
|
|
113
|
+
" mu help | version show this help | print the version",
|
|
114
114
|
"",
|
|
115
|
-
"Everything else goes to
|
|
116
|
-
"and
|
|
115
|
+
"Everything else goes to pi unchanged: mu --help also shows pi's options,",
|
|
116
|
+
"and mu install|remove|update|list|config manage pi packages.",
|
|
117
|
+
"",
|
|
118
|
+
"Formerly KYRN: the kyrn commands still work.",
|
|
117
119
|
].join("\n");
|
|
118
120
|
}
|
|
119
121
|
|
|
@@ -310,7 +312,7 @@ export function ensureAppView({ platform, app, upstream, fs = realFs }) {
|
|
|
310
312
|
/**
|
|
311
313
|
* Where this mu comes from: "repo", a checkout that runs the TypeScript sources (sourceRuntime), or "package", the
|
|
312
314
|
* npm package mu-agent, which carries pi's bundle (dist/bundle/cli.js) and the judgment layer built to
|
|
313
|
-
* JavaScript (judge/
|
|
315
|
+
* JavaScript (judge/mu/index.js). A checkout without its dependencies is still a checkout.
|
|
314
316
|
*/
|
|
315
317
|
export function layoutOf({ root, platform, exists }) {
|
|
316
318
|
const path = pathFor(platform);
|
|
@@ -326,7 +328,7 @@ export function packageEntries({ root, platform }) {
|
|
|
326
328
|
const path = pathFor(platform);
|
|
327
329
|
return {
|
|
328
330
|
cli: path.join(root, "dist", "bundle", "cli.js"),
|
|
329
|
-
extension: path.join(root, "judge", "
|
|
331
|
+
extension: path.join(root, "judge", "mu", "index.js"),
|
|
330
332
|
auth: path.join(root, "judge", "dist", "auth.js"),
|
|
331
333
|
import: path.join(root, "judge", "dist", "import.js"),
|
|
332
334
|
setup: path.join(root, "judge", "dist", "setup.js"),
|
|
@@ -356,13 +358,13 @@ export function installHint({ root, platform }) {
|
|
|
356
358
|
const steps = [`cd ${root}`, "npm ci --ignore-scripts", "npm run hydrate:model-data"];
|
|
357
359
|
// Windows PowerShell 5 has no `&&`.
|
|
358
360
|
return platform === "win32"
|
|
359
|
-
? `mu's dependencies
|
|
360
|
-
: `mu's dependencies
|
|
361
|
+
? `mu's dependencies aren't installed. Run:\n${steps.map((step) => ` ${step}`).join("\n")}`
|
|
362
|
+
: `mu's dependencies aren't installed. Run: ${steps.join(" && ")}`;
|
|
361
363
|
}
|
|
362
364
|
|
|
363
365
|
/** What mu says on a Node that does not strip TypeScript types, which a checkout cannot run on. */
|
|
364
366
|
const NO_TYPE_STRIPPING =
|
|
365
|
-
"mu runs a checkout's TypeScript with Node's
|
|
367
|
+
"mu runs a source checkout's TypeScript with Node's built-in type stripping (on by default since Node 22.18), but this Node doesn't strip types. Use Node 22.19 or newer.";
|
|
366
368
|
|
|
367
369
|
/**
|
|
368
370
|
* Node's arguments for running a checkout's TypeScript, up to the entry file. Node strips the types itself (on by
|
|
@@ -428,10 +430,10 @@ export function localJudgeSupport({ platform, env, wsl = false }) {
|
|
|
428
430
|
runs: false,
|
|
429
431
|
url,
|
|
430
432
|
message: [
|
|
431
|
-
`The local judge (Laya) is a Core ML sidecar
|
|
432
|
-
"mu works without it:
|
|
433
|
-
"the desktop app
|
|
434
|
-
"
|
|
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, 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
|
+
"A local judge for the desktop app is in progress. Without a judge, decision points use their fallbacks.",
|
|
436
|
+
"To use a server that implements the same API (GET /health, POST /evaluate), set:",
|
|
435
437
|
" MU_LOCAL_JUDGE_URL=http://host:port",
|
|
436
438
|
].join("\n"),
|
|
437
439
|
};
|
|
@@ -447,12 +449,39 @@ export function launchStrategy({ platform, env, canExec }) {
|
|
|
447
449
|
/** How the judgment layer's extension joins pi's arguments: in front for a session, never in front of pi's own
|
|
448
450
|
* commands (PI_COMMANDS), which pi reads as its first argument — an -e before them would turn them into a prompt.
|
|
449
451
|
* The judgment layer never runs for them, so its extension stays out of their way. */
|
|
450
|
-
function judgeArgs({ argv, layout, root, platform }) {
|
|
452
|
+
function judgeArgs({ argv, layout, root, platform, agentDir, readFile }) {
|
|
451
453
|
if (PI_COMMANDS.has(argv[0])) return [];
|
|
452
|
-
|
|
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 }) {
|
|
453
459
|
return layout === "package"
|
|
454
|
-
?
|
|
455
|
-
:
|
|
460
|
+
? packageEntries({ root, platform }).extension
|
|
461
|
+
: pathFor(platform).join(root, "packages", "kyrn-judge", "src", "extension", "kyrn-judge.ts");
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/** mu's own terminal themes, beside the judgment layer's files. */
|
|
465
|
+
export function themesDir({ layout, root, platform }) {
|
|
466
|
+
const path = pathFor(platform);
|
|
467
|
+
return layout === "package" ? path.join(root, "judge", "themes") : path.join(root, "packages", "kyrn-judge", "themes");
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* An interactive session gets mu's themes (mu-light and mu-dark, which /settings then lists beside pi's) and, while the
|
|
472
|
+
* person has chosen no theme of their own, starts in them: light or dark as the terminal is. A theme chosen in
|
|
473
|
+
* /settings is saved in settings.json and wins; so does --use-theme. Print and RPC modes draw nothing and get neither.
|
|
474
|
+
*/
|
|
475
|
+
function themeArgs({ argv, layout, root, platform, agentDir, readFile }) {
|
|
476
|
+
if (argv.includes("--mode") || argv.includes("-p") || argv.includes("--print")) return [];
|
|
477
|
+
const args = ["--theme", themesDir({ layout, root, platform })];
|
|
478
|
+
if (argv.includes("--use-theme") || !agentDir || !readFile) return args;
|
|
479
|
+
let chosen = false;
|
|
480
|
+
try {
|
|
481
|
+
const settings = JSON.parse(readFile(pathFor(platform).join(agentDir, "settings.json")));
|
|
482
|
+
chosen = typeof settings?.theme === "string" && settings.theme !== "";
|
|
483
|
+
} catch {}
|
|
484
|
+
return chosen ? args : [...args, "--use-theme", "mu-light/mu-dark"];
|
|
456
485
|
}
|
|
457
486
|
|
|
458
487
|
/**
|
|
@@ -485,7 +514,7 @@ export function planLaunch({
|
|
|
485
514
|
} else {
|
|
486
515
|
const runtime = sourceRuntime({ root, platform, stripsTypes, exists: fs.exists });
|
|
487
516
|
if (runtime.error) return { error: runtime.error };
|
|
488
|
-
entry = [...runtime.args, path.join(root, "packages", "coding-agent", "src", "
|
|
517
|
+
entry = [...runtime.args, path.join(root, "packages", "coding-agent", "src", "cli.ts")];
|
|
489
518
|
}
|
|
490
519
|
|
|
491
520
|
const muDir = muHome({ home, platform, isDir: fs.isDir });
|
|
@@ -503,7 +532,7 @@ export function planLaunch({
|
|
|
503
532
|
const parsed = parseEnvFile(envText);
|
|
504
533
|
fromFile = envFileAdditions({ env, entries: parsed.entries, platform });
|
|
505
534
|
// Line numbers only: the text of a line that did not parse may well be a secret.
|
|
506
|
-
if (parsed.problems.length > 0) notes.push(`mu: .env: skipped line ${parsed.problems.join(", ")} (
|
|
535
|
+
if (parsed.problems.length > 0) notes.push(`mu: .env: skipped ${parsed.problems.length === 1 ? "line" : "lines"} ${parsed.problems.join(", ")} (expected KEY=VALUE)`);
|
|
507
536
|
}
|
|
508
537
|
|
|
509
538
|
const childEnv = {};
|
|
@@ -511,6 +540,8 @@ export function planLaunch({
|
|
|
511
540
|
if (typeof value === "string") childEnv[key] = value;
|
|
512
541
|
}
|
|
513
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 });
|
|
514
545
|
childEnv.MU_CODING_AGENT_DIR = agentDir;
|
|
515
546
|
childEnv.KYRN_CODING_AGENT_DIR = agentDir;
|
|
516
547
|
childEnv.PI_CODING_AGENT_DIR = agentDir;
|
|
@@ -526,14 +557,14 @@ export function planLaunch({
|
|
|
526
557
|
if (support.runs) startJudge = true;
|
|
527
558
|
else if (!support.url) {
|
|
528
559
|
notes.push(
|
|
529
|
-
`mu: the configuration asks for the local judge (
|
|
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)`,
|
|
530
561
|
);
|
|
531
562
|
}
|
|
532
563
|
}
|
|
533
564
|
|
|
534
565
|
return {
|
|
535
566
|
command: execPath,
|
|
536
|
-
args: [...entry, ...judgeArgs({ argv, layout, root, platform }), ...argv],
|
|
567
|
+
args: [...entry, ...judgeArgs({ argv, layout, root, platform, agentDir, readFile: fs.readFile }), ...argv],
|
|
537
568
|
env: childEnv,
|
|
538
569
|
strategy: launchStrategy({ platform, env, canExec }),
|
|
539
570
|
layout,
|
|
@@ -542,7 +573,7 @@ export function planLaunch({
|
|
|
542
573
|
agentDir,
|
|
543
574
|
startJudge,
|
|
544
575
|
notes,
|
|
545
|
-
preface: argv[0] === "-h" || argv[0] === "--help" ? `${usage(platform)}\n\
|
|
576
|
+
preface: argv[0] === "-h" || argv[0] === "--help" ? `${usage(platform)}\n\npi's help (mu passes all of this through):\n` : undefined,
|
|
546
577
|
};
|
|
547
578
|
}
|
|
548
579
|
|
|
@@ -552,6 +583,9 @@ export function planLaunch({
|
|
|
552
583
|
* Node flags that process starts with, pi's arguments (the judgment layer, then `argv`) and its whole environment.
|
|
553
584
|
* The app's pi and the command line's are then one: the same files, settings, sign-ins and keys.
|
|
554
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
|
+
*
|
|
555
589
|
* A checkout runs there as it runs on the command line (sourceRuntime). Returns { error } when pi cannot run.
|
|
556
590
|
*/
|
|
557
591
|
export function planHost({ platform, env, argv, root, home, fs, wsl = false, stripsTypes = true }) {
|
|
@@ -571,8 +605,9 @@ export function planHost({ platform, env, argv, root, home, fs, wsl = false, str
|
|
|
571
605
|
return {
|
|
572
606
|
...common,
|
|
573
607
|
module: path.join(root, "dist", "bundle", "index.js"),
|
|
608
|
+
cli: packageEntries({ root, platform }).cli,
|
|
574
609
|
execArgv: [],
|
|
575
|
-
args: [...judgeArgs({ argv, layout: launch.layout, root, platform }), ...argv],
|
|
610
|
+
args: [...judgeArgs({ argv, layout: launch.layout, root, platform, agentDir: launch.agentDir, readFile: fs.readFile }), ...argv],
|
|
576
611
|
};
|
|
577
612
|
}
|
|
578
613
|
const runtime = sourceRuntime({ root, platform, stripsTypes, exists: fs.exists });
|
|
@@ -580,14 +615,15 @@ export function planHost({ platform, env, argv, root, home, fs, wsl = false, str
|
|
|
580
615
|
return {
|
|
581
616
|
...common,
|
|
582
617
|
module: path.join(root, "packages", "coding-agent", "src", "index.ts"),
|
|
618
|
+
cli: path.join(root, "packages", "coding-agent", "src", "cli.ts"),
|
|
583
619
|
execArgv: runtime.args,
|
|
584
|
-
args: [...judgeArgs({ argv, layout: launch.layout, root, platform }), ...argv],
|
|
620
|
+
args: [...judgeArgs({ argv, layout: launch.layout, root, platform, agentDir: launch.agentDir, readFile: fs.readFile }), ...argv],
|
|
585
621
|
};
|
|
586
622
|
}
|
|
587
623
|
|
|
588
624
|
/** What mu says when the local judge does not come up. */
|
|
589
625
|
const LOCAL_JUDGE_NOT_STARTED =
|
|
590
|
-
"mu: the local judge
|
|
626
|
+
"mu: the local judge (Laya) didn't start; decision points use the next judge or their fallbacks. Run mu judge status for details.";
|
|
591
627
|
|
|
592
628
|
/** The part of the preparation that is files: the app view of a checkout, mu's agent folder, the configuration's notes. */
|
|
593
629
|
function prepareFiles({ plan, platform, root, err }) {
|
|
@@ -641,6 +677,29 @@ export async function prepareLaunchAsync({ plan, platform, root, bin, err }) {
|
|
|
641
677
|
/** What `mu auth` answers itself. Every other `auth` command (check, print-api-key, ...) is pi's own. */
|
|
642
678
|
export const AUTH_COMMANDS = ["status", "login", "logout"];
|
|
643
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
|
+
|
|
644
703
|
/**
|
|
645
704
|
* `mu auth status | login <provider> | logout <provider>`: the sign-in the desktop app runs, with pi's own OAuth
|
|
646
705
|
* flows and credential store (packages/kyrn-judge/src/auth). It is pi's code, so it runs the way pi does here:
|
|
@@ -688,7 +747,7 @@ export function planJudge({ platform, env, argv, bin, wsl = false }) {
|
|
|
688
747
|
// ---------------------------------------------------------------------------------------------------------
|
|
689
748
|
|
|
690
749
|
/**
|
|
691
|
-
* `mu import`: Claude Code and
|
|
750
|
+
* `mu import`: Claude Code, Codex and ZCode conversations into mu's sessions (packages/kyrn-judge/src/import). The importer
|
|
692
751
|
* needs nothing but Node: the npm package runs its build, judge/dist/import.js, and a checkout its TypeScript with
|
|
693
752
|
* Node's own type stripping (on by default from 22.18).
|
|
694
753
|
*/
|
|
@@ -726,6 +785,17 @@ export const SETUP_DECLINED_FILE = "setup-declined";
|
|
|
726
785
|
/** How `mu setup` says: set up, and start a session now. */
|
|
727
786
|
export const SETUP_EXIT_START = 3;
|
|
728
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
|
+
|
|
729
799
|
/** How `mu setup` says it was cancelled (ctrl+c): a first start then goes no further. */
|
|
730
800
|
export const SETUP_EXIT_CANCELLED = 130;
|
|
731
801
|
|
|
@@ -1003,7 +1073,7 @@ export function planLink({ platform, env, home, bin, argv, fs }) {
|
|
|
1003
1073
|
return {
|
|
1004
1074
|
action: "refuse",
|
|
1005
1075
|
errors: [
|
|
1006
|
-
`${link} already exists and is not this mu
|
|
1076
|
+
`${link} already exists and is not this mu, so it was left alone. To use another folder: MU_LINK_DIR=<dir> mu link`,
|
|
1007
1077
|
],
|
|
1008
1078
|
};
|
|
1009
1079
|
}
|
|
@@ -1031,7 +1101,7 @@ export function planLink({ platform, env, home, bin, argv, fs }) {
|
|
|
1031
1101
|
if (windows && /[^\x00-\x7f]/.test(content)) {
|
|
1032
1102
|
notes.push(
|
|
1033
1103
|
"Note: the path from that folder to this repository has non-ASCII characters in it, and cmd reads a batch file in the",
|
|
1034
|
-
"console's code page. If
|
|
1104
|
+
"console's code page. If mu then can't find its target, choose a folder on the same drive (MU_LINK_DIR), or put",
|
|
1035
1105
|
`${bin} on your PATH instead.`,
|
|
1036
1106
|
);
|
|
1037
1107
|
}
|
|
@@ -1046,12 +1116,12 @@ export function planUnlink({ platform, env, home, fs }) {
|
|
|
1046
1116
|
text = fs.readFile(link);
|
|
1047
1117
|
} catch {}
|
|
1048
1118
|
return text?.includes(SHIM_MARK)
|
|
1049
|
-
? { remove: link, message: `
|
|
1050
|
-
: { 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.` };
|
|
1051
1121
|
}
|
|
1052
1122
|
return fs.isLink(link)
|
|
1053
|
-
? { remove: link, message: `
|
|
1054
|
-
: { message: `${link} is not a link; nothing removed
|
|
1123
|
+
? { remove: link, message: `Removed ${link}.` }
|
|
1124
|
+
: { message: `${link} is not a link; nothing removed.` };
|
|
1055
1125
|
}
|
|
1056
1126
|
|
|
1057
1127
|
/** What `mu doctor` says about the command. */
|
|
@@ -1200,8 +1270,8 @@ export function migrate({ platform, home, argv, fs, run, alive, self, out, err }
|
|
|
1200
1270
|
return 0;
|
|
1201
1271
|
}
|
|
1202
1272
|
if (fs.exists(next) || fs.isLink(next)) {
|
|
1203
|
-
err(`${next} already exists, so nothing was moved.
|
|
1204
|
-
err("look at both
|
|
1273
|
+
err(`${next} already exists, so nothing was moved. mu never merges or overwrites two home folders:`);
|
|
1274
|
+
err("look at both and decide which one to keep.");
|
|
1205
1275
|
return 1;
|
|
1206
1276
|
}
|
|
1207
1277
|
// Whatever is running would write into the gap between the rename and the link, and split the home in two.
|
|
@@ -1210,10 +1280,10 @@ export function migrate({ platform, home, argv, fs, run, alive, self, out, err }
|
|
|
1210
1280
|
try {
|
|
1211
1281
|
pid = Number.parseInt(fs.readFile(path.join(old, "local-judge", "judge.pid")).trim(), 10);
|
|
1212
1282
|
} catch {}
|
|
1213
|
-
if (pid > 0 && alive(pid)) busy.push(` the local judge (pid ${pid}). Stop it
|
|
1283
|
+
if (pid > 0 && alive(pid)) busy.push(` the local judge (pid ${pid}). Stop it with mu judge stop`);
|
|
1214
1284
|
const found = findBusy({ platform, old, run, self });
|
|
1215
1285
|
if (!found) {
|
|
1216
|
-
err("Nothing was moved: the list of running processes
|
|
1286
|
+
err("Nothing was moved: mu couldn't read the list of running processes (tasklist, powershell), so it can't tell whether mu is still running.");
|
|
1217
1287
|
return 1;
|
|
1218
1288
|
}
|
|
1219
1289
|
const list = (pids) => pids.join(" ");
|
|
@@ -1222,7 +1292,7 @@ export function migrate({ platform, home, argv, fs, run, alive, self, out, err }
|
|
|
1222
1292
|
}
|
|
1223
1293
|
if (found.browser.length > 0) busy.push(` the mu browser (pid ${list(found.browser)}). Close it.`);
|
|
1224
1294
|
if (found.desktop.length > 0) {
|
|
1225
|
-
busy.push(` the desktop app's
|
|
1295
|
+
busy.push(` the desktop app's local service (pid ${list(found.desktop)}). Quit the desktop app.`);
|
|
1226
1296
|
}
|
|
1227
1297
|
if (busy.length > 0) {
|
|
1228
1298
|
err(`Nothing was moved, because these are still using ${old}:`);
|
|
@@ -1247,7 +1317,7 @@ export function migrate({ platform, home, argv, fs, run, alive, self, out, err }
|
|
|
1247
1317
|
out(`renamed ${path.join(next, config)} -> mu.json`);
|
|
1248
1318
|
}
|
|
1249
1319
|
out(`linked ${old} -> ${next} (paths stored from before the rename keep working)`);
|
|
1250
|
-
out("
|
|
1320
|
+
out("To check it, run mu doctor");
|
|
1251
1321
|
return 0;
|
|
1252
1322
|
}
|
|
1253
1323
|
|
|
@@ -1329,7 +1399,7 @@ function handOver({ command, args, env, strategy }) {
|
|
|
1329
1399
|
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) process.on(signal, () => relay(signal));
|
|
1330
1400
|
if (process.platform === "win32") process.on("SIGBREAK", () => {});
|
|
1331
1401
|
child.on("error", (error) => {
|
|
1332
|
-
err(`mu:
|
|
1402
|
+
err(`mu: couldn't start ${command}: ${error.message}`);
|
|
1333
1403
|
resolve(1);
|
|
1334
1404
|
});
|
|
1335
1405
|
child.on("exit", (code, signal) => resolve(code ?? 128 + (osConstants.signals[signal] ?? 0)));
|
|
@@ -1363,6 +1433,8 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1363
1433
|
out(usage(platform));
|
|
1364
1434
|
return 0;
|
|
1365
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);
|
|
1366
1438
|
if (command === "auth" && AUTH_COMMANDS.includes(rest[0])) {
|
|
1367
1439
|
const plan = planAuth({
|
|
1368
1440
|
platform,
|
|
@@ -1426,7 +1498,7 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1426
1498
|
out(await response.text());
|
|
1427
1499
|
return 0;
|
|
1428
1500
|
} catch {
|
|
1429
|
-
out(`
|
|
1501
|
+
out(`The local judge server isn't answering at ${plan.url}. Check that it's running.`);
|
|
1430
1502
|
return 1;
|
|
1431
1503
|
}
|
|
1432
1504
|
}
|
|
@@ -1471,8 +1543,8 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1471
1543
|
if (plan.keyGiven) process.title = "mu setup";
|
|
1472
1544
|
mkdirSync(plan.agentDir, { recursive: true });
|
|
1473
1545
|
const code = await handOver(plan);
|
|
1474
|
-
if (code !== SETUP_EXIT_START) return code;
|
|
1475
|
-
sessionArgv = [];
|
|
1546
|
+
if (code !== SETUP_EXIT_START && code !== SETUP_EXIT_SIGN_IN) return code;
|
|
1547
|
+
sessionArgv = afterSetup(code, []);
|
|
1476
1548
|
}
|
|
1477
1549
|
if (command === "doctor") {
|
|
1478
1550
|
const link = linkPath({ platform, env, home });
|
|
@@ -1483,8 +1555,12 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1483
1555
|
strategy,
|
|
1484
1556
|
});
|
|
1485
1557
|
}
|
|
1558
|
+
if ((command === "link" || command === "unlink") && (rest[0] === "--help" || rest[0] === "-h")) {
|
|
1559
|
+
out(LINK_USAGE);
|
|
1560
|
+
return 0;
|
|
1561
|
+
}
|
|
1486
1562
|
if ((command === "link" || command === "unlink") && layout === "package") {
|
|
1487
|
-
out("This mu was installed with npm, which already put `mu` on your PATH (npm i -g mu-agent); nothing to
|
|
1563
|
+
out("This mu was installed with npm, which already put `mu` on your PATH (npm i -g mu-agent); nothing to do.");
|
|
1488
1564
|
return 0;
|
|
1489
1565
|
}
|
|
1490
1566
|
if (command === "link") {
|
|
@@ -1517,9 +1593,10 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1517
1593
|
return 0;
|
|
1518
1594
|
}
|
|
1519
1595
|
|
|
1520
|
-
|
|
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")) {
|
|
1521
1598
|
const pkg = JSON.parse(readText(path.join(root, "package.json")));
|
|
1522
|
-
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);
|
|
1523
1600
|
return 0;
|
|
1524
1601
|
}
|
|
1525
1602
|
// Asked before `version` too, as it always was: a checkout without its dependencies says so at the first command.
|
|
@@ -1527,10 +1604,12 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1527
1604
|
err(installHint({ root, platform }));
|
|
1528
1605
|
return 1;
|
|
1529
1606
|
}
|
|
1530
|
-
if (command === "version") {
|
|
1607
|
+
if (command === "version" || command === "--version" || command === "-v") {
|
|
1531
1608
|
// mu's version is the one this tree is released as; the judgment layer's own package.json is never released.
|
|
1532
1609
|
const version = (file) => JSON.parse(readText(path.join(root, ...file.split("/")))).version;
|
|
1533
|
-
|
|
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);
|
|
1534
1613
|
return 0;
|
|
1535
1614
|
}
|
|
1536
1615
|
|
|
@@ -1564,7 +1643,9 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
1564
1643
|
if (!setup.error) {
|
|
1565
1644
|
mkdirSync(setup.agentDir, { recursive: true });
|
|
1566
1645
|
// Whatever the wizard ends with, the session follows (pi says itself when there is no model), unless it was cancelled.
|
|
1567
|
-
|
|
1646
|
+
const code = await handOver(setup);
|
|
1647
|
+
if (code === SETUP_EXIT_CANCELLED) return SETUP_EXIT_CANCELLED;
|
|
1648
|
+
sessionArgv = afterSetup(code, sessionArgv);
|
|
1568
1649
|
}
|
|
1569
1650
|
}
|
|
1570
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
|
|
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
|
-
#
|
|
31
|
-
#
|
|
32
|
-
|
|
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=
|
|
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 (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
#
|
|
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
|
|
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=
|
|
303
|
-
help="Smallest input length to pad to.
|
|
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,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mu-agent",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "mu: a coding agent that
|
|
3
|
+
"version": "0.1.13",
|
|
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": {
|
|
7
7
|
"mu": "kyrn/bin/mu.mjs"
|
|
@@ -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": {
|
|
@@ -33,13 +36,12 @@
|
|
|
33
36
|
"node": ">=22.19.0"
|
|
34
37
|
},
|
|
35
38
|
"dependencies": {
|
|
36
|
-
"@earendil-works/chord": "1.0.2",
|
|
37
39
|
"@silvia-odwyer/photon-node": "0.3.4",
|
|
38
40
|
"jiti": "2.7.0"
|
|
39
41
|
},
|
|
40
42
|
"muBuild": {
|
|
41
|
-
"pi": "1.0
|
|
43
|
+
"pi": "1.3.0",
|
|
42
44
|
"judge": "0.1.0",
|
|
43
|
-
"commit": "
|
|
45
|
+
"commit": "47bdfac19"
|
|
44
46
|
}
|
|
45
47
|
}
|