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.
- package/CHANGELOG.md +52 -0
- package/README.md +34 -29
- package/dist/bundle/chunks/anthropic-messages-4PAJY4R6.js +6 -0
- package/dist/bundle/chunks/anthropic.js +2 -2
- package/dist/bundle/chunks/azure-openai-responses-Y4BBWQ3N.js +2 -0
- package/dist/bundle/chunks/bedrock-converse-stream.js +12 -9
- package/dist/bundle/chunks/{chunk-TRDNDS6A.js → chunk-3V45WARH.js} +6 -3
- package/dist/bundle/chunks/{chunk-XDUWOHPL.js → chunk-5AGIVGSV.js} +3 -3
- package/dist/bundle/chunks/chunk-6QGV3IWD.js +6 -0
- package/dist/bundle/chunks/chunk-7CBJINYJ.js +20 -0
- package/dist/bundle/chunks/{chunk-DRQDKNXA.js → chunk-7DDKXS2Q.js} +1 -1
- package/dist/bundle/chunks/chunk-BSS6T6KQ.js +2 -0
- package/dist/bundle/chunks/chunk-CF5GWZHW.js +31 -0
- package/dist/bundle/chunks/chunk-DNNHZBQR.js +4 -0
- package/dist/bundle/chunks/chunk-FN6OEZYR.js +2 -0
- package/dist/bundle/chunks/chunk-FTJT2X6E.js +2 -0
- package/dist/bundle/chunks/chunk-HZNN636Z.js +130 -0
- package/dist/bundle/chunks/{chunk-4L3WN2XY.js → chunk-JXWJOIH4.js} +1 -1
- package/dist/bundle/chunks/{chunk-4O2SCVYF.js → chunk-KIDDRTZP.js} +4 -4
- package/dist/bundle/chunks/chunk-LMNWTX2L.js +11 -0
- package/dist/bundle/chunks/chunk-LPXNMA2W.js +2 -0
- package/dist/bundle/chunks/{chunk-6CYCI3EJ.js → chunk-LVVZS2NO.js} +1 -1
- package/dist/bundle/chunks/chunk-NN4BEPYX.js +2 -0
- package/dist/bundle/chunks/chunk-O7IPTASE.js +1221 -0
- package/dist/bundle/chunks/chunk-PMRZO46Y.js +2 -0
- package/dist/bundle/chunks/chunk-QOEEJKS4.js +9 -0
- package/dist/bundle/chunks/chunk-R4VPBBDS.js +2 -0
- package/dist/bundle/chunks/chunk-RAQ6TU76.js +2 -0
- package/dist/bundle/chunks/chunk-RDZVAGPR.js +2 -0
- package/dist/bundle/chunks/chunk-SGOTYFMT.js +91 -0
- package/dist/bundle/chunks/chunk-TMTOQ7H7.js +2 -0
- package/dist/bundle/chunks/chunk-UKVNB6C4.js +2 -0
- package/dist/bundle/chunks/chunk-VJLOQZKR.js +9 -0
- package/dist/bundle/chunks/chunk-WCGU75WO.js +11 -0
- package/dist/bundle/chunks/chunk-X234222Q.js +64 -0
- package/dist/bundle/chunks/cli-722OL3K5.js +51 -0
- package/dist/bundle/chunks/cloudflare-workers-ai-system-one-MEZHQVNP.js +2 -0
- package/dist/bundle/chunks/codemode-worker.js +478 -0
- package/dist/bundle/chunks/easter-egg-3d-6H34EDGX.js +2 -0
- package/dist/bundle/chunks/execute-KVSEIDWL.js +499 -0
- package/dist/bundle/chunks/github-copilot.js +1 -1
- package/dist/bundle/chunks/{google-generative-ai-C44KRRZ4.js → google-generative-ai-D2VJQBTK.js} +1 -1
- package/dist/bundle/chunks/{google-vertex-25DUXPTJ.js → google-vertex-SQG2BO6C.js} +1 -1
- package/dist/bundle/chunks/https-proxy-agent-LVOO5TPL.js +13 -0
- package/dist/bundle/chunks/image-resize-worker.js +1 -1
- package/dist/bundle/chunks/{jiti-loader-C3NU2SDH.js → jiti-loader-GL4HJFAD.js} +1 -1
- package/dist/bundle/chunks/{jiti-static-loader-E6ANQD5T.js → jiti-static-loader-2RCDI6B4.js} +1 -1
- package/dist/bundle/chunks/{lib-HGQMNYVT.js → lib-4AVRLBHM.js} +2 -2
- package/dist/bundle/chunks/llama-cpp-classify-5YHLY3RC.js +22 -0
- package/dist/bundle/chunks/mistral-conversations-BDARVJTT.js +5 -0
- package/dist/bundle/chunks/{multipart-parser-SQ6GDJL7.js → multipart-parser-FMGQ7342.js} +1 -1
- package/dist/bundle/chunks/{node-TWVVAGPO.js → node-AUOGOBCN.js} +1 -1
- package/dist/bundle/chunks/node-KCMANYC2.js +16 -0
- package/dist/bundle/chunks/openai-chatgpt.js +86 -0
- package/dist/bundle/chunks/openai-codex-responses-CECGTDYD.js +10 -0
- package/dist/bundle/chunks/openai-codex.js +2 -2
- package/dist/bundle/chunks/openai-completions-UOA7MELD.js +7 -0
- package/dist/bundle/chunks/openai-decisions-HLM372N6.js +5 -0
- package/dist/bundle/chunks/openai-responses-TII7ZY5X.js +3 -0
- package/dist/bundle/chunks/{openrouter-images-JVEJN44Q.js → openrouter-images-7T452AS5.js} +1 -1
- package/dist/bundle/chunks/openrouter.js +2 -2
- package/dist/bundle/chunks/{pi-messages-TM5SOOIW.js → pi-messages-2UDKLKOO.js} +1 -1
- package/dist/bundle/chunks/radius.js +2 -2
- package/dist/bundle/chunks/runtime-O3ELDOM7.js +2 -0
- package/dist/bundle/chunks/{src-UK77SYZE.js → src-QKGKDFSU.js} +1 -1
- package/dist/bundle/chunks/typesafe-system-one-KNCICMO5.js +2 -0
- package/dist/bundle/chunks/virtual-modules-IKEARUOZ.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 +37 -16
- package/dist/modes/interactive/theme/dark.json +60 -73
- package/dist/modes/interactive/theme/light.json +58 -70
- 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-schema.d.ts +28 -0
- package/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -0
- package/dist/modes/interactive/theme/theme-schema.js +105 -0
- package/dist/modes/interactive/theme/theme-schema.js.map +1 -0
- package/dist/modes/interactive/theme/theme-tokens.d.ts +257 -0
- package/dist/modes/interactive/theme/theme-tokens.d.ts.map +1 -0
- package/dist/modes/interactive/theme/theme-tokens.js +94 -0
- package/dist/modes/interactive/theme/theme-tokens.js.map +1 -0
- package/dist/modes/interactive/theme/theme.d.ts +47 -56
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +149 -143
- 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-integration.md +1 -1
- package/docs/cli.md +78 -13
- package/docs/codemode.md +230 -0
- package/docs/compaction.md +5 -5
- package/docs/configuration.md +2 -0
- package/docs/custom-provider.md +53 -1
- package/docs/docs.json +21 -1
- package/docs/environment-variables.md +5 -3
- package/docs/extensions.md +172 -10
- package/docs/index.md +1 -1
- package/docs/json.md +3 -3
- package/docs/keybindings.md +7 -6
- package/docs/llama-cpp.md +21 -1
- package/docs/mcp.md +272 -0
- package/docs/message-types.md +22 -4
- package/docs/models-configuration.md +187 -0
- package/docs/models.md +100 -2
- package/docs/nvidia-rtx-llama-cpp.md +96 -0
- package/docs/packages.md +1 -1
- package/docs/providers.md +66 -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/sessions.md +1 -1
- package/docs/settings.md +30 -8
- package/docs/terminal-setup.md +14 -0
- package/docs/themes.md +31 -7
- package/docs/tui.md +71 -1
- package/docs/usage.md +2 -2
- package/docs/virtual-models.md +114 -0
- package/docs/windows.md +2 -0
- package/examples/README.md +1 -4
- 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/dynamic-resources/dynamic.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/jev-router.ts +113 -0
- package/examples/extensions/minimal-mode.ts +28 -101
- package/examples/extensions/overlay-test.ts +2 -2
- package/examples/extensions/plan-mode/index.ts +6 -2
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/subagent/index.ts +4 -6
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/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 +169 -123
- package/judge/dist/import.js +36 -30
- package/judge/dist/setup.js +250 -120
- package/judge/manifest.json +1008 -316
- package/judge/{dist/kyrn-judge.js → mu/index.js} +6933 -5995
- package/judge/themes/mu-dark.json +80 -0
- package/judge/themes/mu-light.json +80 -0
- package/kyrn/bin/kyrn-doctor +75 -25
- package/kyrn/bin/kyrn-judge-local +2 -2
- package/kyrn/bin/kyrn-ledger +1 -1
- package/kyrn/bin/mu.mjs +149 -107
- package/package.json +4 -5
- 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-A3JYRWB6.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-V7IXECST.js +0 -11
- 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/https-proxy-agent-2VXB7436.js +0 -13
- package/dist/bundle/chunks/mistral-conversations-6IHQOR4R.js +0 -5
- package/dist/bundle/chunks/node-O3RIBJRH.js +0 -16
- package/dist/bundle/chunks/openai-codex-responses-3UAYOSSR.js +0 -10
- 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/dist/modes/interactive/theme/theme-json.d.ts +0 -85
- package/dist/modes/interactive/theme/theme-json.d.ts.map +0 -1
- package/dist/modes/interactive/theme/theme-json.js +0 -132
- package/dist/modes/interactive/theme/theme-json.js.map +0 -1
- package/dist/modes/interactive/theme/theme-schema.json +0 -361
- package/examples/plugins/pi-example-plugin/README.md +0 -38
- package/examples/plugins/pi-example-plugin/package.json +0 -10
- package/examples/plugins/pi-example-plugin/src/contract.ts +0 -13
- package/examples/plugins/pi-example-plugin/src/session.ts +0 -24
- package/examples/plugins/pi-example-plugin/src/tui.ts +0 -31
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jev router - a virtual model that plans on a strong model and implements on a cheap one.
|
|
3
|
+
*
|
|
4
|
+
* Registers `jev/auto`, which routes between three OpenAI Codex models:
|
|
5
|
+
*
|
|
6
|
+
* - Planning: GPT-5.6 Sol for complex work, GPT-5.6 Terra otherwise. The Jev classifier rates the
|
|
7
|
+
* first user message; planning stays on the chosen model.
|
|
8
|
+
* - Implementation: GPT-5.6 Luna.
|
|
9
|
+
*
|
|
10
|
+
* The planning model explores, plans, and makes the first edit. After the first successful `edit`
|
|
11
|
+
* or `write` tool call, the next request of the same turn goes to Luna, and the session stays
|
|
12
|
+
* there. A session therefore switches models once and accepts a single prompt-cache miss.
|
|
13
|
+
*
|
|
14
|
+
* The phase is router state: Pi stores it on the session branch, so it follows the session tree
|
|
15
|
+
* and survives compaction. The selected thinking level passes through as the reasoning effort of
|
|
16
|
+
* the chosen model. Requests outside the agent loop, such as compaction summaries, go to Luna.
|
|
17
|
+
*
|
|
18
|
+
* Requires TypeSafe credentials (TYPESAFE_API_KEY) and an OpenAI Codex login.
|
|
19
|
+
* Usage: pi -e ./jev-router.ts --model jev/auto
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type { Message } from "@earendil-works/pi-ai";
|
|
23
|
+
import type { ExtensionAPI, ExtensionContext, ModelRoute, ModelRouteRequest } from "@earendil-works/pi-coding-agent";
|
|
24
|
+
|
|
25
|
+
const PROVIDER = "openai-codex";
|
|
26
|
+
const SOL = "gpt-5.6-sol";
|
|
27
|
+
const TERRA = "gpt-5.6-terra";
|
|
28
|
+
const LUNA = "gpt-5.6-luna";
|
|
29
|
+
|
|
30
|
+
/** Tools whose successful result means implementation has started. */
|
|
31
|
+
const EDIT_TOOLS = new Set(["edit", "write"]);
|
|
32
|
+
|
|
33
|
+
interface JevState {
|
|
34
|
+
phase: "planning" | "implementation";
|
|
35
|
+
/** OpenAI Codex model for this phase. */
|
|
36
|
+
model: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type JevRequest = ModelRouteRequest<JevState>;
|
|
40
|
+
|
|
41
|
+
function routeTo(request: JevRequest, ctx: ExtensionContext, id: string, state?: JevState): ModelRoute<JevState> {
|
|
42
|
+
const model = ctx.modelRegistry.find(PROVIDER, id);
|
|
43
|
+
if (!model) throw new Error(`Model ${PROVIDER}/${id} is not in the catalog`);
|
|
44
|
+
return { model, thinkingLevel: request.thinkingLevel, state };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function lastUserText(messages: readonly Message[]): string {
|
|
48
|
+
const content = messages.filter((message) => message.role === "user").at(-1)?.content ?? "";
|
|
49
|
+
if (typeof content === "string") return content;
|
|
50
|
+
return content.flatMap((block) => (block.type === "text" ? [block.text] : [])).join("\n");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Whether a tool call since the last user message edited a file successfully. */
|
|
54
|
+
function editedThisTurn(messages: readonly Message[]): boolean {
|
|
55
|
+
const lastUser = messages.findLastIndex((message) => message.role === "user");
|
|
56
|
+
return messages
|
|
57
|
+
.slice(lastUser + 1)
|
|
58
|
+
.some((message) => message.role === "toolResult" && EDIT_TOOLS.has(message.toolName) && !message.isError);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Planning model for a new session: Sol for complex work, Terra otherwise or when Jev is unavailable. */
|
|
62
|
+
async function choosePlanningModel(request: JevRequest, ctx: ExtensionContext): Promise<string> {
|
|
63
|
+
// Keep a planning model the session already uses, so switching to jev/auto costs no cache miss.
|
|
64
|
+
const previous = request.previous?.model;
|
|
65
|
+
if (previous?.provider === PROVIDER && (previous.id === SOL || previous.id === TERRA)) return previous.id;
|
|
66
|
+
|
|
67
|
+
const jev = ctx.modelRegistry.findOfType("classifier", "typesafe", "jev-latest");
|
|
68
|
+
if (!jev) return TERRA;
|
|
69
|
+
const result = await ctx.modelRegistry.classify(
|
|
70
|
+
jev,
|
|
71
|
+
{
|
|
72
|
+
state: { prompt: lastUserText(request.messages).slice(0, 16_000) },
|
|
73
|
+
questions: {
|
|
74
|
+
complexity: {
|
|
75
|
+
type: "choice",
|
|
76
|
+
instructions: "How demanding is the software engineering work requested in `prompt`?",
|
|
77
|
+
criteria: {
|
|
78
|
+
standard: "Ordinary features, fixes, reviews, or questions",
|
|
79
|
+
complex: "Subtle design, cross-cutting changes, or hard debugging",
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{ signal: request.signal },
|
|
85
|
+
);
|
|
86
|
+
const answer = result.stopReason === "stop" ? result.answers.complexity : undefined;
|
|
87
|
+
return answer?.type === "choice" && (answer.probabilities.complex ?? 0) >= 0.5 ? SOL : TERRA;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default function (pi: ExtensionAPI) {
|
|
91
|
+
pi.registerVirtualModel<JevState>({
|
|
92
|
+
provider: "jev",
|
|
93
|
+
id: "auto",
|
|
94
|
+
name: "Auto (Jev)",
|
|
95
|
+
thinkingLevels: ["low", "medium", "high", "xhigh"],
|
|
96
|
+
// Shared by all three models; shown before the first response.
|
|
97
|
+
contextWindow: 272_000,
|
|
98
|
+
maxTokens: 128_000,
|
|
99
|
+
async route(request, ctx) {
|
|
100
|
+
if (request.reason === "direct") return routeTo(request, ctx, LUNA);
|
|
101
|
+
const state = request.state;
|
|
102
|
+
if (!state) {
|
|
103
|
+
const model = await choosePlanningModel(request, ctx);
|
|
104
|
+
return routeTo(request, ctx, model, { phase: "planning", model });
|
|
105
|
+
}
|
|
106
|
+
// The planning model made the first edit: hand the rest of the work to Luna.
|
|
107
|
+
if (state.phase === "planning" && editedThisTurn(request.messages)) {
|
|
108
|
+
return routeTo(request, ctx, LUNA, { phase: "implementation", model: LUNA });
|
|
109
|
+
}
|
|
110
|
+
return routeTo(request, ctx, state.model);
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
@@ -18,13 +18,14 @@
|
|
|
18
18
|
|
|
19
19
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
20
20
|
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
createBashToolDefinition,
|
|
22
|
+
createEditToolDefinition,
|
|
23
|
+
createFindToolDefinition,
|
|
24
|
+
createGrepToolDefinition,
|
|
25
|
+
createLsToolDefinition,
|
|
26
|
+
createReadToolDefinition,
|
|
27
|
+
createWriteToolDefinition,
|
|
28
|
+
formatReadLineRange,
|
|
28
29
|
} from "@earendil-works/pi-coding-agent";
|
|
29
30
|
import { Text } from "@earendil-works/pi-tui";
|
|
30
31
|
import { homedir } from "os";
|
|
@@ -40,55 +41,34 @@ function shortenPath(path: string): string {
|
|
|
40
41
|
return path;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
// Cache for built-in tools by cwd
|
|
44
|
-
const toolCache = new Map<string, ReturnType<typeof createBuiltInTools>>();
|
|
45
|
-
|
|
46
44
|
function createBuiltInTools(cwd: string) {
|
|
47
45
|
return {
|
|
48
|
-
read:
|
|
49
|
-
bash:
|
|
50
|
-
edit:
|
|
51
|
-
write:
|
|
52
|
-
find:
|
|
53
|
-
grep:
|
|
54
|
-
ls:
|
|
46
|
+
read: createReadToolDefinition(cwd),
|
|
47
|
+
bash: createBashToolDefinition(cwd),
|
|
48
|
+
edit: createEditToolDefinition(cwd),
|
|
49
|
+
write: createWriteToolDefinition(cwd),
|
|
50
|
+
find: createFindToolDefinition(cwd),
|
|
51
|
+
grep: createGrepToolDefinition(cwd),
|
|
52
|
+
ls: createLsToolDefinition(cwd),
|
|
55
53
|
};
|
|
56
54
|
}
|
|
57
55
|
|
|
58
|
-
function getBuiltInTools(cwd: string) {
|
|
59
|
-
let tools = toolCache.get(cwd);
|
|
60
|
-
if (!tools) {
|
|
61
|
-
tools = createBuiltInTools(cwd);
|
|
62
|
-
toolCache.set(cwd, tools);
|
|
63
|
-
}
|
|
64
|
-
return tools;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
56
|
export default function (pi: ExtensionAPI) {
|
|
57
|
+
const tools = createBuiltInTools(process.cwd());
|
|
58
|
+
|
|
68
59
|
// =========================================================================
|
|
69
60
|
// Read Tool
|
|
70
61
|
// =========================================================================
|
|
71
62
|
pi.registerTool({
|
|
72
|
-
|
|
73
|
-
label: "read",
|
|
74
|
-
description:
|
|
75
|
-
"Read the contents of a file. Supports text files and images (jpg, png, gif, webp). Images are sent as attachments. For text files, output is truncated to 2000 lines or 50KB (whichever is hit first). Use offset/limit for large files.",
|
|
76
|
-
parameters: getBuiltInTools(process.cwd()).read.parameters,
|
|
77
|
-
|
|
78
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
79
|
-
const tools = getBuiltInTools(ctx.cwd);
|
|
80
|
-
return tools.read.execute(toolCallId, params, signal, onUpdate);
|
|
81
|
-
},
|
|
63
|
+
...tools.read,
|
|
82
64
|
|
|
83
65
|
renderCall(args, theme, _context) {
|
|
84
66
|
const path = shortenPath(args.path || "");
|
|
85
67
|
let pathDisplay = path ? theme.fg("accent", path) : theme.fg("toolOutput", "...");
|
|
86
68
|
|
|
87
|
-
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
const endLine = args.limit !== undefined ? startLine + args.limit - 1 : "";
|
|
91
|
-
pathDisplay += theme.fg("warning", `:${startLine}${endLine ? `-${endLine}` : ""}`);
|
|
69
|
+
const lineRange = formatReadLineRange(args.offset, args.limit);
|
|
70
|
+
if (lineRange) {
|
|
71
|
+
pathDisplay += theme.fg("warning", lineRange);
|
|
92
72
|
}
|
|
93
73
|
|
|
94
74
|
return new Text(`${theme.fg("toolTitle", theme.bold("read"))} ${pathDisplay}`, 0, 0);
|
|
@@ -116,16 +96,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
116
96
|
// Bash Tool
|
|
117
97
|
// =========================================================================
|
|
118
98
|
pi.registerTool({
|
|
119
|
-
|
|
120
|
-
label: "bash",
|
|
121
|
-
description:
|
|
122
|
-
"Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last 2000 lines or 50KB (whichever is hit first).",
|
|
123
|
-
parameters: getBuiltInTools(process.cwd()).bash.parameters,
|
|
124
|
-
|
|
125
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
126
|
-
const tools = getBuiltInTools(ctx.cwd);
|
|
127
|
-
return tools.bash.execute(toolCallId, params, signal, onUpdate);
|
|
128
|
-
},
|
|
99
|
+
...tools.bash,
|
|
129
100
|
|
|
130
101
|
renderCall(args, theme, _context) {
|
|
131
102
|
const command = args.command || "...";
|
|
@@ -165,16 +136,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
165
136
|
// Write Tool
|
|
166
137
|
// =========================================================================
|
|
167
138
|
pi.registerTool({
|
|
168
|
-
|
|
169
|
-
label: "write",
|
|
170
|
-
description:
|
|
171
|
-
"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.",
|
|
172
|
-
parameters: getBuiltInTools(process.cwd()).write.parameters,
|
|
173
|
-
|
|
174
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
175
|
-
const tools = getBuiltInTools(ctx.cwd);
|
|
176
|
-
return tools.write.execute(toolCallId, params, signal, onUpdate);
|
|
177
|
-
},
|
|
139
|
+
...tools.write,
|
|
178
140
|
|
|
179
141
|
renderCall(args, theme, _context) {
|
|
180
142
|
const path = shortenPath(args.path || "");
|
|
@@ -207,16 +169,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
207
169
|
// Edit Tool
|
|
208
170
|
// =========================================================================
|
|
209
171
|
pi.registerTool({
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
description:
|
|
213
|
-
"Edit a file by replacing exact text. The oldText must match exactly (including whitespace). Use this for precise, surgical edits.",
|
|
214
|
-
parameters: getBuiltInTools(process.cwd()).edit.parameters,
|
|
215
|
-
|
|
216
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
217
|
-
const tools = getBuiltInTools(ctx.cwd);
|
|
218
|
-
return tools.edit.execute(toolCallId, params, signal, onUpdate);
|
|
219
|
-
},
|
|
172
|
+
...tools.edit,
|
|
173
|
+
renderShell: "default",
|
|
220
174
|
|
|
221
175
|
renderCall(args, theme, _context) {
|
|
222
176
|
const path = shortenPath(args.path || "");
|
|
@@ -252,16 +206,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
252
206
|
// Find Tool
|
|
253
207
|
// =========================================================================
|
|
254
208
|
pi.registerTool({
|
|
255
|
-
|
|
256
|
-
label: "find",
|
|
257
|
-
description:
|
|
258
|
-
"Find files by name pattern (glob). Searches recursively from the specified path. Output limited to 200 results.",
|
|
259
|
-
parameters: getBuiltInTools(process.cwd()).find.parameters,
|
|
260
|
-
|
|
261
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
262
|
-
const tools = getBuiltInTools(ctx.cwd);
|
|
263
|
-
return tools.find.execute(toolCallId, params, signal, onUpdate);
|
|
264
|
-
},
|
|
209
|
+
...tools.find,
|
|
265
210
|
|
|
266
211
|
renderCall(args, theme, _context) {
|
|
267
212
|
const pattern = args.pattern || "";
|
|
@@ -310,16 +255,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
310
255
|
// Grep Tool
|
|
311
256
|
// =========================================================================
|
|
312
257
|
pi.registerTool({
|
|
313
|
-
|
|
314
|
-
label: "grep",
|
|
315
|
-
description:
|
|
316
|
-
"Search file contents by regex pattern. Uses ripgrep for fast searching. Output limited to 200 matches.",
|
|
317
|
-
parameters: getBuiltInTools(process.cwd()).grep.parameters,
|
|
318
|
-
|
|
319
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
320
|
-
const tools = getBuiltInTools(ctx.cwd);
|
|
321
|
-
return tools.grep.execute(toolCallId, params, signal, onUpdate);
|
|
322
|
-
},
|
|
258
|
+
...tools.grep,
|
|
323
259
|
|
|
324
260
|
renderCall(args, theme, _context) {
|
|
325
261
|
const pattern = args.pattern || "";
|
|
@@ -372,16 +308,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
372
308
|
// Ls Tool
|
|
373
309
|
// =========================================================================
|
|
374
310
|
pi.registerTool({
|
|
375
|
-
|
|
376
|
-
label: "ls",
|
|
377
|
-
description:
|
|
378
|
-
"List directory contents with file sizes. Shows files and directories with their sizes. Output limited to 500 entries.",
|
|
379
|
-
parameters: getBuiltInTools(process.cwd()).ls.parameters,
|
|
380
|
-
|
|
381
|
-
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
382
|
-
const tools = getBuiltInTools(ctx.cwd);
|
|
383
|
-
return tools.ls.execute(toolCallId, params, signal, onUpdate);
|
|
384
|
-
},
|
|
311
|
+
...tools.ls,
|
|
385
312
|
|
|
386
313
|
renderCall(args, theme, _context) {
|
|
387
314
|
const path = shortenPath(args.path || ".");
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { ExtensionAPI, ExtensionCommandContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
12
|
-
import { CURSOR_MARKER, type Focusable, matchesKey, visibleWidth } from "@earendil-works/pi-tui";
|
|
12
|
+
import { CURSOR_MARKER, type Focusable, matchesKey, renderFakeCursor, visibleWidth } from "@earendil-works/pi-tui";
|
|
13
13
|
|
|
14
14
|
export default function (pi: ExtensionAPI) {
|
|
15
15
|
pi.registerCommand("overlay-test", {
|
|
@@ -131,7 +131,7 @@ class OverlayTestComponent implements Focusable {
|
|
|
131
131
|
const after = inputDisplay.slice(item.cursor + 1);
|
|
132
132
|
// Emit hardware cursor marker for IME support when focused
|
|
133
133
|
const marker = this.focused ? CURSOR_MARKER : "";
|
|
134
|
-
inputDisplay = `${before}${marker}
|
|
134
|
+
inputDisplay = `${before}${marker}${renderFakeCursor(cursorChar)}${after}`;
|
|
135
135
|
}
|
|
136
136
|
content = `${prefix + label} ${inputDisplay}`;
|
|
137
137
|
} else {
|
|
@@ -197,8 +197,12 @@ export default function planModeExtension(pi: ExtensionAPI): void {
|
|
|
197
197
|
};
|
|
198
198
|
});
|
|
199
199
|
|
|
200
|
-
// Inject plan/execution context before
|
|
201
|
-
|
|
200
|
+
// Inject plan/execution context before a user prompt. Runs started by custom messages, such as
|
|
201
|
+
// background task notifications, skip it: the context from the last prompt is still in the
|
|
202
|
+
// conversation, and adding it again on every notification would only repeat it.
|
|
203
|
+
pi.on("before_agent_start", async (event) => {
|
|
204
|
+
if (event.messageType !== "user") return;
|
|
205
|
+
|
|
202
206
|
if (planModeEnabled) {
|
|
203
207
|
return {
|
|
204
208
|
message: {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-extension-sandbox",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pi-extension-sandbox",
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.3.0",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@anthropic-ai/sandbox-runtime": "^0.0.26"
|
|
12
12
|
}
|
|
@@ -22,6 +22,7 @@ import { StringEnum } from "@earendil-works/pi-ai";
|
|
|
22
22
|
import {
|
|
23
23
|
CONFIG_DIR_NAME,
|
|
24
24
|
type ExtensionAPI,
|
|
25
|
+
formatReadLineRange,
|
|
25
26
|
getAgentDir,
|
|
26
27
|
getMarkdownTheme,
|
|
27
28
|
withFileMutationQueue,
|
|
@@ -87,13 +88,10 @@ function formatToolCall(
|
|
|
87
88
|
case "read": {
|
|
88
89
|
const rawPath = (args.file_path || args.path || "...") as string;
|
|
89
90
|
const filePath = shortenPath(rawPath);
|
|
90
|
-
const
|
|
91
|
-
const limit = args.limit as number | undefined;
|
|
91
|
+
const lineRange = formatReadLineRange(args.offset, args.limit);
|
|
92
92
|
let text = themeFg("accent", filePath);
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
const endLine = limit !== undefined ? startLine + limit - 1 : "";
|
|
96
|
-
text += themeFg("warning", `:${startLine}${endLine ? `-${endLine}` : ""}`);
|
|
93
|
+
if (lineRange) {
|
|
94
|
+
text += themeFg("warning", lineRange);
|
|
97
95
|
}
|
|
98
96
|
return themeFg("muted", "read ") + text;
|
|
99
97
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-extension-with-deps",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pi-extension-with-deps",
|
|
9
|
-
"version": "
|
|
9
|
+
"version": "1.3.0",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"ms": "^2.1.3"
|
|
12
12
|
},
|
package/examples/rpc-client.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Run one prompt through a Pi RPC child process.
|
|
3
3
|
*
|
|
4
4
|
* Build the coding-agent package first, then run:
|
|
5
|
-
*
|
|
5
|
+
* node examples/rpc-client.ts "Explain this repository"
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { dirname, join } from "node:path";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Demonstrates how to build a custom UI on top of the RPC protocol,
|
|
6
6
|
* including handling extension UI requests (select, confirm, input, editor).
|
|
7
7
|
*
|
|
8
|
-
* Usage:
|
|
8
|
+
* Usage: node examples/rpc-extension-ui.ts
|
|
9
9
|
*
|
|
10
10
|
* Slash commands:
|
|
11
11
|
* /select - demo select dialog
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codemode, Tool Search, and MCP
|
|
3
|
+
*
|
|
4
|
+
* The CLI loads `codemode`, `tool_search`, and MCP as built-in extensions. SDK sessions add
|
|
5
|
+
* them to the resource loader's extension factories.
|
|
6
|
+
*
|
|
7
|
+
* `codemode` and `tool_search` are registered inactive. Enable them through the `defaultTools` setting
|
|
8
|
+
* (`tools` would also restrict the session to the named tools, which hides MCP tools), or let the
|
|
9
|
+
* MCP extension activate them: `codemode` for servers with `codemode` exposure, `tool_search` for
|
|
10
|
+
* servers with `deferred` exposure.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
createAgentSession,
|
|
15
|
+
createCodemodeExtension,
|
|
16
|
+
createMcpExtension,
|
|
17
|
+
createToolSearchExtension,
|
|
18
|
+
DefaultResourceLoader,
|
|
19
|
+
getAgentDir,
|
|
20
|
+
SessionManager,
|
|
21
|
+
SettingsManager,
|
|
22
|
+
} from "@earendil-works/pi-coding-agent";
|
|
23
|
+
|
|
24
|
+
const cwd = process.cwd();
|
|
25
|
+
|
|
26
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
27
|
+
cwd,
|
|
28
|
+
agentDir: getAgentDir(),
|
|
29
|
+
extensionFactories: [
|
|
30
|
+
createCodemodeExtension({ mode: "on" }),
|
|
31
|
+
createToolSearchExtension(),
|
|
32
|
+
// Reads mcp.json from the agent directory and the trusted project, like the CLI.
|
|
33
|
+
createMcpExtension(),
|
|
34
|
+
],
|
|
35
|
+
});
|
|
36
|
+
await resourceLoader.reload();
|
|
37
|
+
|
|
38
|
+
const settingsManager = SettingsManager.create(cwd);
|
|
39
|
+
// `+name` adds to the configured default tools instead of replacing them.
|
|
40
|
+
settingsManager.applyOverrides({ defaultTools: ["+codemode", "+tool_search"] });
|
|
41
|
+
|
|
42
|
+
const { session } = await createAgentSession({
|
|
43
|
+
resourceLoader,
|
|
44
|
+
settingsManager,
|
|
45
|
+
sessionManager: SessionManager.inMemory(),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
// Emits session_start, which connects the MCP servers in the background.
|
|
50
|
+
await session.bindExtensions({});
|
|
51
|
+
console.log("Active tools:", session.getActiveToolNames().join(", "));
|
|
52
|
+
session.subscribe((event) => {
|
|
53
|
+
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
|
54
|
+
process.stdout.write(event.assistantMessageEvent.delta);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
await session.prompt("Use codemode to count the TypeScript files in src/ and list the three largest.");
|
|
58
|
+
console.log();
|
|
59
|
+
} finally {
|
|
60
|
+
session.dispose();
|
|
61
|
+
}
|
package/examples/sdk/README.md
CHANGED
|
@@ -21,12 +21,13 @@ The runtime example shows how to build a recreate function that closes over proc
|
|
|
21
21
|
| `11-sessions.ts` | In-memory, persistent, continue, list sessions |
|
|
22
22
|
| `12-full-control.ts` | Replace everything, no discovery |
|
|
23
23
|
| `13-session-runtime.ts` | Manage runtime-backed session replacement |
|
|
24
|
+
| `14-codemode-mcp.ts` | Add the `codemode`, `tool_search`, and MCP extensions |
|
|
24
25
|
|
|
25
26
|
## Running
|
|
26
27
|
|
|
27
28
|
```bash
|
|
28
29
|
cd packages/coding-agent
|
|
29
|
-
|
|
30
|
+
node examples/sdk/01-minimal.ts
|
|
30
31
|
```
|
|
31
32
|
|
|
32
33
|
## Quick Reference
|
|
@@ -33,3 +33,66 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
33
33
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
34
34
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
35
35
|
SOFTWARE.
|
|
36
|
+
|
|
37
|
+
## kerpopule/hermes-jev-skills
|
|
38
|
+
|
|
39
|
+
https://github.com/kerpopule/hermes-jev-skills
|
|
40
|
+
|
|
41
|
+
The injection screen (`src/decisions/tool-injection.ts`, `src/extension/features/injection.ts`) follows its web
|
|
42
|
+
screen (`jevkit/webscreen.py`): the question's wording, the passage size, the threshold, judging passages that a
|
|
43
|
+
pattern raised in requests of their own, and pattern-only screening when the judge gives no answer. The patterns
|
|
44
|
+
in `looksInjected` are taken from its `INSTRUCTION_PATTERNS` (`jevkit/rerank.py`).
|
|
45
|
+
|
|
46
|
+
MIT License
|
|
47
|
+
|
|
48
|
+
Copyright (c) 2026 Steve Darlow
|
|
49
|
+
|
|
50
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
51
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
52
|
+
in the Software without restriction, including without limitation the rights
|
|
53
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
54
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
55
|
+
furnished to do so, subject to the following conditions:
|
|
56
|
+
|
|
57
|
+
The above copyright notice and this permission notice shall be included in all
|
|
58
|
+
copies or substantial portions of the Software.
|
|
59
|
+
|
|
60
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
61
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
62
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
63
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
64
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
65
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
66
|
+
SOFTWARE.
|
|
67
|
+
|
|
68
|
+
## can1357/oh-my-pi
|
|
69
|
+
|
|
70
|
+
https://github.com/can1357/oh-my-pi
|
|
71
|
+
|
|
72
|
+
The `turn.continue` decision (`src/decisions/turn-continue.ts`) follows its unexpected-stop detection
|
|
73
|
+
(`packages/coding-agent/src/session/unexpected-stop-classifier.ts`): the question and its examples. The
|
|
74
|
+
`judge_items` tool follows the idea of its `judge_batch`.
|
|
75
|
+
|
|
76
|
+
MIT License
|
|
77
|
+
|
|
78
|
+
Copyright (c) 2025 Mario Zechner
|
|
79
|
+
Copyright (c) 2025-2026 Can Bölük
|
|
80
|
+
Copyright (c) 2026 Stencil Labs, Inc.
|
|
81
|
+
|
|
82
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
83
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
84
|
+
in the Software without restriction, including without limitation the rights
|
|
85
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
86
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
87
|
+
furnished to do so, subject to the following conditions:
|
|
88
|
+
|
|
89
|
+
The above copyright notice and this permission notice shall be included in all
|
|
90
|
+
copies or substantial portions of the Software.
|
|
91
|
+
|
|
92
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
93
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
94
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
95
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
96
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
97
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
98
|
+
SOFTWARE.
|