feat(pi-ext): per-request timeout + stall-kill for mempalace-mcp

A wedged mempalace-mcp (classically an OrbStack virtiofs cold-open of a
large chroma.sqlite3 / HNSW load) left the awaiting JSON-RPC promise
pending forever, freezing the pi TUI uninterruptibly: ESC cancels the
LLM stream, not a pending tool execute().

The JSON-RPC client now arms a per-request timer. On expiry it rejects
the request AND kills the stalled child (SIGTERM->SIGKILL), so pi gets
an error instead of hanging; the extension flips available=false so
later calls fail fast (restart pi to retry). Per-REQUEST, not
per-process: the long-lived server only dies on a genuine stall.

Knobs: MEMPALACE_MCP_TIMEOUT_MS (default 60000),
MEMPALACE_MCP_INIT_TIMEOUT_MS (default 120000), 0 = disable.

This supersedes the planned standalone stdio-watchdog shim: the
extension already owns request/response correlation, so a separate
framing-reparsing shim is unnecessary.
This commit is contained in:
2026-06-13 23:48:33 +02:00
parent ce09d25c97
commit a3b8829991
2 changed files with 135 additions and 19 deletions
+17
View File
@@ -52,6 +52,23 @@ 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`).
## Stall protection (per-request timeout)
Every JSON-RPC request to `mempalace-mcp` carries a timeout. Without it, a
wedged server (classically: an OrbStack/virtiofs cold-open of a large
`chroma.sqlite3` or an HNSW load) leaves the awaiting promise pending
*forever*, which freezes the pi TUI — ESC cancels the LLM stream, not a
pending tool `execute()`. On timeout the extension rejects the request
**and** kills the stalled child (SIGTERM→SIGKILL), so pi gets a clear
error instead of hanging and later calls fail fast (`available` flips off;
restart pi to retry). This is a per-REQUEST timeout, not a process-lifetime
one — the long-lived server is only killed when a request genuinely stalls.
- `MEMPALACE_MCP_TIMEOUT_MS` — tool-call/request timeout. Default `60000`.
- `MEMPALACE_MCP_INIT_TIMEOUT_MS``initialize` + `tools/list` handshake
timeout (cold-open is expected to be slower here). Default `120000`.
- Set either to `0` to disable (legacy unbounded behavior).
## Debugging
- `MEMPALACE_EXT_DEBUG=1` — surface `mempalace-mcp` stderr into pi's