Connect pi (alpha)
Guide (informative) · For: operators · Prereqs: Quickstart
@cotal-ai/pi is Cotal’s first host-native framework adapter. It loads into the operator’s own
Pi coding agent, rather than bundling a runtime, and uses the
same Cotal subjects, presence, attention, and messaging tools as the app-bound connectors.
Because it runs inside the session’s process, it is the one connector that can steer a live
turn mid-flight.
Alpha means the core path works today (spawn it, load it into your own interactive pi, or embed it via pi’s SDK). Pi session fork/resume and supervised crash recovery are wired; model variants, MCP sharing, and raw launch options are not and fail loud rather than degrade.
Surfaces
Section titled “Surfaces”One standalone artifact supports three Pi-hosted surfaces:
cotal spawn --agent pilaunches the installedpibinary in the manager’s PTY.--promptis delivered as Pi’s initial message (its first turn); a prompt that is empty or starts with-or@refuses the launch, since Pi would read it as an option or a file reference.- Interactive Pi discovers a copied
~/.pi/agent/extensions/cotal.js. - Pi SDK applications using the default resource loader discover that same copy. SDK applications must bind Pi’s extension lifecycle when they expect an idle session to be driven proactively.
This release pins Pi 0.79.10. The Cotal package requires Node 22; the separately
installed Pi host requires Node 22.19 or newer.
Lifecycle
Section titled “Lifecycle”The adapter sends peer traffic as Pi custom messages with triggerTurn: true and
deliverAs: "steer". This removes an idle/streaming race while preserving structured batch details.
Reliability uses three distinct points:
- The matching custom
message_startproves Pi dequeued the batch locally. - A
contextevent containing that exact batch proves it entered one provider request. - A successful
after_provider_responseproves acceptance early when the transport exposes an HTTP response. Some transports, including the Codex subscription, omit that hook; their following clean terminal assistant boundary proves acceptance for the exact context instead.
Only provider-confirmed IDs become eligible for acknowledgement, and only at a terminal agent
boundary. The Pi-local ledger commits those IDs through MeshAgent.drainInboxIds(), which removes
only exact matches even when quiet ambient is physically interleaved or older IDs were overflow-
evicted. Missing confirmed IDs are marked handled and tombstoned so late copies cannot resurface.
A run turn rides the batch that carries it and counts as shown once the provider takes that batch’s
request: at a successful after_provider_response, or, on a transport without that hook, when Pi
runs a tool from the answer or reaches the clean terminal boundary. The seat can therefore
cotal_yield it during the Pi turn that carries it. A batch the provider never takes leaves the run
turn unshown, and a later batch carries it again.
Pi emits agent_end to extensions without exposing whether it will retry. Error and unknown
reasons, and zero/missing-output length, therefore retain the delivery association in waiting
while the driver itself attempts the continuation: it re-dispatches the retained batch through the
same send path, with a fixed backoff and a fixed attempt count, and each attempt’s agent_start
proves continuation the same way an externally-triggered one does. A clean terminal boundary on any
attempt commits the retained work as usual. Once the attempts are spent, the driver holds with a
presence that names the state as needing intervention, states the attempt count, and says what the
intervention is: start a new turn in the session, or replace the seat. User abort never retries; it
is identified from the AbortSignal captured while the turn is active and stays a plain hold, since
the person who aborted is the party who continues. Non-aborted stop, toolUse, and positive-output
length are locally provable terminal boundaries and may commit confirmed work.
session_before_compact { reason: "overflow", willRetry: true } identifies the overflow path but is
not itself a terminal decision. An abort or dispatch watchdog blocks automatic replay. In managed
headless use, restart is the safe recovery because it terminates any possibly-live provider call
before durable redelivery.
reload, new, resume, and fork tear down Pi’s extension runtime. The adapter keeps its mesh,
control listener, delivery association, and ordered presence chain in a process-global identity map,
then binds the replacement runtime on its next session_start. It also atomically records the new
Pi session id. Only session_shutdown { reason: "quit" } stops the mesh.
For managed PTY seats, cotal spawn --agent pi --resume <pi-session-id> forks that transcript into
a new meshed Pi session (pi --fork; the source is untouched). After readiness, the manager binds
the exact current Pi session through the token-authenticated local control socket. An unexpected Pi
process exit reopens that session with the same Cotal identity, lifecycle UID, credentials and durable
inbox. Three restarts are allowed in a rolling two-minute window; a fourth is a crash loop and retires
the seat loud. A deliberate stop/despawn/maintenance cut never restarts it.
A manifest agent declared continuity: exact is reopened with pi --session <id>, which
fails when Pi no longer has that session instead of creating an empty one under the same id.
Event plane
Section titled “Event plane”A managed Pi seat publishes AG-UI runs, completed assistant text messages, and tool start/end
boundaries to events.<owner>.<actor>. The thread id is Pi’s native session id. Pi’s native
session JSONL is the durable source; extension hooks only wake the reader after persistence.
The event plane is enabled by default. --no-events opts out only on unrestricted spaces.
A registration that requires events arms the plane independently of the environment flag, and
the seat must hold the channel’s publish grant. An event-enabled launch needs a stable
workspace root for its write-ahead log. If the event plane stops for good, the seat stops on a
space that requires events. On any other space it keeps running without events, and Pi’s log
records AG-UI emitter stopped with the reason.
Text is published at completed-message granularity, not as live token deltas. Pi emits live text updates before writing the assistant record, so those deltas cannot be recovered after a crash. Tool arguments and results, reasoning, usage, branch and compaction entries are not published. User text is never published: Pi’s native user record cannot separate peer-authored content from human-authored content. A reload keeps the same native session and event frontier. A new, resumed or forked session gets its own thread and log on the same principal channel.
Host boundaries
Section titled “Host boundaries”- With no mesh identity the extension is inert, even if
COTAL_HOMEorCOTAL_DEFAULT_AGENTexists. - A partial managed control endpoint fails loudly; cooperative stop uses connector-core’s existing
authenticated control server and Pi’s active
ctx.shutdown(). - Peer traffic bypasses Pi’s human
inputtransformations, but provider, tool, permission, and sandbox hooks remain on the normal agent path. cotal_inboxdestructively pulls quiet ambient while the driver retains ownership of automatic traffic; normal focus recall shown alongside it remains read-only.- Pi model variants, MCP sharing, and raw launch options fail loudly until implemented.
Install
Section titled “Install”npm install -g cotal-ai @earendil-works/pi-coding-agent@0.79.10cotal upcotal spawn default --detach --agent piFor interactive/default-loader discovery:
npm install @cotal-ai/pimkdir -p ~/.pi/agent/extensionscp node_modules/@cotal-ai/pi/dist/standalone.js ~/.pi/agent/extensions/cotal.jsSee extensions/pi/README.md for the exact delivery policy and
contributor credits.
See also
Section titled “See also”- Connectors: the feature matrix across all connectors
- Run a mesh · Define a team · Watch a mesh
- MCP tools · Connect Claude Code · Connect OpenCode