diff --git a/CHANGELOG.md b/CHANGELOG.md index b218f0b..9a7302f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,53 @@ Pre-v1.0.0 tags followed the pi npm version (`v{pi_version}[letter]`). --- +## Unreleased + +### Components that move with the next build + +| Component | Baked in v1.11.0 | Next build | Why | +|---|---|---|---| +| pi-extensions | `a88fa99` | **`750b2a8`** (or later: CI resolves `main` at build time) | `mcp-loader.ts` retired; `install.sh` prunes stale links (below) | +| mempalace-toolkit | `975ab92` | **`a30af61`** (or later) | comment/README only: the bridge's `RemoteMcpClient` is now the only copy; `scripts/check-mcp-client-sync.sh` removed. No code change | + +### Changed + +- **MCP servers now use pi's built-in MCP support; pi-extensions' + `mcp-loader.ts` is retired.** pi has shipped MCP as a built-in extension + (`builtin:mcp`) since 0.99.0: servers go in `~/.pi/agent/mcp.json` or are + added with `pi mcp add`, and `/mcp` manages them (tools, reconnect, OAuth + sign-in, enable/disable, exposure). Because an extension that registers + `/mcp` *replaces* the built-in, every start on v1.11.0 printed + `Extension … mcp-loader.ts registers command /mcp, so built-in extension mcp + was not loaded`, and `mcp.json` was never read. Measured on tor-ms22 + (v1.11.0, pi 1.1.0) before the switch: time to ready, 7 interleaved pty runs + each, was 0.276 s with no MCP, 0.283 s with the loader and 0.272 s with the + built-in. A prompt cost the same tokens with either and with neither, and a + stdio server (`server-everything`) connected and answered a tool call + through the built-in. No device had servers in the loader's `mcp` block, so + nothing needs migrating. If one does, pi-extensions' README ("MCP servers") + shows how to move it to `mcp.json`. Tools from `mcp.json` default to + `codemode` exposure; set `"exposure": "direct"` to declare them to the model. + MemPalace is unaffected: `mempalace.ts` registers `mempalace_*` itself. +- **A preserved volume drops the stale link by itself.** `~/.pi/agent/` survives + a recreate, so the old `extensions/mcp-loader.ts -> + /opt/pi-extensions/extensions/mcp-loader.ts` link would outlive the file. The + entrypoint's pi-extensions `install.sh --yes` now removes links into its own + repo whose target is gone (`.ts` and `/ext`-disabled `.ts.off`). It warns once + if `settings.json` still has an `mcp` block. +- **`recreate-sanity-check.sh`: one new check, "no dangling extension links".** + The existing "N extensions deployed" line counts a dangling link as deployed + (`ls` lists it), so it cannot see this case. The new check WARNs instead of + FAILing, because whatever it still finds points outside pi-extensions, which + `install.sh` leaves alone. Expect one more ✓ than v1.11.0 printed. +- **Vendored `pi-extensions` skill floor refreshed** to pi-extensions `750b2a8`. + `check-skill-floor.sh` went red on the retirement commit and is green again. + The skill now says MCP servers are the built-in's, not an extension. Because + `rootfs/` is in the base hash, the next tag rebuilds the base. +- DOCKER_HUB.md's pi-extensions list was three releases stale (`mcp-loader` + listed; `task`, `fork-gate`, `secret-guard`, `aws-sso` missing). It now lists + the 10 that ship and has an MCP line. + ## v1.11.0 — 2026-10-12 Minor release. Headlines: **pi 1.1.0**, **secret env values are kept out of diff --git a/DOCKER_HUB.md b/DOCKER_HUB.md index 71043aa..2d78fbc 100644 --- a/DOCKER_HUB.md +++ b/DOCKER_HUB.md @@ -49,7 +49,8 @@ Full setup guide — authentication for each provider (Anthropic, OpenAI, Gemini - **pi `{{PI_VERSION}}`** ([`@earendil-works/pi-coding-agent`](https://www.npmjs.com/package/@earendil-works/pi-coding-agent)) — installed at `/usr/bin/pi`, pinned to an audited version (not npm `latest`) - **pi-atelier** — TUI sidebar (ordered panels, split-pane, themes), vendored at `/opt/pi-atelier` and pinned to an audited tag; the exact tag is in the image labels (`se.jordbo.pi-devbox.pi-atelier-version`) and `/etc/pi-devbox/build-manifest.json` - **[pi-toolkit](https://gitea.jordbo.se/joakimp/pi-toolkit)** — keybindings (mosh/tmux-friendly Shift+Enter, Ctrl+J, Alt+J newline bindings), AWS env loader, settings template -- **[pi-extensions](https://gitea.jordbo.se/joakimp/pi-extensions)** — 7 user-facing extensions: `ext-toggle`, `mcp-loader`, `todo`, `ssh-controlmaster`, `notify`, `git-checkpoint`, `confirm-destructive` +- **[pi-extensions](https://gitea.jordbo.se/joakimp/pi-extensions)** — 10 user-facing extensions: `ext-toggle`, `todo`, `task`, `fork-gate`, `secret-guard`, `aws-sso`, `ssh-controlmaster`, `notify`, `git-checkpoint`, `confirm-destructive` +- **MCP servers** — pi's built-in MCP support: declare servers in `~/.pi/agent/mcp.json` (or `pi mcp add`), manage them with `/mcp` - **`fork`** ([pi-fork](https://github.com/elpapi42/pi-fork)) and **`recall`** ([pi-observational-memory](https://github.com/elpapi42/pi-observational-memory)) tools - **mempalace bridge** — MCP extension auto-symlinked so pi reads/writes the host-mounted palace - **image-baked agent skills** — skills under `/usr/local/share/pi-devbox/skills/` (e.g. `pi-devbox-environment`, which teaches agents the container's persistence/networking/DNS/tmux/REPL specifics) are symlinked into `~/.agents/skills/` on start, available with or without a mounted skillset repo diff --git a/Dockerfile.variant b/Dockerfile.variant index b3a24e7..10aeaec 100644 --- a/Dockerfile.variant +++ b/Dockerfile.variant @@ -212,9 +212,11 @@ ARG USER_NAME=developer # `exposure` is `codemode`, i.e. such tools are "neither declared to the model # nor listed" and must be found with `searchTools()`. That would gut the # MemPalace protocol if MemPalace were a builtin-MCP server. It is not: both -# mempalace.ts and mcp-loader.ts run their OWN MCP client and surface tools via +# mempalace.ts runs its OWN MCP client and surfaces tools via # `pi.registerTool()`, which is why they are named `mempalace_search` and not -# `mcp__mempalace__search`. Confirmed on a second route — settings.json has +# `mcp__mempalace__search`. (So did pi-extensions' mcp-loader.ts until it was +# retired after v1.11.0 in favour of builtin:mcp. Servers in ~/.pi/agent/mcp.json +# ARE exposure-governed: they need `"exposure": "direct"` to be declared.) Confirmed on a second route — settings.json has # neither an `mcpServers` block (builtin, exposure-governed) nor an `mcp` block. # Extension-registered tools are declared like built-ins, so `exposure` cannot # reach them. diff --git a/rootfs/usr/local/share/pi-devbox/skills/pi-extensions/SKILL.md b/rootfs/usr/local/share/pi-devbox/skills/pi-extensions/SKILL.md index 7ec1750..b7b4ca7 100644 --- a/rootfs/usr/local/share/pi-devbox/skills/pi-extensions/SKILL.md +++ b/rootfs/usr/local/share/pi-devbox/skills/pi-extensions/SKILL.md @@ -23,11 +23,13 @@ Pi has **two distinct extension locations** and it's easy to look in the wrong o | Location | Mechanism | Examples | |---|---|---| -| `~/.pi/agent/extensions/*.ts` (or `.ts.off`) | **Local extensions** — TypeScript files, usually symlinks into `/opt/pi-extensions/extensions/` or similar. Toggled via `/ext` slash command. | `ssh-controlmaster`, `git-checkpoint`, `notify`, `todo`, `mempalace`, `mcp-loader`, `ext-toggle`, `confirm-destructive` | +| `~/.pi/agent/extensions/*.ts` (or `.ts.off`) | **Local extensions** — TypeScript files, usually symlinks into `/opt/pi-extensions/extensions/` or similar. Toggled via `/ext` slash command. | `ssh-controlmaster`, `git-checkpoint`, `notify`, `todo`, `mempalace`, `ext-toggle`, `task`, `fork-gate`, `secret-guard`, `aws-sso`, `confirm-destructive` | | `~/.pi/agent/git////` | **Package extensions (git-installed)** — git-cloned npm packages registered via the `packages` array in `~/.pi/agent/settings.json`. | `pi-fork` (`github.com/elpapi42/pi-fork`), `pi-observational-memory` (`github.com/elpapi42/pi-observational-memory`, **default branch `master`** — a `main` branch does not exist, so `pi install git:...` resolves against `master`) | | `~/.pi/agent/npm/node_modules//` | **Package extensions (npm-installed)** — `pi install npm:`; recorded in `packages[]` as `npm:`. | `pi-atelier` (status rail + sidebar TUI) | | `/opt//` — **pi-devbox containers only** | **Vendored package extensions** — cloned into an image layer at build time with `node_modules` baked, then registered at container start by `entrypoint-user.sh` via `pi install /opt/`. Recorded in `packages[]` as a **relative** path (`../../../../opt/pi-fork`) that resolves out of `~/.pi/agent` into the image layer, so it survives volume recreate. | `/opt/pi-fork`, `/opt/pi-observational-memory`, `/opt/pi-studio` | +**MCP servers are not an extension here.** pi's built-in MCP support (`builtin:mcp`) reads `~/.pi/agent/mcp.json`; manage it with `/mcp` or `pi mcp add|list`. Its tools are named `mcp____` and default to `codemode` exposure (not declared to the model) unless the server sets `"exposure": "direct"`. The `mempalace_*` tools are different: `mempalace.ts` registers them itself, so exposure never applies. The old `mcp-loader.ts` (an `mcp` block in `settings.json`) was retired on 2026-10-12, because an extension that registers `/mcp` replaces the built-in. + When the user asks how to use "the X extension", **check all of these** — `find ~/.pi/agent -maxdepth 4 -name "*X*"` covers the first three, and `ls -d /opt/*X*` the fourth. The `/ext` slash command shows the local-extensions list with enable/disable state. There is also a distinct skill-bundled-script category (e.g. `ci-release-watcher`'s `ssh-control-master-setup.sh`) which is **not** a pi extension at all — it's a helper script inside a skill. Don't conflate the three. **In a pi-devbox container, do not conclude "pi-fork isn't installed" because `~/.pi/agent/git/` is empty.** It is deliberately absent: `Dockerfile.variant` vendors to `/opt` and installs by local path, because a build-time `pi install git:...` would write into `~/.pi/agent`, which the named volume then shadows on first run. diff --git a/scripts/recreate-sanity-check.sh b/scripts/recreate-sanity-check.sh index 85695ae..a59924a 100755 --- a/scripts/recreate-sanity-check.sh +++ b/scripts/recreate-sanity-check.sh @@ -303,6 +303,24 @@ else fail "only $EXT_COUNT extensions deployed (expected ≥4)" fi +# no dangling extension links. The count above includes them (ls lists a link +# whether or not its target exists) but they load nothing, and pi skips them +# without a word. That is exactly how a retired extension outlives its image on +# a preserved volume: mcp-loader.ts (retired after v1.11.0) left +# ~/.pi/agent/extensions/mcp-loader.ts -> /opt/pi-extensions/... behind. The +# pi-extensions install.sh run by the entrypoint now prunes links into ITS repo; +# anything this still finds points somewhere else, hence WARN, not FAIL. +_dangling="" +for _e in "$HOME"/.pi/agent/extensions/*.ts "$HOME"/.pi/agent/extensions/*.ts.off; do + if [ -L "$_e" ] && [ ! -e "$_e" ]; then _dangling="$_dangling $(basename "$_e")"; fi +done +if [ -z "$_dangling" ]; then + pass "no dangling extension links in ~/.pi/agent/extensions" +else + warn "dangling extension links (target gone, pi ignores them):$_dangling" +fi +unset _dangling _e + # mempalace.ts bridge symlink if [ -L "$HOME/.pi/agent/extensions/mempalace.ts" ]; then pass "~/.pi/agent/extensions/mempalace.ts bridge symlink"