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.
Files changed (176) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/README.md +34 -30
  3. package/dist/bundle/chunks/anthropic-messages-4PAJY4R6.js +6 -0
  4. package/dist/bundle/chunks/anthropic.js +1 -1
  5. package/dist/bundle/chunks/azure-openai-responses-Y4BBWQ3N.js +2 -0
  6. package/dist/bundle/chunks/bedrock-converse-stream.js +12 -9
  7. package/dist/bundle/chunks/{chunk-NG4PO5FP.js → chunk-3J3HSHLX.js} +128 -110
  8. package/dist/bundle/chunks/{chunk-TRDNDS6A.js → chunk-3V45WARH.js} +6 -3
  9. package/dist/bundle/chunks/{chunk-S3GGRYIO.js → chunk-5AGIVGSV.js} +1 -1
  10. package/dist/bundle/chunks/chunk-6QGV3IWD.js +6 -0
  11. package/dist/bundle/chunks/{chunk-4OBXYPH4.js → chunk-6Z2347ML.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-F5ZABL6Z.js → chunk-7CBJINYJ.js} +2 -2
  13. package/dist/bundle/chunks/{chunk-DRQDKNXA.js → chunk-7DDKXS2Q.js} +1 -1
  14. package/dist/bundle/chunks/{chunk-H3QXYIF7.js → chunk-CF5GWZHW.js} +1 -1
  15. package/dist/bundle/chunks/{chunk-RLZVFGW4.js → chunk-DYTENXHX.js} +29 -92
  16. package/dist/bundle/chunks/{chunk-NTSWBG4X.js → chunk-FLHKNMLQ.js} +1 -1
  17. package/dist/bundle/chunks/chunk-FTJT2X6E.js +2 -0
  18. package/dist/bundle/chunks/{chunk-4L3WN2XY.js → chunk-JXWJOIH4.js} +1 -1
  19. package/dist/bundle/chunks/{chunk-TAOL3JFA.js → chunk-KIDDRTZP.js} +4 -4
  20. package/dist/bundle/chunks/chunk-LHRPMXVI.js +2 -0
  21. package/dist/bundle/chunks/chunk-LMNWTX2L.js +11 -0
  22. package/dist/bundle/chunks/chunk-LPXNMA2W.js +2 -0
  23. package/dist/bundle/chunks/chunk-LT2C56HV.js +130 -0
  24. package/dist/bundle/chunks/{chunk-6CYCI3EJ.js → chunk-LVVZS2NO.js} +1 -1
  25. package/dist/bundle/chunks/chunk-MF3VB62R.js +91 -0
  26. package/dist/bundle/chunks/chunk-NN4BEPYX.js +2 -0
  27. package/dist/bundle/chunks/{chunk-LD2P6SFZ.js → chunk-PMRZO46Y.js} +1 -1
  28. package/dist/bundle/chunks/chunk-R4VPBBDS.js +2 -0
  29. package/dist/bundle/chunks/chunk-RDZVAGPR.js +2 -0
  30. package/dist/bundle/chunks/chunk-TMTOQ7H7.js +2 -0
  31. package/dist/bundle/chunks/chunk-UKVNB6C4.js +2 -0
  32. package/dist/bundle/chunks/chunk-URBBMV3M.js +9 -0
  33. package/dist/bundle/chunks/chunk-WCGU75WO.js +11 -0
  34. package/dist/bundle/chunks/chunk-WCJNJQO5.js +9 -0
  35. package/dist/bundle/chunks/chunk-WDURT4ZM.js +2 -0
  36. package/dist/bundle/chunks/{cli-AOVD3SSZ.js → cli-M4YBZZ6Q.js} +4 -4
  37. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-MEZHQVNP.js +2 -0
  38. package/dist/bundle/chunks/codemode-worker.js +117 -13
  39. package/dist/bundle/chunks/easter-egg-3d-VFY3YRQR.js +2 -0
  40. package/dist/bundle/chunks/execute-FKI7Y4XR.js +499 -0
  41. package/dist/bundle/chunks/github-copilot.js +1 -1
  42. package/dist/bundle/chunks/{google-generative-ai-XMFHGQC5.js → google-generative-ai-D2VJQBTK.js} +1 -1
  43. package/dist/bundle/chunks/{google-vertex-4XRRNP74.js → google-vertex-SQG2BO6C.js} +1 -1
  44. package/dist/bundle/chunks/https-proxy-agent-LVOO5TPL.js +13 -0
  45. package/dist/bundle/chunks/image-resize-worker.js +1 -1
  46. package/dist/bundle/chunks/{jiti-loader-C3NU2SDH.js → jiti-loader-GL4HJFAD.js} +1 -1
  47. package/dist/bundle/chunks/{jiti-static-loader-E6ANQD5T.js → jiti-static-loader-2RCDI6B4.js} +1 -1
  48. package/dist/bundle/chunks/{lib-TEDK3E5I.js → lib-4AVRLBHM.js} +1 -1
  49. package/dist/bundle/chunks/{llama-cpp-classify-XT44PI3C.js → llama-cpp-classify-5YHLY3RC.js} +2 -2
  50. package/dist/bundle/chunks/mistral-conversations-BDARVJTT.js +5 -0
  51. package/dist/bundle/chunks/{multipart-parser-SQ6GDJL7.js → multipart-parser-FMGQ7342.js} +1 -1
  52. package/dist/bundle/chunks/{node-TWVVAGPO.js → node-AUOGOBCN.js} +1 -1
  53. package/dist/bundle/chunks/{node-N5BSDOA5.js → node-KCMANYC2.js} +1 -1
  54. package/dist/bundle/chunks/openai-chatgpt.js +1 -1
  55. package/dist/bundle/chunks/openai-codex-responses-CECGTDYD.js +10 -0
  56. package/dist/bundle/chunks/openai-codex.js +1 -1
  57. package/dist/bundle/chunks/openai-completions-UOA7MELD.js +7 -0
  58. package/dist/bundle/chunks/openai-decisions-HLM372N6.js +5 -0
  59. package/dist/bundle/chunks/openai-responses-TII7ZY5X.js +3 -0
  60. package/dist/bundle/chunks/{openrouter-images-ZVXWCSIM.js → openrouter-images-7T452AS5.js} +1 -1
  61. package/dist/bundle/chunks/{pi-messages-TM5SOOIW.js → pi-messages-2UDKLKOO.js} +1 -1
  62. package/dist/bundle/chunks/runtime-IKE5OQIW.js +2 -0
  63. package/dist/bundle/chunks/{src-UK77SYZE.js → src-QKGKDFSU.js} +1 -1
  64. package/dist/bundle/chunks/typesafe-system-one-KNCICMO5.js +2 -0
  65. package/dist/bundle/chunks/virtual-modules-TJPHLTRT.js +2 -0
  66. package/dist/bundle/cli-runtime.js +1 -1
  67. package/dist/bundle/index.js +1 -1
  68. package/dist/bundle/rpc-entry.js +1 -1
  69. package/dist/core/export-html/template.js +21 -16
  70. package/dist/modes/interactive/theme/dark.json +1 -1
  71. package/dist/modes/interactive/theme/light.json +1 -1
  72. package/dist/modes/interactive/theme/theme-schema.d.ts +28 -0
  73. package/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -0
  74. package/dist/modes/interactive/theme/theme-schema.js +105 -0
  75. package/dist/modes/interactive/theme/theme-schema.js.map +1 -0
  76. package/dist/modes/interactive/theme/theme-tokens.d.ts +257 -0
  77. package/dist/modes/interactive/theme/theme-tokens.d.ts.map +1 -0
  78. package/dist/modes/interactive/theme/theme-tokens.js +94 -0
  79. package/dist/modes/interactive/theme/theme-tokens.js.map +1 -0
  80. package/dist/modes/interactive/theme/theme.d.ts +13 -16
  81. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  82. package/dist/modes/interactive/theme/theme.js +22 -29
  83. package/dist/modes/interactive/theme/theme.js.map +1 -1
  84. package/docs/cli-integration.md +1 -1
  85. package/docs/cli.md +22 -10
  86. package/docs/codemode.md +29 -6
  87. package/docs/compaction.md +5 -5
  88. package/docs/custom-provider.md +53 -1
  89. package/docs/docs.json +8 -0
  90. package/docs/environment-variables.md +3 -1
  91. package/docs/extensions.md +122 -13
  92. package/docs/json.md +3 -3
  93. package/docs/keybindings.md +5 -4
  94. package/docs/llama-cpp.md +9 -2
  95. package/docs/mcp.md +4 -2
  96. package/docs/message-types.md +22 -4
  97. package/docs/models-configuration.md +187 -0
  98. package/docs/models.md +18 -2
  99. package/docs/nvidia-rtx-llama-cpp.md +96 -0
  100. package/docs/providers.md +47 -0
  101. package/docs/sessions.md +1 -1
  102. package/docs/settings.md +9 -6
  103. package/docs/terminal-setup.md +14 -0
  104. package/docs/themes.md +4 -2
  105. package/docs/tui.md +71 -1
  106. package/docs/usage.md +1 -1
  107. package/examples/README.md +0 -3
  108. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  109. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  110. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  111. package/examples/extensions/dynamic-resources/dynamic.json +1 -1
  112. package/examples/extensions/gondolin/package-lock.json +2 -2
  113. package/examples/extensions/gondolin/package.json +1 -1
  114. package/examples/extensions/minimal-mode.ts +4 -5
  115. package/examples/extensions/overlay-test.ts +2 -2
  116. package/examples/extensions/plan-mode/index.ts +6 -2
  117. package/examples/extensions/sandbox/package-lock.json +2 -2
  118. package/examples/extensions/sandbox/package.json +1 -1
  119. package/examples/extensions/subagent/index.ts +4 -6
  120. package/examples/extensions/with-deps/package-lock.json +2 -2
  121. package/examples/extensions/with-deps/package.json +1 -1
  122. package/judge/dist/auth.js +91 -35
  123. package/judge/dist/chrome.js +2 -2
  124. package/judge/dist/import.js +852 -175
  125. package/judge/dist/setup.js +240 -153
  126. package/judge/manifest.json +2512 -546
  127. package/judge/mu/chunk-3NQ5BIBI.js +4773 -0
  128. package/judge/mu/chunk-SKGYWHW6.js +51 -0
  129. package/judge/mu/chunk-WQM4MV6L.js +58 -0
  130. package/judge/mu/cloud-code-V7FOGB4J.js +22802 -0
  131. package/judge/mu/dist-75PS4OC4.js +1283 -0
  132. package/judge/{dist/kyrn-judge.js → mu/index.js} +5365 -32809
  133. package/judge/mu/multipart-parser-ZDXVIOOP.js +372 -0
  134. package/judge/mu/src-TVVA5ZK2.js +1409 -0
  135. package/judge/themes/mu-dark.json +80 -0
  136. package/judge/themes/mu-light.json +80 -0
  137. package/kyrn/bin/kyrn-doctor +44 -19
  138. package/kyrn/bin/kyrn-judge-local +43 -14
  139. package/kyrn/bin/kyrn-ledger +16 -1
  140. package/kyrn/bin/mu.mjs +142 -61
  141. package/kyrn/local-judge/server.py +26 -14
  142. package/package.json +8 -6
  143. package/dist/bundle/chunks/anthropic-messages-MI4QHRXW.js +0 -6
  144. package/dist/bundle/chunks/azure-openai-responses-E3QGN4VN.js +0 -2
  145. package/dist/bundle/chunks/chunk-33KL6IIQ.js +0 -20
  146. package/dist/bundle/chunks/chunk-A3JYRWB6.js +0 -2
  147. package/dist/bundle/chunks/chunk-C47CLQZK.js +0 -2
  148. package/dist/bundle/chunks/chunk-HQHD6PZI.js +0 -2
  149. package/dist/bundle/chunks/chunk-KJUDFE6Y.js +0 -11
  150. package/dist/bundle/chunks/chunk-TE3GUV6H.js +0 -9
  151. package/dist/bundle/chunks/chunk-VIV3CWXG.js +0 -2
  152. package/dist/bundle/chunks/chunk-WE4MXUQS.js +0 -11
  153. package/dist/bundle/chunks/chunk-Y2PP32XV.js +0 -2
  154. package/dist/bundle/chunks/chunk-YLY6CKPK.js +0 -132
  155. package/dist/bundle/chunks/chunk-ZE5FMF3V.js +0 -99
  156. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-NM3JRRWQ.js +0 -2
  157. package/dist/bundle/chunks/easter-egg-3d-G2LUBEMZ.js +0 -2
  158. package/dist/bundle/chunks/execute-MZVUC6BB.js +0 -388
  159. package/dist/bundle/chunks/https-proxy-agent-2VXB7436.js +0 -13
  160. package/dist/bundle/chunks/mistral-conversations-Q22M3IBG.js +0 -5
  161. package/dist/bundle/chunks/openai-codex-responses-4MYQSO5E.js +0 -10
  162. package/dist/bundle/chunks/openai-completions-OSRCWAAA.js +0 -7
  163. package/dist/bundle/chunks/openai-responses-UHF3DHNG.js +0 -3
  164. package/dist/bundle/chunks/runtime-VJMMDLVH.js +0 -2
  165. package/dist/bundle/chunks/typesafe-system-one-PRR5K6CI.js +0 -2
  166. package/dist/bundle/chunks/virtual-modules-RJXJGCPI.js +0 -2
  167. package/dist/modes/interactive/theme/theme-json.d.ts +0 -85
  168. package/dist/modes/interactive/theme/theme-json.d.ts.map +0 -1
  169. package/dist/modes/interactive/theme/theme-json.js +0 -132
  170. package/dist/modes/interactive/theme/theme-json.js.map +0 -1
  171. package/dist/modes/interactive/theme/theme-schema.json +0 -361
  172. package/examples/plugins/pi-example-plugin/README.md +0 -38
  173. package/examples/plugins/pi-example-plugin/package.json +0 -10
  174. package/examples/plugins/pi-example-plugin/src/contract.ts +0 -13
  175. package/examples/plugins/pi-example-plugin/src/session.ts +0 -24
  176. package/examples/plugins/pi-example-plugin/src/tui.ts +0 -31
