8000 Add pi (@earendil-works/pi-coding-agent) support — context block + optional status extension · Issue #231 · amirlehmam/wmux · GitHub
[go: up one dir, main page]

Skip to content

Add pi (@earendil-works/pi-coding-agent) support — context block + optional status extension #231

Closed

Description

@chenx0414

What are you trying to do?

Run pi (@earendil-works/pi-coding-agent, binary pi) in wmux panes and have the workspace sidebar reflect its state — Idle / Running / Needs you — the way Claude Code, OpenCode, Kiro and omp already do. Today a pi pane sits on Running for the entire time pi is open, even when it is idle waiting for input, so the "which pane needs me" banner never fires.

I know #165 was titled "support for omp/pi harness" and closed as omp is supported. The fix shipped ~/.omp/agent/AGENTS.md and the wmux report-agent guidance, but pi itself was left out — omp and pi are separate harnesses, and pi's user-scope file is ~/.pi/agent/AGENTS.md. This issue is specifically about upstream pi.

What would you like wmux to do?

Two things, in priority order:

1. Add ~/.pi/agent/AGENTS.md to the instruction-block targets.

Same markers, same consent gate, same opt-out as the other four. That alone gives pi declarative status, because the block documents wmux browser, wmux markdown, and wmux report-agent.

At HEAD the consent copy in src/main/agent-integration.ts names exactly four harnesses, and the files written are:

  • ~/.claude/CLAUDE.md
  • ~/.config/opencode/AGENTS.md
  • ~/.kiro/steering/wmux.md
  • ~/.omp/agent/AGENTS.md

pi's file is not among them.

2. Ship a pi extension in resources/ for automatic (non-declarative) status.

Unlike Kiro, this needs no repo-local hook directory — pi auto-discovers global extensions in ~/.pi/agent/extensions/, and its event surface maps almost one-to-one onto wmux's declared-state protocol:

pi event wmux call
agent_start wmux report-agent --run-start
turn_start keep running / update tool label
tool_execution_start / tool_execution_end wmux report-metadata / activity label
turn_end idle for this turn
agent_end / agent_settled wmux report-agent --run-end
waiting on the user (permission/question) wmux report-agent --blocked "…" --choices '<json>'
session_shutdown wmux release-agent

That is ~40 lines, comparable to the omp extension in #165. pi's docs (docs/extensions.md) describe pi.on(...) handlers for all of the above, plus session_start / session_shutdown. If you'd rather not maintain it, I'm happy to write it and open a PR against resources/ — same offer the omp user made.

If a screen-detection manifest for pi is preferred for the silent path, I can contribute one too (src/shared/detection/manifests/), but the extension is the real fix because it is authoritative and doesn't guess.

What do you do today instead?

Ignore the sidebar. The pane reads Running forever, so I can't use the roster or the "needs you" banner to triage across workspaces. I could write my own pi extension calling wmux report-agent, but the point of wmux shipping one in resources/ is that it stays in sync with the protocol.

Evidence (wmux 2.10.5 / tested on 2.10.2)

Live pi pane, same session, same $WMUX_SURFACE_ID:

$ wmux detect explain
{
  "surfaceId": "surf-…",
  "agent": null,
  "state": "unknown",
  "ruleId": null,
  "reason": "no-agent-signature",
  "manifestVersion": null,
  "evidence": [],
  "manifestSource": null,
  "warnings": [],
  "manifestDir": "C:\\Users\\…\\AppData\\Roaming\\wmux\\agent-detection"
}
  • Bundled detection manifests at HEAD are claude.ts, codex.ts, opencode.ts only (src/shared/detection/manifests/).
  • @earendil-works/pi-coding-agent contains zero references to wmux — pi has no idea wmux exists, which is exactly why the context block matters.

Alternatives considered

  • Screen-scrape pi's TUI. pi's footer does render its own state (Working / spinner / model line), so rules are possible, but it's a guess and it will drift with every pi release. The declared-state path is both simpler and honest.
  • Custom user manifest in %APPDATA%\wmux\agent-detection. Works without a release, but it's per-machine and only infers blocked/working, not run depth or choices.
  • Wait for the Optuber01/wmux fork, which has a "Pi session syncing" feature — but that's a fork of this project and the fix belongs upstream.

