mu-agent 0.1.7 → 0.1.10

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 (198) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +34 -29
  3. package/dist/bundle/chunks/anthropic-messages-4PAJY4R6.js +6 -0
  4. package/dist/bundle/chunks/anthropic.js +2 -2
  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-TRDNDS6A.js → chunk-3V45WARH.js} +6 -3
  8. package/dist/bundle/chunks/{chunk-XDUWOHPL.js → chunk-5AGIVGSV.js} +3 -3
  9. package/dist/bundle/chunks/chunk-6QGV3IWD.js +6 -0
  10. package/dist/bundle/chunks/chunk-7CBJINYJ.js +20 -0
  11. package/dist/bundle/chunks/{chunk-DRQDKNXA.js → chunk-7DDKXS2Q.js} +1 -1
  12. package/dist/bundle/chunks/chunk-BSS6T6KQ.js +2 -0
  13. package/dist/bundle/chunks/chunk-CF5GWZHW.js +31 -0
  14. package/dist/bundle/chunks/chunk-DNNHZBQR.js +4 -0
  15. package/dist/bundle/chunks/chunk-FN6OEZYR.js +2 -0
  16. package/dist/bundle/chunks/chunk-FTJT2X6E.js +2 -0
  17. package/dist/bundle/chunks/chunk-HZNN636Z.js +130 -0
  18. package/dist/bundle/chunks/{chunk-4L3WN2XY.js → chunk-JXWJOIH4.js} +1 -1
  19. package/dist/bundle/chunks/{chunk-4O2SCVYF.js → chunk-KIDDRTZP.js} +4 -4
  20. package/dist/bundle/chunks/chunk-LMNWTX2L.js +11 -0
  21. package/dist/bundle/chunks/chunk-LPXNMA2W.js +2 -0
  22. package/dist/bundle/chunks/{chunk-6CYCI3EJ.js → chunk-LVVZS2NO.js} +1 -1
  23. package/dist/bundle/chunks/chunk-NN4BEPYX.js +2 -0
  24. package/dist/bundle/chunks/chunk-O7IPTASE.js +1221 -0
  25. package/dist/bundle/chunks/chunk-PMRZO46Y.js +2 -0
  26. package/dist/bundle/chunks/chunk-QOEEJKS4.js +9 -0
  27. package/dist/bundle/chunks/chunk-R4VPBBDS.js +2 -0
  28. package/dist/bundle/chunks/chunk-RAQ6TU76.js +2 -0
  29. package/dist/bundle/chunks/chunk-RDZVAGPR.js +2 -0
  30. package/dist/bundle/chunks/chunk-SGOTYFMT.js +91 -0
  31. package/dist/bundle/chunks/chunk-TMTOQ7H7.js +2 -0
  32. package/dist/bundle/chunks/chunk-UKVNB6C4.js +2 -0
  33. package/dist/bundle/chunks/chunk-VJLOQZKR.js +9 -0
  34. package/dist/bundle/chunks/chunk-WCGU75WO.js +11 -0
  35. package/dist/bundle/chunks/chunk-X234222Q.js +64 -0
  36. package/dist/bundle/chunks/cli-722OL3K5.js +51 -0
  37. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-MEZHQVNP.js +2 -0
  38. package/dist/bundle/chunks/codemode-worker.js +478 -0
  39. package/dist/bundle/chunks/easter-egg-3d-6H34EDGX.js +2 -0
  40. package/dist/bundle/chunks/execute-KVSEIDWL.js +499 -0
  41. package/dist/bundle/chunks/github-copilot.js +1 -1
  42. package/dist/bundle/chunks/{google-generative-ai-C44KRRZ4.js → google-generative-ai-D2VJQBTK.js} +1 -1
  43. package/dist/bundle/chunks/{google-vertex-25DUXPTJ.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-HGQMNYVT.js → lib-4AVRLBHM.js} +2 -2
  49. package/dist/bundle/chunks/llama-cpp-classify-5YHLY3RC.js +22 -0
  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-KCMANYC2.js +16 -0
  54. package/dist/bundle/chunks/openai-chatgpt.js +86 -0
  55. package/dist/bundle/chunks/openai-codex-responses-CECGTDYD.js +10 -0
  56. package/dist/bundle/chunks/openai-codex.js +2 -2
  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-JVEJN44Q.js → openrouter-images-7T452AS5.js} +1 -1
  61. package/dist/bundle/chunks/openrouter.js +2 -2
  62. package/dist/bundle/chunks/{pi-messages-TM5SOOIW.js → pi-messages-2UDKLKOO.js} +1 -1
  63. package/dist/bundle/chunks/radius.js +2 -2
  64. package/dist/bundle/chunks/runtime-O3ELDOM7.js +2 -0
  65. package/dist/bundle/chunks/{src-UK77SYZE.js → src-QKGKDFSU.js} +1 -1
  66. package/dist/bundle/chunks/typesafe-system-one-KNCICMO5.js +2 -0
  67. package/dist/bundle/chunks/virtual-modules-IKEARUOZ.js +2 -0
  68. package/dist/bundle/cli-runtime.js +1 -1
  69. package/dist/bundle/index.js +1 -1
  70. package/dist/bundle/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
  71. package/dist/bundle/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
  72. package/dist/bundle/rpc-entry.js +1 -1
  73. package/dist/core/export-html/template.js +37 -16
  74. package/dist/modes/interactive/theme/dark.json +60 -73
  75. package/dist/modes/interactive/theme/light.json +58 -70
  76. package/dist/modes/interactive/theme/system-theme.d.ts +55 -0
  77. package/dist/modes/interactive/theme/system-theme.d.ts.map +1 -0
  78. package/dist/modes/interactive/theme/system-theme.js +546 -0
  79. package/dist/modes/interactive/theme/system-theme.js.map +1 -0
  80. package/dist/modes/interactive/theme/theme-controller.d.ts +43 -9
  81. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  82. package/dist/modes/interactive/theme/theme-controller.js +121 -79
  83. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  84. package/dist/modes/interactive/theme/theme-schema.d.ts +28 -0
  85. package/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -0
  86. package/dist/modes/interactive/theme/theme-schema.js +105 -0
  87. package/dist/modes/interactive/theme/theme-schema.js.map +1 -0
  88. package/dist/modes/interactive/theme/theme-tokens.d.ts +257 -0
  89. package/dist/modes/interactive/theme/theme-tokens.d.ts.map +1 -0
  90. package/dist/modes/interactive/theme/theme-tokens.js +94 -0
  91. package/dist/modes/interactive/theme/theme-tokens.js.map +1 -0
  92. package/dist/modes/interactive/theme/theme.d.ts +47 -56
  93. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  94. package/dist/modes/interactive/theme/theme.js +149 -143
  95. package/dist/modes/interactive/theme/theme.js.map +1 -1
  96. package/dist/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
  97. package/dist/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
  98. package/docs/cli-integration.md +1 -1
  99. package/docs/cli.md +78 -13
  100. package/docs/codemode.md +230 -0
  101. package/docs/compaction.md +5 -5
  102. package/docs/configuration.md +2 -0
  103. package/docs/custom-provider.md +53 -1
  104. package/docs/docs.json +21 -1
  105. package/docs/environment-variables.md +5 -3
  106. package/docs/extensions.md +172 -10
  107. package/docs/index.md +1 -1
  108. package/docs/json.md +3 -3
  109. package/docs/keybindings.md +7 -6
  110. package/docs/llama-cpp.md +21 -1
  111. package/docs/mcp.md +272 -0
  112. package/docs/message-types.md +22 -4
  113. package/docs/models-configuration.md +187 -0
  114. package/docs/models.md +100 -2
  115. package/docs/nvidia-rtx-llama-cpp.md +96 -0
  116. package/docs/packages.md +1 -1
  117. package/docs/providers.md +66 -6
  118. package/docs/quickstart.md +16 -2
  119. package/docs/rpc-commands.md +9 -3
  120. package/docs/rpc.md +3 -1
  121. package/docs/sdk.md +8 -3
  122. package/docs/security.md +2 -0
  123. package/docs/session-format.md +5 -1
  124. package/docs/sessions.md +1 -1
  125. package/docs/settings.md +30 -8
  126. package/docs/terminal-setup.md +14 -0
  127. package/docs/themes.md +31 -7
  128. package/docs/tui.md +71 -1
  129. package/docs/usage.md +2 -2
  130. package/docs/virtual-models.md +114 -0
  131. package/docs/windows.md +2 -0
  132. package/examples/README.md +1 -4
  133. package/examples/extensions/README.md +1 -0
  134. package/examples/extensions/built-in-tool-renderer.ts +16 -40
  135. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  136. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  137. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  138. package/examples/extensions/custom-provider-gitlab-duo/test.ts +5 -5
  139. package/examples/extensions/dynamic-resources/dynamic.json +1 -1
  140. package/examples/extensions/gondolin/package-lock.json +2 -2
  141. package/examples/extensions/gondolin/package.json +1 -1
  142. package/examples/extensions/jev-router.ts +113 -0
  143. package/examples/extensions/minimal-mode.ts +28 -101
  144. package/examples/extensions/overlay-test.ts +2 -2
  145. package/examples/extensions/plan-mode/index.ts +6 -2
  146. package/examples/extensions/sandbox/package-lock.json +2 -2
  147. package/examples/extensions/sandbox/package.json +1 -1
  148. package/examples/extensions/subagent/index.ts +4 -6
  149. package/examples/extensions/with-deps/package-lock.json +2 -2
  150. package/examples/extensions/with-deps/package.json +1 -1
  151. package/examples/rpc-client.ts +1 -1
  152. package/examples/rpc-extension-ui.ts +1 -1
  153. package/examples/sdk/14-codemode-mcp.ts +61 -0
  154. package/examples/sdk/README.md +2 -1
  155. package/judge/THIRD_PARTY_NOTICES.md +63 -0
  156. package/judge/dist/auth.js +169 -123
  157. package/judge/dist/import.js +36 -30
  158. package/judge/dist/setup.js +250 -120
  159. package/judge/manifest.json +1008 -316
  160. package/judge/{dist/kyrn-judge.js → mu/index.js} +6933 -5995
  161. package/judge/themes/mu-dark.json +80 -0
  162. package/judge/themes/mu-light.json +80 -0
  163. package/kyrn/bin/kyrn-doctor +75 -25
  164. package/kyrn/bin/kyrn-judge-local +2 -2
  165. package/kyrn/bin/kyrn-ledger +1 -1
  166. package/kyrn/bin/mu.mjs +149 -107
  167. package/package.json +4 -5
  168. package/dist/bundle/chunks/anthropic-messages-VMESUHQ7.js +0 -6
  169. package/dist/bundle/chunks/azure-openai-responses-K232MQFF.js +0 -2
  170. package/dist/bundle/chunks/chunk-A3JYRWB6.js +0 -2
  171. package/dist/bundle/chunks/chunk-AYFRR3OD.js +0 -2
  172. package/dist/bundle/chunks/chunk-DTD7JQ7Y.js +0 -42
  173. package/dist/bundle/chunks/chunk-FU4XQUSL.js +0 -11
  174. package/dist/bundle/chunks/chunk-GNYZK6XN.js +0 -2
  175. package/dist/bundle/chunks/chunk-HEMXN7VJ.js +0 -1653
  176. package/dist/bundle/chunks/chunk-MVPXOVOJ.js +0 -2
  177. package/dist/bundle/chunks/chunk-NUHFSC37.js +0 -25
  178. package/dist/bundle/chunks/chunk-V7IXECST.js +0 -11
  179. package/dist/bundle/chunks/chunk-XNGRGP62.js +0 -2
  180. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-NR2TDCXU.js +0 -2
  181. package/dist/bundle/chunks/https-proxy-agent-2VXB7436.js +0 -13
  182. package/dist/bundle/chunks/mistral-conversations-6IHQOR4R.js +0 -5
  183. package/dist/bundle/chunks/node-O3RIBJRH.js +0 -16
  184. package/dist/bundle/chunks/openai-codex-responses-3UAYOSSR.js +0 -10
  185. package/dist/bundle/chunks/openai-completions-FIEQGWAH.js +0 -7
  186. package/dist/bundle/chunks/openai-responses-OZJXLHFC.js +0 -2
  187. package/dist/bundle/chunks/typesafe-system-one-VKYX4OGY.js +0 -2
  188. package/dist/bundle/chunks/virtual-modules-V7SXVVLP.js +0 -2
  189. package/dist/modes/interactive/theme/theme-json.d.ts +0 -85
  190. package/dist/modes/interactive/theme/theme-json.d.ts.map +0 -1
  191. package/dist/modes/interactive/theme/theme-json.js +0 -132
  192. package/dist/modes/interactive/theme/theme-json.js.map +0 -1
  193. package/dist/modes/interactive/theme/theme-schema.json +0 -361
  194. package/examples/plugins/pi-example-plugin/README.md +0 -38
  195. package/examples/plugins/pi-example-plugin/package.json +0 -10
  196. package/examples/plugins/pi-example-plugin/src/contract.ts +0 -13
  197. package/examples/plugins/pi-example-plugin/src/session.ts +0 -24
  198. package/examples/plugins/pi-example-plugin/src/tui.ts +0 -31
