feat(extensions): version-control pi mempalace extension + install.sh symlink
The pi coding-agent extension at ~/.pi/agent/extensions/mempalace.ts was living only on tor-ms22, including hand-edited fixes (Type.Unsafe schema-passthrough for MCP tool parameters). One disk wipe away from losing it, and no way to reproduce the install on a new machine. - extensions/pi/mempalace.ts: canonical copy (matches tor-ms22 byte-for-byte) - extensions/pi/README.md: what it does, the schema-passthrough gotcha, debugging knobs - install.sh: new install_pi_extension step — gated on ~/.pi/agent/extensions/ existing, backs up any real file in the way, idempotent re-runs, mirror block in uninstall. Works on macOS and Linux (plain ln -s, readlink -f). - README.md: mention extensions/pi/ in the repo-contents list and in the Setup section Verified on tor-ms22: install (backs up existing real file) → uninstall (removes symlink) → reinstall (clean symlink). Re-runs are no-ops.
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
# pi ↔ MemPalace extension
|
||||
|
||||
The canonical source of `~/.pi/agent/extensions/mempalace.ts` — the bridge
|
||||
that wires the [MemPalace](https://github.com/MemPalace/mempalace) MCP
|
||||
server into the [pi coding-agent](https://github.com/mariozechner/pi-coding-agent)
|
||||
harness.
|
||||
|
||||
`install.sh` at the repo root symlinks `mempalace.ts` from this directory
|
||||
into `~/.pi/agent/extensions/` so the live file on every machine tracks
|
||||
version control. Works on macOS and Linux (the extension itself is plain
|
||||
Node / TypeScript; the symlink is a POSIX `ln -s`).
|
||||
|
||||
---
|
||||
|
||||
## What it does
|
||||
|
||||
1. **Spawns `mempalace-mcp`** as a subprocess and does the MCP stdio
|
||||
JSON-RPC handshake (`initialize` + `notifications/initialized` +
|
||||
`tools/list`).
|
||||
2. **Registers each MCP tool** as a pi tool with its real `inputSchema`
|
||||
passed through via `Type.Unsafe(...)` (see gotcha below).
|
||||
3. **Wake-up auto-injection** (`before_agent_start`, one-shot per fresh
|
||||
session): calls `mempalace_status` + `mempalace_diary_read` and
|
||||
injects the result as a `mempalace-wakeup` system message so the
|
||||
agent orients itself the way `~/.agents/skills/mempalace/SKILL.md`
|
||||
describes. Skipped on resume/fork (context is already in the thread).
|
||||
4. **Manual wind-down** via a `/mempalace-diary [topic]` slash command:
|
||||
sends a prompt asking the LLM to call `mempalace_diary_write` with
|
||||
an AAAK-formatted entry summarizing the session. Not fully auto
|
||||
because pi sessions are typically short/tactical and
|
||||
`session_shutdown` fires too late to drive another LLM turn.
|
||||
|
||||
## Fail-soft
|
||||
|
||||
If `mempalace-mcp` can't be spawned (PATH missing, binary crashes at
|
||||
startup, …) the extension logs to stderr and returns early. pi keeps
|
||||
working without palace tools rather than refusing to start.
|
||||
|
||||
## Identity
|
||||
|
||||
`agent_name` for diary calls comes from `$MEMPALACE_AGENT_NAME`, defaulting
|
||||
to `"pi"`. First diary write against that identity creates `wing_<name>`
|
||||
in the palace. Set the env var if you want to run pi under a distinct
|
||||
identity on a given machine (e.g. `pi-laptop` vs `pi-server`).
|
||||
|
||||
## Debugging
|
||||
|
||||
- `MEMPALACE_EXT_DEBUG=1` — surface `mempalace-mcp` stderr into pi's
|
||||
stderr. Without this, stderr is drained silently so a misbehaving
|
||||
server doesn't flood the TUI.
|
||||
- If a tool call fails with a generic "Internal tool error", spawn
|
||||
`mempalace-mcp` manually with raw JSON-RPC on stdin to read the
|
||||
server-side error — much faster than guessing.
|
||||
|
||||
## The `Type.Unsafe` gotcha
|
||||
|
||||
Earlier versions of this extension registered every MCP tool with
|
||||
`parameters: Type.Object({}, { additionalProperties: true })`, which
|
||||
discarded each tool's real `inputSchema`. The LLM then saw no parameter
|
||||
names and had to guess, leading to bugs like `mempalace_diary_read`
|
||||
being called with `agent=` instead of the required `agent_name=` and
|
||||
crashing the Python server with `TypeError: missing 1 required
|
||||
positional argument`.
|
||||
|
||||
The fix (≈ lines 160-170) is to wrap the incoming JSON Schema with
|
||||
`Type.Unsafe<...>(tool.inputSchema)`. TypeBox schemas are plain JSON
|
||||
Schema at runtime plus a `Symbol` marker, so wrapping an
|
||||
externally-sourced schema with `Unsafe` is sufficient — no conversion
|
||||
to a full TypeBox tree is needed, and the LLM now sees every tool's
|
||||
real parameter names.
|
||||
|
||||
If you ever need to re-loosen the schema for debugging, fall back to
|
||||
the `Type.Object({}, { additionalProperties: true })` default only for
|
||||
that specific tool, not globally.
|
||||
|
||||
## File layout
|
||||
|
||||
```
|
||||
mempalace-toolkit/
|
||||
└── extensions/
|
||||
└── pi/
|
||||
├── README.md ← this file
|
||||
└── mempalace.ts ← symlinked into ~/.pi/agent/extensions/
|
||||
```
|
||||
|
||||
`install.sh` detects pi by probing for `~/.pi/agent/extensions/` and
|
||||
only creates the symlink when that directory exists. On machines
|
||||
without pi the file stays dormant in the repo. Re-runs are idempotent
|
||||
(same pattern as `bin/` and `SKILL.md`).
|
||||
Reference in New Issue
Block a user