mu-agent 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README.md +5 -4
- package/dist/bundle/chunks/anthropic-messages-MI4QHRXW.js +6 -0
- package/dist/bundle/chunks/anthropic.js +2 -2
- package/dist/bundle/chunks/azure-openai-responses-E3QGN4VN.js +2 -0
- package/dist/bundle/chunks/bedrock-converse-stream.js +2 -2
- package/dist/bundle/chunks/chunk-33KL6IIQ.js +20 -0
- package/dist/bundle/chunks/chunk-4OBXYPH4.js +4 -0
- package/dist/bundle/chunks/chunk-C47CLQZK.js +2 -0
- package/dist/bundle/chunks/chunk-F5ZABL6Z.js +20 -0
- package/dist/bundle/chunks/chunk-H3QXYIF7.js +31 -0
- package/dist/bundle/chunks/chunk-HQHD6PZI.js +2 -0
- package/dist/bundle/chunks/chunk-KJUDFE6Y.js +11 -0
- package/dist/bundle/chunks/chunk-LD2P6SFZ.js +2 -0
- package/dist/bundle/chunks/chunk-NG4PO5FP.js +1204 -0
- package/dist/bundle/chunks/chunk-NTSWBG4X.js +2 -0
- package/dist/bundle/chunks/chunk-RLZVFGW4.js +127 -0
- package/dist/bundle/chunks/{chunk-XDUWOHPL.js → chunk-S3GGRYIO.js} +2 -2
- package/dist/bundle/chunks/{chunk-4O2SCVYF.js → chunk-TAOL3JFA.js} +1 -1
- package/dist/bundle/chunks/chunk-TE3GUV6H.js +9 -0
- package/dist/bundle/chunks/chunk-VIV3CWXG.js +2 -0
- package/dist/bundle/chunks/{chunk-V7IXECST.js → chunk-WE4MXUQS.js} +3 -3
- package/dist/bundle/chunks/chunk-Y2PP32XV.js +2 -0
- package/dist/bundle/chunks/chunk-YLY6CKPK.js +132 -0
- package/dist/bundle/chunks/chunk-ZE5FMF3V.js +99 -0
- package/dist/bundle/chunks/cli-AOVD3SSZ.js +51 -0
- package/dist/bundle/chunks/cloudflare-workers-ai-system-one-NM3JRRWQ.js +2 -0
- package/dist/bundle/chunks/codemode-worker.js +374 -0
- package/dist/bundle/chunks/easter-egg-3d-G2LUBEMZ.js +2 -0
- package/dist/bundle/chunks/execute-MZVUC6BB.js +388 -0
- package/dist/bundle/chunks/github-copilot.js +1 -1
- package/dist/bundle/chunks/{google-generative-ai-C44KRRZ4.js → google-generative-ai-XMFHGQC5.js} +1 -1
- package/dist/bundle/chunks/{google-vertex-25DUXPTJ.js → google-vertex-4XRRNP74.js} +1 -1
- package/dist/bundle/chunks/{lib-HGQMNYVT.js → lib-TEDK3E5I.js} +2 -2
- package/dist/bundle/chunks/llama-cpp-classify-XT44PI3C.js +22 -0
- package/dist/bundle/chunks/mistral-conversations-Q22M3IBG.js +5 -0
- package/dist/bundle/chunks/node-N5BSDOA5.js +16 -0
- package/dist/bundle/chunks/openai-chatgpt.js +86 -0
- package/dist/bundle/chunks/{openai-codex-responses-3UAYOSSR.js → openai-codex-responses-4MYQSO5E.js} +1 -1
- package/dist/bundle/chunks/openai-codex.js +2 -2
- package/dist/bundle/chunks/openai-completions-OSRCWAAA.js +7 -0
- package/dist/bundle/chunks/openai-responses-UHF3DHNG.js +3 -0
- package/dist/bundle/chunks/{openrouter-images-JVEJN44Q.js → openrouter-images-ZVXWCSIM.js} +1 -1
- package/dist/bundle/chunks/openrouter.js +2 -2
- package/dist/bundle/chunks/radius.js +2 -2
- package/dist/bundle/chunks/runtime-VJMMDLVH.js +2 -0
- package/dist/bundle/chunks/typesafe-system-one-PRR5K6CI.js +2 -0
- package/dist/bundle/chunks/virtual-modules-RJXJGCPI.js +2 -0
- package/dist/bundle/cli-runtime.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
- package/dist/bundle/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/core/export-html/template.js +16 -0
- package/dist/modes/interactive/theme/dark.json +59 -72
- package/dist/modes/interactive/theme/light.json +57 -69
- package/dist/modes/interactive/theme/system-theme.d.ts +55 -0
- package/dist/modes/interactive/theme/system-theme.d.ts.map +1 -0
- package/dist/modes/interactive/theme/system-theme.js +546 -0
- package/dist/modes/interactive/theme/system-theme.js.map +1 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts +43 -9
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-controller.js +121 -79
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
- package/dist/modes/interactive/theme/theme-json.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-json.js +1 -1
- package/dist/modes/interactive/theme/theme-json.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +2 -2
- package/dist/modes/interactive/theme/theme.d.ts +34 -40
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +139 -126
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
- package/dist/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
- package/docs/cli.md +60 -7
- package/docs/codemode.md +207 -0
- package/docs/configuration.md +2 -0
- package/docs/docs.json +13 -1
- package/docs/environment-variables.md +2 -2
- package/docs/extensions.md +53 -0
- package/docs/index.md +1 -1
- package/docs/keybindings.md +2 -2
- package/docs/llama-cpp.md +13 -0
- package/docs/mcp.md +270 -0
- package/docs/models.md +83 -1
- package/docs/packages.md +1 -1
- package/docs/providers.md +19 -6
- package/docs/quickstart.md +16 -2
- package/docs/rpc-commands.md +9 -3
- package/docs/rpc.md +3 -1
- package/docs/sdk.md +8 -3
- package/docs/security.md +2 -0
- package/docs/session-format.md +5 -1
- package/docs/settings.md +24 -5
- package/docs/themes.md +28 -6
- package/docs/usage.md +1 -1
- package/docs/virtual-models.md +114 -0
- package/docs/windows.md +2 -0
- package/examples/README.md +1 -1
- package/examples/extensions/README.md +1 -0
- package/examples/extensions/built-in-tool-renderer.ts +16 -40
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +5 -5
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/jev-router.ts +113 -0
- package/examples/extensions/minimal-mode.ts +24 -96
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/rpc-client.ts +1 -1
- package/examples/rpc-extension-ui.ts +1 -1
- package/examples/sdk/14-codemode-mcp.ts +61 -0
- package/examples/sdk/README.md +2 -1
- package/judge/THIRD_PARTY_NOTICES.md +63 -0
- package/judge/dist/auth.js +128 -113
- package/judge/dist/import.js +10 -9
- package/judge/dist/kyrn-judge.js +5444 -5354
- package/judge/dist/setup.js +138 -12
- package/judge/manifest.json +414 -102
- package/kyrn/bin/kyrn-doctor +61 -11
- package/kyrn/bin/mu.mjs +84 -69
- package/package.json +4 -4
- package/dist/bundle/chunks/anthropic-messages-VMESUHQ7.js +0 -6
- package/dist/bundle/chunks/azure-openai-responses-K232MQFF.js +0 -2
- package/dist/bundle/chunks/chunk-AYFRR3OD.js +0 -2
- package/dist/bundle/chunks/chunk-DTD7JQ7Y.js +0 -42
- package/dist/bundle/chunks/chunk-FU4XQUSL.js +0 -11
- package/dist/bundle/chunks/chunk-GNYZK6XN.js +0 -2
- package/dist/bundle/chunks/chunk-HEMXN7VJ.js +0 -1653
- package/dist/bundle/chunks/chunk-MVPXOVOJ.js +0 -2
- package/dist/bundle/chunks/chunk-NUHFSC37.js +0 -25
- package/dist/bundle/chunks/chunk-XNGRGP62.js +0 -2
- package/dist/bundle/chunks/cloudflare-workers-ai-system-one-NR2TDCXU.js +0 -2
- package/dist/bundle/chunks/mistral-conversations-6IHQOR4R.js +0 -5
- package/dist/bundle/chunks/node-O3RIBJRH.js +0 -16
- package/dist/bundle/chunks/openai-completions-FIEQGWAH.js +0 -7
- package/dist/bundle/chunks/openai-responses-OZJXLHFC.js +0 -2
- package/dist/bundle/chunks/typesafe-system-one-VKYX4OGY.js +0 -2
- package/dist/bundle/chunks/virtual-modules-V7SXVVLP.js +0 -2
package/docs/codemode.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
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:
|
|
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. 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(...)` | Like `text()`; `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
|
+
- Other tools, such as `read`, `edit`, and `write`, resolve to their text output.
|
|
46
|
+
|
|
47
|
+
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.
|
|
48
|
+
|
|
49
|
+
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`.
|
|
50
|
+
|
|
51
|
+
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.
|
|
52
|
+
|
|
53
|
+
## Store values
|
|
54
|
+
|
|
55
|
+
`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.
|
|
56
|
+
|
|
57
|
+
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.
|
|
58
|
+
|
|
59
|
+
## Models
|
|
60
|
+
|
|
61
|
+
`models` reaches the model catalog and runs non-LLM models with the session's credentials: classifiers, which answer typed questions about JSON state, 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).
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
type ModelType = "chat" | "image" | "classifier";
|
|
65
|
+
|
|
66
|
+
/** A catalog entry. `provider` and `id` identify it; other fields depend on the type. */
|
|
67
|
+
interface ModelInfo {
|
|
68
|
+
type?: ModelType;
|
|
69
|
+
provider: string;
|
|
70
|
+
id: string;
|
|
71
|
+
name: string;
|
|
72
|
+
api: string;
|
|
73
|
+
input: ("text" | "image")[];
|
|
74
|
+
contextWindow?: number;
|
|
75
|
+
[key: string]: unknown;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
declare const models: {
|
|
79
|
+
/** Every known model of a type, optionally for one provider. */
|
|
80
|
+
getModelsOfType(type: ModelType, provider?: string): Promise<ModelInfo[]>;
|
|
81
|
+
/** Models of a type whose provider has working credentials. */
|
|
82
|
+
getAvailableOfType(type: ModelType, provider?: string): Promise<ModelInfo[]>;
|
|
83
|
+
/** One catalog entry, or undefined. */
|
|
84
|
+
getModelOfType(type: ModelType, provider: string, id: string): Promise<ModelInfo | undefined>;
|
|
85
|
+
/** Answer `context.questions` about `context.state`; answers are in `result.answers` by question ID. */
|
|
86
|
+
classify(model: ModelInfo, context: ClassifierContext): Promise<ClassifierResult>;
|
|
87
|
+
/** Generate images from `context.input` text and image blocks; show `result.output` blocks with image(). Can take minutes. */
|
|
88
|
+
generateImages(model: ModelInfo, context: ImagesContext): Promise<ImagesResult>;
|
|
89
|
+
};
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`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.
|
|
93
|
+
|
|
94
|
+
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.
|
|
95
|
+
|
|
96
|
+
### Classify
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
interface ClassifierContext {
|
|
100
|
+
/** The data to classify. */
|
|
101
|
+
state: Record<string, unknown>;
|
|
102
|
+
/** Questions by ID. One call answers all of them. */
|
|
103
|
+
questions: Record<string, ClassifierQuestion>;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
type ClassifierQuestion =
|
|
107
|
+
/** Pick one label. `criteria` maps each label to what it means. */
|
|
108
|
+
| { type: "choice"; instructions: string; criteria: Record<string, string> }
|
|
109
|
+
/** Score on an ordered scale. `criteria` describes each level, lowest first. */
|
|
110
|
+
| { type: "score"; instructions: string; criteria: string[] }
|
|
111
|
+
/** Yes or no. */
|
|
112
|
+
| { type: "bool"; instructions: string; criteria: { true: string; false: string } };
|
|
113
|
+
|
|
114
|
+
interface ClassifierResult {
|
|
115
|
+
provider: string;
|
|
116
|
+
model: string;
|
|
117
|
+
/** Answers by question ID. */
|
|
118
|
+
answers: Record<string, ClassifierAnswer>;
|
|
119
|
+
usage?: ModelUsage;
|
|
120
|
+
stopReason: "stop" | "error" | "aborted";
|
|
121
|
+
errorMessage?: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
type ClassifierAnswer =
|
|
125
|
+
| { type: "choice"; choice: string; probabilities: Record<string, number>; confidence: number }
|
|
126
|
+
/** `score` is the expected level index, from 0 to `criteria.length - 1`. */
|
|
127
|
+
| { type: "score"; score: number; confidence: number }
|
|
128
|
+
/** Probability of `true`. */
|
|
129
|
+
| { type: "bool"; probability: number };
|
|
130
|
+
|
|
131
|
+
/** Token counts and cost in USD, when the service reports them. */
|
|
132
|
+
type ModelUsage = { input: number; output: number; totalTokens: number; cost: { total: number } };
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Classify several items by calling `classify()` once per item. This script sorts feedback messages, for example ones a tool returned earlier in the script:
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
const jev = await models.getModelOfType("classifier", "typesafe", "jev-latest");
|
|
139
|
+
const results = await Promise.all(
|
|
140
|
+
messages.map((message) =>
|
|
141
|
+
models.classify(jev, {
|
|
142
|
+
state: { message },
|
|
143
|
+
questions: {
|
|
144
|
+
sentiment: {
|
|
145
|
+
type: "choice",
|
|
146
|
+
instructions: "How does the user feel about the product?",
|
|
147
|
+
criteria: { positive: "Satisfied or happy", negative: "Unhappy or frustrated", neutral: "Neither" },
|
|
148
|
+
},
|
|
149
|
+
urgency: {
|
|
150
|
+
type: "score",
|
|
151
|
+
instructions: "How urgently does this need a reply?",
|
|
152
|
+
criteria: ["no reply needed", "reply this week", "reply today"],
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
}),
|
|
156
|
+
),
|
|
157
|
+
);
|
|
158
|
+
return results.map((result, i) =>
|
|
159
|
+
result.stopReason === "stop"
|
|
160
|
+
? { message: messages[i], sentiment: result.answers.sentiment.choice, urgency: result.answers.urgency.score }
|
|
161
|
+
: { message: messages[i], error: result.errorMessage },
|
|
162
|
+
);
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Generate images
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
interface ImagesContext {
|
|
169
|
+
/** The prompt as text blocks, plus image blocks to edit or use as references. */
|
|
170
|
+
input: (TextBlock | ImageBlock)[];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
interface ImagesResult {
|
|
174
|
+
provider: string;
|
|
175
|
+
model: string;
|
|
176
|
+
/** Generated images, and text blocks for models that also return text. */
|
|
177
|
+
output: (TextBlock | ImageBlock)[];
|
|
178
|
+
usage?: ModelUsage;
|
|
179
|
+
stopReason: "stop" | "error" | "aborted";
|
|
180
|
+
errorMessage?: string;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
type TextBlock = { type: "text"; text: string };
|
|
184
|
+
/** `data` is base64. */
|
|
185
|
+
type ImageBlock = { type: "image"; data: string; mimeType: string };
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
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.
|
|
189
|
+
|
|
190
|
+
```js
|
|
191
|
+
// @options: {"timeout_ms": 300000}
|
|
192
|
+
const painter = await models.getModelOfType("image", "openrouter", "google/gemini-2.5-flash-image");
|
|
193
|
+
const result = await models.generateImages(painter, {
|
|
194
|
+
input: [{ type: "text", text: "A red fox in the snow, watercolor" }],
|
|
195
|
+
});
|
|
196
|
+
if (result.stopReason !== "stop") return result.errorMessage;
|
|
197
|
+
for (const block of result.output) {
|
|
198
|
+
if (block.type === "image") image(block);
|
|
199
|
+
else text(block.text);
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Limits
|
|
204
|
+
|
|
205
|
+
- 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.
|
|
206
|
+
- A script that waits on a promise that can never settle (no tool call pending) fails immediately, since there are no timers.
|
|
207
|
+
- Scripts cannot start other `codemode` scripts.
|
package/docs/configuration.md
CHANGED
|
@@ -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. |
|
package/docs/docs.json
CHANGED
|
@@ -91,6 +91,10 @@
|
|
|
91
91
|
{
|
|
92
92
|
"title": "Use Pi Packages",
|
|
93
93
|
"path": "packages.md"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"title": "Connect MCP Servers",
|
|
97
|
+
"path": "mcp.md"
|
|
94
98
|
}
|
|
95
99
|
]
|
|
96
100
|
},
|
|
@@ -105,6 +109,10 @@
|
|
|
105
109
|
"title": "Add Custom Providers",
|
|
106
110
|
"path": "custom-provider.md"
|
|
107
111
|
},
|
|
112
|
+
{
|
|
113
|
+
"title": "Route with Virtual Models",
|
|
114
|
+
"path": "virtual-models.md"
|
|
115
|
+
},
|
|
108
116
|
{
|
|
109
117
|
"title": "Build Terminal UI Components",
|
|
110
118
|
"path": "tui.md"
|
|
@@ -128,6 +136,10 @@
|
|
|
128
136
|
"title": "CLI",
|
|
129
137
|
"path": "cli.md"
|
|
130
138
|
},
|
|
139
|
+
{
|
|
140
|
+
"title": "Codemode",
|
|
141
|
+
"path": "codemode.md"
|
|
142
|
+
},
|
|
131
143
|
{
|
|
132
144
|
"title": "Slash Commands",
|
|
133
145
|
"path": "slash-commands.md"
|
|
@@ -145,7 +157,7 @@
|
|
|
145
157
|
"path": "keybindings.md"
|
|
146
158
|
},
|
|
147
159
|
{
|
|
148
|
-
"title": "
|
|
160
|
+
"title": "Providers",
|
|
149
161
|
"path": "providers.md"
|
|
150
162
|
},
|
|
151
163
|
{
|
|
@@ -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 [
|
|
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
|
|
|
@@ -95,4 +95,4 @@ These variables are read by Pi itself:
|
|
|
95
95
|
| `VISUAL`, `EDITOR` | External editor fallback when `externalEditor` is unset |
|
|
96
96
|
| `HTTP_PROXY`, `HTTPS_PROXY` | Proxy outbound HTTP requests |
|
|
97
97
|
|
|
98
|
-
Provider credentials such as `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, and
|
|
98
|
+
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).
|
package/docs/extensions.md
CHANGED
|
@@ -80,6 +80,8 @@ Automatic retries, recovery, compaction, or queued work can continue afterward.
|
|
|
80
80
|
| Persist non-context session data | `pi.appendEntry()` |
|
|
81
81
|
| Change active tools, model, or thinking level | Session control methods on `pi` |
|
|
82
82
|
| Add a model provider | `pi.registerProvider()` |
|
|
83
|
+
| Add an MCP server | `pi.registerMcpServer()` |
|
|
84
|
+
| Route each request to a model | [`pi.registerVirtualModel()`](virtual-models.md) |
|
|
83
85
|
| Add terminal rendering | Renderer registration and `ctx.ui` |
|
|
84
86
|
| Communicate with another extension | `pi.events` |
|
|
85
87
|
|
|
@@ -141,14 +143,65 @@ Use sequential execution when tools share mutable in-memory state.
|
|
|
141
143
|
File-mutating tools should wrap the complete read-modify-write operation with `withFileMutationQueue()`.
|
|
142
144
|
Truncate large model-facing results and tell the model where to read the complete output.
|
|
143
145
|
|
|
146
|
+
Declare `outputSchema` and return a matching `structuredContent` when the result is data. The model still receives `content`; programmatic callers such as codemode scripts receive `structuredContent` instead of the text. Tools without `outputSchema` are passed to scripts as their text content. To report a failure that still carries data, return the result with `isError: true` instead of throwing: the model sees an error, and scripts still receive `structuredContent`.
|
|
147
|
+
|
|
148
|
+
A tool can run other tools with `ctx.executeTool(name, args, { signal, onUpdate })`. Nested calls go through argument validation and the `tool_call` and `tool_result` handlers like model-issued calls, and emit `tool_execution_start`, `tool_execution_update`, and `tool_execution_end`; all of these events carry `parentToolCallId`, and their `toolCallId` is assigned by pi as `<parent id>/<n>`. These ids do not appear as tool calls or tool results in the transcript. Nested calls do not add transcript entries: their results only reach the calling tool, which reports them itself, for example through `onUpdate` and `details`. The session keeps a bounded record of them (name, arguments, status, duration, error; never results) as `nestedCalls` on the calling tool's result message. It is used for compaction file lists and shown in HTML exports. Arguments over 8 KiB per call or 32 KiB per tool result are omitted, at most 256 calls are kept, and `complete: false` marks a record that lost anything. The `usage` of nested results, at every depth, is added to the calling tool's result `usage`, so a tool reports only its own usage, not that of the tools it called. `ctx.tools` lists the tools `ctx.executeTool()` can call. `tool_result` handlers that redact `content` should also replace `structuredContent`; replacing only `content` drops it.
|
|
149
|
+
|
|
144
150
|
See [`hello.ts`](../examples/extensions/hello.ts), [`todo.ts`](../examples/extensions/todo.ts), [`dynamic-tools.ts`](../examples/extensions/dynamic-tools.ts), and [`truncated-tool.ts`](../examples/extensions/truncated-tool.ts).
|
|
145
151
|
|
|
152
|
+
### Tool exposure
|
|
153
|
+
|
|
154
|
+
`exposure` controls how the model reaches a tool. "Callable" means callable from other tools through `ctx.executeTool()` (`ctx.tools`), as the `codemode` tool's scripts do:
|
|
155
|
+
|
|
156
|
+
- `direct` (default): declared to the model while active, and callable while active.
|
|
157
|
+
- `model-only`: declared to the model while active, never callable. Use it for tools that orchestrate other tools or ask the user.
|
|
158
|
+
- `codemode`: callable whenever registered, and listed by the `codemode` tool. Not declared to the model unless activated explicitly.
|
|
159
|
+
- `deferred`: like `codemode`, but codemode tools do not list it; `tool_search` can find and activate it.
|
|
160
|
+
- `hidden`: registered but unreachable. Re-register a tool with `exposure: "hidden"` to withdraw it, since tools cannot be unregistered.
|
|
161
|
+
|
|
162
|
+
`namespace: { name, description, instructions }` groups related tools, as MCP servers do. Codemode tools list a namespace under one heading with its `description`. `instructions` holds longer usage guidance; it is not listed, and codemode scripts read it with `describeNamespace(name)`.
|
|
163
|
+
|
|
164
|
+
Registering a `direct` or `model-only` tool activates it; the other exposures are not activated on registration. The active set (`pi.getActiveTools()`, `pi.setActiveTools()`) is the set of tools declared to the model. `pi.getAllTools()` reports each tool's `exposure`, `namespace`, and `annotations`.
|
|
165
|
+
|
|
166
|
+
`annotations` are hints about what a tool does, with the meaning of MCP tool annotations: `readOnlyHint`, `destructiveHint`, `idempotentHint`, and `openWorldHint`. MCP tools carry the hints their server declares. Missing hints take the MCP defaults: a tool is not read-only, and may be destructive and reach an open world. The hints are not verified, but a permission extension can use them to decide which calls to confirm. This confirms the calls Codex asks approval for:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
pi.on("tool_call", async (event, ctx) => {
|
|
170
|
+
const hints = pi.getAllTools().find((tool) => tool.name === event.toolName)?.annotations;
|
|
171
|
+
const needsApproval =
|
|
172
|
+
hints?.destructiveHint === true ||
|
|
173
|
+
(!hints?.readOnlyHint && ((hints?.destructiveHint ?? true) || (hints?.openWorldHint ?? true)));
|
|
174
|
+
if (needsApproval && !(await ctx.ui.confirm("Allow tool call?", event.toolName))) {
|
|
175
|
+
return { block: true, reason: `${event.toolName} was not approved` };
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
A tool that orchestrates other tools can adjust what the model sees while it is active with `prepareLoadout(loadout)`. It runs whenever the active tools change and receives the declared tools, the callable tools, and every registered tool with its exposure and namespace. It returns replacement `descriptions` for declared tools (including its own) and `hiddenDeclarations`: active tools whose declarations requests leave out while they stay active and callable. `codemode` uses only this hook, `exposure`, and `ctx.executeTool()`, so another tool can implement the same behavior under a different name.
|
|
181
|
+
|
|
146
182
|
### Activate tools dynamically
|
|
147
183
|
|
|
148
184
|
Register every tool first, keep optional tools inactive, and use `pi.setActiveTools()` from a loader tool to select the desired active tools. Names must already be registered; unknown names are ignored.
|
|
149
185
|
|
|
150
186
|
Pi records the initial prompt and tool set in the transcript's first system message, then appends tool and prompt changes before the next model request. Providers that cannot represent the transition receive a complete transcript checkpoint, which can invalidate the cached prefix.
|
|
151
187
|
|
|
188
|
+
### Tool rendering
|
|
189
|
+
|
|
190
|
+
A tool's `renderCall` and `renderResult` draw its calls in the interactive transcript and in HTML exports. `pi.registerToolRenderer((toolName, next) => renderers)` chooses renderers for calls to any tool, including tools that are not registered yet, such as MCP tools in a resumed session before their server connected. `next()` returns what the remaining resolvers (in extension load order), then the registered tool, would use, so `next() ?? mine` only fills in.
|
|
191
|
+
|
|
192
|
+
### MCP servers
|
|
193
|
+
|
|
194
|
+
`pi.registerMcpServer(name, config)` adds an MCP server for the current session. `config` has the shape of an `mcpServers` entry in [`mcp.json`](mcp.md): `command`, `args`, `env`, and `cwd` for stdio servers, `url`, `headers`, and `oauth` for HTTP servers, plus `exposure`, `toolExposure`, `description`, `enabled`, and `timeout`.
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
pi.registerMcpServer("jira", { url: "https://mcp.example.com/jira", exposure: "codemode" });
|
|
198
|
+
pi.unregisterMcpServer("jira");
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Servers registered while the extension loads connect when the session starts, together with the `mcp.json` servers; servers registered later connect right away, and `pi.unregisterMcpServer()` closes the connection and makes the server's tools unreachable. Registrations are not saved: register again on every load, for example based on the extension's own settings. A server in `mcp.json` with the same name takes precedence, and `/mcp` shows the override. Registering the same name again replaces the extension's earlier registration; names registered by another extension, invalid names, and invalid configs throw.
|
|
202
|
+
|
|
203
|
+
The built-in MCP support connects registered servers. When nothing does, because another extension replaced it (see [MCP](mcp.md#other-mcp-extensions)), each registration is reported as an extension error. Other MCP extensions can connect registered servers too: read them with `pi.getMcpServers()` on `session_start` and handle the `mcp_servers_change` event for later changes.
|
|
204
|
+
|
|
152
205
|
<a id="extensioncontext"></a>
|
|
153
206
|
<a id="extensioncommandcontext"></a>
|
|
154
207
|
<a id="use-extension-context"></a>
|
package/docs/index.md
CHANGED
|
@@ -30,7 +30,7 @@ Use the [Quickstart customization chooser](quickstart.md#choose-how-to-customize
|
|
|
30
30
|
|
|
31
31
|
## Find reference and setup information
|
|
32
32
|
|
|
33
|
-
Use the reference pages to look up [CLI options](cli.md), [settings](settings.md), [
|
|
33
|
+
Use the reference pages to look up [CLI options](cli.md), [settings](settings.md), [providers](providers.md), [keybindings](keybindings.md), and [environment variables](environment-variables.md).
|
|
34
34
|
|
|
35
35
|
For platform-specific help, see [Terminal Setup](terminal-setup.md), [Windows](windows.md), [tmux](tmux.md), [Termux on Android](termux.md), or [Containerization](containerization.md).
|
|
36
36
|
|
package/docs/keybindings.md
CHANGED
|
@@ -125,7 +125,7 @@ In fullscreen mode, these actions control the transcript and take precedence ove
|
|
|
125
125
|
| `app.exit` | `ctrl+d` | Exit (when editor empty) |
|
|
126
126
|
| `app.suspend` | `ctrl+z` (None on Windows) | Suspend to background |
|
|
127
127
|
| `app.editor.external` | `ctrl+g` | Open in external editor (`externalEditor`, `$VISUAL`, `$EDITOR`, Notepad on Windows, or `nano` elsewhere) |
|
|
128
|
-
| `app.clipboard.pasteImage` | `ctrl+v` (`alt+v` on Windows and WSL) | Paste
|
|
128
|
+
| `app.clipboard.pasteImage` | `ctrl+v` (`alt+v` on Windows and WSL) | Paste files on macOS, images, or text from clipboard |
|
|
129
129
|
|
|
130
130
|
On native Windows, `app.suspend` has no default because Windows terminals do not support Unix job control. If you assign it manually, Pi shows a status message instead of suspending. WSL uses the normal `ctrl+z` and `fg` behavior.
|
|
131
131
|
|
|
@@ -161,7 +161,7 @@ On native Windows, `app.suspend` has no default because Windows terminals do not
|
|
|
161
161
|
| Keybinding id | Default | Description |
|
|
162
162
|
|--------|---------|-------------|
|
|
163
163
|
| `app.tools.expand` | `ctrl+o` | Collapse or expand tool output |
|
|
164
|
-
| `app.message.copy` | `ctrl+x` | Copy the selected message in `/tree`; in fullscreen mode, copy the active selection when `fullscreenCopyOnSelect` is `false`; otherwise copy the last assistant message |
|
|
164
|
+
| `app.message.copy` | `ctrl+x` | Copy the selected message in `/tree`; in fullscreen mode, copy the active selection when `fullscreenCopyOnSelect` is `false`; otherwise copy the last assistant message. On OAuth sign-in screens, copy the sign-in URL |
|
|
165
165
|
| `app.message.followUp` | `alt+enter` (`ctrl+q` on Windows and WSL) | Queue follow-up message |
|
|
166
166
|
| `app.message.dequeue` | `alt+up` (`alt+q` on Windows and WSL) | Restore queued messages to editor |
|
|
167
167
|
|
package/docs/llama-cpp.md
CHANGED
|
@@ -86,6 +86,17 @@ Loaded and sleeping models appear in `/model`. Sleeping models wake automaticall
|
|
|
86
86
|
|
|
87
87
|
If the router disconnects, `/llama` shows **Retry** and **Close**. Retry reconnects and refreshes model state without replaying the interrupted operation.
|
|
88
88
|
|
|
89
|
+
## Classification
|
|
90
|
+
|
|
91
|
+
Every model listed for chat is also listed as a classifier model with the same ID and the `llama-cpp-classify` API. Classifier models answer typed `choice`, `bool`, and `score` questions about JSON state, like TypeSafe's Jev models. The model reaches them from [`codemode`](cli.md#enable-codemode) scripts, and extensions through `ctx.modelRegistry.classify()`; see [Classifier models](models.md#use-classifier-models).
|
|
92
|
+
|
|
93
|
+
The model does not generate an answer. Each question becomes one chat prompt: the state, every question of the request, the state again, and then the question with its answers under single-token labels. Labels are letters for a choice (up to 62 options), `Yes`/`No` for a bool, and digits for a score (up to 10 levels). The second copy of the state is read with the questions in view, which improved accuracy on JevBench with small models. Pi reads the probabilities of the labels as the next token and normalizes them. A choice returns every option's probability and a confidence of `(n * peak - 1) / (n - 1)`; a score returns the expected level.
|
|
94
|
+
|
|
95
|
+
- Raw label probabilities are usually overconfident. The per-request `temperature` option divides the label logits before normalizing; values above 1 soften the distribution. It changes no answer.
|
|
96
|
+
- Questions run one after another. Everything before the final question is the same for all questions of a request, so the server's prompt cache evaluates it once. The state appears twice, so it needs twice its size in context.
|
|
97
|
+
- Small models may follow instructions written inside the state. The prompt tells the model to judge the state as data, but that is not a guarantee.
|
|
98
|
+
- Hybrid models such as Qwen3.5 cannot rewind a partially cached prompt without context checkpoints. If each question reprocesses the whole state, start the router with `--ctx-checkpoints 32 --checkpoint-min-step 0`.
|
|
99
|
+
|
|
89
100
|
## Troubleshooting
|
|
90
101
|
|
|
91
102
|
Check that the router is reachable:
|
|
@@ -99,3 +110,5 @@ curl http://127.0.0.1:8080/models
|
|
|
99
110
|
- **Model missing from `/model` with `--no-models-autoload`:** Load it with `/llama` first.
|
|
100
111
|
- **Load fails or uses too much memory:** Lower `-c` or unload another model.
|
|
101
112
|
- **Server is not in router mode:** Start it without `--model`, `-m`, or `-hf`.
|
|
113
|
+
|
|
114
|
+
To remove the `llama.cpp` provider and `/llama`, disable `llama.cpp` under Built-in in `pi config`, or set `"extensions": ["-builtin:llama.cpp"]` in [settings](settings.md#resources).
|