mu-agent 0.1.4 → 0.1.7

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 (119) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/README.md +7 -3
  3. package/dist/bundle/chunks/anthropic-messages-VMESUHQ7.js +6 -0
  4. package/dist/bundle/chunks/azure-openai-responses-K232MQFF.js +2 -0
  5. package/dist/bundle/chunks/bedrock-converse-stream.js +2 -2
  6. package/dist/bundle/chunks/{chunk-FUXEF6JQ.js → chunk-4O2SCVYF.js} +1 -1
  7. package/dist/bundle/chunks/chunk-AYFRR3OD.js +2 -0
  8. package/dist/bundle/chunks/chunk-FU4XQUSL.js +11 -0
  9. package/dist/bundle/chunks/{chunk-S7SZN6Z3.js → chunk-GNYZK6XN.js} +1 -1
  10. package/dist/bundle/chunks/{chunk-XOBBIK6P.js → chunk-HEMXN7VJ.js} +75 -67
  11. package/dist/bundle/chunks/chunk-MVPXOVOJ.js +2 -0
  12. package/dist/bundle/chunks/chunk-UTB4DTYQ.js +2 -0
  13. package/dist/bundle/chunks/{chunk-WIUZI2CJ.js → chunk-V7IXECST.js} +2 -2
  14. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-NR2TDCXU.js +2 -0
  15. package/dist/bundle/chunks/github-copilot.js +1 -1
  16. package/dist/bundle/chunks/google-generative-ai-C44KRRZ4.js +2 -0
  17. package/dist/bundle/chunks/google-vertex-25DUXPTJ.js +2 -0
  18. package/dist/bundle/chunks/meta.js +2 -0
  19. package/dist/bundle/chunks/mistral-conversations-6IHQOR4R.js +5 -0
  20. package/dist/bundle/chunks/{openai-codex-responses-QDSOTBBO.js → openai-codex-responses-3UAYOSSR.js} +2 -2
  21. package/dist/bundle/chunks/{openai-completions-KQPOPEVM.js → openai-completions-FIEQGWAH.js} +3 -3
  22. package/dist/bundle/chunks/openai-responses-OZJXLHFC.js +2 -0
  23. package/dist/bundle/chunks/{openrouter-images-N4XVVFSD.js → openrouter-images-JVEJN44Q.js} +1 -1
  24. package/dist/bundle/chunks/{pi-messages-GFKBWJHZ.js → pi-messages-TM5SOOIW.js} +2 -2
  25. package/dist/bundle/chunks/typesafe-system-one-VKYX4OGY.js +2 -0
  26. package/dist/bundle/chunks/{virtual-modules-MYFARBUK.js → virtual-modules-V7SXVVLP.js} +1 -1
  27. package/dist/bundle/cli-runtime.js +3 -0
  28. package/dist/bundle/cli.js +4 -2
  29. package/dist/bundle/index.js +1 -1
  30. package/dist/bundle/rpc-entry.js +1 -1
  31. package/dist/core/export-html/template.css +10 -0
  32. package/dist/core/export-html/template.js +56 -18
  33. package/dist/modes/interactive/theme/theme-controller.d.ts +6 -0
  34. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  35. package/dist/modes/interactive/theme/theme-controller.js +41 -3
  36. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  37. package/dist/modes/interactive/theme/theme-json.d.ts +1 -0
  38. package/dist/modes/interactive/theme/theme-json.d.ts.map +1 -1
  39. package/dist/modes/interactive/theme/theme-json.js +3 -1
  40. package/dist/modes/interactive/theme/theme-json.js.map +1 -1
  41. package/dist/modes/interactive/theme/theme-schema.json +7 -2
  42. package/dist/modes/interactive/theme/theme.d.ts +40 -7
  43. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  44. package/dist/modes/interactive/theme/theme.js +122 -214
  45. package/dist/modes/interactive/theme/theme.js.map +1 -1
  46. package/docs/cli-integration.md +106 -0
  47. package/docs/cli.md +268 -0
  48. package/docs/compaction.md +45 -26
  49. package/docs/configuration.md +45 -0
  50. package/docs/containerization.md +109 -82
  51. package/docs/custom-provider.md +109 -722
  52. package/docs/docs.json +139 -99
  53. package/docs/environment-variables.md +3 -5
  54. package/docs/extensions.md +137 -2953
  55. package/docs/how-pi-works.md +49 -0
  56. package/docs/images/interactive-mode.png +0 -0
  57. package/docs/index.md +24 -69
  58. package/docs/json.md +193 -65
  59. package/docs/keybindings.md +57 -102
  60. package/docs/llama-cpp.md +3 -3
  61. package/docs/message-types.md +261 -0
  62. package/docs/models.md +64 -546
  63. package/docs/packages.md +67 -166
  64. package/docs/prompt-templates.md +31 -68
  65. package/docs/providers.md +102 -232
  66. package/docs/quickstart.md +61 -106
  67. package/docs/rpc-commands.md +854 -0
  68. package/docs/rpc-extension-ui.md +200 -0
  69. package/docs/rpc.md +129 -1556
  70. package/docs/sdk.md +76 -1160
  71. package/docs/security.md +70 -32
  72. package/docs/session-format.md +25 -216
  73. package/docs/sessions.md +35 -141
  74. package/docs/settings.md +109 -387
  75. package/docs/shell-aliases.md +85 -5
  76. package/docs/skills.md +51 -190
  77. package/docs/slash-commands.md +60 -0
  78. package/docs/terminal-setup.md +105 -78
  79. package/docs/termux.md +74 -83
  80. package/docs/themes.md +71 -279
  81. package/docs/tmux.md +31 -39
  82. package/docs/tui.md +74 -910
  83. package/docs/usage.md +54 -272
  84. package/docs/windows.md +43 -17
  85. package/examples/README.md +13 -2
  86. package/examples/extensions/README.md +1 -0
  87. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  88. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  89. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  90. package/examples/extensions/debug-provider.ts +90 -0
  91. package/examples/extensions/gondolin/package-lock.json +2 -2
  92. package/examples/extensions/gondolin/package.json +1 -1
  93. package/examples/extensions/sandbox/package-lock.json +2 -2
  94. package/examples/extensions/sandbox/package.json +1 -1
  95. package/examples/extensions/with-deps/package-lock.json +2 -2
  96. package/examples/extensions/with-deps/package.json +1 -1
  97. package/examples/plugins/pi-example-plugin/src/session.ts +3 -2
  98. package/examples/rpc-client.ts +35 -0
  99. package/examples/rpc-extension-ui.ts +25 -5
  100. package/examples/sdk/README.md +1 -1
  101. package/judge/dist/auth.js +166 -28
  102. package/judge/dist/chrome.js +12 -1
  103. package/judge/dist/kyrn-judge.js +1424 -589
  104. package/judge/dist/setup.js +2051 -0
  105. package/judge/manifest.json +13 -13
  106. package/judge/skills/mu-browser/SKILL.md +1 -0
  107. package/kyrn/bin/kyrn-doctor +107 -18
  108. package/kyrn/bin/mu.mjs +389 -29
  109. package/package.json +4 -4
  110. package/dist/bundle/chunks/anthropic-messages-MYU5ZMRF.js +0 -6
  111. package/dist/bundle/chunks/azure-openai-responses-HUSQ3GP2.js +0 -2
  112. package/dist/bundle/chunks/chunk-6R7UI4IY.js +0 -2
  113. package/dist/bundle/chunks/chunk-CK5Z65DM.js +0 -2
  114. package/dist/bundle/chunks/chunk-RCZIEVGO.js +0 -11
  115. package/dist/bundle/chunks/google-generative-ai-IMS5EK2Y.js +0 -2
  116. package/dist/bundle/chunks/google-vertex-O4YJEQUW.js +0 -2
  117. package/dist/bundle/chunks/mistral-conversations-NOSQHBEY.js +0 -5
  118. package/dist/bundle/chunks/openai-responses-HWEUZ6WZ.js +0 -2
  119. package/docs/development.md +0 -90
package/kyrn/bin/mu.mjs CHANGED
@@ -9,7 +9,7 @@
9
9
  // - npm's own `mu` command, in the npm package mu-agent (kyrn/npm/build.mjs). The package is laid out like the
10
10
  // repository, so this file is at kyrn/bin/mu.mjs there too, and the root is two folders up in both.
11
11
  //
12
- // No dependency and no TypeScript here: this runs before tsx has been found.
12
+ // No dependency and no TypeScript here: this runs before anything that could run TypeScript has been found.
13
13
  //
14
14
  // Every decision is an exported function that takes the platform, the environment and the file system as
15
15
  // parameters and returns what would be done. There is no Windows machine where this is developed, so the
@@ -46,7 +46,7 @@ import {
46
46
  } from "node:fs";
47
47
  import { homedir, constants as osConstants } from "node:os";
48
48
  import nodePath from "node:path";
