paratenic

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

Every path into a Paratenic host Inside one Pi process, the Paratenic extension observes the AgentSession and TUI and holds one session connection to Paratenic; other Pi extensions register input providers with the Pi-AHP bridge the extension installs. For AHP createSession, paratenic provider pi starts Pi with the same extension in its own tmux session; that Pi claims the client-chosen session with a one-time creation token and then joins exactly like a terminal Pi. Culture connects to Paratenic over AHP and, independently, to any other AHP host such as pi-daemon; hosts never connect to each other. AgentSessionand the TUI your extensionsinput providers Paratenic extensionsession connection Pi-AHP bridgeone global symbol Pi in tmuxParatenic extensioncreation claim provider piPi spawner Paratenicone route per sessionone host sequence Cultureone connection per host another AHP hostpi-daemon · its own sequence ONE PI PROCESS HOST-CREATED SESSIONS 1 socket · 1 session createSession commands AHP AHP hosts never connect to each other
Fig. III.1Two kinds of backend connection reach the host. Other AHP hosts never connect to Paratenic; Culture connects to each of them.
PathRuntime started byConnectionProvidercreateSession
Pi in a terminalyouone session connection eachpi, reservedno
Extensions inside PiPi, when it loads themthe bridge, inside that Pi’s connection——
Pi providerthe provider, for createSessionone session connection each, claimed with a creation tokenpi or a distinct idyes
Another AHP host, such as pi-daemonthat hostnone to Paratenicits ownits 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.

Attaching a terminal Pi, then a local turn and a remote one Pi starts the extension, which sends hello with a stable source identity derived from the session file and receives session and chat URIs. A turn typed at the terminal runs once in Pi; the extension forwards its events and Paratenic orders them for every client. A turn from a client is echoed with its serverSeq, sent to the extension as a command, submitted through Pi's extension API only when Pi is idle, and acknowledged; the resulting events arrive like any other. ATTACHonce per Pi process and conversation LOCAL TURNtyped at the terminal REMOTE TURNsent from a phone session_start1 hello · sourceId = sha256(session file)instanceId · capabilities2 registered · session and chat URIs3 source snapshot · the transcript so far4 session appears in listSessions5 prompt6 runs once, here7 user_message · deltas · tools · settled8 the same events, in order9 ordered actions at serverSeq10 dispatch · clientId, clientSeq11 echo · accepted at serverSeq12 command · commandId, backendInstanceId13 sendUserMessage — only if Pi is idle14 command_result · accepted15 user_message · deltas · settled16 ordered actions17 Youat the terminalYouat the terminal PiAgentSession + TUIPiAgentSession + TUI extensionsame processextensionsame process ParatenichostParatenichost Cultureany clientCultureany client
Fig. III.2A turn typed at the terminal runs once in Pi and is mirrored. A turn from a client is routed once and submitted through Pi’s extension API.
  • 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=1 loads the package without registering — required wherever another backend or host owns the session.

What a client can do to an attached session

From a clientResultBecause
start a turnonly while Pi is idle; the draft is kept otherwisePi’s API cannot tie queued input to a message
steer or follow uprefusedPi exposes no identity-bearing queue receipt
stoprefused; nothing is sent to PiPi’s abort returns no authoritative restoration receipt
change model or thinkingappliedsetModel, setThinkingLevel
answer a questionrouted to the provider that askedthe Pi-AHP bridge, below
complete / commandsPi’s own commands offeredadvertised as AHP completions
run !commandrefused on current hostsuse the local Pi shell
dispose the sessionnot offeredthe Pi process owns its life cycle

Sessions a client creates can also be disposed

The connection’s life