Activity

  1. amirlehmam commented on Sep 14, 2026

    @amirlehmam
    Owner

    Both halves shipped in v2.11.0. You were right that #165 closed with pi left out — thanks for the precise write-up, the file list and the detect explain output made this straight to implement.

    1. Context block

    ~/.pi/agent/ is now an instructions target alongside the other four — same markers, same consent gate, same per-feature opt-out in Settings → General.

    One correction to the proposal, and it matters. pi loads exactly one user-scope context file, taking the first that exists of:

    AGENTS.override.md, AGENTS.md, AGENTS.MD, CLAUDE.md, CLAUDE.MD
    

    (loadContextFileFromDir, in pi's resource-loader). First match wins; the rest are never read. So "always write AGENTS.md" — which is exactly what the omp integration does, correctly, because omp has no such list — would have created a file, reported success and reached nobody on any machine that already has an AGENTS.override.md. wmux now splices into the file pi will actually read, and falls back to creating AGENTS.md only when the directory has none of them.

    The uninstall path sweeps every candidate name rather than just today's target, because the target can change between launches: write AGENTS.md, add an override later, and a removal that only loo 889A ked at the current target would strand the block in a file nothing reads and nothing can ever clean up.

    PI_CODING_AGENT_DIR is honoured when it's set in wmux's own environment. A value exported per-shell after wmux launched is unknowable from wmux's side and falls back to ~/.pi/agent — a miss, never a write to the wrong place.

    2. The extension

    resources/pi-extension/wmux.js, installed to ~/.pi/agent/extensions/wmux.js, version-marker gated, removed when you switch the feature off — and left alone if you have a wmux.js of your own there, since a file with no wmux marker isn't ours to delete. It's inert outside wmux (WMUX !== '1'), which matters because pi loads global extensions for every session on the machine.

    Your table was the right shape. Four places I landed differently, all for reasons worth stating:

    --run-depth, not --run-start / --run-end. pi's own docs say agent_end "fires when that run ends, but Pi may still auto-retry, auto-compact and retry, or continue with queued follow-up messages", and point status integrations at agent_settled. So one settle can follow several starts — and wmux's --run-start/--run-end is a refcount, so it would never return to zero and the pane would be pinned on "working". That's this issue's own bug, reintroduced one layer down. --run-depth 1 / --run-depth 0 states the fact instead of accumulating a guess, and is idempotent under any number of repeats.

    agent_end is not handled at all, for the same reason — calling the pane idle there flickers the sidebar through "done" in the middle of work that is still running. Likewise turn_end refreshes metadata but does not report idle: a turn ending inside a multi-turn run is not the agent going quiet.

    blocked comes from ui_prompt_start / ui_prompt_end and nothing else. Those events exist in pi precisely so hosts can report "waiting for user" instead of "running", they coalesce nested prompts into one outer span, and pi emits the end from a finally. There's no second source to merge in — pi has no built-in per-tool approval prompt (its security.md is explicit: no sandbox, no gate) — and inferring one from tool activity is how wmux #189 went wrong with OpenCode, where the ask itself streamed and "Needs you" flashed and vanished ten seconds before the user answered. The one extra clear is on agent_start: a run cannot begin unless the human came back.

    That clear is unconditional rather than guarded by an "am I blocked?" flag, which is the obvious optimisation and the wrong trade: that flag is per-extension-instance state, and pi rebinds extensions across /new, /resume, /fork and /reload, so a new instance would inherit false, silently no-op the clear, and leave the pane reading "Needs you" for the rest of the session. It buys one CLI spawn per user prompt, which is not worth the worst failure this feature can have.

    No --choices. pi's ui_prompt_start carries a kind and a title, never the options. wmux's back-channel relays the exact bytes the agent declared, so a choice wmux invented would be a button that sends the wrong key. A blocked pane with no buttons is still the signal that matters.

    The rest maps as you drew it: tool_execution_start → throttled activity label, tool_execution_end → a PostToolUse hook so the explorer's diff rollup refreshes (skipped for pi's read-only four — read, ls, grep, find), session_start → a stated idle, session_shutdown → release-agent. That last one is awaited and deadlined at 2 s: pi awaits shutdown handlers and then exits, so a fire-and-forget spawn can lose the race and leave the pane declared "working" with nothing alive to correct it.

    Metadata rides along too — --model provider/id, --tokens and --context-pct from ctx.getContextUsage(). Both of those are independently nullable in pi (they go null right after a compaction), and an absent flag is omitted rather than sent as a placeholder, so a real number is never overwritten with a guess.

    3. On the detection manifest

    Deliberately not added, and your own issue makes the case: "it's a guess and it will drift with every pi release". wmux stopped scraping agent state in 0.39.0 (#128) and this isn't the place to start again — a mis-read footer pins the wrong state and the user has no way to tell it happened. wmux detect explain will keep saying no-agent-signature for a pi pane; the declared state is what the sidebar uses, and it's authoritative.

    Also in this release

    • pi and omp joined the agent-identity alias table — both were missing, so a pane running either was unlabelled.
    • The orchestrator can launch pi workers: WMUX_AGENT_CMD=pi. No run subcommand — pi takes the prompt positionally after -- and starts interactive, like Claude.
    • The consent dialog now names every path it writes, pi's two included. That disclosure is the whole point of #132, and it had gone stale at four agents.
    • The extension is gated on the Status hooks toggle rather than Orchestrator plugin, since that is exactly what it is.

    Verification

    45 unit tests across the two new files, driving the shipped extension file rather than a paraphrase — that's the #187 lesson, where the bug was one token (process.execPath) that no test of a copy would have caught. Beyond that, end-to-end against a running wmux, with the file loaded through pi's own jiti loader the way pi loads it: working → blocked (with the prompt title as the reason) → idle → released, each step from a separate process. And the published zip was re-opened to confirm resources/pi-extension/wmux.js and the compiled pi-context.js are actually in it — that exact packaging gap has shipped twice before (#81, #149).

    One thing I could not verify: pi isn't installed on my machine, so no live pi session drove those events — the fake ExtensionAPI is faithful to pi 0.85.1's types and loader, but it is still a stand-in. If you run 2.11.0 against real pi and the sidebar disagrees with what pi is doing, that's the gap and I'd want to hear about it.

    Thanks for the offer to write it — you'd have saved me the reading, but the mapping decisions above are ones I'd rather own in-repo so they stay in step with the protocol, which is exactly the reasoning in your own last paragraph.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      0