Plate III · Join paths
Every way a session joins.
A session reaches Paratenic through exactly one backend. Which one decides who creates it, how a prompt is delivered, what a client may do to it, and what happens when something restarts.
The paths
Two backends, one bridge, and the hosts beside you
| Path | Runtime started by | Connection | Provider | createSession |
|---|---|---|---|---|
| Pi in a terminal | you | one session connection each | pi, reserved | no |
| Extensions inside Pi | Pi, when it loads them | the bridge, inside that Pi’s connection | — | — |
| Pi provider | the provider, for createSession | one session connection each, claimed with a creation token | pi or a distinct id | yes |
| Another AHP host, such as pi-daemon | that host | none to Paratenic | its own | its own |
Pi in a terminal
The terminal stays in charge
Load the extension into the Pi you already run. It attaches to that process’s live AgentSession — it never starts or resumes another Pi — and opens one session connection to the host’s backend socket.
- Identity. The source is a SHA-256 of the session file’s path, so resuming the same conversation later lands on the same AHP URI. A session with no file falls back to its id and working directory.
- Incarnation. Each extension start has a fresh
instanceId. Commands carry it, and a newer registration for the same source fences the older one. - Outcome. Acceptance means Pi admitted the input. If no native receipt for it appears within ten seconds, the outcome is indeterminate and nothing is resent automatically.
- Opting out.
PI_DISABLE_AHP=1loads the package without registering — required wherever another backend or host owns the session.
What a client can do to an attached session
| From a client | Result | Because |
|---|---|---|
| start a turn | only while Pi is idle; the draft is kept otherwise | Pi’s API cannot tie queued input to a message |
| steer or follow up | refused | Pi exposes no identity-bearing queue receipt |
| stop | refused; nothing is sent to Pi | Pi’s abort returns no authoritative restoration receipt |
| change model or thinking | applied | setModel, setThinkingLevel |
| answer a question | routed to the provider that asked | the Pi-AHP bridge, below |
complete / commands | Pi’s own commands offered | advertised as AHP completions |
run !command | refused on current hosts | use the local Pi shell |
| dispose the session | not offered | the Pi process owns its life cycle |
Sessions a client creates can also be disposed
The connection’s life
/paratenic inspects or forces a reconnect from inside Pi.Extensions inside Pi
Any extension can ask through AHP without depending on it
The Paratenic extension installs a frozen bridge object under a global symbol. Another extension looks it up, registers an input provider, and keeps working locally when the bridge is absent or PI_DISABLE_AHP=1 is set. The bridge knows nothing about the consumer: no package names, tool names or result meanings.
const bridge = globalThis[Symbol.for("paratenic.pi.ahp-bridge.v1")];
const handle = bridge?.registerInputProvider({
version: 1, providerId, questionKinds: ["single_select", "text"], freeform: true,
snapshot: () => pendingRequests, // authoritative after every reconnect
complete: async command => ({ accepted: true }),
});
handle?.requested(request); // later: handle.resolved({ requestId, resolution, commandId })
- Kinds. Text, number, integer, boolean, single and multiple choice, plus optional freeform; up to 16 questions and 64 options a request.
- Owned by the runtime. Bridge requests carry no turn id. They survive later turns, can be posted while Pi is idle, and leave only when the provider resolves them or its snapshot omits them.
- Reconnect barrier. On re-registration the extension drops queued input frames and sends one complete snapshot at the cut, so no stale question reappears and no live one is lost.
Pi provider
Sessions a client creates
An AHP client can ask Paratenic to create a session. paratenic provider pi answers: for each createSession it starts an ordinary Pi with the Paratenic extension in its own tmux session, in the requested working directory and with the requested model and thinking level. That Pi then joins exactly like one you started in a terminal, so everything on the previous plates applies to it — and you can watch or take over with tmux attach.
paratenic provider pi --provider-id pi \
--pi-arg --no-skills # repeat for flags every launch needs
tmux attach -t paratenic-<id> # watch or take over one agent
- Claim. Paratenic records the session as creating and sends the provider a one-time creation token. The launched Pi presents it with
requestedSessionin its hello, so only that process can bind the client-chosen URI; a hello for another session or provider is refused. - Checked before launch. An option-like or malformed
model, athinkinglevel Pi does not define, or a working directory that does not exist — or lies outside--allowed-root— is refused, andcreateSessionfails with the reason. - No secret on a command line. The token reaches Pi through an owner-only environment file that the launch shell reads and deletes before Pi starts.
- Life cycle. Agents outlive the provider and the host: tmux keeps them running and their extension reconnects on its own.
disposeSessioncloses the agent’s tmux session, whose name is derived from the session URI. - Profiles. Each named profile — its Pi arguments, working directory and default model and thinking — is offered as its own agent;
--profilesreads them from a JSON file, and without one the command-line flags form a single profile. - Options. Each profile advertises
modelandthinking, with its defaults, throughresolveSessionConfig; a session’s choice overrides the profile and becomes Pi’s--modeland--thinking.
pi-daemon and other AHP hosts own their sessions: connect Culture to each of them directly, beside Paratenic, as described under another AHP host.
Other AHP hosts
The mesh lives in the client
Paratenic is one AHP host per node, and it does not federate. A Culture configuration lists hosts — Paratenic on this machine, Paratenic on a build box, any other AHP 0.8 host — and Culture opens an independent connection to each, with its own reconnect, sequence and token.
# ~/.config/culture/config.yaml
hosts:
- id: local
url: ws://127.0.0.1:7788/ahp
- id: atlas
url: wss://agents.example.net/ahp
token_file: ~/.config/culture/atlas.token
- One workspace. Agents, the feed and the inbox merge across hosts. An inbox row is keyed by host, session and request, and its answer goes back on that host’s connection.
- Extras are additive. Paratenic adds a bounded semantic feed resource, backend-online state and identity images. A host without them still works: Culture builds its feed from session summaries instead.
Side by side
What a client can do, by path
| From a client | Pi in a terminal | Pi started by the provider |
|---|---|---|
| start a turn | while Pi is idle | while Pi is idle |
| steer, follow up | refused | refused |
| stop | refused | refused |
| model, thinking | applied | applied, and chosen at creation |
| answer a question | via the Pi-AHP bridge | via the Pi-AHP bridge |
| create a session | — | a new Pi in its own tmux session |
| dispose | not offered | closes its tmux session |
| watch at a terminal | it is one | tmux attach |
Who sees a turn
| A turn delivered by | Pi transcript | Paratenic’s clients |
|---|---|---|
| typing in a terminal Pi | yes | yes |
| typing in an attached tmux session | yes | yes |
| AHP → the extension | yes | yes |
Every turn lands in that Pi’s own session file, whichever door it came through, so every client sees one history. pi-daemon’s sessions are seen by connecting to pi-daemon itself.