@overmux/pi
Pi extension and private Unix-socket protocol for Overmux
Package details
Install @overmux/pi from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@overmux/pi- Package
@overmux/pi- Version
0.0.5- Published
- Sep 15, 2026
- Downloads
- 552/mo · 552/wk
- Author
- richardgill
- License
- unknown
- Types
- extension
- Size
- 553.5 KB
- Dependencies
- 9 dependencies · 4 peers
Pi manifest JSON
{
"extensions": [
"./dist/index.js"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@overmux/pi
The Pi extension and private Unix-socket protocol used by Overmux. It exposes live conversation events, structured inbound messages, and parent/delegate completion notifications over a private Unix socket. It registers no tools or commands.
This package was migrated from a standalone Pi extension at commit 52fb1712f205c939d3f58b84619c1fb4e0aab972 and is now owned and versioned by Overmux.
Integration
Install the extension through Pi:
pi install npm:@overmux/pi
For development, run pnpm --filter @overmux/pi build, then pi install /absolute/path/to/packages/ecosystem/pi to use the local package directly. Pi loads the package's built JavaScript extension. Load @overmux/pi in every parent and child Pi through the global Pi settings.json. A launcher starts a child with PI_CHILD=1, PI_PARENT_SESSION_ID="$PI_SESSION_ID", and PI_TASK_SLUG=<task-slug>. PI_TASK_SLUG is required whenever PI_CHILD=1; it must match [a-z0-9](?:[a-z0-9-]{0,11}[a-z0-9])?. PI_PARENT_SESSION_ID is optional; omitting it disables callbacks to the parent. Parent Pi processes do not need a task slug. The extension captures the inherited parent ID and task slug at load time. It derives the parent's short Unix socket path from the ID; no socket path is passed through the environment.
Because the tmux server is an environment boundary, launchers must pass these values explicitly when creating the child. Supervision should react to the model-visible overmux-pi.delegate-settled message. Pi runs the configured inspection command, renders its output under [<task-slug> finished], and includes it in model context.
Configure
Create overmux-pi.jsonc in ~/.pi/agent, or in $PI_EXTENSION_CONFIG_DIR when set:
{
"inspectionCommand": [
"pi-jq",
"{{childSessionId}}",
"--messages",
"3",
"--role",
"assistant",
],
"inspectionTimeoutMs": 5000,
"supervisionPrompt": "Continue supervision.",
"liveEventsDir": null,
}
inspectionCommand is executed directly with pi.exec, never through a shell. It must contain exactly one {{childSessionId}} placeholder, its only permitted template variable. inspectionTimeoutMs must be a positive integer no greater than 60000.
Inbound user messages
The per-session private socket also accepts versioned user_message requests. requestId is required and bounded; retries with the same ID return the original response without delivering twice. Requests have non-empty message, deliverAs: "steer" | "followUp", and optional expandPromptTemplates, which defaults to true.
Install the sender CLI with npm, then send from the command line:
npm install -g @overmux/pi
overmux-pi-send <sessionId> [--after-turn|--follow-up] <message>
printf 'Review the changes.' | overmux-pi-send <sessionId>
--after-turn is the default and sends a steer; --follow-up queues until the current agent run finishes. It prints the JSON response and exits nonzero when delivery is unavailable.
Use the exported sender API rather than deriving socket paths. It has native Node.js JavaScript and declaration exports:
import { sendUserMessage } from "@overmux/pi/protocol";
const response = await sendUserMessage(sessionId, {
requestId: crypto.randomUUID(),
message: "Review the changes.",
deliverAs: "steer",
});
Successful responses include delivery: "immediate" | "steer" | "followUp". Idle sessions deliver immediately; busy sessions use steer after the current assistant turn and tools, or followUp after the current agent run. Malformed or oversized frames are disconnected without a response. Delegate notifications retain their existing ACK response.
Live event stream
Set liveEventsDir to an absolute directory to enable the sidecar. It defaults to null because the stream contains conversation and tool data and grows without rotation. The directory must be outside Pi's session directory tree.
{
"liveEventsDir": "/home/me/.local/state/overmux/pi/events",
}
Each session runtime writes one append-only stream:
<liveEventsDir>/<sessionId>/<streamId>.jsonl
Reloads and session replacements close the old stream and create a new streamId. Every complete JSONL record has this envelope:
{
"version": 1,
"sessionId": "...",
"processInstanceId": "<pid>-<uuid>",
"streamId": "...",
"sequence": 1,
"timestamp": 1700000000000,
"event": {
"type": "session_start",
"reason": "startup",
"cwd": "/project",
"pid": 1234
}
}
sequence starts at 1 and increases within a stream. A tailer should checkpoint (sessionId, streamId, sequence) or its byte offset, process only newline-terminated records, and retain an incomplete final line until the next read. processInstanceId is stable across extension reloads in one Pi process; streamId distinguishes each session runtime.
The stream includes session metadata/compaction/tree events, agent and turn boundaries, message lifecycle events, tool execution start/end, and model/thinking changes. message_start and message_end contain message snapshots. message_update deliberately contains only a compact assistantMessageEvent: cumulative message and partial snapshots plus final block content are omitted, while text, thinking, and tool-call deltas retain contentIndex. The complete finalized message remains on message_end. Generated messageId and messageSequence correlate each message lifecycle within the stream.
Writes are serialized and each record is appended with its newline in one operation. Normal event handlers never await filesystem I/O. Shutdown waits for queued records, including session_shutdown; a write or serialization failure disables that stream without notifying, blocking, or changing the TUI. The exported LiveEventRecord, CompactAssistantMessageEvent, liveEventSessionDir, and liveEventStreamPath APIs are the supported tailer seam.
Manual smoke test
From the repository root, load only this extension:
pi --no-extensions -e ./packages/ecosystem/pi
Launch children through tmux with the parent's current Pi session ID, for example tmux new-session -e PI_CHILD=1 -e PI_PARENT_SESSION_ID="$PI_SESSION_ID" -e PI_TASK_SLUG=<task-slug> .... A child launching a grandchild passes its own PI_SESSION_ID the same way. Omit PI_PARENT_SESSION_ID when the child should not call back. The extension sends a bounded, acknowledged notification once per settled child leaf, deduplicating receipts by the child session ID and leaf ID; a later leaf produces a new notification.