The extension's connection State machine of the Paratenic extension's connection. On session_start it connects and sends hello; once registered it streams events. If the socket is lost it backs off, starting at 100 milliseconds and doubling to a five-minute ceiling, then reconnects to the same source identity and URI. Repeated source-publication failures quarantine it, refusing commands until a /paratenic reconnect; session_shutdown stops it. connecting registeredevents flow, commands run backing off100 ms × 2 … 5 min quarantinedcommands refused stopped hello → registered socket lost timer source failures /paratenic reconnect session_shutdown session_start
Fig. III.3The extension reconnects on its own; /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 })
A question from a Pi extension, answered from a phone An extension inside Pi registers an input provider with the Pi-AHP bridge and posts a request. The bridge sends input_requested; Paratenic adds it to the session's input-needed queue and Culture shows it in the inbox. An answer from Culture becomes a complete_input command that the bridge hands to the provider. The provider arbitrates against any local answer and reports the winner; only then does Paratenic publish the completion and remove the request. ARBITRATEthe provider decides handle.requested(request)1 input_requested2 inputNeeded · chat/inputRequested3 one inbox row per(host, session, request) chat/inputCompleted4 complete_input · commandId5 complete(command)6 first valid answer wins:a keypress here or the phone7 handle.resolved · winning commandId8 input_resolved9 completion published · request removed10 an extensioninput provideran extensioninput provider bridgeParatenic extensionbridgeParatenic extension ParatenichostParatenichost CultureinboxCultureinbox
Fig. III.4A client’s answer is a candidate. The provider’s resolution is the fact.
  • 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
Creating a session through the Pi provider Culture asks Paratenic to create session S with a model. Paratenic records S as creating and sends the Pi provider one CreateSession command with a one-time creation token. The provider writes the token to an owner-only file, starts Pi in a new tmux session in the requested directory, and accepts. The launch shell reads and deletes the file before Pi starts, so the token never appears on a command line. Pi's Paratenic extension sends hello with requestedSession S and the token; Paratenic binds S to that Pi, and the session is ready for every client. Pi keeps running if Paratenic or the provider restarts, and reconnects on its own; a person can attach with tmux at any time. AFTER ANY RESTARTPi keeps running createSession Smodel · directory1 CreateSession Sone-time token2 tmux new-sessiontoken via owner-only file3 command_result · accepted4 S is creating5 hello · requestedSession Screation token6 registered · S7 S is ready8 hello again · same source9 Cultureany clientCultureany client ParatenichostParatenichost provider piPi spawnerprovider piPi spawner Piin tmuxPiin tmux
Fig. III.5The creation token reaches Pi without a command line; after the claim, the created Pi joins like any other.
  • Claim. Paratenic records the session as creating and sends the provider a one-time creation token. The launched Pi presents it with requestedSession in 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, a thinking level Pi does not define, or a working directory that does not exist — or lies outside --allowed-root — is refused, and createSession fails 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. disposeSession closes 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; --profiles reads them from a JSON file, and without one the command-line flags form a single profile.
  • Options. Each profile advertises model and thinking, with its defaults, through resolveSessionConfig; a session’s choice overrides the profile and becomes Pi’s --model and --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

The mesh lives in the client Three independent AHP hosts — Paratenic on atlas, Paratenic on vega, and another AHP host — each keep their own server sequence, authentication and replay; they never connect to each other. Culture holds a separate connection to each and merges what it sees into one agent list, one feed and one inbox, every row qualified by its host. An answer in the inbox is sent back on the connection of the host that asked. atlas Paratenic seq 18204 vega Paratenic seq 9311 editor host another AHP host seq 402 one connection each Culture ONE WORKSPACE AGENTS atlas release guide vega test sentinel editor host refactor FEED vega checks green on API 35 atlas tool · ./gradlew test INBOX atlas choose the release channel vega approve the schema change answers return on the asking host’s connection
Fig. III.6Hosts never peer. Each keeps its own sequence, authentication and replay; Culture qualifies every row by host.

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 clientPi in a terminalPi started by the provider
start a turnwhile Pi is idlewhile Pi is idle
steer, follow uprefusedrefused
stoprefusedrefused
model, thinkingappliedapplied, and chosen at creation
answer a questionvia the Pi-AHP bridgevia the Pi-AHP bridge
create a session—a new Pi in its own tmux session
disposenot offeredcloses its tmux session
watch at a terminalit is onetmux attach

Who sees a turn

A turn delivered byPi transcriptParatenic’s clients
typing in a terminal Piyesyes
typing in an attached tmux sessionyesyes
AHP → the extensionyesyes

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.