@@ -0,0 +1,230 @@
1
+ # Codemode
2
+
3
+ The `codemode` tool lets the model write a JavaScript script that calls pi's other tools and runs non-LLM models, such as classifiers and image models. Only the script's output reaches the model, so a script can run calls in parallel and filter large results before the model sees them. To turn it on, see [Enable codemode](cli.md#enable-codemode).
4
+
5
+ ## Scripts
6
+
7
+ The tool input is raw JavaScript source, not JSON and not a markdown code fence. It runs as the body of an async function in a QuickJS sandbox, so top-level `await` and `return` work. The sandbox has no Node APIs, file system, network, or timers; scripts reach the outside world only through tools and `models`.
8
+
9
+ A script may start with an options line. It must be the first line; an options line anywhere else fails the call before the script runs:
10
+
11
+ ```js
12
+ // @options: {"max_output_tokens": 2000, "timeout_ms": 60000}
13
+ ```
14
+
15
+ - `max_output_tokens` (default 10000) limits the output. Longer output keeps its start and end, and the full text is written to a temp file whose path is included in the result. A script fails when its output passes 16777216 characters of text and base64 image data or 100000 `text()`, `image()`, and `console` calls; write large data to a file with a tool instead.
16
+ - `timeout_ms` is a hard deadline for the whole script. It is unset by default. Image generation can take minutes, so do not set a short deadline for scripts that generate images.
17
+
18
+ The result starts with `Script completed` or `Script failed`, the wall time, and the output. Text and image items appear in order, each on its own line. When the output has more than one text item (from `text()` or `return`), each starts with a `==> text N/M <==` line. `console` calls follow in one `<console_output>` block with one line per call. A failed script keeps its partial output, followed by `Script error:` and the error. Tool calls are real: calls made before a failure are not undone. Calls still running when the script ends are cancelled, and unawaited promises are discarded.
19
+
20
+ ## Globals
21
+
22
+ | Global | Purpose |
23
+ |---|---|
24
+ | `tools.<name>(args)` | Call a tool. See [Call tools](#call-tools). |
25
+ | `text(value)` | Add a text item to the output. Strings are added as is, other values as JSON. |
26
+ | `image(value)` | Add an image to the output: a base64 `data:` URL, an `{ image_url }` object, or an image block `{ type: "image", data, mimeType }` such as those returned by MCP tools and `models.generateImages()`. Remote URLs are not supported. PNG, JPEG, GIF, and WebP are accepted. Each image is also saved to a temp file, and the result names the path before the image. |
27
+ | `console.log(...)` | Add a line to the `<console_output>` block after the other output. Arguments are joined with spaces; `info`, `warn`, `error`, and `debug` do the same. |
28
+ | `return value` | A top-level `return` adds the value like `text()`. |
29
+ | `exit()` | End the script successfully. |
30
+ | `store(key, value)` / `load(key)` | Keep small JSON values across `codemode` calls. See [Store values](#store-values). |
31
+ | `ALL_TOOLS` | Every callable tool as `{ name, description }`, including tools the description does not list. |
32
+ | `searchTools(query, { limit?, namespace? })` | Rank callable tools by relevance (BM25, default limit 8). Resolves to `{ name, description }[]`. |
33
+ | `describeTool(name)` | Resolves to a tool's description and TypeScript declaration, or `undefined`. |
34
+ | `describeNamespace(name)` | Resolves to `{ name, description?, instructions?, tools }` for a namespace such as an MCP server, or `undefined`. |
35
+ | `models` | List and run non-LLM models. See [Models](#models). |
36
+
37
+ ## Call tools
38
+
39
+ Every tool the session can call is a method of `tools`, named by its identifier: characters that are not valid in a JavaScript identifier become `_`, so the MCP tool `mcp__dev-radius__search` is `tools.mcp__dev_radius__search`. Each method takes one object with the tool's arguments.
40
+
41
+ What a call resolves to depends on the tool:
42
+
43
+ - Tools with an output schema resolve to a structured value. `bash` resolves to `{ output, truncated, full_output_path?, exit_code, wall_time_seconds }`, also for non-zero exit codes. Its `output` is not limited to the 2000 lines or 50KB the model sees: it holds up to 1 MiB, and longer output keeps its first and last 512 KiB around an omission marker, with `truncated` set and the full output in `full_output_path`.
44
+ - MCP tools resolve to their `CallToolResult`, including `isError` and `structuredContent`.
45
+ - `read` resolves to the file's text, or for an image to an image block `{ type: "image", data, mimeType, note }` that `image()` shows. `data` is the base64 image the model would see and `note` the text that goes with it, such as resize hints.
46
+ - Other tools, such as `edit` and `write`, resolve to their text output.
47
+
48
+ A call that fails, is blocked, or gets invalid arguments rejects with an `Error` that carries the tool's error text. Use `Promise.allSettled()` to keep the results of the calls that succeed.
49
+
50
+ The `codemode` description lists tools with their TypeScript declarations, grouped by namespace (for example one MCP server). Tools with `deferred` exposure, which includes MCP tools with the default `codemode` exposure, are not listed, so the description stays the same while MCP servers connect. Listed declarations share a budget of 3000 estimated tokens (`codemode.inlineBudget` in [settings](settings.md#tools)). Scripts find the other tools with `searchTools()`, `describeTool()`, `describeNamespace()`, or by filtering `ALL_TOOLS`.
51
+
52
+ While `codemode` is active, `codemode.mode` in [settings](settings.md#tools) decides how the other tools are presented. With `on` (default) declared tools stay declared, and their descriptions say how to call them from scripts. With `only` they are hidden from the model and listed in the `codemode` description instead, so the model calls them through scripts. Tool declarations in the `codemode` description, `describeTool()`, and `ALL_TOOLS` carry the tools' prompt guidelines, since the system prompt rules only cover declared tools.
53
+
54
+ ## Store values
55
+
56
+ `store(key, value)` keeps a JSON value under a string key for later `codemode` calls; storing `undefined` deletes the key. `load(key)` returns the value, or `undefined`. Writes are kept only when the script succeeds: each successful script that stores values appends a `codemode-store` custom entry to the session, so resumed sessions keep the values and each branch sees only the values written on its path.
57
+
58
+ The store is for small state such as IDs, cursors, or summaries. One value may have at most 262144 characters of JSON and all values together at most 1048576. Do not store image data; show images with `image()`, which also saves them to a temp file.
59
+
60
+ ## Models
61
+
62
+ `models` reaches the model catalog and runs non-LLM models with the session's credentials: classifiers, which answer typed questions about JSON state and, for some models, images, and image models, which generate images. Chat models are listed but cannot be run from scripts. Which classifier and image models exist is described in [Use classifier models](models.md#use-classifier-models) and [Use image models](models.md#use-image-models).
63
+
64
+ ```ts
65
+ type ModelType = "chat" | "image" | "classifier";
66
+
67
+ /** A catalog entry. `provider` and `id` identify it; other fields depend on the type. */
68
+ interface ModelInfo {
69
+ type?: ModelType;
70
+ provider: string;
71
+ id: string;
72
+ name: string;
73
+ api: string;
74
+ input: ("text" | "image")[];
75
+ contextWindow?: number;
76
+ [key: string]: unknown;
77
+ }
78
+
79
+ declare const models: {
80
+ /** Every known model of a type, optionally for one provider. */
81
+ getModelsOfType(type: ModelType, provider?: string): Promise<ModelInfo[]>;
82
+ /** Models of a type whose provider has working credentials. */
83
+ getAvailableOfType(type: ModelType, provider?: string): Promise<ModelInfo[]>;
84
+ /** One catalog entry, or undefined. */
85
+ getModelOfType(type: ModelType, provider: string, id: string): Promise<ModelInfo | undefined>;
86
+ /** Answer `context.questions` about `context.state`; answers are in `result.answers` by question ID. */
87
+ classify(model: ModelInfo, context: ClassifierContext): Promise<ClassifierResult>;
88
+ /** Generate images from `context.input` text and image blocks; show `result.output` blocks with image(). Can take minutes. */
89
+ generateImages(model: ModelInfo, context: ImagesContext): Promise<ImagesResult>;
90
+ };
91
+ ```
92
+
93
+ `classify()` and `generateImages()` use only the `provider` and `id` of `model`, so `{ provider, id }` works as well. They do not throw on provider errors: check `stopReason` and `errorMessage`. At most four such calls run at once per script; more calls wait for a free slot, so `Promise.all()` over many items is fine. Their usage is added to the `codemode` tool result and counts toward the session cost.
94
+
95
+ Model IDs differ between providers, for example `typesafe/jev-latest` and `openrouter/typesafe/jev-1.13`. Use `models.getAvailableOfType(type)` to find the IDs that work with the current credentials.
96
+
97
+ ### Classify
98
+
99
+ ```ts
100
+ interface ClassifierContext {
101
+ /** The data to classify. */
102
+ state: Record<string, unknown>;
103
+ /** Images judged together with `state`. Only models whose `input` includes "image" accept them. */
104
+ images?: { type: "image"; data: string; mimeType: string }[];
105
+ /** Questions by ID. One call answers all of them. */
106
+ questions: Record<string, ClassifierQuestion>;
107
+ }
108
+
109
+ type ClassifierQuestion =
110
+ /** Pick one label. `criteria` maps each label to what it means. */
111
+ | { type: "choice"; instructions: string; criteria: Record<string, string> }
112
+ /** Score on an ordered scale. `criteria` describes each level, lowest first. */
113
+ | { type: "score"; instructions: string; criteria: string[] }
114
+ /** Yes or no. */
115
+ | { type: "bool"; instructions: string; criteria: { true: string; false: string } };
116
+
117
+ interface ClassifierResult {
118
+ provider: string;
119
+ model: string;
120
+ /** Answers by question ID. */
121
+ answers: Record<string, ClassifierAnswer>;
122
+ usage?: ModelUsage;
123
+ stopReason: "stop" | "error" | "aborted";
124
+ errorMessage?: string;
125
+ }
126
+
127
+ type ClassifierAnswer =
128
+ | { type: "choice"; choice: string; probabilities: Record<string, number>; confidence: number }
129
+ /** `score` is the expected level index, from 0 to `criteria.length - 1`. */
130
+ | { type: "score"; score: number; confidence: number }
131
+ /** Probability of `true`. */
132
+ | { type: "bool"; probability: number };
133
+
134
+ /** Token counts and cost in USD, when the service reports them. */
135
+ type ModelUsage = { input: number; output: number; totalTokens: number; cost: { total: number } };
136
+ ```
137
+
138
+ Classify several items by calling `classify()` once per item. This script sorts feedback messages, for example ones a tool returned earlier in the script:
139
+
140
+ ```js
141
+ const jev = await models.getModelOfType("classifier", "typesafe", "jev-latest");
142
+ const results = await Promise.all(
143
+ messages.map((message) =>
144
+ models.classify(jev, {
145
+ state: { message },
146
+ questions: {
147
+ sentiment: {
148
+ type: "choice",
149
+ instructions: "How does the user feel about the product?",
150
+ criteria: { positive: "Satisfied or happy", negative: "Unhappy or frustrated", neutral: "Neither" },
151
+ },
152
+ urgency: {
153
+ type: "score",
154
+ instructions: "How urgently does this need a reply?",
155
+ criteria: ["no reply needed", "reply this week", "reply today"],
156
+ },
157
+ },
158
+ }),
159
+ ),
160
+ );
161
+ return results.map((result, i) =>
162
+ result.stopReason === "stop"
163
+ ? { message: messages[i], sentiment: result.answers.sentiment.choice, urgency: result.answers.urgency.score }
164
+ : { message: messages[i], error: result.errorMessage },
165
+ );
166
+ ```
167
+
168
+ Classifiers whose `input` includes `"image"` also judge images. `tools.read()` returns an image file as an image block that `images` accepts. Other classifiers return an error result when `images` is not empty.
169
+
170
+ ```js
171
+ const luna = await models.getModelOfType("classifier", "openai", "gpt-6-luna");
172
+ const photo = await tools.read({ path: "screenshot.png" });
173
+ const result = await models.classify(luna, {
174
+ state: { task: "Settings page redesign" },
175
+ images: [photo],
176
+ questions: {
177
+ broken: {
178
+ type: "bool",
179
+ instructions: "Does the screenshot show a broken layout?",
180
+ criteria: { true: "Overlapping, cut-off, or misaligned elements", false: "Clean layout" },
181
+ },
182
+ },
183
+ });
184
+ ```
185
+
186
+ ### Generate images
187
+
188
+ ```ts
189
+ interface ImagesContext {
190
+ /** The prompt as text blocks, plus image blocks to edit or use as references. */
191
+ input: (TextBlock | ImageBlock)[];
192
+ }
193
+
194
+ interface ImagesResult {
195
+ provider: string;
196
+ model: string;
197
+ /** Generated images, and text blocks for models that also return text. */
198
+ output: (TextBlock | ImageBlock)[];
199
+ usage?: ModelUsage;
200
+ stopReason: "stop" | "error" | "aborted";
201
+ errorMessage?: string;
202
+ }
203
+
204
+ type TextBlock = { type: "text"; text: string };
205
+ /** `data` is base64. */
206
+ type ImageBlock = { type: "image"; data: string; mimeType: string };
207
+ ```
208
+
209
+ Show generated images with `image(block)`. Do not print `data` with `text()`, `console`, or `return`: it is large and the model cannot read it as text. `image()` also saves each image to a temp file and puts its path in the result, so a later turn can copy or move the file.
210
+
211
+ ```js
212
+ // @options: {"timeout_ms": 300000}
213
+ const painter = await models.getModelOfType("image", "openrouter", "google/gemini-2.5-flash-image");
214
+ const result = await models.generateImages(painter, {
215
+ input: [{ type: "text", text: "A red fox in the snow, watercolor" }],
216
+ });
217
+ if (result.stopReason !== "stop") return result.errorMessage;
218
+ for (const block of result.output) {
219
+ if (block.type === "image") image(block);
220
+ else text(block.text);
221
+ }
222
+ ```
223
+
224
+ ## Limits
225
+
226
+ - A script's VM has 256 MB of memory. Running out throws `InternalError: out of memory`; filter or aggregate large data instead of accumulating it.
227
+ - On Bun, stopping a script or reaching its deadline ends the call promptly, but a built-in that does not poll QuickJS's interrupt handler can keep computing in its worker until it returns.
228
+ - At most two codemode workers may be alive across all sessions in one pi process. A cancelled worker occupies its slot until it exits. At capacity, new scripts fail immediately without starting another worker; retry after an existing worker exits.
229
+ - A script that waits on a promise that can never settle (no tool call pending) fails immediately, since there are no timers.
230
+ - Scripts cannot start other `codemode` scripts.
@@ -32,7 +32,7 @@ Auto-compaction triggers when:
32
32
  contextTokens > contextWindow - reserveTokens
33
33
  ```
34
34
 
35
- By default, `reserveTokens` is 16384 tokens (configurable in `~/.pi/agent/settings.json` or `<project-dir>/.pi/settings.json`). This leaves room for the LLM's response.
35
+ By default, `reserveTokens` is 16384 tokens (configurable in `~/.pi/agent/settings.json` or `<project-dir>/.pi/settings.json`). It only decides when compaction starts. Summary requests use the model's output limit, reduced when needed so input and output fit the context window.
36
36
 
37
37
  During a multi-turn agent run, Pi checks the canonical projected context after tools finish and their results are appended, before starting the next assistant response. If the threshold is crossed, Pi compacts during `prepareNextTurn`, then performs the existing catch-up steering poll before `turn_start`. It skips this between-turn check when the completed tool batch terminates the run and no queued message requires another response. Pi also checks before a new user prompt and performs final-attempt overflow recovery after the low-level run ends.
38
38
 
@@ -279,7 +279,6 @@ Before summarization, messages are serialized to text via [`serializeConversatio
279
279
 
280
280
  ```
281
281
  [User]: What they said
282
- [Assistant thinking]: Internal reasoning
283
282
  [Assistant]: Response text
284
283
  [Assistant tool calls]: read(path="foo.ts"); edit(path="bar.ts", ...)
285
284
  [Tool result]: Output from tool
@@ -287,6 +286,8 @@ Before summarization, messages are serialized to text via [`serializeConversatio
287
286
 
288
287
  This prevents the model from treating it as a conversation to continue.
289
288
 
289
+ Thinking blocks are omitted. Providers return summaries of the real reasoning, and replayed thinking is often not part of the model's live context, so including it could make the summarization request larger than the context it summarizes.
290
+
290
291
  Tool results are truncated to 2000 characters during serialization. Content beyond that limit is replaced with a marker indicating how many characters were truncated. This keeps summarization requests within reasonable token budgets, since tool results (especially from `read` and `bash`) are typically the largest contributors to context size.
291
292
 
292
293
  ## Custom Summarization via Extensions
@@ -346,7 +347,6 @@ pi.on("session_before_compact", async (event, ctx) => {
346
347
  );
347
348
  // Returns:
348
349
  // [User]: message text
349
- // [Assistant thinking]: thinking content
350
350
  // [Assistant]: response text
351
351
  // [Assistant tool calls]: read(path="..."); bash(command="...")
352
352
  // [Tool result]: output text
@@ -431,7 +431,7 @@ Configure compaction in `~/.pi/agent/settings.json` or `<project-dir>/.pi/settin
431
431
  | Setting | Default | Description |
432
432
  |---------|---------|-------------|
433
433
  | `enabled` | `true` | Enable auto-compaction |
434
- | `reserveTokens` | `16384` | Tokens to reserve for LLM response |
434
+ | `reserveTokens` | `16384` | Free context tokens below which auto-compaction starts |
435
435
  | `keepRecentTokens` | `20000` | Recent tokens to keep (not summarized) |
436
436
 
437
437
  Disable auto-compaction with `"enabled": false`. You can still compact manually with `/compact`.
@@ -454,7 +454,7 @@ Use `compaction.modelOverrides` to tune token budgets for different models:
454
454
  }
455
455
  ```
456
456
 
457
- For a model with a 1M context window, this override triggers compaction above 600K tokens and keeps the ordinary 20000 recent tokens. Other models retain the ordinary 16384-token reserve. `reserveTokens` also influences summarization output limits, capped by the model's maximum output tokens; it is not solely a trigger threshold.
457
+ For a model with a 1M context window, this override triggers compaction above 600K tokens and keeps the ordinary 20000 recent tokens. Other models retain the ordinary 16384-token reserve.
458
458
 
459
459
  Keys are exact, case-sensitive `provider/modelId` values, including any slashes within the model ID. Each `reserveTokens` and `keepRecentTokens` value falls back independently from the model override to the ordinary setting to the built-in default. Values must be non-negative safe integers. Invalid values in the matching model override produce an error when read; only omitted fields fall back to the ordinary setting. Model override entries must be objects. Invalid ordinary token settings produce an error when read, even if the active model has a valid override. Only omitted ordinary values use built-in defaults. `enabled` remains global, not model-specific.
460
460
 
@@ -12,6 +12,7 @@ The agent directory is shown as `<agent-dir>` below. Set its location with the `
12
12
  |---|---|
13
13
  | `<agent-dir>/settings.json` | User-level [settings](settings.md), including preferences, defaults, resource paths, and Pi package declarations. |
14
14
  | `<agent-dir>/keybindings.json` | Custom terminal UI and application [keybindings](keybindings.md). |
15
+ | `<agent-dir>/mcp.json` | [MCP servers](mcp.md) available in every project. |
15
16
  | `<agent-dir>/models.json` | [Compatible endpoints, models, and model overrides](models.md#configure-a-compatible-endpoint). |
16
17
  | `<agent-dir>/auth.json` | Saved API keys and OAuth credentials. |
17
18
  | `<agent-dir>/AGENTS.override.md`, `AGENTS.md`, `AGENTS.MD`, `CLAUDE.md`, or `CLAUDE.MD` | User instructions applied across working directories. |
@@ -27,6 +28,7 @@ The agent directory is shown as `<agent-dir>` below. Set its location with the `
27
28
  | Path | Responsibility |
28
29
  |---|---|
29
30
  | `.pi/settings.json` | Project-level [settings](settings.md), resource paths, and Pi package declarations. |
31
+ | `.pi/mcp.json` | Project [MCP servers](mcp.md). |
30
32
  | `.pi/SYSTEM.md` | Replaces the system prompt for the project. |
31
33
  | `.pi/APPEND_SYSTEM.md` | Adds project-specific instructions to the system prompt. |
32
34
  | `.pi/extensions/` | Project extensions. |
@@ -29,7 +29,59 @@ Prefer a complete provider for new integrations that own more than static endpoi
29
29
 
30
30
  Registering only `baseUrl` or `headers` for an existing provider preserves its built-in models. Supplying `models` in the legacy form replaces that provider's models across chat, image, and classifier operations. An omitted `type` means `"chat"`; image and classifier models require explicit discriminants and implementations keyed by their `api` values through the `images` and `classifiers` fields.
31
31
 
32
- For example, a mixed-operation provider can register non-chat models and their implementations together:
32
+ ### Redirect an existing provider
33
+
34
+ Register only the fields that should change. This keeps the provider's built-in catalog, authentication, API implementation, and compatibility settings:
35
+
36
+ ```typescript
37
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
38
+
39
+ export default function (pi: ExtensionAPI) {
40
+ pi.registerProvider("anthropic", {
41
+ baseUrl: "https://gateway.example.com/anthropic",
42
+ headers: {
43
+ "X-Gateway-Key": "$GATEWAY_API_KEY",
44
+ },
45
+ });
46
+ }
47
+ ```
48
+
49
+ Use this form for a proxy or gateway that preserves the upstream protocol. Do not copy the built-in model list into the extension because doing so turns a small endpoint override into a catalog replacement that can drift.
50
+
51
+ ### Register a compatible endpoint
52
+
53
+ When a service implements a supported protocol but has its own model catalog, register the endpoint and only the models the service actually exposes:
54
+
55
+ ```typescript
56
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
57
+
58
+ export default function (pi: ExtensionAPI) {
59
+ pi.registerProvider("acme", {
60
+ name: "Acme AI",
61
+ baseUrl: "https://api.acme.example/v1",
62
+ apiKey: "$ACME_API_KEY",
63
+ api: "openai-completions",
64
+ models: [
65
+ {
66
+ type: "chat",
67
+ id: "acme-chat",
68
+ name: "Acme Chat",
69
+ reasoning: false,
70
+ input: ["text"],
71
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
72
+ contextWindow: 128000,
73
+ maxTokens: 8192,
74
+ },
75
+ ],
76
+ });
77
+ }
78
+ ```
79
+
80
+ Prefer `models.json` for this case when no executable extension behavior is needed. Use an extension when registration depends on runtime discovery, custom authentication, or code shared with other extension features.
81
+
82
+ ### Register mixed-operation models
83
+
84
+ A mixed-operation provider can register non-chat models and their implementations together:
33
85
 
34
86
  ```typescript
35
87
  pi.registerProvider("media-tools", {
package/docs/docs.json CHANGED
@@ -47,6 +47,10 @@
47
47
  "title": "Run Local Models",
48
48
  "path": "llama-cpp.md"
49
49
  },
50
+ {
51
+ "title": "Run Local Models on NVIDIA RTX",
52
+ "path": "nvidia-rtx-llama-cpp.md"
53
+ },
50
54
  {
51
55
  "title": "Configure Your Terminal",
52
56
  "path": "terminal-setup.md"
@@ -91,6 +95,10 @@
91
95
  {
92
96
  "title": "Use Pi Packages",
93
97
  "path": "packages.md"
98
+ },
99
+ {
100
+ "title": "Connect MCP Servers",
101
+ "path": "mcp.md"
94
102
  }
95
103
  ]
96
104
  },
@@ -105,6 +113,10 @@
105
113
  "title": "Add Custom Providers",
106
114
  "path": "custom-provider.md"
107
115
  },
116
+ {
117
+ "title": "Route with Virtual Models",
118
+ "path": "virtual-models.md"
119
+ },
108
120
  {
109
121
  "title": "Build Terminal UI Components",
110
122
  "path": "tui.md"
@@ -128,6 +140,10 @@
128
140
  "title": "CLI",
129
141
  "path": "cli.md"
130
142
  },
143
+ {
144
+ "title": "Codemode",
145
+ "path": "codemode.md"
146
+ },
131
147
  {
132
148
  "title": "Slash Commands",
133
149
  "path": "slash-commands.md"
@@ -136,6 +152,10 @@
136
152
  "title": "Settings",
137
153
  "path": "settings.md"
138
154
  },
155
+ {
156
+ "title": "Models Configuration",
157
+ "path": "models-configuration.md"
158
+ },
139
159
  {
140
160
  "title": "Environment Variables",
141
161
  "path": "environment-variables.md"
@@ -145,7 +165,7 @@
145
165
  "path": "keybindings.md"
146
166
  },
147
167
  {
148
- "title": "Provider Authentication",
168
+ "title": "Providers",
149
169
  "path": "providers.md"
150
170
  },
151
171
  {
@@ -6,7 +6,7 @@ Pi uses environment variables in three ways:
6
6
  - Pi sets process markers so child processes can identify Pi as the launching agent.
7
7
  - Commands run by the LLM-callable shell tools receive `PI_*` variables describing the current session.
8
8
 
9
- Provider API-key variables are documented separately in [Provider Authentication](providers.md#use-an-api-key-from-the-environment).
9
+ Provider API-key variables are documented separately in [Providers](providers.md#use-an-api-key-from-the-environment).
10
10
 
11
11
  ## Process Marker
12
12
 
@@ -86,13 +86,15 @@ These variables are read by Pi itself:
86
86
  | `PI_TELEMETRY` | Override install/update telemetry and provider attribution headers: `1`/`true`/`yes` or `0`/`false`/`no` |
87
87
  | `PI_CACHE_RETENTION` | Set to `long` for extended provider prompt caching where supported |
88
88
  | `PI_SHARE_VIEWER_URL` | Override the base URL used by `/share` |
89
- | `PI_RADIUS_GATEWAY` | Override the Radius gateway origin used by `/bug` uploads and Radius relay connections |
89
+ | `PI_RADIUS_GATEWAY` | Override the Radius gateway origin used by `/bug` uploads |
90
90
  | `PI_HARDWARE_CURSOR` | Set to `1` to show the hardware cursor; see [Terminal setup](terminal-setup.md) |
91
+ | `PI_CLIPBOARD_OSC52` | Set to `1` to also send copied text to the terminal with OSC 52 after the local clipboard write, as Pi does over SSH. Use it when a remote client reaches Pi without SSH variables, such as a multiplexer attached from another machine or a remote IDE daemon |
91
92
  | `PI_HYPERLINKS` | Override OSC 8 hyperlink detection with `1`, `0`, or `auto` |
93
+ | `PI_PROGRAM_STATUS` | Override OSC 7501 program status detection: `1` always reports, `0` never reports; otherwise Pi reports only after the terminal confirms support. See [Terminal setup](terminal-setup.md#program-status) |
92
94
  | `PI_IMAGE_PROTOCOL` | Override inline image detection with `kitty`, `iterm2`, `none`, or `auto` |
93
95
  | `PI_TRUE_COLOR` | Override truecolor detection with `1`, `0`, or `auto` |
94
96
  | `PI_TUI_ESC_TIMEOUT` | How long to wait after a lone ESC before treating it as Escape, in milliseconds; defaults to `100` over SSH and `10` otherwise. Increase if Alt-key input is misread as Escape |
95
97
  | `VISUAL`, `EDITOR` | External editor fallback when `externalEditor` is unset |
96
98
  | `HTTP_PROXY`, `HTTPS_PROXY` | Proxy outbound HTTP requests |
97
99
 
98
- Provider credentials such as `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and cloud-provider configuration are listed in [Provider Authentication](providers.md#use-an-api-key-from-the-environment).
100
+ Provider credentials such as `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and provider-specific configuration are listed in [Providers](providers.md#use-an-api-key-from-the-environment).