@comput/pi-intercom
v0.3.0
Published
Explicit local communication between Pi sessions; Pi orchestrates.
Readme
PiIntercom (V1 supported core)
One Windows/Linux interactive Pi extension for coordinator and workers. Intercom communicates; Pi orchestrates. No automatic assignment, retry, removal, replacement, launcher fallback, commit, or rollback.
intercom_stop_worker and intercom_close_worker are disabled. They throw an unsupported-host error before sending control or calling cancellation/shutdown. Incoming stop/close controls also fail. Pi 0.84.4 leaves retry continuation alive after extension abort. See blocker. Newer versions are not automatically assumed safe; a verified supported lifecycle API and tests are required before enabling these capabilities.
Use
Requires Windows or Linux, Node 22+, interactive Pi with project trust, and this package's peer dependencies supplied by Pi (@earendil-works/pi-coding-agent, typebox). Launch the coordinator explicitly from the intended root:
cd D:\Source\YourProject
pi -e D:\Source\PiIntercom\src\index.tsAlternatively install from npm:
pi install npm:@comput/pi-intercomThen start a new Pi session or run Pi /reload. pi install records the package in user settings by default; add -l for project settings. The Intercom extension itself does not change Pi settings. Load only one copy: do not enable the installed package alongside an explicit source checkout, or load both source and built output.
Updating the loaded copy
- npm installation: update the installed package, not this checkout:
pi update npm:@comput/pi-intercom. A pinned version must be explicitly changed (for example,pi install npm:@comput/[email protected], using the same settings scope as the original installation).pi listshows configured package sources; inspect that installation'spackage.jsonto confirm its version. - Source loading (
pi -e .../src/index.ts): update that checkout.npm run buildproduces test output; it does not update a separate npm installation. - After obtaining the intended code, start a new session or use Pi
/reloadin each affected running host.intercom_reload_workeronly reads shared configuration; it cannot load extension-code fixes. Workers launched by a coordinator receive the coordinator extension's actual file path. Updating a different copy does not change that path, and existing workers do not automatically reload.
The 0.1.2 update/reload and subsequent worker creation were observed live; comprehensive reload/session-replacement lifecycle coverage remains pending.
Linux source installation
pi install /absolute/path/to/pi-intercomRun Pi /reload (or start a new interactive session) after installing or changing source. Linux supports messaging, configuration, the dashboard, and Herdr worker launch/resume. Launch the coordinator inside a Herdr workspace; herdr, sh, and pi must be on PATH in worker panes. Workers run a quoted POSIX shell command in the returned pane, with the coordinator's actual extension path. Project trust remains required.
Linux multiplexer: none is explicitly unsupported: no terminal guessing, headless fallback, or automatic cleanup. Stop/close remain disabled on both platforms. Automated Linux tests cover shell argument preservation and adapter/HTTP behavior. A live source-loaded Linux Herdr smoke test launched two workers: both registered, loaded their configured names/responsibilities, and acknowledged coordinator messages through Intercom. This verifies launch and round-trip messaging, not resume or cancellation.
Startup and launchers
Without shared config, the current working directory becomes the coordinator root. Startup creates .pi-intercom/config.json, opens a loopback listener, synchronizes the name Coordinator, and waits for user input (no model turn). Existing config is found by walking upward. Invalid/unreadable config is an error and is not overwritten. Unknown session IDs, including forks, are workers, never replacement coordinators.
Default launcher herdr requires the coordinator to be inside Herdr (HERDR_ENV=1 and workspace context), with herdr on PATH. Windows needs powershell.exe plus pi.ps1 in the worker pane; Linux needs sh plus pi. Each worker gets one tab, one pane, no split, no focus change. The Windows-only none launcher requires pi.cmd and Windows PowerShell on PATH and opens a separate visible PowerShell terminal. Missing launcher is an error, not fallback. Launch acknowledgment means command submission/terminal creation, not Pi readiness. A failed launch can leave a tab or process behind; no automatic cleanup occurs. See the scoped live evidence below.
Explicit workflow
- Coordinator calls
intercom_create_worker({projectDirectory:"."}). No worker identity, responsibility, callback address or special config path is passed. - Anonymous worker opens its own endpoint and sends its session ID, actual port and root-relative project directory to coordinator Pi. Registration starts a normal coordinator turn when idle or queues steering when busy. It creates no config entry or hidden pending-registration map.
- Coordinator decides and calls
intercom_configure_workerwith all five explicit fields:sessionId,port,projectDirectory,name,description. This only writes config. - Coordinator separately calls
intercom_reload_worker({to:"Builder"}). Worker rereads responsibility and synchronizes Pi/Herdr tab names. This is not Pi/reload, a restart, cancellation, or work assignment. No model turn starts. - Coordinator sends explicit work using
intercom_send({to:"Builder",message:"..."}). Workers can communicate directly with configured peers. Findings do not authorize unrelated implementation. Progress questions ask for reporting and continuation, not cancellation. - Workers report as instructed and wait, without autonomous exit. Coordinator unavailability does not close the worker or cancel its assignment.
Existing workers restore responsibility by session ID, bind their saved port or, if it is already in use, an OS-selected fallback, and report their actual port. Coordinator updates existing ports as bookkeeping. Status from removed/unknown sessions notifies Pi but creates no entry. Resume uses the configured project directory and full session ID, with a saved-session lookup first; a never-used Pi session might not yet exist on disk. Failure leaves config unchanged, without fabricated history or forced model turns.
Tools and permissions
Every tool rereads current config and checks the live Pi session ID. Names are case-insensitive for lookup/uniqueness; display capitalization is preserved. Coordinator is reserved.
| Tool | Arguments | Permission / effect |
|---|---|---|
| intercom_create_worker | projectDirectory? (default .) | Coordinator; launch anonymous worker |
| intercom_configure_worker | sessionId, port, projectDirectory, name, description | Coordinator; all five explicit fields, writes only |
| intercom_reload_worker | to (worker name) | Coordinator; passive config/name reload |
| intercom_send | to (name), message | Configured, responsibility-loaded sessions; asynchronous message |
| intercom_list | None | Configured coordinator/workers; saved roster and derived dashboardUrl, never live health |
| intercom_request_status | to (worker name) | Coordinator; independent extension report |
| intercom_report_status | None | Workers including anonymous; shared report function, never registration |
| intercom_stop_worker, intercom_close_worker | to (worker name) | Coordinator role checked, then unsupported-host error, no side effects |
| intercom_resume_worker | to (worker name) | Coordinator; saved session, configured launcher/directory |
| intercom_remove_worker | to (worker name) | Coordinator; config entry only, no shutdown/session deletion |
| intercom_set_multiplexer | multiplexer: herdr or none | Coordinator; future launches only |
Removal precondition: explicitly close a running worker before removing its entry. Since remote close is disabled, arrange explicit user closure in its terminal first. Intercom does not infer liveness from saved IDs/ports and cannot certify closure. No hidden probing or force-kill is performed.
Anonymous send/list and all coordinator operations fail explicitly. Anonymous registration and status remain available. A dedicated repeat-registration operation and broader anonymous permissions remain unresolved contract TODOs, not silently added tools. If initial registration fails, the worker remains reachable and reports the error locally. Explicit user Pi /reload restarts this extension and repeats startup registration; status is not a substitute and there is no automatic retry.
Config and wire contracts
Shared, source-controlled file: <root>/.pi-intercom/config.json. Do not ignore it or split IDs/ports into runtime config. Example schema only (not automatically installed placeholder data):
{
"version": 1,
"multiplexer": "herdr",
"agents": [{
"sessionId": "actual-pi-session-id",
"name": "Coordinator",
"coordinator": true,
"description": "Coordinate workers, delegate work, and manage shared configuration.",
"port": 49152,
"projectDirectory": "."
}]
}Version 0.2.0 dashboard support adds optional dashboardPort (integer 1–65535) on the coordinator entry only. Older config without it remains valid; workers cannot have it. This is separate from each agent's messaging port. intercom_list keeps version, multiplexer and agents, and adds top-level dashboardUrl: http://127.0.0.1:<saved dashboardPort>/, or null when no dashboard port is saved. It reads saved config without probing or starting a dashboard; the URL is last-known, not a liveness claim.
Workers have coordinator:false and an existing project directory equal to root or below it. Writes canonicalize directories and check real paths (including symlink escapes). Only coordinator extension operations write config; ordinary Pi file/shell tools are not restricted.
Initial creation writes/fsyncs a complete temporary file then atomically publishes via a no-replace hard link. Contenders see only complete config; exactly one wins. Unsupported hard-link filesystems fail explicitly, without unsafe fallback. Interrupted staging files can remain as ignored .tmp files but are never mistaken for config. Updates are serialized, join Pi's file-mutation queue, validate, fsync a temporary file and atomically rename. Duplicate activation of the same coordinator session in multiple processes and external editors racing writes remain outside V1's guarantees (session locks deferred).
HTTP binds 127.0.0.1 only, POST /intercom, UTF-8 JSON, at most 64 KiB. Envelope:
{"version":1,"kind":"message","from":"sender-session-id","to":"expected-recipient-session-id","payload":{"message":"explicit message"}}Kinds: message, registration (port, projectDirectory), status (port, busy), request_status, reload, stop, close (empty control payload). Sender session identity is the envelope from; registration/status agent notifications include an explicit sessionId field. Agent messages require configured sender and recipient; controls require the current coordinator's sender ID and a worker recipient. Registration/unknown status are intentional exceptions to configured-sender checks, accepted only by coordinator. All receivers verify expected recipient ID, protecting against stale ports reaching another session. Local processes are trusted: this is role validation, not authentication. No remote networking/proxies/redirects or credentials.
202 {"accepted":true} means Intercom extension receipt/dispatch, not guaranteed Pi input acceptance, queueing, or model completion. In Pi 0.84.4, sendUserMessage can reject during manual compaction after Intercom has acknowledged receipt: its void extension API reports the asynchronous rejection only as a local host error. Intercom has no delivery queue or retry to repair this gap; inspect the recipient's local error and arrange an explicit resend after compaction if needed. Replacing it with sendMessage is not a safe workaround because that path can start a concurrent run. Invalid/unsupported requests return an error; excessive bodies return 413. Request-status sends its report separately after accepting control. Starting in 0.2.1, Intercom always supplies deliverAs: 'steer': Pi starts normally when idle and steers when busy. This avoids the idle-snapshot-to-busy race that caused “Agent is already processing” errors; it does not fix manual-compaction rejection. It does not hard-interrupt an executing tool. Each send resolves the recipient again from config; 5-second receipt deadline, no retries. Timeout means unknown outcome, not proof of nondelivery. No durable inbox, deduplication or replay log in V1.
Version 0.2.0 telemetry adds an optional envelope correlationId for one transport attempt; legacy envelopes without it remain accepted. It is not agent identity, deduplication, a task ID, or a reply/completion guarantee.
Tool text output is bounded to 50 KiB/2000 lines. The full saved roster remains in the shared config file; dashboardUrl is derived for list output. Name sync errors are surfaced; startup retains the useful endpoint, and explicit reload reports failure (Pi name/responsibility may already have changed before a Herdr tab rename fails).
Local observation dashboard (0.2.0)
Current source adds metadata-only event logs and a coordinator-only, read-only localhost dashboard. Startup binds an OS-assigned port, then saves the actual port as the coordinator's dashboardPort and prints the local URL. Configured coordinator and worker sessions can retrieve the saved URL using intercom_list—no new dashboard tool or control. Open that URL in a local browser. The saved address can remain stale after shutdown or failure; it is not proof of reachability. Workers log metadata but do not start a dashboard. No browser opens automatically, and no agent controls, assignments or automatic recovery are added.
The roster shows saved names/responsibilities/endpoints plus last-observed busy/idle, timestamp and age. Evidence older than 60 seconds is marked stale, never offline; missing evidence is unknown. The timeline refreshes approximately every three seconds and can be filtered by agent, event type and errors. Expand events for safe metadata/correlation, not prompt bodies. A disconnected dashboard retains old data with an explicit warning.
Logs under .pi-intercom/logs/ exclude message bodies and raw errors; roster responsibility text still comes from shared config, so do not put secrets there. writerId identifies an ephemeral log writer, not an agent; Pi session ID remains agent identity. Transport correlationId identifies one send attempt, not a task or completion. Logging is bounded/best effort and does not change HTTP receipt semantics or repair manual-compaction rejection. Never interpret a quiet timeline as completed work.
See observability reference for privacy, rotation/retention limits, partial-error semantics and the HTTP boundary. This feature requires version 0.2.0 or later; update the package and reload each participating Pi session to activate it. The dashboard is not a durable inbox, replay log or task journal.
Development and validation
Run these commands from a source checkout; tests and workflows are not shipped in the npm package. The original implementation validation snapshot is historical, not the current live-test inventory.
npm ci
npm run typecheck
npm test
npm run check:package
# Optional installed-host compatibility probe (confirms the known defect):
PI_INTERCOM_PI_ROOT='C:/path/to/pi-coding-agent' npm run test:hostProduction source runs through Pi's TypeScript loader; npm run build generates dist for tests. Pi core/typebox are peer dependencies, not bundled; pinned development copies are included in the lockfile for reproducible CI without local junctions.
Publishing
.github/workflows/publish.yml publishes through npm Trusted Publishing (GitHub OIDC), without an npm token secret. Configure the npm trusted publisher as owner comput-sh, repository pi-intercom, workflow publish.yml, with no environment.
The workflow validates on Windows, then publishes with provenance from a GitHub-hosted Ubuntu runner. It runs when a GitHub release is published or when manually dispatched. Release tags must be v<package.json version>.
Published versions are immutable. For a new release, bump package and lockfile versions, commit/push, then publish a matching GitHub release. Manual dispatch publishes the selected ref and is not a dry run; the release-tag check only applies when a release tag is present. Do not dispatch publishing for an already published version. CI validates pushes to main and pull requests separately without publishing.
0.3.0 Linux support
- Interactive startup, messaging and dashboard support on Linux.
- Herdr worker launch/resume command generation uses quoted POSIX shell arguments; Windows behavior is retained. Linux
nonefails explicitly without fallback. - Linux validation: typecheck and 67/67 tests passed, with no skips. Live source-loaded Herdr launch, registration, configure/reload and round-trip messaging passed with two workers.
- CI and release validation now run on Windows and Linux. Stop/close remain disabled; live Linux resume is not yet verified.
Release evidence
v0.1.1: successful npm OIDC run 35466923379.
v0.1.2: successful npm OIDC run 35467586052. Fixes Windows Herdr launching by using
herdr pane runon the returned pane ID with encoded PowerShell invokingpi.ps1, instead of the failingStart-Process piwrapper. No Herdr agent alias is needed.v0.1.3: successful npm OIDC run 35474304380. Contains the fixes below, not the observability feature added in 0.2.0.
0.1.3 changes and verification
Version 0.1.3 includes these fixes (not present in 0.1.2):
- Lifecycle generation/session checks reject stale queued or in-flight operations before submitting new launches, sends, deliveries or config publication. This includes delayed listener startup, anonymous registration and deferred status reporting. An OS link/rename or external action already submitted before invalidation is not cancelled or rolled back; there is no guarantee that in-flight external actions are drained.
- Failed temporary-file write, sync or close attempts best-effort close/unlink cleanup while preserving the primary error. Filesystem cleanup failures can still leave temporary files.
- Resume preflight now follows the child session-storage context:
PI_CODING_AGENT_SESSION_DIR, then target-project/globalsessionDirsettings, then Pi's default. Relative storage paths resolve against the target working directory. Launchers do not propagate the coordinator's CLI--session-dir; no machine-specific session path is stored in shared config. A static persisted-session fixture and mocked launcher validate lookup, not live resumed-worker startup.
Pre-observability fix milestone: coordinator validation recorded npm run typecheck PASS; npm test 36/36, no failures; npm run check:package PASS; git diff --check PASS (line-ending warnings only). Quality independently confirmed typecheck and all 36 tests, with no remaining actionable regression identified in those fixes. This count predates the logging/dashboard changes; it is not their aggregate validation. Tests include actual extension-adapter event handling against a mocked Pi API/context and runtime-to-runtime loopback HTTP; they do not establish real Pi queue or compaction behavior.
0.2.1 delivery regression validation
Always specifying steering fixes the missing-streaming-mode race without adding retries or an Intercom queue. Coordinator and Quality independently passed typecheck and all 64 tests, including a source-backed Pi 0.84.4 regression. Test fixtures now isolate discovery from real ancestor configurations; production discovery boundaries are unchanged and remain a separate design issue.
0.2.0 observability validation
Coordinator and Quality independently ran npm run typecheck && npm test: 63/63 tests passed, zero skips. Coordinator also confirmed the 15-file package dry-run (dashboard assets and observability reference included; runtime logs excluded) and a clean git diff --check.
Coverage includes bounded metadata logging, transport/runtime instrumentation, read-only dashboard HTTP/security, actual UI code against hostile-text DOM fixtures, actual bound-port persistence before readiness, passive saved-URL discovery, bind/persistence failures and shutdown races. These use mocked Pi hosts and test-only loopback services; no live Pi dashboard startup or URL-liveness guarantee is claimed.
A separate synthetic-browser smoke check verified desktop rendering, literal hostile labels, unknown/stale evidence, error filtering and expandable metadata. Browser native-click automation timed out; programmatic DOM clicks verified the interactions instead, so this is not a full keyboard/mouse accessibility or live Pi end-to-end test.
Tests use temporary directories, local test-only HTTP servers, static session fixtures and mocked Pi hosts/launch executors. They never start agents, tabs, terminals, contact models, services or bridges, or access credentials.
Observed live behavior
The release/session evidence reported during the 0.1.2 validation establishes these specific observations, not blanket lifecycle compliance:
- Encoded PowerShell invoking
pi.ps1recovered an existing failed Herdr tab; Pi started and the worker registered. - After package update and Pi reload, coordinator
intercom_create_workerlaunched three workers; each registered, then was explicitly configured and separately reloaded. Integration's configured name was applied through configure/reload. - An asynchronous Core → Coordinator message was relayed with
telegram_sendand receipt was confirmed by the owner. Telegram is a separate integration, not an Intercom transport or automatic forwarding feature.
Remaining limitations / not comprehensively live-tested
- Idle/steering queue edge cases, busy snapshots, session-name persistence across restarts, trust UI and session replacement/reload lifecycle. The specific manual-compaction rejection gap described above remains unresolved; successful ordinary messaging does not verify that case.
- The observations above do not establish all Herdr failure paths, tab-association edge cases or readiness guarantees.
- Windows visible terminal/Pi process startup. Success only proves terminal creation, not Pi readiness; startup failures remain visible in that terminal. No live readiness handshake was added.
- Actual persisted-session lookup and resumed worker startup; never-used session failure.
- Cross-process/network-drive atomicity and Windows ACL/sharing failures (same-process temp-filesystem races are tested).
- Stop/close remain disabled on all hosts until a verified cancellation API covers retries, compaction continuations and queues. No workaround or weakened guarantee.
- Explicit repeat-registration tool and broad anonymous permissions require a contract decision. Duplicate-session locks, takeover, durable logging, non-Herdr Linux launchers and macOS support are deferred.