49
- import { fileURLToPath } from "node:url";
49
+ import { fileURLToPath, pathToFileURL } from "node:url";
50
50
 
51
51
  /** pi's minimum. */
52
52
  export const MIN_NODE = [22, 19];
@@ -100,6 +100,8 @@ export function usage(platform) {
100
100
  ' mu "prompt" interactive, starting with this prompt',
101
101
  ' mu -p "prompt" one-shot: print the answer and exit',
102
102
  " mu -c | -r continue the last session | pick one to resume",
103
+ " mu setup connect a model (an API key, or a service from a list) and choose the judge",
104
+ " (mu setup --help: scripts, a key from stdin, the services it knows)",
103
105
  " mu judge <cmd> the local judge (Laya): setup | start | stop | status | run",
104
106
  " mu ledger [n] [--json] what the judge decided in the last n sessions",
105
107
  " mu import --list | <file>... bring Claude Code and Codex conversations into mu (mu import --help)",
@@ -306,7 +308,7 @@ export function ensureAppView({ platform, app, upstream, fs = realFs }) {
306
308
  // ---------------------------------------------------------------------------------------------------------
307
309
 
308
310
  /**
309
- * Where this mu comes from: "repo", a checkout that runs the TypeScript sources through tsx, or "package", the
311
+ * Where this mu comes from: "repo", a checkout that runs the TypeScript sources (sourceRuntime), or "package", the
310
312
  * npm package mu-agent, which carries pi's bundle (dist/bundle/cli.js) and the judgment layer built to
311
313
  * JavaScript (judge/dist/kyrn-judge.js). A checkout without its dependencies is still a checkout.
312
314
  */
@@ -316,7 +318,10 @@ export function layoutOf({ root, platform, exists }) {
316
318
  return exists(path.join(root, "dist", "bundle", "cli.js")) ? "package" : "repo";
317
319
  }
318
320
 
319
- /** The files the npm package runs: pi, the judgment layer, the sign-in of `mu auth` and the importer of `mu import`. */
321
+ /**
322
+ * The files the npm package runs: pi, the judgment layer, the sign-in of `mu auth`, the importer of `mu import` and the
323
+ * wizard of `mu setup`.
324
+ */
320
325
  export function packageEntries({ root, platform }) {
321
326
  const path = pathFor(platform);
322
327
  return {
@@ -324,6 +329,7 @@ export function packageEntries({ root, platform }) {
324
329
  extension: path.join(root, "judge", "dist", "kyrn-judge.js"),
325
330
  auth: path.join(root, "judge", "dist", "auth.js"),
326
331
  import: path.join(root, "judge", "dist", "import.js"),
332
+ setup: path.join(root, "judge", "dist", "setup.js"),
327
333
  };
328
334
  }
329
335
 
@@ -370,6 +376,35 @@ export function installHint({ root, platform }) {
370
376
  : `mu's dependencies are not installed. Run: ${steps.join(" && ")}`;
371
377
  }
372
378
 
379
+ /**
380
+ * Node's arguments for running a checkout's TypeScript, up to the entry file. A Node that strips types itself (on
381
+ * by default from 22.18) runs the sources as they are, with pi's own source resolver
382
+ * (packages/coding-agent/src/experimental/source-resolver.ts: each workspace package resolves to its sources, as
383
+ * tsconfig.json's paths say) and Node's compile cache (compile-cache.mjs). tsx runs its loader on a thread of its
384
+ * own and hands every file over from there: pi with the judgment layer took 2 s to start that way, about 0.85 s
385
+ * this way, and a checkout starts mu for every conversation the desktop app opens. A runtime that does not strip
386
+ * types (`stripsTypes` false; an Electron that runs mu as Node may not), or a checkout from before the resolver,
387
+ * still goes through tsx. tsx is one of the checkout's dependencies either way: without it they are not installed.
388
+ */
389
+ export function sourceRuntime({ root, platform, stripsTypes, exists, readFile }) {
390
+ const path = pathFor(platform);
391
+ const tsx = resolveTsx({ root, platform, exists, readFile });
392
+ if (!tsx) return { error: installHint({ root, platform }) };
393
+ const resolver = path.join(root, "packages", "coding-agent", "src", "experimental", "source-resolver.ts");
394
+ if (!stripsTypes || !exists(resolver)) return { args: [tsx, "--tsconfig", path.join(root, "tsconfig.json")] };
395
+ // As URLs: `--import` reads a Windows path such as C:\... as a URL whose scheme is c:.
396
+ const url = (file) => pathToFileURL(file, { windows: platform === "win32" }).href;
397
+ return {
398
+ args: [
399
+ "--disable-warning=ExperimentalWarning",
400
+ "--import",
401
+ url(path.join(root, "kyrn", "bin", "compile-cache.mjs")),
402
+ "--import",
403
+ url(resolver),
404
+ ],
405
+ };
406
+ }
407
+
373
408
  /**
374
409
  * pi reads <APP NAME>_CODING_AGENT_DIR and nothing else, so with the app named mu that export decides the
375
410
  * home. The pi and KYRN spellings are exported too, for scripts that still read them.
@@ -429,7 +464,18 @@ export function launchStrategy({ platform, env, canExec }) {
429
464
  *
430
465
  * `fs` is { exists, isDir, readFile }. Returns { error } when pi cannot be started.
431
466
  */
432
- export function planLaunch({ platform, env, argv, root, home, execPath, fs, canExec = false, wsl = false }) {
467
+ export function planLaunch({
468
+ platform,
469
+ env,
470
+ argv,
471
+ root,
472
+ home,
473
+ execPath,
474
+ fs,
475
+ canExec = false,
476
+ wsl = false,
477
+ stripsTypes = true,
478
+ }) {
433
479
  const path = pathFor(platform);
434
480
  const layout = layoutOf({ root, platform, exists: fs.exists });
435
481
  let entry;
@@ -440,12 +486,10 @@ export function planLaunch({ platform, env, argv, root, home, execPath, fs, canE
440
486
  }
441
487
  entry = [files.cli, "-e", files.extension];
442
488
  } else {
443
- const tsx = resolveTsx({ root, platform, exists: fs.exists, readFile: fs.readFile });
444
- if (!tsx) return { error: installHint({ root, platform }) };
489
+ const runtime = sourceRuntime({ root, platform, stripsTypes, exists: fs.exists, readFile: fs.readFile });
490
+ if (runtime.error) return { error: runtime.error };
445
491
  entry = [
446
- tsx,
447
- "--tsconfig",
448
- path.join(root, "tsconfig.json"),
492
+ ...runtime.args,
449
493
  path.join(root, "packages", "coding-agent", "src", "experimental", "cli.ts"),
450
494
  "-e",
451
495
  path.join(root, "packages", "kyrn-judge", "src", "extension", "kyrn-judge.ts"),
@@ -510,6 +554,101 @@ export function planLaunch({ platform, env, argv, root, home, execPath, fs, canE
510
554
  };
511
555
  }
512
556
 
557
+ /**
558
+ * Everything about running pi inside a process of another program, such as the desktop app's runtime host, decided
559
+ * as planLaunch decides it and without touching anything: the module to import pi's `setupCli` and `main` from, the
560
+ * Node flags that process starts with, pi's arguments (the judgment layer, then `argv`) and its whole environment.
561
+ * The app's pi and the command line's are then one: the same files, settings, sign-ins and keys.
562
+ *
563
+ * A checkout runs in such a process only on a Node that strips TypeScript types itself, with pi's source resolver:
564
+ * tsx cannot be put in front of a module another program imports. Returns { error } otherwise.
565
+ */
566
+ export function planHost({ platform, env, argv, root, home, fs, wsl = false, stripsTypes = true }) {
567
+ const path = pathFor(platform);
568
+ const launch = planLaunch({ platform, env, argv, root, home, execPath: "", fs, wsl, stripsTypes });
569
+ if (launch.error !== undefined) return { error: launch.error };
570
+ const common = {
571
+ env: launch.env,
572
+ layout: launch.layout,
573
+ muDir: launch.muDir,
574
+ appDir: launch.appDir,
575
+ agentDir: launch.agentDir,
576
+ startJudge: launch.startJudge,
577
+ notes: launch.notes,
578
+ };
579
+ if (launch.layout === "package") {
580
+ return {
581
+ ...common,
582
+ module: path.join(root, "dist", "bundle", "index.js"),
583
+ execArgv: [],
584
+ args: ["-e", packageEntries({ root, platform }).extension, ...argv],
585
+ };
586
+ }
587
+ const resolver = path.join(root, "packages", "coding-agent", "src", "experimental", "source-resolver.ts");
588
+ if (!stripsTypes || !fs.exists(resolver)) {
589
+ return {
590
+ error: `pi's sources run inside another program only on a Node that strips TypeScript types itself (22.18 or newer), with pi's source resolver (${resolver})`,
591
+ };
592
+ }
593
+ const runtime = sourceRuntime({ root, platform, stripsTypes, exists: fs.exists, readFile: fs.readFile });
594
+ if (runtime.error) return { error: runtime.error };
595
+ return {
596
+ ...common,
597
+ module: path.join(root, "packages", "coding-agent", "src", "index.ts"),
598
+ execArgv: runtime.args,
599
+ args: ["-e", path.join(root, "packages", "kyrn-judge", "src", "extension", "kyrn-judge.ts"), ...argv],
600
+ };
601
+ }
602
+
603
+ /** What mu says when the local judge does not come up. */
604
+ const LOCAL_JUDGE_NOT_STARTED =
605
+ "mu: the local judge did not start (mu judge status); decisions fall back to pi's behaviour";
606
+
607
+ /** The part of the preparation that is files: the app view of a checkout, mu's agent folder, the configuration's notes. */
608
+ function prepareFiles({ plan, platform, root, err }) {
609
+ const path = pathFor(platform);
610
+ if (plan.layout === "repo") {
611
+ ensureAppView({ platform, app: plan.appDir, upstream: path.join(root, "packages", "coding-agent") });
612
+ }
613
+ mkdirSync(plan.agentDir, { recursive: true });
614
+ for (const note of plan.notes) err(note);
615
+ }
616
+
617
+ /**
618
+ * What is done before pi starts, for the command line and the desktop app's runtime host alike: the app view that
619
+ * names a checkout mu, mu's agent folder, the configuration's notes (to `err`), and the local judge when the
620
+ * configuration asks for it. `bin` is this launcher's folder. The local judge is one shared sidecar: starting it is
621
+ * idempotent and never blocks pi.
622
+ */
623
+ export function prepareLaunch({ plan, platform, root, bin, err }) {
624
+ prepareFiles({ plan, platform, root, err });
625
+ if (plan.startJudge) {
626
+ const started = spawnSync(pathFor(platform).join(bin, "kyrn-judge-local"), ["start"], {
627
+ stdio: "ignore",
628
+ env: plan.env,
629
+ });
630
+ if (started.status !== 0) err(LOCAL_JUDGE_NOT_STARTED);
631
+ }
632
+ }
633
+
634
+ /**
635
+ * prepareLaunch for a program that must not stop while the local judge starts, such as the desktop app's main process:
636
+ * the same steps and notes, with the local judge started through a child process it waits for without blocking.
637
+ */
638
+ export async function prepareLaunchAsync({ plan, platform, root, bin, err }) {
639
+ prepareFiles({ plan, platform, root, err });
640
+ if (!plan.startJudge) return;
641
+ const status = await new Promise((resolve) => {
642
+ const child = spawn(pathFor(platform).join(bin, "kyrn-judge-local"), ["start"], {
643
+ stdio: "ignore",
644
+ env: plan.env,
645
+ });
646
+ child.once("error", () => resolve(null));
647
+ child.once("close", (code) => resolve(code));
648
+ });
649
+ if (status !== 0) err(LOCAL_JUDGE_NOT_STARTED);
650
+ }
651
+
513
652
  // ---------------------------------------------------------------------------------------------------------
514
653
  // mu auth
515
654
  // ---------------------------------------------------------------------------------------------------------
@@ -520,9 +659,9 @@ export const AUTH_COMMANDS = ["status", "login", "logout"];
520
659
  /**
521
660
  * `mu auth status | login <provider> | logout <provider>`: the sign-in the desktop app runs, with pi's own OAuth
522
661
  * flows and credential store (packages/kyrn-judge/src/auth). It is pi's code, so it runs the way pi does here:
523
- * through tsx in a checkout, built in the package. It reads no key, so no .env is read for it.
662
+ * from its sources in a checkout (sourceRuntime), built in the package. It reads no key, so no .env is read for it.
524
663
  */
525
- export function planAuth({ platform, env, argv, root, home, execPath, fs, canExec = false }) {
664
+ export function planAuth({ platform, env, argv, root, home, execPath, fs, canExec = false, stripsTypes = true }) {
526
665
  const path = pathFor(platform);
527
666
  let entry;
528
667
  if (layoutOf({ root, platform, exists: fs.exists }) === "package") {
@@ -532,10 +671,9 @@ export function planAuth({ platform, env, argv, root, home, execPath, fs, canExe
532
671
  }
533
672
  entry = [auth];
534
673
  } else {
535
- const tsx = resolveTsx({ root, platform, exists: fs.exists, readFile: fs.readFile });
536
- if (!tsx) return { error: installHint({ root, platform }) };
537
- const main = path.join(root, "packages", "kyrn-judge", "src", "auth", "main.ts");
538
- entry = [tsx, "--tsconfig", path.join(root, "tsconfig.json"), main];
674
+ const runtime = sourceRuntime({ root, platform, stripsTypes, exists: fs.exists, readFile: fs.readFile });
675
+ if (runtime.error) return { error: runtime.error };
676
+ entry = [...runtime.args, path.join(root, "packages", "kyrn-judge", "src", "auth", "main.ts")];
539
677
  }
540
678
  const agentDir = agentDirFor({ env, muDir: muHome({ home, platform, isDir: fs.isDir }), platform });
541
679
  const childEnv = {};
@@ -598,6 +736,178 @@ export function planImport({ platform, env, argv, root, home, execPath, fs, stri
598
736
  return { command: execPath, args: [...entry, ...argv], env: childEnv, agentDir };
599
737
  }
600
738
 
739
+ // ---------------------------------------------------------------------------------------------------------
740
+ // mu setup
741
+ // ---------------------------------------------------------------------------------------------------------
742
+
743
+ /** What a "no" to the first-start question leaves in the agent folder (packages/kyrn-judge/src/setup/wizard.ts). */
744
+ export const SETUP_DECLINED_FILE = "setup-declined";
745
+
746
+ /** How `mu setup` says: set up, and start a session now. */
747
+ export const SETUP_EXIT_START = 3;
748
+
749
+ /** How `mu setup` says it was cancelled (ctrl+c): a first start then goes no further. */
750
+ export const SETUP_EXIT_CANCELLED = 130;
751
+
752
+ /** The options of `mu setup` that take a value (wizard.ts keeps the same list): the word after one is not a key. */
753
+ export const SETUP_VALUE_FLAGS = ["--service", "--model", "--base-url", "--api", "--name", "--judge"];
754
+
755
+ /**
756
+ * The environment variables that give pi a model on their own: packages/ai/src/env-api-keys.ts, which a test holds this
757
+ * list to, and AWS_BEARER_TOKEN_BEDROCK, which is there for nothing else. TYPESAFE_API_KEY is left out, as pi's
758
+ * typesafe provider has classifiers only (the key is Jev's). So are AWS profiles and Google Cloud's default
759
+ * credentials: many machines have them for other work.
760
+ */
761
+ export const MODEL_KEY_VARIABLES = [
762
+ "COPILOT_GITHUB_TOKEN",
763
+ "ANTHROPIC_AUTH_TOKEN",
764
+ "ANTHROPIC_OAUTH_TOKEN",
765
+ "ANTHROPIC_API_KEY",
766
+ "ANT_LING_API_KEY",
767
+ "QWEN_TOKEN_PLAN_API_KEY",
768
+ "QWEN_TOKEN_PLAN_CN_API_KEY",
769
+ "OPENAI_API_KEY",
770
+ "AZURE_OPENAI_API_KEY",
771
+ "NVIDIA_API_KEY",
772
+ "DEEPSEEK_API_KEY",
773
+ "GEMINI_API_KEY",
774
+ "GOOGLE_CLOUD_API_KEY",
775
+ "GROQ_API_KEY",
776
+ "CEREBRAS_API_KEY",
777
+ "XAI_API_KEY",
778
+ "RADIUS_API_KEY",
779
+ "OPENROUTER_API_KEY",
780
+ "AI_GATEWAY_API_KEY",
781
+ "ZAI_API_KEY",
782
+ "ZAI_CODING_CN_API_KEY",
783
+ "MISTRAL_API_KEY",
784
+ "MINIMAX_API_KEY",
785
+ "MINIMAX_CN_API_KEY",
786
+ "MOONSHOT_API_KEY",
787
+ "HF_TOKEN",
788
+ "FIREWORKS_API_KEY",
789
+ "TOGETHER_API_KEY",
790
+ "BASETEN_API_KEY",
791
+ "OPENCODE_API_KEY",
792
+ "KIMI_API_KEY",
793
+ "META_API_KEY",
794
+ "CLOUDFLARE_API_KEY",
795
+ "XIAOMI_API_KEY",
796
+ "XIAOMI_TOKEN_PLAN_CN_API_KEY",
797
+ "XIAOMI_TOKEN_PLAN_AMS_API_KEY",
798
+ "XIAOMI_TOKEN_PLAN_SGP_API_KEY",
799
+ "AWS_BEARER_TOKEN_BEDROCK",
800
+ ];
801
+
802
+ /**
803
+ * `mu setup`: the wizard that connects a model and chooses the judge (packages/kyrn-judge/src/setup). It is pi's code,
804
+ * so it runs the way `mu auth` runs: from the sources in a checkout, built in the package. A key on the command line
805
+ * (`mu setup <key>`) goes to the wizard in the environment, MU_SETUP_KEY, never in its argv, which every process on
806
+ * the machine can read. The launcher waits for the wizard, since a finished setup may go on into a session.
807
+ */
808
+ export function planSetup({ platform, env, argv, root, home, execPath, fs, stripsTypes = true }) {
809
+ const path = pathFor(platform);
810
+ const layout = layoutOf({ root, platform, exists: fs.exists });
811
+ let entry;
812
+ if (layout === "package") {
813
+ const { setup } = packageEntries({ root, platform });
814
+ if (!fs.exists(setup)) {
815
+ return { error: `This mu-agent package has no mu setup (${setup} is missing). Update it: npm i -g mu-agent` };
816
+ }
817
+ entry = [setup];
818
+ } else {
819
+ const runtime = sourceRuntime({ root, platform, stripsTypes, exists: fs.exists, readFile: fs.readFile });
820
+ if (runtime.error) return { error: runtime.error };
821
+ entry = [...runtime.args, path.join(root, "packages", "kyrn-judge", "src", "setup", "main.ts")];
822
+ }
823
+ const args = [];
824
+ let key;
825
+ for (let index = 0; index < argv.length; index++) {
826
+ const arg = argv[index];
827
+ if (SETUP_VALUE_FLAGS.includes(arg)) {
828
+ args.push(arg);
829
+ if (index + 1 < argv.length) args.push(argv[++index]);
830
+ } else if (arg.startsWith("-") || arg === "help") args.push(arg);
831
+ else if (key === undefined) key = arg;
832
+ else return { error: "mu setup takes one key at most (mu setup --help)", code: 2 };
833
+ }
834
+ const muDir = muHome({ home, platform, isDir: fs.isDir });
835
+ const agentDir = agentDirFor({ env, muDir, platform });
836
+ const childEnv = {};
837
+ for (const [name, value] of Object.entries(env)) if (typeof value === "string") childEnv[name] = value;
838
+ if (key !== undefined) childEnv.MU_SETUP_KEY = key;
839
+ childEnv.MU_AGENT_DIR = agentDir;
840
+ childEnv.MU_CODING_AGENT_DIR = agentDir;
841
+ childEnv.KYRN_CODING_AGENT_DIR = agentDir;
842
+ childEnv.PI_CODING_AGENT_DIR = agentDir;
843
+ childEnv.MU_SETUP_HOME = muDir;
844
+ // The .env a session gets: the wizard writes the Jev key there, and reads a proxy from it.
845
+ childEnv.MU_SETUP_ENV_FILE = envFilePath({ layout, root, muDir, platform });
846
+ return { command: execPath, args: [...entry, ...args], env: childEnv, strategy: "spawn", agentDir, keyGiven: key !== undefined };
847
+ }
848
+
849
+ /** pi's own commands: none of them starts a session. */
850
+ const PI_COMMANDS = new Set(["install", "remove", "uninstall", "update", "list", "config", "auth"]);
851
+ /** Options for a run without a person (-p, --mode rpc), one that ends at once, or one that names its model itself. */
852
+ const NO_SETUP_FLAGS = new Set([
853
+ "-p",
854
+ "--print",
855
+ "--mode",
856
+ "-h",
857
+ "--help",
858
+ "-v",
859
+ "--version",
860
+ "--list-models",
861
+ "--export",
862
+ "--api-key",
863
+ "--provider",
864
+ "--model",
865
+ "--models",
866
+ ]);
867
+
868
+ /**
869
+ * Whether `mu` offers to set up a model before a session: only to a person at a terminal (stdin and stdout) who starts
870
+ * a session, on a machine where nothing gives mu a model yet, and who has not answered no before (SETUP_DECLINED_FILE)
871
+ * or turned the question off (MU_NO_SETUP=1). Nothing gives a model: no credential in auth.json, no provider in
872
+ * models.json, no default provider in settings.json, no key of MODEL_KEY_VARIABLES in the environment or the .env.
873
+ * A file that cannot be read counts as set up: the question is for an empty home, never for a broken one.
874
+ *
875
+ * `fs` is { exists, readFile }; `envText` is the .env the launcher reads, when there is one.
876
+ */
877
+ export function planFirstRun({ platform, env, argv, interactive, agentDir, envText, fs }) {
878
+ if (!interactive) return false;
879
+ const off = muEnv("NO_SETUP", env);
880
+ if (off && off !== "0" && off.toLowerCase() !== "false") return false;
881
+ if (argv[0] !== undefined && PI_COMMANDS.has(argv[0])) return false;
882
+ if (argv.some((arg) => NO_SETUP_FLAGS.has(arg.split("=")[0]))) return false;
883
+ const path = pathFor(platform);
884
+ if (fs.exists(path.join(agentDir, SETUP_DECLINED_FILE))) return false;
885
+ const read = (name) => {
886
+ const file = path.join(agentDir, name);
887
+ if (!fs.exists(file)) return {};
888
+ try {
889
+ const text = fs.readFile(file).replace(/^/, "");
890
+ const value = text.trim() ? JSON.parse(text) : {};
891
+ return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
892
+ } catch {
893
+ return undefined;
894
+ }
895
+ };
896
+ const auth = read("auth.json");
897
+ if (!auth || Object.keys(auth).length > 0) return false;
898
+ const models = read("models.json");
899
+ const providers = models?.providers;
900
+ if (!models || (providers && typeof providers === "object" && Object.keys(providers).length > 0)) return false;
901
+ const settings = read("settings.json");
902
+ if (!settings || settings.defaultProvider) return false;
903
+ if (MODEL_KEY_VARIABLES.some((name) => env[name])) return false;
904
+ if (envText !== undefined) {
905
+ const { entries } = parseEnvFile(envText);
906
+ if (entries.some(([name, value]) => value && MODEL_KEY_VARIABLES.includes(name))) return false;
907
+ }
908
+ return true;
909
+ }
910
+
601
911
  // ---------------------------------------------------------------------------------------------------------
602
912
  // mu link | unlink
603
913
  // ---------------------------------------------------------------------------------------------------------
@@ -1047,6 +1357,7 @@ export async function main(argv = process.argv.slice(2)) {
1047
1357
  home,
1048
1358
  execPath: process.execPath,
1049
1359
  canExec,
1360
+ stripsTypes: Boolean(process.features.typescript),
1050
1361
  fs: { exists: existsSync, isDir, readFile: readText },
1051
1362
  });
1052
1363
  if (plan.error) {
@@ -1124,6 +1435,30 @@ export async function main(argv = process.argv.slice(2)) {
1124
1435
  }
1125
1436
  return handOver({ command: plan.command, args: plan.args, env: plan.env, strategy });
1126
1437
  }
1438
+ // What the session gets, when there is one: after a `mu setup` that ends in "start mu now", a plain session.
1439
+ let sessionArgv = argv;
1440
+ if (command === "setup") {
1441
+ const plan = planSetup({
1442
+ platform,
1443
+ env,
1444
+ argv: rest,
1445
+ root,
1446
+ home,
1447
+ execPath: process.execPath,
1448
+ stripsTypes: Boolean(process.features.typescript),
1449
+ fs: { exists: existsSync, isDir, readFile: readText },
1450
+ });
1451
+ if (plan.error) {
1452
+ err(plan.error);
1453
+ return plan.code ?? 1;
1454
+ }
1455
+ // The key leaves this process's own command line too, as far as the platform lets a process rewrite it.
1456
+ if (plan.keyGiven) process.title = "mu setup";
1457
+ mkdirSync(plan.agentDir, { recursive: true });
1458
+ const code = await handOver(plan);
1459
+ if (code !== SETUP_EXIT_START) return code;
1460
+ sessionArgv = [];
1461
+ }
1127
1462
  if (command === "doctor") {
1128
1463
  const link = linkPath({ platform, env, home });
1129
1464
  return handOver({
@@ -1184,15 +1519,51 @@ export async function main(argv = process.argv.slice(2)) {
1184
1519
  return 0;
1185
1520
  }
1186
1521
 
1522
+ // A first start with nothing to work with: one question, and the wizard, before the session (`mu setup`). Never
1523
+ // right after `mu setup` itself: its subscription route leaves nothing written, and the person was just asked.
1524
+ const muDir = muHome({ home, platform, isDir });
1525
+ let envText;
1526
+ try {
1527
+ envText = readText(envFilePath({ layout, root, muDir, platform }));
1528
+ } catch {}
1529
+ const firstRun = command !== "setup" && planFirstRun({
1530
+ platform,
1531
+ env,
1532
+ argv: sessionArgv,
1533
+ interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY),
1534
+ agentDir: agentDirFor({ env, muDir, platform }),
1535
+ envText,
1536
+ fs: { exists: existsSync, readFile: readText },
1537
+ });
1538
+ if (firstRun) {
1539
+ const setup = planSetup({
1540
+ platform,
1541
+ env,
1542
+ argv: ["--first-run"],
1543
+ root,
1544
+ home,
1545
+ execPath: process.execPath,
1546
+ stripsTypes: Boolean(process.features.typescript),
1547
+ fs: { exists: existsSync, isDir, readFile: readText },
1548
+ });
1549
+ if (!setup.error) {
1550
+ mkdirSync(setup.agentDir, { recursive: true });
1551
+ // Whatever the wizard ends with, the session follows (pi says itself when there is no model), unless it was cancelled.
1552
+ if ((await handOver(setup)) === SETUP_EXIT_CANCELLED) return SETUP_EXIT_CANCELLED;
1553
+ }
1554
+ }
1555
+
1556
+ // Planned after the wizard: what it wrote into the .env (the Jev key) goes to the session too.
1187
1557
  const plan = planLaunch({
1188
1558
  platform,
1189
1559
  env,
1190
- argv,
1560
+ argv: sessionArgv,
1191
1561
  root,
1192
1562
  home,
1193
1563
  execPath: process.execPath,
1194
1564
  canExec,
1195
1565
  wsl,
1566
+ stripsTypes: Boolean(process.features.typescript),
1196
1567
  fs: { exists: existsSync, isDir, readFile: readText },
1197
1568
  });
1198
1569
  if (plan.error) {
@@ -1200,18 +1571,7 @@ export async function main(argv = process.argv.slice(2)) {
1200
1571
  return 1;
1201
1572
  }
1202
1573
 
1203
- if (plan.layout === "repo") {
1204
- ensureAppView({ platform, app: plan.appDir, upstream: path.join(root, "packages", "coding-agent") });
1205
- }
1206
- mkdirSync(plan.agentDir, { recursive: true });
1207
- for (const note of plan.notes) err(note);
1208
- // The local judge is one shared sidecar; starting it is idempotent and must never block mu.
1209
- if (plan.startJudge) {
1210
- const started = spawnSync(path.join(bin, "kyrn-judge-local"), ["start"], { stdio: "ignore", env: plan.env });
1211
- if (started.status !== 0) {
1212
- err("mu: the local judge did not start (mu judge status); decisions fall back to pi's behaviour");
1213
- }
1214
- }
1574
+ prepareLaunch({ plan, platform, root, bin, err });
1215
1575
  if (plan.preface) out(plan.preface);
1216
1576
  return handOver(plan);
1217
1577
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mu-agent",
3
- "version": "0.1.4",
3
+ "version": "0.1.7",
4
4
  "description": "mu: a coding agent that thinks before it acts. A small, fast judge makes the routine calls; the big model keeps its attention for the work. Built on pi.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,13 +33,13 @@
33
33
  "node": ">=22.19.0"
34
34
  },
35
35
  "dependencies": {
36
- "@earendil-works/chord": "0.86.0",
36
+ "@earendil-works/chord": "0.87.1",
37
37
  "@silvia-odwyer/photon-node": "0.3.4",
38
38
  "jiti": "2.7.0"
39
39
  },
40
40
  "muBuild": {
41
- "pi": "0.86.0",
41
+ "pi": "0.87.1",
42
42
  "judge": "0.1.0",
43
- "commit": "206e694c6"
43
+ "commit": "c645b9c64"
44
44
  }
45
45
  }
@@ -1,6 +0,0 @@
1
- import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url);
2
- import{buildCopilotDynamicHeaders,hasCopilotVisionInput}from"./chunk-PGXDEOVV.js";import{Anthropic}from"./chunk-DTD7JQ7Y.js";import{retryProviderRequest}from"./chunk-XNGRGP62.js";import{adjustMaxTokensForThinking,buildBaseOptions,clampMaxTokensToContext,getJsonSchemaToolParameters,getPiUserAgent,resolveJsonSchemaStrictSampling,transformMessages}from"./chunk-S7SZN6Z3.js";import{sanitizeSurrogates}from"./chunk-L2ZPNLYD.js";import{headersToRecord}from"./chunk-6R7UI4IY.js";import{getProviderEnvValue}from"./chunk-DRQDKNXA.js";import{parseJsonWithRepair,parseStreamingJson}from"./chunk-TRDNDS6A.js";import{calculateCost,getCurrentTools,getDeclaredTools,getInitialSystemMessage,getSystemMessageText,hasToolRedefinitions,renderSystemMessageUpdate,resolveTranscript}from"./chunk-RCZIEVGO.js";import{AssistantMessageEventStream,appendAssistantMessageDiagnostic}from"./chunk-A3JYRWB6.js";import"./chunk-4L3WN2XY.js";function resolveCacheRetention(cacheRetention,env){return cacheRetention||(getProviderEnvValue("PI_CACHE_RETENTION",env)==="long"?"long":"short")}function getCacheControl(model,cacheRetention,env){let retention=resolveCacheRetention(cacheRetention,env);if(retention==="none")return{retention};let ttl=retention==="long"&&getAnthropicCompat(model).supportsLongCacheRetention?"1h":void 0;return{retention,cacheControl:{type:"ephemeral",...ttl&&{ttl}}}}var claudeCodeVersion="2.1.251",claudeCodeTools=["Read","Write","Edit","Bash","Grep","Glob","AskUserQuestion","EnterPlanMode","ExitPlanMode","KillShell","NotebookEdit","Skill","Task","TaskOutput","TodoWrite","WebFetch","WebSearch"],ccToolLookup=new Map(claudeCodeTools.map(t=>[t.toLowerCase(),t])),toClaudeCodeName=name=>ccToolLookup.get(name.toLowerCase())??name,fromClaudeCodeName=(name,tools)=>{if(tools&&tools.length>0){let lowerName=name.toLowerCase(),matchedTool=tools.find(tool=>tool.name.toLowerCase()===lowerName);if(matchedTool)return matchedTool.name}return name};function convertContentBlocks(content){if(!content.some(c=>c.type==="image"))return sanitizeSurrogates(content.map(c=>c.text).join(`
3
- `));let blocks=content.map(block=>block.type==="text"?{type:"text",text:sanitizeSurrogates(block.text)}:{type:"image",source:{type:"base64",media_type:block.mimeType,data:block.data}});return blocks.some(b=>b.type==="text")||blocks.unshift({type:"text",text:"(see attached image)"}),blocks}var FINE_GRAINED_TOOL_STREAMING_BETA="fine-grained-tool-streaming-2025-05-14",INTERLEAVED_THINKING_BETA="interleaved-thinking-2025-05-14",SERVER_SIDE_FALLBACK_BETA="server-side-fallback-2026-07-01",MID_CONVERSATION_OUTPUT_CONFIG_BETA="mid-conversation-output-config-2026-07-01",THINKING_BINDING_CONTROLS_BETA="thinking-binding-controls-2026-08-01",MID_CONVERSATION_TOOL_CHANGES_BETA="mid-conversation-tool-changes-2026-07-01",DEFERRED_TOOL_PLACEHOLDER={name:"__pi_deferred_placeholder__",description:"Reserved placeholder. Never available. Never call this.",input_schema:{type:"object",properties:{},required:[]},defer_loading:!0};function shouldUseServerSideFallbackBeta(model){return(model.compat?.allowedFallbackModels?.length??0)>0}function getAnthropicCompat(model){let isOpenRouter=model.provider==="openrouter"||model.baseUrl.includes("openrouter.ai");return{supportsEagerToolInputStreaming:model.compat?.supportsEagerToolInputStreaming??!0,supportsLongCacheRetention:model.compat?.supportsLongCacheRetention??!0,sendSessionAffinityHeaders:model.compat?.sendSessionAffinityHeaders??isOpenRouter,sessionAffinityFormat:model.compat?.sessionAffinityFormat??(isOpenRouter?"openrouter":void 0),supportsCacheControlOnTools:model.compat?.supportsCacheControlOnTools??!0,supportsTemperature:model.compat?.supportsTemperature??!0,allowEmptySignature:model.compat?.allowEmptySignature??!1,supportsStrictTools:model.compat?.supportsStrictTools??!1,supportsMidConvoSystemMessages:model.compat?.supportsMidConvoSystemMessages??!1,supportsMidConvoToolChanges:model.compat?.supportsMidConvoToolChanges??!1}}function mergeHeaders(...headerSources){let merged={};for(let headers of headerSources)headers&&Object.assign(merged,headers);return merged}function mergeClientHeaders(...headerSources){return mergeHeaders({"User-Agent":getPiUserAgent()},...headerSources)}function hasHeader(headers,name){if(!headers)return!1;let expected=name.toLowerCase();for(let[key,value]of Object.entries(headers))if(key.toLowerCase()===expected&&value!==null&&value.trim().length>0)return!0;return!1}function assertRequestAuth(provider,apiKey,headers){if(!apiKey&&!(hasHeader(headers,"authorization")||hasHeader(headers,"x-api-key")||hasHeader(headers,"cf-aig-authorization")))throw new Error(`No API key for provider: ${provider}`)}var ANTHROPIC_MESSAGE_EVENTS=new Set(["message_start","message_delta","message_stop","content_block_start","content_block_delta","content_block_stop"]);function flushSseEvent(state){if(!state.event&&state.data.length===0)return null;let event={event:state.event,data:state.data.join(`
4
- `),raw:[...state.raw]};return state.event=null,state.data=[],state.raw=[],event}function decodeSseLine(line,state){if(line==="")return flushSseEvent(state);if(state.raw.push(line),line.startsWith(":"))return null;let delimiterIndex=line.indexOf(":"),fieldName=delimiterIndex===-1?line:line.slice(0,delimiterIndex),value=delimiterIndex===-1?"":line.slice(delimiterIndex+1);return value.startsWith(" ")&&(value=value.slice(1)),fieldName==="event"?state.event=value:fieldName==="data"&&state.data.push(value),null}function nextLineBreakIndex(text){let carriageReturnIndex=text.indexOf("\r"),newlineIndex=text.indexOf(`
5
- `);return carriageReturnIndex===-1?newlineIndex:newlineIndex===-1?carriageReturnIndex:Math.min(carriageReturnIndex,newlineIndex)}function consumeLine(text){let lineBreakIndex=nextLineBreakIndex(text);if(lineBreakIndex===-1)return null;let nextIndex=lineBreakIndex+1;return text[lineBreakIndex]==="\r"&&text[nextIndex]===`
6
- `&&(nextIndex+=1),{line:text.slice(0,lineBreakIndex),rest:text.slice(nextIndex)}}async function*iterateSseMessages(body,signal){let reader=body.getReader(),decoder=new TextDecoder,state={event:null,data:[],raw:[]},buffer="";try{for(;;){if(signal?.aborted)throw new Error("Request was aborted");let{value,done}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});let consumed2=consumeLine(buffer);for(;consumed2;){buffer=consumed2.rest;let event=decodeSseLine(consumed2.line,state);event&&(yield event),consumed2=consumeLine(buffer)}}buffer+=decoder.decode();let consumed=consumeLine(buffer);for(;consumed;){buffer=consumed.rest;let event=decodeSseLine(consumed.line,state);event&&(yield event),consumed=consumeLine(buffer)}if(buffer.length>0){let event=decodeSseLine(buffer,state);event&&(yield event)}let trailingEvent=flushSseEvent(state);trailingEvent&&(yield trailingEvent)}finally{reader.releaseLock()}}async function*iterateAnthropicEvents(response,signal){if(!response.body)throw new Error("Attempted to iterate over an Anthropic response with no body");let sawMessageStart=!1,sawMessageEnd=!1;for await(let sse of iterateSseMessages(response.body,signal)){if(sse.event==="error")throw new Error(sse.data);if(ANTHROPIC_MESSAGE_EVENTS.has(sse.event??""))try{let event=parseJsonWithRepair(sse.data);event.type==="message_start"?sawMessageStart=!0:event.type==="message_stop"&&(sawMessageEnd=!0),yield event}catch(error){let message=error instanceof Error?error.message:String(error);throw new Error(`Could not parse Anthropic SSE event ${sse.event}: ${message}; data=${sse.data}; raw=${sse.raw.join("\\n")}`)}}if(sawMessageStart&&!sawMessageEnd)throw new Error("Anthropic stream ended before message_stop")}var stream=(model,context,options)=>{let stream2=new AssistantMessageEventStream,normalizedContext=resolveTranscript(context,getAnthropicCompat(model).supportsMidConvoSystemMessages),currentTools=getCurrentTools(normalizedContext.messages);return(async()=>{let providerThinkingLevel=model.compat?.supportsMidConvoEffort?options?.effort??"high":void 0,output={role:"assistant",content:[],api:model.api,provider:model.provider,model:model.id,...providerThinkingLevel===void 0?{}:{providerThinkingLevel},usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"pending",timestamp:Date.now()};try{let client,isOAuth,usageModel=model,inputTransformations;if(options?.client)client=options.client,isOAuth=!1;else{let apiKey=options?.apiKey;assertRequestAuth(model.provider,apiKey,options?.headers);let copilotDynamicHeaders;if(model.provider==="github-copilot"){let hasImages=hasCopilotVisionInput(normalizedContext.messages);copilotDynamicHeaders=buildCopilotDynamicHeaders({messages:normalizedContext.messages,hasImages})}let cacheSessionId=resolveCacheRetention(options?.cacheRetention,options?.env)==="none"?void 0:options?.sessionId,created=createClient(model,apiKey,options?.headers,options?.fetch,copilotDynamicHeaders,cacheSessionId);client=created.client,isOAuth=created.isOAuthToken}let params=buildParams(model,normalizedContext,isOAuth,options),nextParams=await options?.onPayload?.(params,model);nextParams!==void 0&&(params={...nextParams,stream:!0});let requestOptions={...options?.signal?{signal:options.signal}:{},...options?.timeoutMs!==void 0?{timeout:options.timeoutMs}:{},maxRetries:0},response=await retryProviderRequest(()=>client.beta.messages.create(params,requestOptions).asResponse(),{maxRetries:options?.maxRetries,maxRetryDelayMs:options?.maxRetryDelayMs,signal:options?.signal});await options?.onResponse?.({status:response.status,headers:headersToRecord(response.headers)},model),stream2.push({type:"start",partial:output});let blocks=output.content;for await(let event of iterateAnthropicEvents(response,options?.signal))if(event.type==="message_start"){output.responseId=event.message.id;let transformations=event.message.input_transformations;Array.isArray(transformations)&&(inputTransformations=transformations);let responseModel=event.message.model;responseModel!==model.id&&(output.responseModel=responseModel);let fallbackCost=responseModel===model.id?void 0:model.compat?.allowedFallbackModels?.find(fallback=>fallback.provider===model.provider&&fallback.model===responseModel)?.cost;usageModel=fallbackCost?{...model,id:responseModel,cost:fallbackCost}:model,output.usage.input=event.message.usage.input_tokens||0,output.usage.output=event.message.usage.output_tokens||0,output.usage.cacheRead=event.message.usage.cache_read_input_tokens||0,output.usage.cacheWrite=event.message.usage.cache_creation_input_tokens||0,output.usage.cacheWrite1h=event.message.usage.cache_creation?.ephemeral_1h_input_tokens||0,output.usage.totalTokens=output.usage.input+output.usage.output+output.usage.cacheRead+output.usage.cacheWrite,calculateCost(usageModel,output.usage)}else if(event.type==="content_block_start"){if(event.content_block.type==="fallback"){if(output.content.length>0)throw new Error("Anthropic performed an unsupported mid-output model fallback");continue}if(event.content_block.type==="text"){let block={type:"text",text:event.content_block.text??"",index:event.index};output.content.push(block),stream2.push({type:"text_start",contentIndex:output.content.length-1,partial:output})}else if(event.content_block.type==="thinking"){let block={type:"thinking",thinking:event.content_block.thinking??"",thinkingSignature:event.content_block.signature??"",index:event.index};output.content.push(block),stream2.push({type:"thinking_start",contentIndex:output.content.length-1,partial:output})}else if(event.content_block.type==="redacted_thinking"){let block={type:"thinking",thinking:"[Reasoning redacted]",thinkingSignature:event.content_block.data,redacted:!0,index:event.index};output.content.push(block),stream2.push({type:"thinking_start",contentIndex:output.content.length-1,partial:output})}else if(event.content_block.type==="tool_use"){let block={type:"toolCall",id:event.content_block.id,name:isOAuth?fromClaudeCodeName(event.content_block.name,currentTools):event.content_block.name,arguments:event.content_block.input??{},partialJson:"",index:event.index};output.content.push(block),stream2.push({type:"toolcall_start",contentIndex:output.content.length-1,partial:output})}}else if(event.type==="content_block_delta"){if(event.delta.type==="text_delta"){let index=blocks.findIndex(b=>b.index===event.index),block=blocks[index];block&&block.type==="text"&&(block.text+=event.delta.text,stream2.push({type:"text_delta",contentIndex:index,delta:event.delta.text,partial:output}))}else if(event.delta.type==="thinking_delta"){let index=blocks.findIndex(b=>b.index===event.index),block=blocks[index];block&&block.type==="thinking"&&(block.thinking+=event.delta.thinking,stream2.push({type:"thinking_delta",contentIndex:index,delta:event.delta.thinking,partial:output}))}else if(event.delta.type==="input_json_delta"){let index=blocks.findIndex(b=>b.index===event.index),block=blocks[index];block&&block.type==="toolCall"&&(block.partialJson+=event.delta.partial_json,block.arguments=parseStreamingJson(block.partialJson),stream2.push({type:"toolcall_delta",contentIndex:index,delta:event.delta.partial_json,partial:output}))}else if(event.delta.type==="signature_delta"){let index=blocks.findIndex(b=>b.index===event.index),block=blocks[index];block&&block.type==="thinking"&&(block.thinkingSignature=block.thinkingSignature||"",block.thinkingSignature+=event.delta.signature)}}else if(event.type==="content_block_stop"){let index=blocks.findIndex(b=>b.index===event.index),block=blocks[index];block&&(delete block.index,block.type==="text"?stream2.push({type:"text_end",contentIndex:index,content:block.text,partial:output}):block.type==="thinking"?stream2.push({type:"thinking_end",contentIndex:index,content:block.thinking,partial:output}):block.type==="toolCall"&&(block.arguments=parseStreamingJson(block.partialJson),delete block.partialJson,stream2.push({type:"toolcall_end",contentIndex:index,toolCall:block,partial:output})))}else if(event.type==="message_delta"){let transformations=event.input_transformations;if(Array.isArray(transformations)&&(inputTransformations=transformations),event.delta.stop_reason){output.rawStopReason=event.delta.stop_reason;let stopReasonResult=mapStopReason(event.delta.stop_reason,event.delta.stop_details);output.stopReason=stopReasonResult.stopReason,stopReasonResult.errorMessage&&(output.errorMessage=stopReasonResult.errorMessage)}if(event.usage){event.usage.input_tokens!=null&&(output.usage.input=event.usage.input_tokens),event.usage.output_tokens!=null&&(output.usage.output=event.usage.output_tokens),event.usage.cache_read_input_tokens!=null&&(output.usage.cacheRead=event.usage.cache_read_input_tokens),event.usage.cache_creation_input_tokens!=null&&(output.usage.cacheWrite=event.usage.cache_creation_input_tokens);let thinkingTokens=event.usage.output_tokens_details?.thinking_tokens;thinkingTokens!=null&&(output.usage.reasoning=thinkingTokens)}output.usage.totalTokens=output.usage.input+output.usage.output+output.usage.cacheRead+output.usage.cacheWrite,calculateCost(usageModel,output.usage)}if(options?.signal?.aborted)throw new Error("Request was aborted");if(output.stopReason==="pending")throw new Error("Anthropic stream ended without a stop reason");if(output.stopReason==="aborted"||output.stopReason==="error")throw new Error(output.errorMessage||"An unknown error occurred");inputTransformations&&inputTransformations.length>0&&appendAssistantMessageDiagnostic(output,{type:"anthropic_input_transformations",timestamp:Date.now(),details:{transformations:inputTransformations.map(transformation=>({type:transformation.type??void 0,path:transformation.path??void 0,reason:transformation.reason??void 0}))}}),stream2.push({type:"done",reason:output.stopReason,message:output}),stream2.end()}catch(error){for(let block of output.content)delete block.index,delete block.partialJson;output.stopReason=options?.signal?.aborted?"aborted":"error",output.errorMessage=error instanceof Error?error.message:JSON.stringify(error),stream2.push({type:"error",reason:output.stopReason,error:output}),stream2.end()}})(),stream2};function mapThinkingLevelToEffort(model,level){let mapped=level?model.thinkingLevelMap?.[level]:void 0;if(typeof mapped=="string")return mapped;switch(level){case"minimal":case"low":return"low";case"medium":return"medium";case"high":return"high";default:return"high"}}var streamSimple=(model,context,options)=>{assertRequestAuth(model.provider,options?.apiKey,options?.headers);let base={...buildBaseOptions(model,context,options,options?.apiKey),toolChoice:options?.toolChoice};if(!options?.reasoning)return stream(model,context,{...base,thinkingEnabled:!1});if(model.compat?.forceAdaptiveThinking===!0){let effort=mapThinkingLevelToEffort(model,options.reasoning);return stream(model,context,{...base,thinkingEnabled:!0,effort})}let adjusted=adjustMaxTokensForThinking(base.maxTokens,model.maxTokens,options.reasoning,options.thinkingBudgets),maxTokens=clampMaxTokensToContext(model,context,adjusted.maxTokens);return stream(model,context,{...base,maxTokens,thinkingEnabled:!0,thinkingBudgetTokens:Math.min(adjusted.thinkingBudget,Math.max(0,maxTokens-1024))})};function isOAuthToken(apiKey){return apiKey.includes("sk-ant-oat")}function createClient(model,apiKey,optionsHeaders,fetch,dynamicHeaders,sessionId){if(model.provider==="github-copilot")return{client:new Anthropic({apiKey:null,authToken:apiKey??null,baseURL:model.baseUrl,dangerouslyAllowBrowser:!0,fetch,defaultHeaders:mergeClientHeaders({accept:"application/json","anthropic-dangerous-direct-browser-access":"true"},model.headers,dynamicHeaders,optionsHeaders)}),isOAuthToken:!1};if(apiKey&&isOAuthToken(apiKey))return{client:new Anthropic({apiKey:null,authToken:apiKey,baseURL:model.baseUrl,dangerouslyAllowBrowser:!0,fetch,defaultHeaders:mergeClientHeaders({accept:"application/json","anthropic-dangerous-direct-browser-access":"true","user-agent":`claude-cli/${claudeCodeVersion}`,"x-app":"cli"},model.headers,optionsHeaders)}),isOAuthToken:!0};let compat=getAnthropicCompat(model),sessionAffinityHeaders={};if(sessionId&&compat.sendSessionAffinityHeaders){let header=compat.sessionAffinityFormat==="openrouter"?"x-session-id":"x-session-affinity";sessionAffinityHeaders[header]=sessionId}let defaultHeaders=mergeClientHeaders({accept:"application/json","anthropic-dangerous-direct-browser-access":"true"},sessionAffinityHeaders,model.headers,optionsHeaders);return{client:new Anthropic({apiKey:apiKey??null,authToken:null,baseURL:model.baseUrl,dangerouslyAllowBrowser:!0,fetch,defaultHeaders}),isOAuthToken:!1}}function getBetaFeatures(model,context,isOAuthToken2,nativeToolChanges,options){let configuredFeatures;for(let headers of[model.headers,options?.headers])for(let[name,value]of Object.entries(headers??{}))name.toLowerCase()==="anthropic-beta"&&(configuredFeatures=value);if(configuredFeatures===null)return[];if(configuredFeatures!==void 0)return[...new Set(configuredFeatures.split(",").map(feature=>feature.trim()).filter(feature=>feature.length>0))];let features=[];return isOAuthToken2&&features.push("claude-code-20250219","oauth-2025-04-20"),shouldUseFineGrainedToolStreamingBeta(model,context)&&features.push(FINE_GRAINED_TOOL_STREAMING_BETA),model.reasoning&&options?.thinkingEnabled===!0&&(options.interleavedThinking??!0)&&model.compat?.forceAdaptiveThinking!==!0&&features.push(INTERLEAVED_THINKING_BETA),shouldUseServerSideFallbackBeta(model)&&features.push(SERVER_SIDE_FALLBACK_BETA),model.compat?.supportsMidConvoEffort===!0&&features.push(MID_CONVERSATION_OUTPUT_CONFIG_BETA,THINKING_BINDING_CONTROLS_BETA),nativeToolChanges&&features.push(MID_CONVERSATION_TOOL_CHANGES_BETA),[...new Set(features)]}function buildParams(model,context,isOAuthToken2,options){let{cacheControl}=getCacheControl(model,options?.cacheRetention,options?.env),compat=getAnthropicCompat(model),initialSystemMessage=getInitialSystemMessage(context.messages),initialSystemText=initialSystemMessage?getSystemMessageText(initialSystemMessage):"",transformedMessages=transformMessages(context.messages,model,normalizeToolCallId),conversationMessages=initialSystemMessage?transformedMessages.slice(1):transformedMessages,initialTools=initialSystemMessage?.toolsAdded??[],nativeToolChanges=compat.supportsMidConvoSystemMessages&&compat.supportsMidConvoToolChanges&&initialTools.length>0&&!hasToolRedefinitions(context.messages),converted=convertMessages(conversationMessages,isOAuthToken2,cacheControl,compat.allowEmptySignature,model.compat?.supportsMidConvoEffort===!0?model.provider:void 0,nativeToolChanges),activeEffort=options?.effort??"high",betaFeatures=getBetaFeatures(model,context,isOAuthToken2,nativeToolChanges,options),params={model:model.id,messages:model.compat?.supportsMidConvoEffort===!0?insertThinkingLevelMessages(converted,activeEffort):converted.messages,max_tokens:options?.maxTokens??model.maxTokens,stream:!0,...betaFeatures.length>0?{betas:betaFeatures}:{}};isOAuthToken2?(params.system=[{type:"text",text:"You are Claude Code, Anthropic's official CLI for Claude.",...cacheControl?{cache_control:cacheControl}:{}}],initialSystemText&&params.system.push({type:"text",text:sanitizeSurrogates(initialSystemText),...cacheControl?{cache_control:cacheControl}:{}})):initialSystemText&&(params.system=[{type:"text",text:sanitizeSurrogates(initialSystemText),...cacheControl?{cache_control:cacheControl}:{}}]),options?.temperature!==void 0&&!options?.thinkingEnabled&&model.compat?.supportsMidConvoEffort!==!0&&compat.supportsTemperature&&(params.temperature=options.temperature);let toolCacheControl=compat.supportsCacheControlOnTools?cacheControl:void 0;if(nativeToolChanges){let initialNames=new Set(initialTools.map(tool=>tool.name)),laterTools=getDeclaredTools(context.messages).filter(tool=>!initialNames.has(tool.name));params.tools=[...convertTools(initialTools,isOAuthToken2,compat.supportsEagerToolInputStreaming,compat.supportsStrictTools,toolCacheControl),DEFERRED_TOOL_PLACEHOLDER,...convertTools(laterTools,isOAuthToken2,compat.supportsEagerToolInputStreaming,compat.supportsStrictTools).map(tool=>({...tool,defer_loading:!0}))]}else{let tools=getCurrentTools(context.messages);tools.length>0&&(params.tools=convertTools(tools,isOAuthToken2,compat.supportsEagerToolInputStreaming,compat.supportsStrictTools,toolCacheControl))}if(model.compat?.supportsMidConvoEffort===!0)params.thinking={type:"adaptive",display:options?.thinkingDisplay??"summarized",block_binding:{prefix_mismatch_behavior:"drop_block"}},params.output_config={effort:"high"};else if(model.reasoning)if(options?.thinkingEnabled){let display=options.thinkingDisplay??"summarized";model.compat?.forceAdaptiveThinking===!0?(params.thinking={type:"adaptive",display},options.effort&&(params.output_config={effort:options.effort})):params.thinking={type:"enabled",budget_tokens:options.thinkingBudgetTokens||1024,display}}else options?.thinkingEnabled===!1&&model.thinkingLevelMap?.off!==null&&(params.thinking={type:"disabled"});if(options?.metadata){let userId=options.metadata.user_id;typeof userId=="string"&&(params.metadata={user_id:userId})}options?.toolChoice&&(typeof options.toolChoice=="string"?params.tool_choice={type:options.toolChoice}:params.tool_choice=options.toolChoice);let allowedFallbackModels=model.compat?.allowedFallbackModels;return allowedFallbackModels&&allowedFallbackModels.length>0&&(params.fallbacks=allowedFallbackModels.map(fallback=>({model:fallback.model}))),params}function normalizeToolCallId(id){return id.replace(/[^a-zA-Z0-9_-]/g,"_").slice(0,64)}function convertToolResult(msg){return{type:"tool_result",tool_use_id:msg.toolCallId,content:convertContentBlocks(msg.content),is_error:msg.isError}}function convertMessages(transformedMessages,isOAuthToken2,cacheControl,allowEmptySignature=!1,managedProvider,nativeToolChanges=!1){let params=[],assistantLevels=new Map,pendingSystemMessages=[],flushPendingSystemMessages=()=>{params.push(...pendingSystemMessages),pendingSystemMessages.length=0};for(let i=0;i<transformedMessages.length;i++){let msg=transformedMessages[i];if(msg.role==="system"){let text=renderSystemMessageUpdate(msg),blocks=[];if(text.length>0&&blocks.push({type:"text",text:sanitizeSurrogates(text)}),nativeToolChanges){for(let tool of msg.toolsRemoved??[])blocks.push({type:"tool_removal",tool:{type:"tool_reference",name:isOAuthToken2?toClaudeCodeName(tool.name):tool.name}});for(let tool of msg.toolsAdded??[])blocks.push({type:"tool_addition",tool:{type:"tool_reference",name:isOAuthToken2?toClaudeCodeName(tool.name):tool.name}})}blocks.length>0&&pendingSystemMessages.push({role:"system",content:blocks})}else if(msg.role==="user")if(typeof msg.content=="string")msg.content.trim().length>0&&params.push({role:"user",content:sanitizeSurrogates(msg.content)});else{let filteredBlocks=msg.content.map(item=>item.type==="text"?{type:"text",text:sanitizeSurrogates(item.text)}:{type:"image",source:{type:"base64",media_type:item.mimeType,data:item.data}}).filter(b=>b.type==="text"?b.text.trim().length>0:!0);if(filteredBlocks.length===0)continue;params.push({role:"user",content:filteredBlocks})}else if(msg.role==="assistant"){flushPendingSystemMessages();let blocks=[];for(let block of msg.content)if(block.type==="text"){if(block.text.trim().length===0)continue;blocks.push({type:"text",text:sanitizeSurrogates(block.text)})}else if(block.type==="thinking"){if(block.redacted){blocks.push({type:"redacted_thinking",data:block.thinkingSignature});continue}let thinkingSignature=block.thinkingSignature,hasThinkingSignature=!!thinkingSignature&&thinkingSignature.trim().length>0;if(block.thinking.trim().length===0&&!hasThinkingSignature)continue;hasThinkingSignature?blocks.push({type:"thinking",thinking:sanitizeSurrogates(block.thinking),signature:thinkingSignature}):blocks.push(allowEmptySignature?{type:"thinking",thinking:sanitizeSurrogates(block.thinking),signature:""}:{type:"text",text:sanitizeSurrogates(block.thinking)})}else block.type==="toolCall"&&blocks.push({type:"tool_use",id:block.id,name:isOAuthToken2?toClaudeCodeName(block.name):block.name,input:block.arguments??{}});if(blocks.length===0)continue;let messageIndex=params.length;params.push({role:"assistant",content:blocks}),managedProvider!==void 0&&msg.api==="anthropic-messages"&&msg.provider===managedProvider&&isAnthropicEffort(msg.providerThinkingLevel)&&assistantLevels.set(messageIndex,msg.providerThinkingLevel)}else if(msg.role==="toolResult"){let toolResults=[],j=i;for(;j<transformedMessages.length&&transformedMessages[j].role==="toolResult";)toolResults.push(convertToolResult(transformedMessages[j])),j++;i=j-1,params.push({role:"user",content:toolResults})}}if(flushPendingSystemMessages(),cacheControl&&params.length>0){let lastMessage=params[params.length-1];if(lastMessage.role==="user"||lastMessage.role==="system")if(Array.isArray(lastMessage.content)){let lastBlock=lastMessage.content[lastMessage.content.length-1];lastBlock&&(lastBlock.type==="text"||lastBlock.type==="image"||lastBlock.type==="tool_result"||lastBlock.type==="tool_addition"||lastBlock.type==="tool_removal")&&(lastBlock.cache_control=cacheControl)}else typeof lastMessage.content=="string"&&(lastMessage.content=[{type:"text",text:lastMessage.content,cache_control:cacheControl}])}return{messages:params,assistantLevels}}function isAnthropicEffort(value){return value==="low"||value==="medium"||value==="high"||value==="xhigh"||value==="max"}function insertThinkingLevelMessages(converted,activeEffort){let messages=[];for(let index=0;index<converted.messages.length;index++){let historicalEffort=converted.assistantLevels.get(index);historicalEffort!==void 0&&messages.push({role:"system",content:[],output_config:{effort:historicalEffort}}),messages.push(converted.messages[index])}return messages.push({role:"system",content:[],output_config:{effort:activeEffort}}),messages}function shouldUseFineGrainedToolStreamingBeta(model,context){return getCurrentTools(context.messages).length>0&&!getAnthropicCompat(model).supportsEagerToolInputStreaming}function convertTools(tools,isOAuthToken2,supportsEagerToolInputStreaming,supportsStrictTools,cacheControl){return tools?tools.map((tool,index)=>{let strict=resolveJsonSchemaStrictSampling(tool,supportsStrictTools),parameters=getJsonSchemaToolParameters(tool,strict),schema=parameters,legacyInputSchema={type:"object",properties:schema.properties??{},required:schema.required??[]},inputSchema=strict===!0?{...parameters,...legacyInputSchema}:legacyInputSchema;return{name:isOAuthToken2?toClaudeCodeName(tool.name):tool.name,description:tool.description,...supportsEagerToolInputStreaming?{eager_input_streaming:!0}:{},...strict===!0?{strict:!0}:{},input_schema:inputSchema,...cacheControl&&index===tools.length-1?{cache_control:cacheControl}:{}}}):[]}function mapStopReason(reason,stopDetails){switch(reason){case"end_turn":return{stopReason:"stop"};case"max_tokens":return{stopReason:"length"};case"tool_use":return{stopReason:"toolUse"};case"refusal":return{stopReason:"error",errorMessage:stopDetails?.explanation||"The model refused to complete the request"};case"pause_turn":return{stopReason:"stop"};case"stop_sequence":return{stopReason:"stop"};case"sensitive":return{stopReason:"error",errorMessage:"Provider stopped with: sensitive"};default:throw new Error(`Unhandled stop reason: ${reason}`)}}export{stream,streamSimple};