diff options
| author | Igor Soarez <igor@soarez.org> | 2026-10-15 06:49:35 +0100 |
|---|---|---|
| committer | Igor Soarez <igor@soarez.org> | 2026-10-15 06:49:35 +0100 |
| commit | cc86d6114291305fcea1ed8b0904c8be0e633767 (patch) | |
| tree | f8647ed99c461e5a20560a5e927c6285a62e8e94 /mcp | |
| parent | 50c251e082b8122bfd1a6e80427d0dc19fa4f246 (diff) | |
pi-chrome 0.15.57: stand in for pi-tui, collect the primer from prompt sections
Diffstat (limited to 'mcp')
| -rw-r--r-- | mcp/pi-tui.mjs | 3 | ||||
| -rwxr-xr-x | mcp/server.mjs | 23 |
2 files changed, 21 insertions, 5 deletions
diff --git a/mcp/pi-tui.mjs b/mcp/pi-tui.mjs new file mode 100644 index 0000000..836d686 --- /dev/null +++ b/mcp/pi-tui.mjs @@ -0,0 +1,3 @@ +// Stands in for @earendil-works/pi-tui, which pi-chrome imports for its transcript renderers. Those +// draw pi's terminal UI and never run here, so the classes only have to exist. +export class Text {} diff --git a/mcp/server.mjs b/mcp/server.mjs index 9c3bea4..c6e70f1 100755 --- a/mcp/server.mjs +++ b/mcp/server.mjs @@ -211,6 +211,8 @@ const pi = { active = new Set(names); }, sendMessage() {}, + // pi-chrome logs authorization changes to the session history; there is none here. + appendEntry() {}, exec: async () => ({ code: 1, stdout: "", stderr: "not available in the MCP server" }), }; @@ -223,10 +225,14 @@ async function emit(event, payload) { // --- load pi-chrome --------------------------------------------------------------------------- // pi-chrome imports typebox at runtime, and its own directory may have no node_modules: point it -// at the copy pinned here, the version pi ships. jiti rather than Node's type stripping, because -// pi-chrome uses constructor parameter properties, which strip-only mode rejects. +// at the copy pinned here, the version pi ships, and pi-tui at a stand-in. jiti rather than Node's +// type stripping, because pi-chrome uses constructor parameter properties, which strip-only mode +// rejects. const jiti = createJiti(import.meta.url, { - alias: { typebox: fileURLToPath(import.meta.resolve("typebox")) }, + alias: { + typebox: fileURLToPath(import.meta.resolve("typebox")), + "@earendil-works/pi-tui": fileURLToPath(new URL("pi-tui.mjs", import.meta.url)), + }, }); const extension = await jiti.import(EXTENSION, { default: true }); extension(pi); @@ -245,12 +251,19 @@ if (active.size === 0) { // A fork's primer may name another browser's tools, which differ by client: browser_* in pi, // mcp__<server>__* elsewhere. pi-chrome reads PI_CHROME_OTHER_BROWSER_TOOLS as it builds the // primer, synchronously inside the handler, so the variable only has to hold for that call. +const UPSTREAM_SECTION = "chrome-profile-bridge"; async function instructionsWith(otherTools) { const saved = process.env.PI_CHROME_OTHER_BROWSER_TOOLS; if (otherTools) process.env.PI_CHROME_OTHER_BROWSER_TOOLS = otherTools; - const pending = emit("before_agent_start", { systemPrompt: "" }); + // pi-chrome adds its primer as named prompt sections, one per handler (upstream's and a fork's), + // or, written for an older pi, returns a whole systemPrompt instead. Take both; upstream's goes + // first. + const sections = {}; + const pending = emit("before_agent_start", { systemPrompt: "", systemPromptOptions: { sections } }); process.env.PI_CHROME_OTHER_BROWSER_TOOLS = saved; - const primer = (await pending)?.systemPrompt ?? ""; + const returned = (await pending)?.systemPrompt ?? ""; + const names = Object.keys(sections).sort((a, b) => (b === UPSTREAM_SECTION) - (a === UPSTREAM_SECTION)); + const primer = [...names.map((name) => `<${name}>\n${sections[name]}\n</${name}>`), returned].join("\n"); return [ESSENTIALS, primer.split("\n").filter((line) => !/\/chrome\b/.test(line)).join("\n")].join("\n"); } const OTHER_BROWSER_TOOLS = process.env.PI_CHROME_OTHER_BROWSER_TOOLS?.trim(); |