package/CHANGELOG.md CHANGED
@@ -2,6 +2,127 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.1.13] - 2026-10-22
6
+
7
+ ### Added
8
+
9
+ - A run deadline: `MU_TIME_BUDGET_S` (or `features.deadline.budgetS`) is the run's time budget in seconds, counted from when the mu process started. With a budget, the watchdog cuts a reply that has only been thinking after `min(thinkingMs, max(60 s, half the time left when the reply started))` instead of after `thinkingMs`, and the model gets a one-line steer when 50% and 80% of the budget have passed: the minutes left and, at 80%, to get the requested result in place (files written, services running) before polishing (`features.deadline.notices`). No judge is asked, a sub-agent ignores the budget, and without one nothing changes. On Terminal-Bench 2.1 all 3 watchdog cuts came 788-886 s into a 900 s budget, too late for the retry to finish.
10
+
11
+ ### Changed
12
+
13
+ - `mu setup` lists services by language (`servicesInOrder`): in English OpenAI, Anthropic, Google, xAI, OpenRouter and OpenCode Zen come first, then the table's order; in Chinese the table's order. Ollama and `other` stay last. The same goes for "Choose a service from the list" and the picker for a key mu doesn't recognize; `--service` ids are unchanged.
14
+ - `quietStartup` defaults to `"header"` under mu's name (pi keeps `false`): the welcome header shows, the startup listing of loaded resources doesn't; diagnostics still do. A value in settings.json, `false` included, holds; `--verbose` overrides it.
15
+ - `/login` offers no Claude subscription sign-in under mu's name (Anthropic doesn't allow other apps to offer one); an Anthropic API key is still offered, and an earlier sign-in stays until `/logout`. `mu setup` refuses a Claude subscription token (`sk-ant-oat…`) given as a key, pasted or with `--key-stdin`, and sends it nowhere.
16
+ - The plain-language board runs only where someone watches it, the terminal and the desktop app, not in `mu --print` or json mode (`features.board.unattended` brings it back). While subagents work, it looks again only when one of them starts, finishes or fails, not every 20 seconds (`followAlways`). An unsure "has anything changed?" with nothing new no longer rewrites the board, and "is it waiting for you?" is asked only once the agent stopped. Replayed on 82 judged looks, the change gate alone cuts the writer calls from 82 to 29.
17
+ - `delegate` and `hive` ask the judge how hard a task is only when a model ladder is set (`features.swarm.models`) or a role pins a model of its own: without one, a delegation that names every role asks nothing, and one that doesn't asks only which role. Hive bees get their thinking level the way delegate's subagents do: on the session's model, the session's level, which keeps the parent's prompt cache warm.
18
+ - Hive: what a bee says starting with `FOUND:` or `DEAD END:` goes on the board without asking the judge (`features.hive.publishMarked`), and tool output is no longer put to the judge as a possible note (`publishToolOutputs`). When the judge can't answer, notes go to every bee up to `maxDeliveriesPerBee` instead of to none. A new note is held against at most 2 earlier ones (`maxRelatedPerNote`, was 6).
19
+ - `swarm.patch` is off by default: the delegate result already lists every changed file with its line counts. `"modes": { "swarm.patch": "active" }` in `mu.json` turns it back on.
20
+ - `browse` with a goal refuses up front when the judge for `browser.step` can't relate a goal to a page (Laya alone), and says how to route it to another judge; before, it opened the page and blocked after the first look.
21
+ - In shadow mode, the decisions mu acts on anyway (`browser.step`, `swarm.routing`, `judge.items`) are recorded as the judge's, not as the fallback's (`actsInShadow`).
22
+ - `notify.routing` and `input.interjection` are in shadow by default: the verdict is recorded and nothing waits for it. Without a verdict, a background job's end or watched line and the 70% and 85% context-budget notices reach the model at once, as a steer (`features.background.unjudged` and `features.notify.unjudged`: `now`, `next_turn` or `drop`); before, a job's end waited for a next turn that a one-shot run never has, and the budget notice was dropped. A watched line is not routed while `bg_output` waits on its job. A message sent mid-run goes on at once instead of after up to 1.5 s.
23
+ - `turn.continue` (v2) asks whether what the closing message offers is more than the user asked for (`beyond_request`). Only a go-ahead on the requested work sends the agent back; an offer of extra work no longer does. The local judge (Laya) is not asked this question, so on Laya alone no stop counts as a go-ahead.
24
+ - `cache.warming` is asked only while the agent is idle and waits at most 3 s (`features.warming.waitMs`). During a run pi's own decision stands, so a message that reads like a sign-off can no longer stop warming in the middle of the run.
25
+ - `skills.disclosure`: a verdict on the session's first message that arrives after the 4 s wait is applied from the next turn, instead of the session keeping every skill description (`features.skills.applyLate`).
26
+ - `tool.injection` asks the judge only about passages with prose in them: six words in a row on a line, or eight characters in a row of a script written without spaces (`features.injection.proseWords`, 0 judges every passage). Directory listings, link lists, response headers and short grep hits get the rules alone. On Terminal-Bench 2.1 that is about half of all screened passages, and half of the screened results need no request at all.
27
+ - `tool.injection` and `tool.admission` ask for a new judge capability, `screen`, which the built-in `laya` profile sets to false. With `laya,jev` they go straight to Jev; with Laya alone, injection is left to the rules and long output goes in whole. On Terminal-Bench 2.1 every passage Laya withheld was clean, its one admission drop was wrong, and most of its calls on both failed or came too late.
28
+ - A cascade's stand-in answer (`judge: "untrusted"` or `"unavailable"`, a probability of 0.5 nobody gave) is no verdict for `tool.injection`: the rules read the passage, where before it passed unread. Such a call, and an admission call answered only by stand-ins, is recorded as a fallback (`abstain`).
29
+ - `tool.admission` no longer judges `web_fetch` results (added to `passThrough`, whose listed default now also shows `delegate` and `hive`) or a bash or PowerShell command that only prints files (`features.admission.printCommands`: `cat`, `sed -n`, `head`, `tail`, `nl`, `less`, `bat`, `grep`, `rg`, `od`, `xxd`, `git show`, `git diff`, `readelf`, `objdump`, with `cd` and `echo` between them). A redirect, a here document, `$(…)`, `&&` into another program or `tail -f` makes it more than a print. On Terminal-Bench 2.1 this skips 46 of 89 admission calls; every drop the judge got right there was in background-job or script output, which is still judged.
30
+ - An unattended run (`mu --print`, json mode) runs a lean profile. Preflight, capability disclosure and the drift check are not asked; no skill is drafted and no lesson is learned, while kept lessons are still read; and `delegate`, `apply_patch_from`, `browse`, `locate`, `remember`, `find_skill`, `find_capability`, `hive` and the bundled `mu-browser` skill are left out of the request, so its first request is about 1.9k tokens smaller (17.8k to 10.5k characters on a faux-provider session). Packs stay hidden as before. A tool called for by its command stays (`mu -p "/hive …"`). Sub-agents, which run in json mode too, are not affected. Each part has an option: `preflight.unattended`, `catalog.askUnattended`, `catalog.unattendedHidden`, `monitor.unattended`, `procedures.unattended`, `memory.unattendedWrites`.
31
+ - `turn.drift` only records by default (`modes: { "turn.drift": "shadow" }`): in benchmark runs Jev never flagged drift, and about 9 in 10 of another judge's drift steers were wrong. Set it to `active` to have it steer again.
32
+ - `skill.capture` drafts a skill only when the judge is at least 90% sure the run was a procedure (was 70%).
33
+ - `memory.outcome` learns only from a loop the turn got out of, not from drift, which names no failing call.
34
+ - Permission modes: a command made only of read-only commands joined by `;`, `&&`, `||` or `|` (`pwd && find . -name '*.ts' | sort | head`, `git status --short; git log --oneline -5`), throwing output away with `2>/dev/null` or `2>&1`, runs without asking the judge or you, like a single read-only command, and the constraint gate no longer checks it. A redirect that writes a file, `sed -i`, `tee`, `find` with `-exec`, `-delete` or `-fprint`, a substitution, or `cd` followed by `git` still asks. Quoting is read the way the shell reads it, so `rg '--pre=./x'` and `sort --out=f` no longer pass as read-only (`features.permissions.readOnlyChains`, `features.constraints.skipReadOnly`).
35
+ - The risk rules flag `rm` only when it deletes recursively (`-r`, `-R`, `-rf`, `--recursive`, also after a file name); `rm -f file` and `docker run --rm` are not flagged. `curl … | python3 -c '…'` (and `node -e`, `perl -e`, `ruby -e`) reads the download as data and is no longer "runs a downloaded script", unless the code runs what it reads (`exec`, `eval`, `system`, …). A download saved and then run in the same command (`curl -o i.sh … && sh i.sh`) is flagged now.
36
+ - `tool.risk` (v2) asks only whether you asked for the command. Its "destructive" question never changed a verdict in Judge mode; with permission modes off, a "not destructive" no longer lets the guard run a flagged command.
37
+ - `tool.description` (v2) keeps the judge's verdicts in `~/.mu/agent/mu/tool-descriptions.json`, so the next `mu --print`, subagent or restart doesn't screen an unchanged MCP server again or wait up to 8 seconds for it. A call to an MCP tool taken off the list is refused before anyone is asked to allow it, and a cascade's stand-in answer is no verdict here either: the rules read that passage, and nothing is kept.
38
+ - `turn.drift` (v2) is reworked, and stays in shadow until a benchmark validates it. The judge sees the task frame's goal and first open acceptance item; the last 8 actions as `bash: make -> exit 2 in 14s; last line: …`; the minutes since the last sign of progress (a file written for the first time, more tests passing or fewer failing, an error not seen before, a failing call passing, a call of a minute or more that succeeds, a ticked item) and since the start; and how often one call came back the same. It is asked after 6 tool results once 3 minutes have passed since the last check, or 10 minutes after it whatever happened, on a timer that never keeps the process alive (`features.monitor`: `every`, `minMinutes`, `maxMinutes`). A steer needs drift at p ≥ 0.9 on two checks in a row and 10 minutes without progress (`stuckMinutes`), or a loop reading at p ≥ 0.8, named once per call like the loop rule; at most one steer per 15 minutes, the loop rule's included (`cooldownMinutes`). It names what looks stuck and asks for a different approach. In shadow, the steer it would have sent is recorded as a `kyrn.monitor` entry with `shadow: true`. Replayed over the 109 Terminal-Bench 2.1 sessions of 0.1.11 with their recorded answers, it asks about 30% less often, leaves less of a timed-out run unchecked, and sends none of the 29 drift steers v1 sent: 1 of them came after 10 minutes without progress, and it followed an `on_track` reading.
39
+ - An unattended run takes no checkpoints (`features.checkpoint.unattended`): no snapshot before the first call that can change files, and no blocking rescan after each one, where nobody can confirm a rewind and no file was ever put back. In Terminal-Bench 2.1 trials the snapshot and rescans cost a median 4-7.5 s a trial; on a Mac, 20 file-changing calls in a 2,000-file folder spent 2.5-3.0 s in git, and now spend none. A dead end is still asked about (`turn.rewind`), in a turn that ran such a call, and the model is told to step back. Sub-agents took none already. Nothing changes in the terminal or the desktop app.
40
+ - Judge warm-up (`features.warmup`) sends no probe in an unattended run (`unattended`), to a judge at a loopback, private-network or `host.docker.internal` address (`nearby`), or while a real question used the connection within the last `intervalMs`. A probe still on its way is cancelled when the session ends, and `/status` counts probes and their input tokens. Unattended Jev runs sent about 19 probes a trial (5.2k input tokens), more than their logged judge traffic.
41
+ - `notify.routing` is not asked in an unattended run (`features.notify.unattended`), like the drift check: in shadow, its default, the verdict changes nothing there.
42
+ - Sub-agents (`delegate`, hive bees) take their parent's settled `skills.disclosure` and `capability.disclosure` answers instead of asking the judge again: the parent passes the skills it hides and the capabilities it has open (`KYRN_SKILLS_HIDDEN`, `KYRN_CAPABILITIES_OPEN`), and the child hides and opens exactly those, whatever its own mode, and asks nothing. That saves about 0.8k judge tokens and up to 4 s at every child's start, and with 4 or more skills a further 6.5-11k tokens; a child whose own skills verdict came late used to carry every skill description on every request. While the parent's first skills verdict is still on its way, a child asks for itself as before. `features.skills.subagentsInherit` and `features.catalog.subagentsInherit` turn it off.
43
+ - In `mu --print`, json mode and sub-agents the system prompt is about 375 tokens shorter per request (9.1k to 7.7k characters on a faux-provider print session): pi's `<docs>` list of its guides becomes one line with the path to the docs, and the `<mu>` section leaves out the commands to type, the reply to act on, the hints before a turn and the `browse` tool. In Terminal-Bench runs the docs were never read in 286 trials. Interactive and desktop sessions keep both (`features.welcome.unattended` brings them back elsewhere).
44
+ - `web_fetch`'s description, its note on a page that needs JavaScript and `web_search`'s advice when no source answers name the `browse` tool only when the model has it; before, an unattended run, which leaves `browse` out, was told to use it.
45
+ - The settings texts added in this release for the drift monitor's timing options, sub-agents taking the session's skills and capabilities, and the unattended-run options of checkpoints, notification routing, judge warm-up and the welcome note are in all eleven extra languages.
46
+ - The judgment layer's bundle is split: code it imports only when used (the Google sign-in providers' request code with Google's SDK, 1.2 MB) moves from `judge/mu/index.js` (3.5 MB to 2.4 MB) to chunks beside it, which Node reads and compiles only on first use. Importing the extension takes about 9 ms less on a cold compile cache (48 to 40 ms on an Apple M6), at every start and in every sub-agent. The build imports every chunk once to check that Node can.
47
+
48
+ ### Fixed
49
+
50
+ - `mu doctor` (kyrn-doctor) prints "mu doctor found N things to fix" or "mu doctor: everything is set up" first when stdout is a terminal; piped output is still one row per line.
51
+ - `/login` hides the `openai-codex` sign-in under mu's name, as `mu auth` and the desktop app do; `openai` (Sign in with ChatGPT) replaces it. A stored `openai-codex` sign-in still works and shows in `/logout`. The first `/login` menu leaves out the "Sign in with Radius" shortcut under mu's name; Radius is still under Sign in with an account. `mu setup`'s subscription hint names `/login`'s actual entries.
52
+ - The footer shows "no model" for the agent's `unknown/unknown` placeholder.
53
+ - `mu setup` ends with exit code 4 (`SETUP_EXIT.signIn`) when a subscription was chosen and a session follows; the launcher then starts the session with `/login` as its first message, which opens the sign-in like the typed command (`afterSetup`). Inside a session, "No models available" leads with `/login` (`formatNoModelsAvailableMessage({ inSession: true })`).
54
+ - `mu judge` (kyrn-judge-local): `help`/`--help` prints a usage, an unknown command prints it to stderr and exits 2, `status` prints one readable line (`status --json` the server's JSON), and its messages are sentence case. `mu ledger --help` prints a usage instead of reading the sessions. `mu auth --help` (or a bare `mu auth`) prints mu's `status | login | logout` before pi's auth help. `mu import --help` names `MU_AGENT_DIR`, the variable the docs use. `mu link --help` / `mu unlink --help` print a usage (they linked and unlinked); unlink's messages are sentences. The launcher answers `mu --version` and `-v` itself with mu's bare version: in a checkout from kyrn/npm/package.template.json (pi's own answer there was pi's version), in the package from its package.json; the smoke test checks it; the launch-chain tests start pi with `--help` instead.
55
+ - `mu setup`'s "Not now" judge writes `"modes": { "default": "off" }` (before: nothing, so the default `jev` tier with no key used the free Jev on OpenCode Zen); a judge picked later removes that "off". `mu setup --judge off` writes the same for scripts; `--judge none` still leaves the judge unchanged. The engine takes `builtInModes` (DEFAULT_CONFIG.modes): with the default "off", a point whose mode is only its built-in one is off too, so no judge is asked; a mode set to another value holds. With every point off, the welcome line shows the judge as off and neither it nor the warm-up probes the judge.
56
+ - The loop rule ("the same call 3 times with the same outcome") compares the whole input and the result text. Paging through a file with `read` at different offsets, or two scripts whose first 300 characters match, no longer counts as a loop.
57
+ - The end-of-run checks (`turn.completion`, `turn.continue`) cancel their judge call when their 3 s wait runs out. A late verdict no longer keeps `mu --print` from exiting until the judge's own timeout: with a slow local judge, runs ended 7-13 s after their last message. Notification verdicts still on their way are cancelled when the session ends.
58
+ - The `remember` tool dropped a lesson when the judge gave no verdict (a timeout, no judge, shadow mode) or could not tell. It now stores it; only a judge that reads it as one-off or known already turns it down. Sub-agents' `Lesson:` lines still need a verdict.
59
+ - `manifest.json` gives a decision point its built-in mode (`defaultMode`) when `DEFAULT_CONFIG.modes` sets one for it: `swarm.patch` off, `notify.routing`, `input.interjection` and `turn.drift` in shadow. Without it the desktop app's settings and kernel bar showed these points at the default mode, as active, and usemu.dev counted 38 of the 40 points as acting from the start instead of 34. `scripts/write-docs-reference.mjs` reads the modes from the manifest too.
60
+ - The local judge (Laya) no longer crashes when the disk runs low. Core ML keeps its compiled copy of the model in `~/Library/Caches/python/com.apple.e5rt.e5bundlecache`, marked purgeable, and macOS deletes it to free space; each change of input length read it back, so the next one recompiled the model (3-36 s) or aborted the sidecar inside MetalPerformanceShadersGraph (`failed assertion shape.count = 0 != strides.count = 2`), five times in one night of Terminal-Bench. The sidecar now pads every request to the checkpoint's whole window (1024 tokens), so after warm-up the length never changes: about 10 ms more a question at a steady length, and no more 0.4-1 s switches. `MU_LOCAL_JUDGE_MIN_LENGTH=256` brings the shorter buckets back. Warm-up skips the lengths no request can reach and no longer logs `warm-up for length 16 failed: StopIteration`.
61
+
62
+ ## [0.1.12] - 2026-10-22
63
+
64
+ ### Added
65
+
66
+ - A long-thinking watchdog: a reply that has only been thinking for 10 minutes, with no text and no tool call yet, is cut and asked again at the same reasoning effort, with the end of its reasoning so far and a request to act on it first. The retry is never cut. At most 3 cuts per message (`features.watchdog`: `thinkingMs`, `maxCuts`).
67
+ - `mu import` reads ZCode conversations too (`~/.zcode/cli/db/db.sqlite`), beside Claude Code and Codex, with rewinds, compactions, sub-agent threads and forks.
68
+ - Imported conversations keep their whole history: thinking (shown, never sent to the new model) and images, as well as messages and tool calls. A Claude Code conversation keeps the title Claude Code shows for it.
69
+ - Importing a conversation again brings over what's new in it, into the same conversation; if you also went on in mu, the new import is a conversation of its own. `mu import --list` marks conversations that changed since.
70
+
71
+ ### Changed
72
+
73
+ - The permission mode `jev` ("Jev approves") is now `judge` ("Judge mode"): `/permissions full | judge | ask`, `MU_PERMISSIONS=judge`. A saved `jev` falls back to the default, which is judge mode. The kernel's and the command line's messages say "the judge" where they meant mu's judge in general, and keep "Jev" for TypeSafe's hosted model.
74
+ - Background jobs started with `bg_start` keep running after an unattended run (`mu --print`, json mode) ends, so a server a task asked for is still up afterwards. Such jobs write to their log file instead of a pipe to mu. Interactive and desktop sessions still stop their jobs at the end (`features.background.keepOnExit`: `headless`, `always` or `never`).
75
+ - A failed model request is retried up to 8 times instead of 3 (`retry.maxRetries`). With the 60-second cap on each wait, a provider that stops answering for about four minutes no longer ends the run.
76
+
77
+ ### Fixed
78
+
79
+ - A run that ended on a reply with only thinking in it, no text and no tool call, stopped there without a word. mu now sends the agent back to the task, up to the same limit as its other nudges.
80
+ - A reply cut by the provider's content filter (`finish_reason: content_filter`) ended the run. It is now asked again, twice at most.
81
+ - A Claude Code conversation compacted more than once is imported whole; before, the import stopped at the latest compaction.
82
+ - An imported conversation goes on with mu's model, not the one Claude Code, Codex or ZCode answered with, so `mu` no longer warns that it can't restore that model.
83
+
84
+ ## [0.1.11] - 2026-10-21
85
+
86
+ ### Changed
87
+
88
+ - The package's keywords add `pi-coding-agent`, `mcp` and `prompt-injection`, so directories that index pi coding agents and MCP clients find it.
89
+ - English: a copy edit of what the command line and the kernel show, after `docs/writing-style.md`: "subagent" as one word, "canceled", "session" for what is allowed until the session ends ("Allow for this session"), and clearer wording in `mu setup`, `/browse`, the board's account and the settings' help. The `/browse` hint names `mu.json`, not `kyrn.json`. The Chinese text is unchanged.
90
+
91
+ ### Fixed
92
+
93
+ - With a Jev key (TypeSafe), the welcome screen and each verdict line name the judge `Jev`, not its model id `jev-latest`. A versioned Jev on OpenCode Zen shows as `Jev 1.13`.
94
+ - `/hive <question>` shows what you typed in the chat and as the conversation's title. The request to start a hive at once, written for the model in English, goes with it as a message only the model reads; before, it filled your message, also in a Chinese session. A `/hive` sent while a turn runs still sends the request as its message.
95
+ - The footer's line after each message reads like the verdict line, `Jev: multi-step task · full effort · 71 ms`, instead of `jev-latest active: multi_step_task -> heavy 71ms`. A verdict in shadow mode ends with `recorded only`.
96
+ - `planHost` names the script the command line runs pi with (`cli`: `dist/bundle/cli.js` in the package, `packages/coding-agent/src/cli.ts` in a checkout). The desktop app's host hands it to pi as `process.argv[1]`, so the sub-agents pi starts by running that script again (`hive`, `delegate`, the swarm) no longer fail with "The mu host needs a parent".
97
+ - With no model connected, `mu -p` and the other no-key errors now say to run `mu setup`, instead of pointing to `/login` (which only exists inside a session) and two files inside the package.
98
+
99
+ ## [0.1.10] - 2026-10-21
100
+
101
+ ### Changed
102
+
103
+ - English: the kernel's messages (the `en` side of `say({ zh, en })`), the manifest's titles and summaries, `mu --help`, `mu doctor` and `mu ledger` were rewritten after `docs/writing-style.md`. The Chinese text is unchanged.
104
+ - Themes: the package ships `judge/themes/mu-dark.json` and `mu-light.json`. An interactive `mu` passes `--theme <judge/themes>`, and `--use-theme mu-light/mu-dark` unless the command line has `--use-theme` or `settings.json` names a theme. `-p`, `--print` and `--mode` get neither. `system` is still pi's theme.
105
+ - pi's startup list names the judgment layer `mu`, not `kyrn-judge.js`: its bundle in the package is `judge/mu/index.js` (it was `judge/dist/kyrn-judge.js`).
106
+ - The package's description and README say what mu does by default: 38 of the 40 decision points act from the start, and approvals fall back to asking you.
107
+ - The welcome screen drops its box: the wordmark, the version, pi's version, then the judge, the model and the working directory, and a closing rule. Judge names show as `Jev` and `Laya`; a verdict's mark is a pink `◆`.
108
+
109
+ ## [0.1.9] - 2026-10-19
110
+
111
+ ### Added
112
+
113
+ - Two decision points, 40 in all:
114
+ - `tool.description` (feature `injection`, option `descriptions`, on by default): every MCP tool's description, with its parameters' descriptions, is asked about once per server when it first appears and again when it changes. Does it ask the AI for more than calling the tool? Over 0.5 the tool is removed from the active tools before any request carries it (before the turn, and before the results of a step in which a server connected), kept off at each step, and calls to it are blocked; the user is told once. Without a verdict only `<IMPORTANT>` blocks and "do not tell the user" count. Measured in `kyrn/docs/features/tool-descriptions.md`.
115
+ - `skill.capture` (feature `procedures`): after a run of `minSteps` (6) or more steps that ran `minCommands` (2) or more shell commands and did not end in an error, the judge says whether it was a reusable procedure. Over 0.7 the writer model (else the conversation's model) drafts a SKILL.md, kept in `<agent dir>/mu/skill-drafts` where pi does not load it. `/skills`, `/skills show|accept|reject <name>`; `accept --project` installs into the project. At most `maxPending` (3) drafts per project. Measured in `kyrn/docs/features/procedures.md`.
116
+ - `tool.injection` screens bash and PowerShell results when the command runs a fetching program (`features.injection.fetchCommands`: curl, wget, gh, glab, lynx, w3m, Invoke-WebRequest, Invoke-RestMethod, iwr, irm), unless every address it names is local; `gh` and `glab` always count.
117
+ - The report of a `delegate` or `hive` sub-agent whose tool results had passages withheld comes back under a warning; the parent counts the withheld notes in the child's event stream, with no extra judge call.
118
+ - Built on pi 1.3.0 and upstream main after it (e28e70804, d2e7d8b7c).
119
+
120
+ ### Fixed
121
+
122
+ - Claude Sonnet 5.5 keeps Anthropic's cache read price, and Kimi K3 on Moonshot its official rates, when models.dev lists others.
123
+ - The goal's time cap and the web download cap show their units as units.
124
+ - `@earendil-works/pi-telemetry` declares the `source` export condition, so tests that run without a build resolve it.
125
+
5
126
  ## [0.1.8] - 2026-10-06
6
127
 
7
128
  ### Added
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # mu
2
2
 
3
- **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.
3
+ **A coding agent with a judgment kernel.** It hands its routine decisions to a small, fast judge model, so the main model can keep its attention on the work.
4
4
 
5
- > **Early development.** mu works day to day for its authors, but names, settings and file formats may still change. Source, docs and the desktop app: [github.com/qybaihe/mu](https://github.com/qybaihe/mu) (README in English, 简体中文, 繁體中文, 日本語 and 한국어).
5
+ > **Early development.** The authors use mu every day, but names, settings and file formats may still change. Source, docs and the desktop app: [github.com/qybaihe/mu](https://github.com/qybaihe/mu) (README in English, 简体中文, 繁體中文, 日本語 and 한국어).
6
6
 
7
7
  ## Install
8
8
 
@@ -11,50 +11,54 @@ npm i -g mu-agent
11
11
  mu
12
12
  ```
13
13
 
14
- You need Node.js 22.19 or newer. The package is called `mu-agent`; the command is `mu`. It works on macOS, Linux, Windows and WSL.
14
+ You need Node.js 22.19 or newer. The package is `mu-agent`; the command is `mu`. It runs on macOS, Linux, Windows and WSL.
15
15
 
16
- `mu setup` connects a model from your shell: paste an API key and mu tells which service it belongs to (DeepSeek, Qwen, Kimi, GLM, SiliconFlow, StepFun, OpenRouter, OpenCode Zen, OpenAI, Anthropic, Google Gemini, xAI), or pick Ollama or an address of your own. It checks the key with one request, picks the model to start with, and sets up the judge. The first `mu` offers to run it while no model is set up.
16
+ `mu setup` connects a model from your shell. Paste an API key and mu detects the service from the key's format (DeepSeek, Qwen, Kimi, GLM, SiliconFlow, StepFun, OpenRouter, OpenCode Zen, OpenAI, Anthropic, Google Gemini, xAI), or pick Ollama or your own endpoint. It checks the key with one request, picks a starting model and sets up the judge. If no model is set up yet, the first `mu` run offers to start setup.
17
17
 
18
- Inside mu, `/login` signs in to a model provider or a subscription and `/model` picks a model. `/help` lists everything. `mu doctor` checks the setup from your shell.
18
+ Inside mu, `/login` signs in to a provider or a subscription and `/model` switches models. `/help` lists everything. `mu doctor` checks your setup from the shell.
19
19
 
20
20
  ## What it adds to pi
21
21
 
22
- mu is built on [pi](https://github.com/earendil-works/pi). It hands the small questions a coding agent keeps asking to a **judge**: is this message a new task or a correction, does this command need your permission, which part of a long test log matters, which sub-agent fits a job, what should the browser click next. Code acts on the answers, and the main model only sees what it needs.
22
+ mu is built on [pi](https://github.com/earendil-works/pi). It hands the small questions a coding agent keeps asking to a **judge**: is this message a new task or a correction, does this command need your permission, which part of a long test log matters, which subagent fits a job, what should the browser click next. Code acts on the answers, and the main model only sees what it needs.
23
23
 
24
- - **Every decision is visible.** Each one is `off`, `shadow` (asked and logged, not acted on) or `active`. `/ledger` and `mu ledger` show the verdicts.
25
- - **Every decision fails open.** When the judge is unsure, slow or unreachable, a plain rule decides, or pi's own behaviour runs.
26
- - Also: a task frame that holds your constraints in your own words, three permission modes (`/permissions`), goal mode (`/goal`), a plain-language progress board (`/board`), checkpoints and `/rewind`, sub-agents in their own git worktrees (`delegate`, `hive`), a judge-driven browser (`browse`), and your rules, skills and MCP servers from Claude Code, Cursor and Codex.
24
+ - **Every decision is visible.** Each decision point is `off`, `shadow` (asked and logged, but not acted on) or `active`. `/ledger` and `mu ledger` show the verdicts.
25
+ - **Every decision has a fallback.** When the judge is unsure, slow or unreachable, a plain rule decides, or pi's default behavior runs. For approvals, the fallback is to ask you.
26
+ - **And more:** a task frame that keeps your constraints in your own words, three permission modes (`/permissions`), goal mode (`/goal`), a plain-language progress board (`/board`), checkpoints and `/rewind`, subagents that edit in their own git worktrees (`delegate`), read-only hives of investigators (`hive`), a judge-driven browser (`browse`), and it picks up the rules, skills and MCP servers you already set up in Claude Code, Cursor and Codex.
27
27
 
28
28
  ## Choose a judge
29
29
 
30
- With nothing set up, Jev 1.13 on OpenCode Zen answers, free for a limited time and with no key: what it judges goes to OpenCode, which does not train on it, and mu says so once a day. To send nothing anywhere, start mu with `MU_JUDGE=off`, or use the local Laya.
30
+ Out of the box, the judge is Jev 1.13, TypeSafe's hosted judge model, free on OpenCode Zen for a limited time and with no key needed. What it judges goes to OpenCode, which doesn't train on it, and mu reminds you of that once a day. To send nothing anywhere, run `mu setup --judge off` (or choose "Not now" when `mu setup` asks): no decision point asks a judge, and mu's features keep working on their rules. Or use the local judge, Laya.
31
31
 
32
- - **Jev** (hosted): set `TYPESAFE_API_KEY` (TypeSafe), `MU_JUDGE_OPENROUTER_API_KEY` (Jev on OpenRouter), `AI_GATEWAY_API_KEY` (Vercel AI Gateway) or `OPENCODE_API_KEY` (OpenCode Zen) in your environment, or put it in `~/.mu/.env` as `KEY=value`; `mu setup` asks for a key and writes it there. mu reads that file as data and never prints it. The judge switches to the key by itself.
33
- - **Another classifier** from pi's catalog: `/mu judge classifier:<provider>/<model>`, e.g. Cloudflare's Clef (`clef`, `clef-flash`) or a System One model on OpenRouter, with that provider's key.
34
- - **CLM** (self-hosted; its encoder needs a GPU): start `clm-serve` from [Contrastive-LM/CLM](https://github.com/Contrastive-LM/CLM), then `/mu judge clm`. For a server on another machine, add `"judges": { "clm": { "type": "clm", "baseUrl": "http://<host>:8700" } }` to `~/.mu/agent/mu.json`. A server started with `CLM_API_KEY` needs the same value in `MU_JUDGE_CLM_API_KEY`. `mu doctor` checks the server.
35
- - **Laya** (local, macOS on Apple Silicon): `mu judge setup` installs it into `~/.mu/local-judge`. It downloads about 930 MB, only when you run that command.
32
+ - **Jev** (hosted): set `TYPESAFE_API_KEY` (TypeSafe), `MU_JUDGE_OPENROUTER_API_KEY` (Jev on OpenRouter), `AI_GATEWAY_API_KEY` (Vercel AI Gateway), `OPENCODE_API_KEY` (OpenCode Zen) or `CLOUDFLARE_API_KEY` with `CLOUDFLARE_ACCOUNT_ID` (Cloudflare Workers AI) in your environment, or add it to `~/.mu/.env` as `KEY=value`. `mu setup` asks for a key and writes it there. mu reads that file as data, never prints it and starts using the key automatically.
33
+ - **Another classifier** from pi's catalog: `/mu judge classifier:<provider>/<model>`, for example Cloudflare's Clef (`clef`, `clef-flash`) or a System One model on OpenRouter, with that provider's key.
34
+ - **CLM** (self-hosted; its encoder needs a GPU): start `clm-serve` from [Contrastive-LM/CLM](https://github.com/Contrastive-LM/CLM), then run `/mu judge clm`. For a server on another machine, add `"judges": { "clm": { "type": "clm", "baseUrl": "http://<host>:8700" } }` to `~/.mu/agent/mu.json`. If the server was started with `CLM_API_KEY`, set the same value in `MU_JUDGE_CLM_API_KEY`. `mu doctor` checks the server.
35
+ - **Laya** (local, macOS on Apple silicon): `mu judge setup` installs it into `~/.mu/local-judge`. It downloads about 930 MB, and only when you run that command.
36
36
  - **Any model you already use:** `/mu judge llm:<provider>/<model>`.
37
37
 
38
- Decisions take effect from the start (`active`). To only record what the judge would do: `/mu mode default shadow`, or `"modes": { "default": "shadow" }` in `~/.mu/agent/mu.json`.
38
+ 34 of the 40 decision points act on their verdicts from the start (`active`); `context.compact` and `output.drift` belong to features that are off until you turn them on, `swarm.patch` is `off`, and `notify.routing`, `input.interjection` and `turn.drift` are in `shadow`. To only record what the judge would do, run `/mu mode default shadow`, or put `"modes": { "default": "shadow" }` in `~/.mu/agent/mu.json`.
39
39
 
40
40
  ## Commands
41
41
 
42
42
  ```text
43
- mu interactive session in the current directory
44
- mu "prompt" interactive, starting with this prompt
45
- mu -p "prompt" one-shot: print the answer and exit
46
- mu -c | -r continue the last session | pick one to resume
47
- mu setup [key] connect a model and choose the judge (mu setup --help: scripts, the services)
48
- mu judge <cmd> the local judge (Laya): setup | start | stop | status | run
49
- mu ledger [n] [--json] what the judge decided in the last n sessions
50
- mu doctor check the installation
51
- mu auth status the subscription sign-in the desktop app uses (JSON lines)
52
- mu import --list Claude Code and Codex conversations on this computer
53
- mu import <file>... bring them into mu, to continue with /resume or mu --session
54
- mu help | version
43
+ mu start a session in the current directory
44
+ mu "prompt" start a session with this prompt
45
+ mu -p "prompt" run one prompt, print the answer and exit
46
+ mu -c | -r continue the last session | pick a session to resume
47
+ mu setup connect a model and choose a judge (see mu setup --help for scripting and services)
48
+ mu judge <cmd> manage the local judge, Laya (macOS on Apple silicon): setup | start | stop | status | run
49
+ mu ledger [n] [--json] print the judge's verdicts from the last n sessions
50
+ mu import <file>... import Claude Code, Codex and ZCode conversations; --list shows them (mu import --help)
51
+ mu doctor check the installation, the judges and the connections
52
+ mu auth <cmd> sign-in helper for the desktop app: status | login | logout
53
+ mu link | unlink put the mu command on your PATH | remove it
54
+ mu help | version show this help | print the version
55
55
  ```
56
56
 
57
- `mu --help` lists every flag of the agent. mu keeps its files in `~/.mu`, apart from a stock pi in `~/.pi`.
57
+ Everything else goes to pi unchanged: `mu --help` also shows pi's options. mu keeps its files in `~/.mu`, separate from a regular pi install in `~/.pi`.
58
+
59
+ ## Themes
60
+
61
+ In the terminal, mu uses its own themes, `mu-dark` or `mu-light` to match your terminal, until you pick a theme in `/settings`. Choose `system` there to use your terminal's own palette.
58
62
 
59
63
  ## Update and remove
60
64
 
@@ -63,8 +67,8 @@ npm i -g mu-agent@latest
63
67
  npm rm -g mu-agent
64
68
  ```
65
69
 
66
- Removing the package leaves `~/.mu` (your sessions, settings and logins) in place.
70
+ Removing the package keeps `~/.mu` (your sessions, settings and sign-ins).
67
71
 
68
72
  ## License
69
73
 
70
- MIT. pi is by Mario Zechner and contributors; third-party code in the judgment layer is listed in `judge/THIRD_PARTY_NOTICES.md`.
74
+ MIT. pi is by Mario Zechner and contributors. Third-party code in the judgment kernel is listed in `judge/THIRD_PARTY_NOTICES.md`.
@@ -0,0 +1,6 @@
1
+ import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url);
2
+ import{ANTHROPIC_FEDERATION_RULE_ID_ENV,ANTHROPIC_IDENTITY_TOKEN_FILE_ENV,ANTHROPIC_ORGANIZATION_ID_ENV,ANTHROPIC_SERVICE_ACCOUNT_ID_ENV,ANTHROPIC_WORKSPACE_ID_ENV}from"./chunk-UKVNB6C4.js";import{buildCopilotDynamicHeaders,hasCopilotVisionInput}from"./chunk-PGXDEOVV.js";import{Anthropic}from"./chunk-7CBJINYJ.js";import{retryProviderRequest}from"./chunk-PMRZO46Y.js";import{adjustMaxTokensForThinking,buildBaseOptions,clampMaxTokensToContext,getJsonSchemaToolParameters,getPiUserAgent,resolveJsonSchemaStrictSampling,splitErrorToolResultImages,transformMessages}from"./chunk-R4VPBBDS.js";import{sanitizeSurrogates}from"./chunk-L2ZPNLYD.js";import{headersToRecord}from"./chunk-UTB4DTYQ.js";import{getProviderEnvValue}from"./chunk-7DDKXS2Q.js";import{parseJsonWithRepair,parseStreamingJson,parseToolCallArguments}from"./chunk-3V45WARH.js";import{calculateCost,getCurrentTools,getInitialSystemMessage,getSystemMessageText,renderSystemMessageUpdate,resolveTranscript}from"./chunk-WCGU75WO.js";import{AssistantMessageEventStream,appendAssistantMessageDiagnostic}from"./chunk-LPXNMA2W.js";import"./chunk-JXWJOIH4.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.280",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 convertImageBlock(image){return{type:"image",source:{type:"base64",media_type:image.mimeType,data:image.data}}}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)}:convertImageBlock(block));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",INLINE_TOOLS_BETA="inline-tools-2026-09-15",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 hasRequestAuth(apiKey,headers){return!!apiKey||hasHeader(headers,"authorization")||hasHeader(headers,"x-api-key")||hasHeader(headers,"cf-aig-authorization")}function assertRequestAuth(provider,apiKey,headers){if(!hasRequestAuth(apiKey,headers))throw new Error(`No API key for provider: ${provider}`)}var PiAnthropic=class extends Anthropic{_shouldResolveDefaultCredentials(){return!1}};function getAnthropicFederation(model,apiKey,headers,env){if(model.provider!=="anthropic"||hasRequestAuth(apiKey,headers))return;let federationRuleId=getProviderEnvValue(ANTHROPIC_FEDERATION_RULE_ID_ENV,env),organizationId=getProviderEnvValue(ANTHROPIC_ORGANIZATION_ID_ENV,env),identityTokenFile=getProviderEnvValue(ANTHROPIC_IDENTITY_TOKEN_FILE_ENV,env);if(!(!federationRuleId||!organizationId||!identityTokenFile))return{organization_id:organizationId,workspace_id:getProviderEnvValue(ANTHROPIC_WORKSPACE_ID_ENV,env),authentication:{type:"oidc_federation",federation_rule_id:federationRuleId,service_account_id:getProviderEnvValue(ANTHROPIC_SERVICE_ACCOUNT_ID_ENV,env),identity_token:{source:"file",path:identityTokenFile}}}}var federationClient,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 output={role:"assistant",content:[],api:model.api,provider:model.provider,model:model.id,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,federation=getAnthropicFederation(model,apiKey,options?.headers,options?.env);federation||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,federation);client=created.client,isOAuth=created.isOAuthToken}let requestOptions={...options?.signal?{signal:options.signal}:{},...options?.timeoutMs!==void 0?{timeout:options.timeoutMs}:{},maxRetries:0},disablePreferredStrictTools=!1,response;for(;;){options?.signal?.throwIfAborted();let built=buildParams(model,normalizedContext,isOAuth,options,disablePreferredStrictTools);built.providerThinkingLevel&&(output.providerThinkingLevel=built.providerThinkingLevel);let params=built.params,nextParams=await options?.onPayload?.(params,model);nextParams!==void 0&&(params={...nextParams,stream:!0});try{response=await retryProviderRequest(()=>client.beta.messages.create(params,requestOptions).asResponse(),{maxRetries:options?.maxRetries,maxRetryDelayMs:options?.maxRetryDelayMs,signal:options?.signal});break}catch(error){if(options?.signal?.aborted||disablePreferredStrictTools||!built.hasPreferredStrictTools||!(error instanceof Anthropic.APIError)||error.status!==400||error.type!=="invalid_request_error"||!["The compiled grammar is too large","Too many strict tools (","Schemas contains too many parameters with union types ("].some(message=>error.message.includes(message)))throw error;disablePreferredStrictTools=!0,appendAssistantMessageDiagnostic(output,{type:"anthropic_strict_tools_fallback",timestamp:Date.now(),error:{message:error.message},details:{reason:"strict_tool_limits_exceeded"}})}}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(await options?.onProviderStreamEvent?.(event,model),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"&&(Object.assign(block,parseToolCallArguments(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 cacheCreation=event.usage.cache_creation;cacheCreation?.ephemeral_1h_input_tokens!=null&&(output.usage.cacheWrite1h=cacheCreation.ephemeral_1h_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)=>{getAnthropicFederation(model,options?.apiKey,options?.headers,options?.env)||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,federation){if(model.provider==="github-copilot")return{client:new PiAnthropic({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 PiAnthropic({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);if(federation){let key=JSON.stringify([model.baseUrl,federation]);if(federationClient?.key!==key||federationClient.fetch!==fetch){let client2=new PiAnthropic({apiKey:null,authToken:null,config:federation,baseURL:model.baseUrl,dangerouslyAllowBrowser:!0,fetch});federationClient={key,fetch,client:client2}}return{client:federationClient.client.withOptions({defaultHeaders}),isOAuthToken:!1}}return{client:new PiAnthropic({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(INLINE_TOOLS_BETA),[...new Set(features)]}function buildParams(model,context,isOAuthToken2,options,disablePreferredStrictTools=!1){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,hasPreferredStrictTools=!1,convertToolDefinitions=(tools,toolCacheControl2)=>{let definitions=convertTools(tools,isOAuthToken2,compat.supportsEagerToolInputStreaming,compat.supportsStrictTools,toolCacheControl2,disablePreferredStrictTools);return definitions.some((definition,index)=>{let config=tools[index].constrainedSampling;return definition.strict===!0&&config&&config.type==="json_schema"&&config.strict==="prefer"})&&(hasPreferredStrictTools=!0),definitions},initialTools=initialSystemMessage?.toolsAdded??[],nativeToolChanges=compat.supportsMidConvoSystemMessages&&compat.supportsMidConvoToolChanges&&initialTools.length>0,thinkingDisabled=model.reasoning&&options?.thinkingEnabled===!1&&model.thinkingLevelMap?.off!==null,effortMessages=model.compat?.supportsMidConvoEffort===!0&&!thinkingDisabled,converted=convertMessages(conversationMessages,isOAuthToken2,cacheControl,compat.allowEmptySignature,effortMessages?model.provider:void 0,nativeToolChanges?convertToolDefinitions:void 0),activeEffort=options?.effort??"high",betaFeatures=getBetaFeatures(model,context,isOAuthToken2,nativeToolChanges,options),params={model:model.id,messages:effortMessages?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)params.tools=[...convertToolDefinitions(initialTools,toolCacheControl),DEFERRED_TOOL_PLACEHOLDER];else{let tools=getCurrentTools(context.messages);tools.length>0&&(params.tools=convertToolDefinitions(tools,toolCacheControl))}if(thinkingDisabled)params.thinking={type:"disabled"};else if(effortMessages)params.thinking={type:"adaptive",display:options?.thinkingDisplay??"summarized",block_binding:{prefix_mismatch_behavior:"drop_block"}},params.output_config={effort:"high"};else if(model.reasoning&&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}}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,hasPreferredStrictTools,providerThinkingLevel:effortMessages?activeEffort:void 0}}function normalizeToolCallId(id){return id.replace(/[^a-zA-Z0-9_-]/g,"_").slice(0,64)}function convertToolResult(msg,content=msg.content){return{type:"tool_result",tool_use_id:msg.toolCallId,content:convertContentBlocks(content),is_error:msg.isError}}function convertMessages(transformedMessages,isOAuthToken2,cacheControl,allowEmptySignature=!1,managedProvider,convertToolDefinitions){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)}),convertToolDefinitions){let added=msg.toolsAdded??[],redefined=new Set(added.map(tool=>tool.name));for(let tool of msg.toolsRemoved??[])redefined.has(tool.name)||blocks.push({type:"tool_removal",tool:{type:"tool_reference",name:isOAuthToken2?toClaudeCodeName(tool.name):tool.name}});for(let definition of convertToolDefinitions(added))blocks.push({type:"tool_addition",tool:{type:"tool_definition",definition}})}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)}:convertImageBlock(item)).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=[],errorImages=[],j=i;for(;j<transformedMessages.length&&transformedMessages[j].role==="toolResult";){let toolMsg=transformedMessages[j],split=splitErrorToolResultImages(toolMsg);toolResults.push(convertToolResult(toolMsg,split?.content)),split&&errorImages.push({type:"text",text:split.caption},...split.images.map(convertImageBlock)),j++}i=j-1,params.push({role:"user",content:[...toolResults,...errorImages]})}}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}var ANTHROPIC_STRICT_UNSUPPORTED_KEYWORDS=new Set(["minimum","maximum","exclusiveMinimum","exclusiveMaximum","multipleOf","maxItems","uniqueItems","minContains","maxContains","minProperties","maxProperties"]),ANTHROPIC_STRICT_STRING_FORMATS=new Set(["date-time","time","date","duration","email","hostname","uri","ipv4","ipv6","uuid"]),isAnthropicStrictUnsupportedKeyword=(key,value)=>ANTHROPIC_STRICT_UNSUPPORTED_KEYWORDS.has(key)?!0:key==="minItems"?value!==0&&value!==1:key==="format"?typeof value!="string"||!ANTHROPIC_STRICT_STRING_FORMATS.has(value):!1;function convertTools(tools,isOAuthToken2,supportsEagerToolInputStreaming,supportsStrictTools,cacheControl,disablePreferredStrictTools=!1){return tools?tools.map((tool,index)=>{let config=tool.constrainedSampling,strict=disablePreferredStrictTools&&config&&config.type==="json_schema"&&config.strict==="prefer"?void 0:resolveJsonSchemaStrictSampling(tool,supportsStrictTools,isAnthropicStrictUnsupportedKeyword),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};
@@ -83,4 +83,4 @@ var __require=(x=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(x,{get:(
83
83
  ${details?`<div class="details">${details}</div>`:""}
84
84
  </main>
85
85
  </body>
86
- </html>`}function oauthSuccessHtml(message){return renderPage({heading:"Signed in",message})}function oauthErrorHtml(message,details){return renderPage({heading:"Sign-in did not complete",message,details})}function sendPage(response,status,html){response.writeHead(status,{"content-type":"text/html; charset=utf-8","cache-control":"no-store"}),response.end(html)}async function startOAuthCallbackServer(options){let{providerName,signal}=options;if(signal?.aborted)throw new Error("Login cancelled");let resolveWait=()=>{},rejectWait=()=>{},waitPromise=new Promise((resolve,reject)=>{resolveWait=resolve,rejectWait=reject});waitPromise.catch(()=>{});let claimed=!1,settled=!1,timer,onAbort=()=>finish({error:new Error("Login cancelled")}),finish=result=>{settled||(settled=!0,timer&&clearTimeout(timer),signal?.removeEventListener("abort",onAbort),"error"in result?rejectWait(result.error):resolveWait(result.value))},server=createServer((request,response)=>{(async()=>{let url=new URL(request.url??"/","http://localhost");if(request.method!=="GET"||url.pathname!==options.path){sendPage(response,404,oauthErrorHtml("Callback route not found."));return}if(options.state!==void 0&&url.searchParams.get("state")!==options.state){sendPage(response,400,oauthErrorHtml("State mismatch."));return}if(claimed||settled){sendPage(response,409,oauthErrorHtml("This sign-in has already been handled."));return}let error=url.searchParams.get("error");if(error){let description=url.searchParams.get("error_description")??error;sendPage(response,400,oauthErrorHtml(`${providerName} authorization failed.`,description)),finish({error:new Error(`${providerName} authorization failed: ${description}`)});return}let code=url.searchParams.get("code");if(!code){sendPage(response,400,oauthErrorHtml("Missing authorization code."));return}claimed=!0;try{let value=await options.complete(code);sendPage(response,200,oauthSuccessHtml(`Signed in to ${providerName}. You may now close this page.`)),finish({value})}catch(error2){let failure=error2 instanceof Error?error2:new Error(String(error2));sendPage(response,502,oauthErrorHtml(`${providerName} sign-in failed.`,failure.message)),finish({error:failure})}})()});await new Promise((resolve,reject)=>{server.once("error",reject),server.listen(options.port,options.host,()=>{server.off("error",reject),resolve()})});let address=server.address();if(!address||typeof address=="string")throw server.close(),new Error("OAuth callback server did not bind to TCP");server.on("error",error=>finish({error})),signal?.addEventListener("abort",onAbort,{once:!0}),options.timeoutMs!==void 0&&(timer=setTimeout(()=>finish({error:new Error(`${providerName} sign-in timed out`)}),options.timeoutMs));let redirectHost=options.redirectHost??options.host;return{redirectUri:`http://${redirectHost.includes(":")?`[${redirectHost}]`:redirectHost}:${address.port}${options.path}`,wait:()=>waitPromise,cancel:()=>{claimed||finish({value:void 0})},close:()=>{finish({error:new Error("OAuth callback server closed")}),server.close()}}}async function waitForCallbackOrManualInput(interaction,callback,prompt){let manualAbort=new AbortController,manualError,manual=interaction.prompt({type:"manual_code",...prompt,signal:manualAbort.signal}).then(input=>(callback?.cancel(),input)).catch(error=>{manualError=error instanceof Error?error:new Error(String(error)),callback?.cancel()});try{let value=await callback?.wait();if(manualError)throw manualError;if(value!==void 0)return{type:"callback",value};let input=await manual;if(manualError)throw manualError;return{type:"manual",input:input??""}}finally{manualAbort.abort()}}function base64urlEncode(bytes){let binary="";for(let byte of bytes)binary+=String.fromCharCode(byte);return btoa(binary).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function generatePKCE(){let verifierBytes=new Uint8Array(32);crypto.getRandomValues(verifierBytes);let verifier=base64urlEncode(verifierBytes),data=new TextEncoder().encode(verifier),hashBuffer=await crypto.subtle.digest("SHA-256",data),challenge=base64urlEncode(new Uint8Array(hashBuffer));return{verifier,challenge}}var decode=s=>atob(s),CLIENT_ID=decode("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl"),AUTHORIZE_URL="https://claude.ai/oauth/authorize",TOKEN_URL="https://platform.claude.com/v1/oauth/token",CALLBACK_HOST=getProviderEnvValue("PI_OAUTH_CALLBACK_HOST")||"127.0.0.1",CALLBACK_PORT=53692,CALLBACK_PATH="/callback",REDIRECT_URI=`http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`,COPY_CODE_REDIRECT_URI="https://platform.claude.com/oauth/code/callback",ANTHROPIC_BROWSER_LOGIN_METHOD="browser",ANTHROPIC_COPY_CODE_LOGIN_METHOD="copy_code",SCOPES="org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";function parseAuthorizationInput(input){let value=input.trim();if(!value)return{};try{let url=new URL(value);return{code:url.searchParams.get("code")??void 0,state:url.searchParams.get("state")??void 0}}catch{}if(value.includes("#")){let[code,state]=value.split("#",2);return{code,state}}if(value.includes("code=")){let params=new URLSearchParams(value);return{code:params.get("code")??void 0,state:params.get("state")??void 0}}return{code:value}}function formatErrorDetails(error){if(error instanceof Error){let details=[`${error.name}: ${error.message}`],errorWithCode=error;return errorWithCode.code&&details.push(`code=${errorWithCode.code}`),typeof errorWithCode.errno<"u"&&details.push(`errno=${String(errorWithCode.errno)}`),typeof error.cause<"u"&&details.push(`cause=${formatErrorDetails(error.cause)}`),error.stack&&details.push(`stack=${error.stack}`),details.join("; ")}return String(error)}async function postJson(url,body,signal){let response=await fetch(url,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(body),signal:AbortSignal.any([signal,AbortSignal.timeout(3e4)])}),responseBody=await response.text();if(!response.ok)throw new Error(`HTTP request failed. status=${response.status}; url=${url}; body=${responseBody}`);return responseBody}async function exchangeAuthorizationCode(code,state,verifier,redirectUri,signal){let responseBody;try{responseBody=await postJson(TOKEN_URL,{grant_type:"authorization_code",client_id:CLIENT_ID,code,state,redirect_uri:redirectUri,code_verifier:verifier},signal)}catch(error){throw new Error(`Token exchange request failed. url=${TOKEN_URL}; redirect_uri=${redirectUri}; response_type=authorization_code; details=${formatErrorDetails(error)}`)}let tokenData;try{tokenData=JSON.parse(responseBody)}catch(error){throw new Error(`Token exchange returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`)}return{type:"oauth",refresh:tokenData.refresh_token,access:tokenData.access_token,expires:Date.now()+tokenData.expires_in*1e3-300*1e3}}async function loginAnthropic(interaction){let{verifier,challenge}=await generatePKCE(),callback=await startOAuthCallbackServer({providerName:"Anthropic",host:CALLBACK_HOST,port:CALLBACK_PORT,path:CALLBACK_PATH,state:verifier,complete:async code=>code,signal:interaction.signal}).catch(()=>{});try{let authParams=new URLSearchParams({code:"true",client_id:CLIENT_ID,response_type:"code",redirect_uri:REDIRECT_URI,scope:SCOPES,code_challenge:challenge,code_challenge_method:"S256",state:verifier});interaction.notify({type:"auth_url",url:`${AUTHORIZE_URL}?${authParams.toString()}`,instructions:"Complete login in your browser. If the browser is on another machine, paste the final redirect URL here."});let result=await waitForCallbackOrManualInput(interaction,callback,{message:"Complete login in your browser, or paste the authorization code / redirect URL here:",placeholder:REDIRECT_URI}),code,state=verifier;if(result.type==="callback")code=result.value;else{let parsed=parseAuthorizationInput(result.input);if(parsed.state&&parsed.state!==verifier)throw new Error("OAuth state mismatch");code=parsed.code,state=parsed.state??verifier}if(!code)throw new Error("Missing authorization code");return interaction.notify({type:"progress",message:"Exchanging authorization code for tokens..."}),await exchangeAuthorizationCode(code,state,verifier,REDIRECT_URI,interaction.signal)}finally{callback?.close()}}async function loginAnthropicCopyCode(interaction){let{verifier,challenge}=await generatePKCE(),authParams=new URLSearchParams({code:"true",client_id:CLIENT_ID,response_type:"code",redirect_uri:COPY_CODE_REDIRECT_URI,scope:SCOPES,code_challenge:challenge,code_challenge_method:"S256",state:verifier});interaction.notify({type:"auth_url",url:`${AUTHORIZE_URL}?${authParams.toString()}`,instructions:"Complete login in your browser, then copy the code Anthropic shows and paste it here."});let input=await interaction.prompt({type:"manual_code",message:"Paste the code Anthropic shows after you sign in:",placeholder:"code#state",signal:interaction.signal}),parsed=parseAuthorizationInput(input);if(parsed.state&&parsed.state!==verifier)throw new Error("OAuth state mismatch");if(!parsed.code)throw new Error("Missing authorization code");return interaction.notify({type:"progress",message:"Exchanging authorization code for tokens..."}),await exchangeAuthorizationCode(parsed.code,parsed.state??verifier,verifier,COPY_CODE_REDIRECT_URI,interaction.signal)}async function refreshAnthropicToken(refreshToken,signal){let responseBody;try{responseBody=await postJson(TOKEN_URL,{grant_type:"refresh_token",client_id:CLIENT_ID,refresh_token:refreshToken},signal)}catch(error){throw new Error(`Anthropic token refresh request failed. url=${TOKEN_URL}; details=${formatErrorDetails(error)}`)}let data;try{data=JSON.parse(responseBody)}catch(error){throw new Error(`Anthropic token refresh returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`)}return{type:"oauth",refresh:data.refresh_token,access:data.access_token,expires:Date.now()+data.expires_in*1e3-300*1e3}}var anthropicOAuth={name:"Anthropic (Claude Pro/Max)",isSubscription:!0,async login(interaction){let method=await interaction.prompt({type:"select",message:"Select Anthropic login method:",options:[{id:ANTHROPIC_BROWSER_LOGIN_METHOD,label:"Browser login (default)"},{id:ANTHROPIC_COPY_CODE_LOGIN_METHOD,label:"Copy code login (headless)"}]});if(method===ANTHROPIC_COPY_CODE_LOGIN_METHOD)return loginAnthropicCopyCode(interaction);if(method!==ANTHROPIC_BROWSER_LOGIN_METHOD)throw new Error(`Unknown Anthropic login method: ${method}`);return loginAnthropic(interaction)},refresh:(credential,signal)=>refreshAnthropicToken(credential.refresh,signal),async toAuth(credential){return{apiKey:credential.access}}};export{anthropicOAuth};
86
+ </html>`}function oauthSuccessHtml(message){return renderPage({heading:"Signed in",message})}function oauthErrorHtml(message,details){return renderPage({heading:"Sign-in did not complete",message,details})}function sendPage(response,status,html){response.writeHead(status,{"content-type":"text/html; charset=utf-8","cache-control":"no-store"}),response.end(html)}async function startOAuthCallbackServer(options){let{providerName,signal}=options;if(signal?.aborted)throw new Error("Login cancelled");let resolveWait=()=>{},rejectWait=()=>{},waitPromise=new Promise((resolve,reject)=>{resolveWait=resolve,rejectWait=reject});waitPromise.catch(()=>{});let claimed=!1,settled=!1,timer,onAbort=()=>finish({error:new Error("Login cancelled")}),finish=result=>{settled||(settled=!0,timer&&clearTimeout(timer),signal?.removeEventListener("abort",onAbort),"error"in result?rejectWait(result.error):resolveWait(result.value))},server=createServer((request,response)=>{(async()=>{let url=new URL(request.url??"/","http://localhost");if(request.method!=="GET"||url.pathname!==options.path){sendPage(response,404,oauthErrorHtml("Callback route not found."));return}if(options.state!==void 0&&url.searchParams.get("state")!==options.state){sendPage(response,400,oauthErrorHtml("State mismatch."));return}if(claimed||settled){sendPage(response,409,oauthErrorHtml("This sign-in has already been handled."));return}let error=url.searchParams.get("error");if(error){let description=url.searchParams.get("error_description")??error;sendPage(response,400,oauthErrorHtml(`${providerName} authorization failed.`,description)),finish({error:new Error(`${providerName} authorization failed: ${description}`)});return}let code=url.searchParams.get("code");if(!code){sendPage(response,400,oauthErrorHtml("Missing authorization code."));return}claimed=!0;try{let value=await options.complete(code);sendPage(response,200,oauthSuccessHtml(`Signed in to ${providerName}. You may now close this page.`)),finish({value})}catch(error2){let failure=error2 instanceof Error?error2:new Error(String(error2));sendPage(response,502,oauthErrorHtml(`${providerName} sign-in failed.`,failure.message)),finish({error:failure})}})()});await new Promise((resolve,reject)=>{server.once("error",reject),server.listen(options.port,options.host,()=>{server.off("error",reject),resolve()})});let address=server.address();if(!address||typeof address=="string")throw server.close(),new Error("OAuth callback server did not bind to TCP");server.on("error",error=>finish({error})),signal?.addEventListener("abort",onAbort,{once:!0}),options.timeoutMs!==void 0&&(timer=setTimeout(()=>finish({error:new Error(`${providerName} sign-in timed out`)}),options.timeoutMs));let redirectHost=options.redirectHost??options.host;return{redirectUri:`http://${redirectHost.includes(":")?`[${redirectHost}]`:redirectHost}:${address.port}${options.path}`,wait:()=>waitPromise,cancel:()=>{claimed||finish({value:void 0})},close:()=>{finish({error:new Error("OAuth callback server closed")}),server.close()}}}async function waitForCallbackOrManualInput(interaction,callback,prompt){let manualAbort=new AbortController,manualError,manual=interaction.prompt({type:"manual_code",...prompt,signal:manualAbort.signal}).then(input=>(callback?.cancel(),input)).catch(error=>{manualError=error instanceof Error?error:new Error(String(error)),callback?.cancel()});try{let value=await callback?.wait();if(manualError)throw manualError;if(value!==void 0)return{type:"callback",value};let input=await manual;if(manualError)throw manualError;return{type:"manual",input:input??""}}finally{manualAbort.abort()}}function base64urlEncode(bytes){let binary="";for(let byte of bytes)binary+=String.fromCharCode(byte);return btoa(binary).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function generatePKCE(){let verifierBytes=new Uint8Array(32);crypto.getRandomValues(verifierBytes);let verifier=base64urlEncode(verifierBytes),data=new TextEncoder().encode(verifier),hashBuffer=await crypto.subtle.digest("SHA-256",data),challenge=base64urlEncode(new Uint8Array(hashBuffer));return{verifier,challenge}}var decode=s=>atob(s),CLIENT_ID=decode("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl"),AUTHORIZE_URL="https://claude.ai/oauth/authorize",TOKEN_URL="https://platform.claude.com/v1/oauth/token",CALLBACK_HOST=getProviderEnvValue("PI_OAUTH_CALLBACK_HOST")||"127.0.0.1",CALLBACK_PORT=53692,CALLBACK_PATH="/callback",REDIRECT_URI=`http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`,COPY_CODE_REDIRECT_URI="https://platform.claude.com/oauth/code/callback",ANTHROPIC_BROWSER_LOGIN_METHOD="browser",ANTHROPIC_COPY_CODE_LOGIN_METHOD="copy_code",SCOPES="org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";function parseAuthorizationInput(input){let value=input.trim();if(!value)return{};try{let url=new URL(value);return{code:url.searchParams.get("code")??void 0,state:url.searchParams.get("state")??void 0}}catch{}if(value.includes("#")){let[code,state]=value.split("#",2);return{code,state}}if(value.includes("code=")){let params=new URLSearchParams(value);return{code:params.get("code")??void 0,state:params.get("state")??void 0}}return{code:value}}function formatErrorDetails(error){if(error instanceof Error){let details=[`${error.name}: ${error.message}`],errorWithCode=error;return errorWithCode.code&&details.push(`code=${errorWithCode.code}`),typeof errorWithCode.errno<"u"&&details.push(`errno=${String(errorWithCode.errno)}`),typeof error.cause<"u"&&details.push(`cause=${formatErrorDetails(error.cause)}`),error.stack&&details.push(`stack=${error.stack}`),details.join("; ")}return String(error)}async function postJson(url,body,signal){let response=await fetch(url,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(body),signal:AbortSignal.any([signal,AbortSignal.timeout(3e4)])}),responseBody=await response.text();if(!response.ok)throw new Error(`HTTP request failed. status=${response.status}; url=${url}; body=${responseBody}`);return responseBody}async function exchangeAuthorizationCode(code,state,verifier,redirectUri,signal){let responseBody;try{responseBody=await postJson(TOKEN_URL,{grant_type:"authorization_code",client_id:CLIENT_ID,code,state,redirect_uri:redirectUri,code_verifier:verifier},signal)}catch(error){throw new Error(`Token exchange request failed. url=${TOKEN_URL}; redirect_uri=${redirectUri}; response_type=authorization_code; details=${formatErrorDetails(error)}`)}let tokenData;try{tokenData=JSON.parse(responseBody)}catch(error){throw new Error(`Token exchange returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`)}return{type:"oauth",refresh:tokenData.refresh_token,access:tokenData.access_token,expires:Date.now()+tokenData.expires_in*1e3-300*1e3}}async function loginAnthropic(interaction){let{verifier,challenge}=await generatePKCE(),startCallbackServer=port=>startOAuthCallbackServer({providerName:"Anthropic",host:CALLBACK_HOST,port,path:CALLBACK_PATH,redirectHost:"localhost",state:verifier,complete:async code=>code,signal:interaction.signal}),callback=await startCallbackServer(CALLBACK_PORT).catch(()=>startCallbackServer(0)).catch(()=>{}),redirectUri=callback?.redirectUri??REDIRECT_URI;try{let authParams=new URLSearchParams({code:"true",client_id:CLIENT_ID,response_type:"code",redirect_uri:redirectUri,scope:SCOPES,code_challenge:challenge,code_challenge_method:"S256",state:verifier});interaction.notify({type:"auth_url",url:`${AUTHORIZE_URL}?${authParams.toString()}`,instructions:"Complete login in your browser. If the browser is on another machine, paste the final redirect URL here."});let result=await waitForCallbackOrManualInput(interaction,callback,{message:"Complete login in your browser, or paste the authorization code / redirect URL here:",placeholder:redirectUri}),code,state=verifier;if(result.type==="callback")code=result.value;else{let parsed=parseAuthorizationInput(result.input);if(parsed.state&&parsed.state!==verifier)throw new Error("OAuth state mismatch");code=parsed.code,state=parsed.state??verifier}if(!code)throw new Error("Missing authorization code");return interaction.notify({type:"progress",message:"Exchanging authorization code for tokens..."}),await exchangeAuthorizationCode(code,state,verifier,redirectUri,interaction.signal)}finally{callback?.close()}}async function loginAnthropicCopyCode(interaction){let{verifier,challenge}=await generatePKCE(),authParams=new URLSearchParams({code:"true",client_id:CLIENT_ID,response_type:"code",redirect_uri:COPY_CODE_REDIRECT_URI,scope:SCOPES,code_challenge:challenge,code_challenge_method:"S256",state:verifier});interaction.notify({type:"auth_url",url:`${AUTHORIZE_URL}?${authParams.toString()}`,instructions:"Complete login in your browser, then copy the code Anthropic shows and paste it here."});let input=await interaction.prompt({type:"manual_code",message:"Paste the code Anthropic shows after you sign in:",placeholder:"code#state",signal:interaction.signal}),parsed=parseAuthorizationInput(input);if(parsed.state&&parsed.state!==verifier)throw new Error("OAuth state mismatch");if(!parsed.code)throw new Error("Missing authorization code");return interaction.notify({type:"progress",message:"Exchanging authorization code for tokens..."}),await exchangeAuthorizationCode(parsed.code,parsed.state??verifier,verifier,COPY_CODE_REDIRECT_URI,interaction.signal)}async function refreshAnthropicToken(refreshToken,signal){let responseBody;try{responseBody=await postJson(TOKEN_URL,{grant_type:"refresh_token",client_id:CLIENT_ID,refresh_token:refreshToken},signal)}catch(error){throw new Error(`Anthropic token refresh request failed. url=${TOKEN_URL}; details=${formatErrorDetails(error)}`)}let data;try{data=JSON.parse(responseBody)}catch(error){throw new Error(`Anthropic token refresh returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`)}return{type:"oauth",refresh:data.refresh_token,access:data.access_token,expires:Date.now()+data.expires_in*1e3-300*1e3}}var anthropicOAuth={name:"Anthropic (Claude Pro/Max)",isSubscription:!0,async login(interaction){let method=await interaction.prompt({type:"select",message:"Select Anthropic login method:",options:[{id:ANTHROPIC_BROWSER_LOGIN_METHOD,label:"Browser login (default)"},{id:ANTHROPIC_COPY_CODE_LOGIN_METHOD,label:"Copy code login (headless)"}]});if(method===ANTHROPIC_COPY_CODE_LOGIN_METHOD)return loginAnthropicCopyCode(interaction);if(method!==ANTHROPIC_BROWSER_LOGIN_METHOD)throw new Error(`Unknown Anthropic login method: ${method}`);return loginAnthropic(interaction)},refresh:(credential,signal)=>refreshAnthropicToken(credential.refresh,signal),async toAuth(credential){return{apiKey:credential.access}}};export{anthropicOAuth};
@@ -0,0 +1,2 @@
1
+ import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url);
2
+ import{convertResponsesMessages,convertResponsesTools,processResponsesStream}from"./chunk-LMNWTX2L.js";import{resolveAzureConfig,resolveDeploymentName}from"./chunk-TMTOQ7H7.js";import{clampOpenAIPromptCacheKey}from"./chunk-PRRNXB7S.js";import{AzureOpenAI}from"./chunk-CF5GWZHW.js";import{formatProviderError,normalizeProviderError}from"./chunk-TEPMHNKQ.js";import"./chunk-PJACZCIR.js";import{retryProviderRequest}from"./chunk-PMRZO46Y.js";import{buildBaseOptions,createGrammarToolInputProperties,getPiUserAgent,resolveSamplingParams}from"./chunk-R4VPBBDS.js";import"./chunk-L2ZPNLYD.js";import{headersToRecord}from"./chunk-UTB4DTYQ.js";import"./chunk-7DDKXS2Q.js";import"./chunk-3V45WARH.js";import{clampThinkingLevel,getDeclaredTools,resolveTranscript,resolveTranscriptTools}from"./chunk-WCGU75WO.js";import{AssistantMessageEventStream}from"./chunk-LPXNMA2W.js";import"./chunk-JXWJOIH4.js";var AZURE_TOOL_CALL_PROVIDERS=new Set(["openai","openai-codex","opencode","azure"]),OPENAI_RESPONSES_MIN_OUTPUT_TOKENS=16;function formatAzureOpenAIError(error){return formatProviderError(normalizeProviderError(error),"Azure OpenAI API error")}var stream=(model,context,options)=>{let stream2=new AssistantMessageEventStream,normalizedContext=resolveTranscript(context,model.compat?.supportsMidConvoSystemMessages);return(async()=>{let deploymentName=resolveDeploymentName(model,options),output={role:"assistant",content:[],api:"azure-openai-responses",provider:model.provider,model:model.id,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 apiKey=options?.apiKey;if(!apiKey)throw new Error(`No API key for provider: ${model.provider}`);let client=createClient(model,apiKey,options),grammarToolInputProperties=createGrammarToolInputProperties(getDeclaredTools(normalizedContext.messages),model.compat?.supportsOpenAIGrammarTools??!1),params=buildParams(model,normalizedContext,options,deploymentName,grammarToolInputProperties),nextParams=await options?.onPayload?.(params,model);nextParams!==void 0&&(params=nextParams);let requestOptions={...options?.signal?{signal:options.signal}:{},...options?.timeoutMs!==void 0?{timeout:options.timeoutMs}:{},maxRetries:0},{data:openaiStream,response}=await retryProviderRequest(()=>client.responses.create(params,requestOptions).withResponse(),{maxRetries:options?.maxRetries,maxRetryDelayMs:options?.maxRetryDelayMs,signal:options?.signal});if(await options?.onResponse?.({status:response.status,headers:headersToRecord(response.headers)},model),stream2.push({type:"start",partial:output}),await processResponsesStream(openaiStream,output,stream2,model,{onProviderStreamEvent:options?.onProviderStreamEvent,grammarToolInputProperties}),options?.signal?.aborted)throw new Error("Request was aborted");if(output.stopReason==="pending")throw new Error("Azure OpenAI Responses stream ended without a stop reason");if(output.stopReason==="aborted"||output.stopReason==="error")throw new Error(output.errorMessage||"An unknown error occurred");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,delete block.customInput;output.stopReason=options?.signal?.aborted?"aborted":"error",output.errorMessage=formatAzureOpenAIError(error),stream2.push({type:"error",reason:output.stopReason,error:output}),stream2.end()}})(),stream2},streamSimple=(model,context,options)=>{let apiKey=options?.apiKey;if(!apiKey)throw new Error(`No API key for provider: ${model.provider}`);let base={...buildBaseOptions(model,context,options,apiKey),toolChoice:options?.toolChoice},clampedReasoning=options?.reasoning?clampThinkingLevel(model,options.reasoning):void 0;return stream(model,context,{...base,reasoningEffort:clampedReasoning==="off"?void 0:clampedReasoning})};function createClient(model,apiKey,options){let headers={"User-Agent":getPiUserAgent(),...model.headers};options?.headers&&Object.assign(headers,options.headers);let{baseUrl,apiVersion}=resolveAzureConfig(model,options);return new AzureOpenAI({apiKey,apiVersion,dangerouslyAllowBrowser:!0,fetch:options?.fetch,defaultHeaders:headers,baseURL:baseUrl})}function buildParams(model,context,options,deploymentName,grammarToolInputProperties=createGrammarToolInputProperties(getDeclaredTools(context.messages),model.compat?.supportsOpenAIGrammarTools??!1)){let supportsAdditionalTools=model.compat?.supportsAdditionalTools??!1,supportsToolSearch=model.compat?.supportsToolSearch??!1,transcriptTools=resolveTranscriptTools(context.messages,supportsAdditionalTools||supportsToolSearch),messages=convertResponsesMessages(model,context,AZURE_TOOL_CALL_PROVIDERS,{grammarToolInputProperties,supportsMidConvoSystemMessages:model.compat?.supportsMidConvoSystemMessages??!1,supportsAdditionalTools,supportsToolSearch,toolOptions:{supportsStrictMode:model.compat?.supportsStrictMode??!0,supportsOpenAIGrammarTools:model.compat?.supportsOpenAIGrammarTools??!1}}),params={model:deploymentName,input:messages,stream:!0,prompt_cache_key:clampOpenAIPromptCacheKey(options?.sessionId),store:!1};options?.maxTokens&&(params.max_output_tokens=Math.max(options.maxTokens,OPENAI_RESPONSES_MIN_OUTPUT_TOKENS)),options?.temperature!==void 0&&(params.temperature=options?.temperature),transcriptTools.requestTools.length>0&&(params.tools=convertResponsesTools(transcriptTools.requestTools,{supportsStrictMode:model.compat?.supportsStrictMode??!0,supportsOpenAIGrammarTools:model.compat?.supportsOpenAIGrammarTools??!1})),options?.toolChoice!==void 0&&(params.tool_choice=options.toolChoice);let reasoningEffort=options?.reasoningEffort??(options?.reasoningSummary?"medium":void 0);if(model.reasoning)if(reasoningEffort){let effort=options?.reasoningEffort?model.thinkingLevelMap?.[options.reasoningEffort]??options.reasoningEffort:reasoningEffort;params.reasoning={effort,summary:options?.reasoningSummary||"auto"},params.include=["reasoning.encrypted_content"]}else model.thinkingLevelMap?.off!==null&&(params.reasoning={effort:model.thinkingLevelMap?.off??"none"});let samplingParams=resolveSamplingParams(model,reasoningEffort??"off",options?.samplingParams);return samplingParams&&Object.assign(params,samplingParams),params}export{stream,streamSimple};