Repository navigation
Description
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.tsonly (src/shared/detection/manifests/). @earendil-works/pi-coding-agentcontains 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/wmuxfork, which has a "Pi session syncing" feature — but that's a fork of this project and the fix belongs upstream.
Activity
feat(pi): teach pi about wmux, and let it declare its own state (#231)
amirlehmam commentedon Sep 14, 2026
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 explainoutput 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:
(
loadContextFileFromDir, in pi'sresource-loader). First match wins; the rest are never read. So "always writeAGENTS.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 anAGENTS.override.md. wmux now splices into the file pi will actually read, and falls back to creatingAGENTS.mdonly 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 looked at the current target 8881 would strand the block in a file nothing reads and nothing can ever clean up.PI_CODING_AGENT_DIRis 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 awmux.jsof 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 sayagent_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 atagent_settled. So one settle can follow several starts — and wmux's--run-start/--run-endis 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 0states the fact instead of accumulating a guess, and is idempotent under any number of repeats.agent_endis 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. Likewiseturn_endrefreshes metadata but does not report idle: a turn ending inside a multi-turn run is not the agent going quiet.blockedcomes fromui_prompt_start/ui_prompt_endand 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 afinally. There's no second source to merge in — pi has no built-in per-tool approval prompt (itssecurity.mdis 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 onagent_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,/forkand/reload, so a new instance would inheritfalse, 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'sui_prompt_startcarries akindand atitle, 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→ aPostToolUsehook 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,--tokensand--context-pctfromctx.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 explainwill keep sayingno-agent-signaturefor a pi pane; the declared state is what the sidebar uses, and it's authoritative.Also in this release
piandompjoined the agent-identity alias table — both were missing, so a pane running either was unlabelled.WMUX_AGENT_CMD=pi. Norunsubcommand — pi takes the prompt positionally after--and starts interactive, like Claude.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 confirmresources/pi-extension/wmux.jsand the compiledpi-context.jsare 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
ExtensionAPIis 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.
fix(cli): resync the checked-in CLI mirror after the spawn-batch flag (…