feat(pi-plugin): add user pi_binary override for embedded Pi hosts

Draft
TR
2d ago
+237-25

Refs #851

What this changes

pi_binary (user config only) names the Pi executable Magic Context spawns for its child processes. When set, resolvePiInvocation returns it immediately and skips the argv[1] / execPath / bundled-package / PATH detection chain. This is the escape hatch for embedded hosts where Pi runs in a process whose execPath is the host binary (a VS Code extension hosting the SDK, embedded web servers) — the case in #851 — without changing the default detection order.

  • resolvePiInvocation accepts piBinary; ~/ expands, a bare command name stays a PATH lookup, and a path-shaped value that is not a readable file is reported through fallbackDiagnostic instead of being spawned blindly.
  • When an embedded host shape is still spawned as Pi, spawn and non-zero-exit errors carry a hint naming pi_binary.
  • Wired into historian (resolveHistorianFromConfig), /ctx-recomp, /ctx-wrapup, and the Dreamer runner factory. It is listed in LIVE_RELOAD_CONFIG_PATHS, so fixing the value applies on the next run without a restart.
  • Stripped from project config in stripUnsafeProjectConfigFields, the same tier rule as auto_update: a repository must not choose which executable the machine runs.
  • Schema, config reference, CONFIGURATION.md, and the dashboard coverage manifest regenerated.

What this does not change

The default execPath classification is untouched. An embedded host that does not set pi_binary still spawns the host binary as Pi and exits with a bad-option error; that half of #851 is left for maintainers to decide (rename detection, host adapters, or in-process child sessions).

Tests

  • packages/pi-plugin/src/subagent-runner.test.ts: five new cases — short-circuit over an embedded-host execPath, unreadable configured path diagnostic, ~/ expansion, embedded-host failure hint, and no hint for a renamed packaged Pi binary.
  • packages/plugin/src/config/project-security.test.ts: project-config stripping.
  • Ran: packages/plugin typecheck and bun test src/config (334 pass, including the live-schema drift check); packages/pi-plugin typecheck, bun test src/config (48 pass), src/subagent-runner.test.ts (168 pass, 2 failures unrelated to this change: this machine's Bun reports os.userInfo().username === "unknown", so the redaction fixture unknown-provider becomes <USER>-provider; Node reports trim21), src/dreamer + src/index-in-process-latch.test.ts (159 pass); dashboard ConfigEditor tests (63 pass); biome on the touched files.

Harness coverage (required)

  • OpenCode 1: not applicable — the OpenCode lane creates child sessions through the host SDK client; it never spawns a Pi executable.
  • OpenCode 2: not applicable — same reason.
  • Pi: covered — resolver short-circuit, historian/recomp/wrapup wiring, Dreamer factory, failure hint, tests.
  • OMP: covered — shares the same resolvePiInvocation path; pi_binary names the OMP binary through the shared basename detection.
  • Rust module: not applicable — the ck-mc module handles transforms and does not spawn Pi children.

Summary by cubic

Adds a user-level pi_binary config setting that overrides Pi executable detection, so embedded hosts (VS Code extensions or web servers hosting the SDK) can tell Magic Context which Pi executable to spawn for historian, dreamer, recomp, and wrapup child processes. Without it, those hosts are misdetected as Pi binaries and fail with bad-option exits (#851).

  • pi_binary skips the execPath / bundled-package / PATH detection chain; ~/ expands, bare command names stay PATH lookups, and unreadable explicit paths surface a fallback diagnostic.
  • Errors hint at pi_binary when an embedded host is still spawned as Pi.
  • Wired into the reloadable config paths so a fix applies on the next run; project configs cannot set it.

Bug Fixes

  • Embedded editor/SDK hosts no longer need workarounds to run Pi child processes.