24 KiB
title: p3: Feature Set and Work Packages (prep for the design meeting) cover: p3 / Features kicker: Meeting prep · p3 on Chord v2 · 2026-11-03 subtitle: What p2 does today, what p3 should ship first, how hands, sync and the plugin API fit, and a first cut into work packages stats:
- 28 | feature areas in p2 today
- 21 | model-facing tools in p2
- 78 | coding-agent extension examples checked against the p3 plugin API
- 12 | proposed work packages
note: |
Written by the redesign session for the meeting on 2026-11-03, from three research passes over p2
(
apps/p2atb7af55ace), the coding agent's extension examples and p2's environments and Radius code (/tmp/p3-prep/features.md,extensions.md,envs.md), the owner's notes and Armin's summary of the 2026-11-02 meeting. Chord v2's slices 1-13 are built (the app kit included); slice 11 (web and TUI) is building. Everything marked "proposed" is for the meeting to decide. colophon: p3 Feature Set and Work Packages · meeting prep · 2026-11-03
Before we begin
p2 grew feature by feature on the phone: 33 plugins, about 65,000 lines, a web shell and a TUI. p3 is its rewrite on Chord v2, with an app kit that already gives hosts, workers, HTTP with sessions and pairing, plugins with versioned APIs, files and export. The meeting's job is to decide what p3 ships first, what waits, and how the work splits.
Some of it was settled yesterday (Armin's summary): sessions sync to R2 through Radius by commit log, the session index is a replicated Chord table, every host kind loads plugins through registries, hands connect proactively. The owner added: voice waits; environments turn around into hands; continuing a local session on Radius is core; subagents and handoff are in, artifacts are questionable; the plugin API stays small enough to evolve.
Ten things to know
:::findings
- Proposed base: sessions (with delete), the agent and conversations, subagents, history and handoff, code mode, shell commands, models and login, prompt resources, workspace (read and review), file index, status and notices, search through the index, the web shell and the TUI, hands, session sync and continue-on-Radius.
- Proposed later: voice, terminals (need channels and hands), artifacts, port forwards, chat mode and guest
sharing, MCP and
tool_search. - Hands replace SSH-dialled daemons: a long-lived daemon dials the server (or Radius), is enrolled once by a pairing code, and carries p2's existing daemon protocol over one multiplexed WebSocket.
- The sync handshake needs a driver identity. Two machines writing one session both push "from Radius's last seq" and silently diverge; an epoch or lease per session (Chord's rule S7) closes it.
- The index has two writers once a session continues on Radius; three options, one recommended (derive rows from session stores).
- Tools in base:
read,write,edit,bash,codemode,subagent,team,agent,conversations,history_*, location tools,rename_session,send_file; artifacts, ports andreload_pluginswait or change. - One CLI, no environment variables:
p3 serve,p3 tui,p3 hand,p3 owner-link,p3 install; settings as arguments and files, per the app kit. - The plugin API v1 is seven clusters, almost all already existing: Durable's extension, a
Conversationcontract, input transforms, environments and models, questions, UI registries, objects for state. - Litmus test: all 78 coding-agent extension examples are expressible; Doom needs two small fixes (frame splitting in the TUI bridge, a path for large plugin assets); streaming frames from a worker and raw terminal ownership do not fit, by design.
- Twelve work packages, with p3's skeleton on the app kit and the session/index/sync core first. :::
Words used throughout
A hand is a daemon on a machine that connects out to a p3 server or to Radius and offers execution (commands,
files, watching, terminals). A driver is the single host allowed to write a session's store at a time; a
replica follows its commits. An epoch numbers driver changes. The index is the table of session metadata
(title, status, idle state) the session list reads. The app kit is Chord's startApp/startWorker layer.
p2 Today, and the Proposed Cut
Sizes are non-test lines in p2. "Base" means p3's first release; "later" means designed for, built after; "drop" means not carried over.
| Area | What it does | p2 size | Proposed |
|---|---|---|---|
| Sessions | list by machine and directory, create, rename, pin, archive, relocate, clone, fork, unread, automatic titles | 2,186 | base, plus delete; clone and fork in the web too |
| Conversations and agent | transcript, send, steer, follow-up, outbox, stop, model and thinking, cost, compaction | 3,682 + web 2,700 | base |
| Subagents, one-shot agents, cross-session messages | named background agents, team, agent from scripts, conversations |
2,182 | base (owner: "awesome") |
| History and handoff | re-read earlier entries after compaction, hand off with a continuation | 533 | base |
| Code mode | scripts calling tools as nested durable calls | 155 + Durable | base (small, Durable owns it) |
| Shell commands | !cmd and !!cmd in the conversation's environment |
in chat | base, web too (TUI only today) |
| Chat mode | people talk without a turn, @pi mentions |
876 | later |
| Sharing and guests | share links, guest names, revocation | 1,047 | later (the app kit's users and invites cover the start) |
| Devices and owner login | owner links, device cookies | 684 | base, replaced by the app kit's sessions and pairing |
| Models, credentials, provider login | picker, scope, API keys, OAuth flows | 3,828 | base |
| Settings | settings.json, hot reload |
87 | base |
| Prompt resources | AGENTS.md, skills, templates, read in the session's environment | 958 | base |
| Workspace | files, git status, diffs, search, review comments | 2,655 + web 1,341 | base (read and review; uploads later) |
File index and @ |
ranked file completion | 242 | base |
| Terminals | detached PTYs, ghostty-vt in the web | 3,518 | later (channels, hands) |
| Environments and locations | SSH environments, directory picker, agents moving themselves | 2,546 | base, redesigned as hands |
| Artifacts and files | attachments, send_file, versioned artifacts with previews |
1,713 | attachments and send_file base (app kit Files); artifacts later |
| Search | sidebar search over titles and messages (FTS5) | 775 | base over the index; message text search through the same sync mechanism later |
| Status and presence | busy, tasks, usage, viewers, worker failures | 566 | base (feeds the index) |
| Logs and notices | owner log viewer, notices | 487 | base |
| Voice | rooms, barge-in, speech service | 3,770 + Python | later (owner) |
| Radius relay and port forwards | brain WebSocket, up to 50 forwards | 2,000 | relay base (as the hands/sync link), forwards later |
| Web shell | PWA, sheets, navigation, reconnect | 9,252 | base, rebuilt on slice 11 ("look and feel great") |
| TUI | launcher, sandbox, 40 commands, themes, keybindings | 6,720 + launcher | base on slice 11 |
| Dev and reload | supervisor, dirty marks, reload tool | 468 + scripts | base as the app kit's "apply changes"; the supervisor goes |
Fake provider, PI_MOBILE_* aliases, P2_DEV_RESUME |
test and legacy leftovers | 218 | drop |
tool_search, MCP |
not installed in p2 | - | later |
Rough edges p3 should not inherit: features only in the TUI (shell commands, clone, fork, compact, new context), no session delete, terminals and voice without a TUI side, docs that drifted from code (voice routes, TUI notes), the plugin loader's module leak on reload (solved by Chord's plugin component).
Hands
What p2 does
p2's server and workers start a daemon (pi-env) over SSH on demand, one connection per session and per use (four
connection pools: each worker, the directory picker, the connection test, the terminal host). The daemon is
stateless and kills its commands 30 seconds after its connection goes quiet. Its frame protocol (exec, files, watch,
file listing, PTYs) does not depend on SSH.
What a hand is (proposed)
| p2's daemon | hand | |
|---|---|---|
| who dials | the server, over SSH | the hand, over WSS, to the server or to Radius |
| needs | sshd, a key, a network path to the machine | outbound HTTPS only |
| lifetime | per connection | a service (systemd, launchd, Termux:Boot), reconnecting |
| install | deployed and verified at each start | installed once, signed updates |
| machine identity | SSH host key | a hand key pair, enrolled once |
| connections | one per session and use | one per hand and server, multiplexed |
- Enrollment:
p3 hand enroll <server>shows a code; the owner approves it in the UI (the app kit's device pairing); the server pins the hand's public key; fingerprints shown on both sides. Revocation in a Hands panel. - Reachability: a hand reaches the owner's own server directly (LAN, own tunnel) or through Radius, which relays hands to whichever host drives a session (the upsell: no tunnel to set up; Radius can also offer sandboxed machines and hands of its own).
- Protocol: p2's daemon protocol over the WebSocket, unchanged in shape; two changes: several sessions share one connection (cancellation, handles and backpressure per session), and a disconnect starts a grace period instead of killing everything (detached commands and PTYs reattach on reconnect; this moves the terminal host's job into the hand).
- Binding: a session's location becomes
{ hand, cwd }; one server object holds the hand connections and theHarnessEnvironmentsentry resolves a hand id to an execution environment; workers reach hands through the server's links. An SSH machine stays possible as a hand kind the server dials. - Offline: an operation needing an offline hand fails with a clear error the model sees; the hand's status shows in the session list.
Session Sync and the Index
Decided
The worker updates the session index from events and sends it to the server; sessions sync to R2 through Radius: Radius reports per session its last sequence number, pi uploads the commit log from there; R2 holds the SQLite file and the commit log, a SQLite Durable Object applies them; sessions have an owner id; the index is a Chord table replicated like a session, its row updated only on metadata, session idle and subagent idle changes.
Chord v2 has the pieces: a versioned outbox written in each commit's transaction, export archives, batch apply that refuses gaps and divergence, export and import per address (app kit). Missing (the sync layer): upload and trim, epochs, pull-back after a power loss, the split, starting from an archive.
Gap 1: who is the driver
Two machines both write session X (the laptop resumed it while Radius continued it). Both ask Radius for its last seq, both upload from there; Radius applies whichever arrives first and refuses the other as "not following". The copies have now diverged and nobody knows.
Proposed: a driver epoch per session, handed out by Radius. An upload names its epoch; an old epoch is refused; the refused copy becomes a new session (Chord's rule S7: a split, never a rollback, because tools already ran). "Continue on Radius" is an epoch change: the local driver waits for idle, uploads the tail and the session's files, Radius bumps the epoch and starts the agent from its store, the local side becomes a replica or closes. Back is the reverse.
Gap 2: two writers of the index
When a session continues on Radius, Radius writes its index row, and so does the local server.
| Option | How | Cost |
|---|---|---|
| a. one index per writer | each host keeps its own index, Radius merges per owner, newest epoch wins per row | a second merge rule |
| b. rows derived from session stores | every host computes a session's row from the session's own commits | no index sync at all; each host needs the session's commits |
c. rows tagged { epoch, seq } |
Radius keeps the highest per row | rows must carry their source position |
Recommended: (b) where the session's commits are present (Radius has them all), with (c)'s tag on the rows a local server writes for sessions it drives, so a list without the session store still sorts correctly.
Gap 3: what else moves
Files outside the store (uploads, sent files, the app kit's Files) must sync as blobs before the batches naming
them, R2 as their home. Model access and secrets for a session continued on Radius: Radius's own models, or the
owner's provider keys stored on Radius (decide). Local prompt resources (the device's AGENTS.md, skills) must be read
through a hand or synced. Closed sessions have no worker: the server reads a session's outbox without starting it, or
starts it to sync.
Tools Built In
| Tool | p2 plugin | Proposed |
|---|---|---|
read, write, edit, bash |
coding-tools (Durable) | base |
powershell |
powershell | base (deferred tool, Windows hands) |
codemode |
codemode | base |
agent (from scripts) |
agent | base |
subagent, team |
subagents | base |
conversations |
conversations | base |
history_search, history_read, history_handoff |
history | base |
location_envs, location_dirs, location_move |
locations | base, renamed around hands (hands, hand_dirs, move) |
rename_session |
sessions | base |
send_file |
send-file | base (on the app kit's Files) |
artifact |
artifacts | later (owner: maybe not as useful) |
ports |
ports | later (with forwards) |
reload_plugins |
dev-tools | base as an "apply changes" tool for the owner, not tied to a checkout |
tool_search |
Durable | later (with MCP) |
Every tool is a plugin in the app's own root, so any of them can be removed, replaced or added without touching base code (the app kit's rule: base code is the API, bootstrap and a start-data facet).
Running p3
The CLI (proposed)
One binary, p3, and no environment variables (app kit decision); settings are arguments or files in the data
directory.
| Command | Does |
|---|---|
p3 serve [--data-dir <dir>] [--host <addr>] [--port <n>] |
starts the server (startApp); prints the one-time owner link |
p3 owner-link |
prints a new owner link for a running server (through the owner token file) |
p3 tui [url] [--pair] |
the sandboxed TUI; on the same machine it uses the owner token file, elsewhere device pairing |
p3 hand enroll <url>, p3 hand run |
enrolls this machine as a hand, runs it (or installs a service) |
p3 radius login |
signs the server into Radius (device code) |
| `p3 install <git:... | npm:...>` |
p3 export <session> <file>, p3 import <file> |
the app kit's export per address |
The data directory defaults to ~/.p3. Its files: settings.json, models.json, auth.json (locked),
AGENTS.md, skills/, prompts/, plugins/ (the user's root), the server store, a directory per session worker,
owner-token, logs/. Plugin-specific configuration moves into plugin objects (documents) instead of more files.
Themes and keybindings
Themes follow the looks-and-themes proposal (decided 2026-11-02): design tokens as CSS variables on the web, roles in
the TUI, one Themes registry plugins contribute to, one dark and one light choice per user. Keybindings: commands are
registry entries with default keys (p2's TuiCommands with when conditions, 78 binding ids); the user's overrides
live in a preferences document (per user, synced like any data), not in files on each client; the web gets the same
command registry with its own key handling.
The Plugin API
The rule
Offer few things, each stable: what an API offers, it must keep (versioned in the import path, @p3/app/v1). Every
cluster below is a registry or a contract that already exists in Chord, Durable or p2; p3 v1 publishes those and
nothing else. Legacy concepts of the old extension API are not carried: CLI flags, an event bus, RPC-mode UI, hasUI
checks, "destructive switch" gates, pi.exec running locally regardless of the environment.
Seven clusters
| Cluster | Host | Mechanism | Covers |
|---|---|---|---|
| A. The agent's turn | worker | Durable's Extension in HarnessExtensions: tools, beforeTool/afterTool, beforeRequest, sections, wraps, onYield, afterRun, beforeCompact |
20 examples: hello, permission-gate, protected-paths, tool-override, plan-mode, todo, custom-compaction, ... |
| B. Conversation control | worker object | a Conversation contract: submit, configure, append (typed entries), compact, fork, rename; state: busy, agent, usage |
send-user-message, file-trigger, session-name, preset, handoff, qna, ... |
| C. Input | worker | InputTransforms registry (rewrite or answer input before the model) |
input-transform, inline-bash |
| D. Environments and models | worker, server | HarnessEnvironments (ssh, gondolin, hands as kinds), HarnessModels (providers, virtual models), LoginProviders |
ssh, gondolin, custom providers, jev-router |
| E. Asking the user | server object, UI renderers | Questions.ask from worker code; QuestionKinds per UI kind |
permission-gate, question, questionnaire, timed-confirm, project-trust |
| F. UI | tui, web | registries: Commands (slash, palette, keys), ToolRenderers, EntryRenderers, Slots (header, footer, status, widgets), overlays through the UI context, editor extension points |
every UI-only example, the games |
| G. State and standing behaviour | any | local objects owning natives, documents for state |
file-trigger's watcher, gondolin's VM, game saves |
// a plugin with a tool, a gate and a renderer: plugins/todo/
// worker.ts
self.contribute(HarnessExtensions, { name: "todo", tools: [todoTool], hooks: [hook(ToolTask, { beforeTool: guard })] });
// tui.ts
self.contribute(TuiToolRenderers, { todo: { call: (args) => todoCall(args), result: (r) => todoList(r.details) } });
self.contribute(TuiCommands, { "todo.show": { slash: { name: "todos" }, run: (ui) => ui.overlay("todos", new TodoView(ui)) } });
// web.tsx: the same registries with React components
The litmus test
All 78 examples map onto the seven clusters (table in /tmp/p3-prep/extensions.md). The examples that mix agent and
UI split into a worker facet and a tui/web facet sharing a contract file: a tool that wants a dialog asks the
Questions object, which works from any client, several clients or none.
- Snake, Space Invaders, tic-tac-toe: a command plus an overlay in
tui.tsand a canvas inweb.tsx, engine in a shared file, saves in a user-keyed document. Fit today. - Doom: fits with two fixes in the TUI: the sandbox bridge caps a frame at 64 KiB and a Doom frame is about 160 KB (split writes at escape boundaries), and the sandbox has no files or network, so large plugin assets (a 4 MB WAD) need a documented "asset of my plugin" path (chunked reads through the plugin's server facet).
- Does not fit, by design: streaming 35 FPS frames from a worker to clients (objects carry state; needs channels),
engine state surviving an in-place reload (needs an explicit save), raw terminal ownership (
interactive-shellbecomes a terminal view; terminal notifications need a launcher action).
What v1 leaves out
Editor replacement (extension points only), raw provider stream events and response headers, a fork hook, a
turn_start hook, an event bus. Each has one or two examples; add when a real plugin needs it.
Work Packages
| # | Package | Contains | Depends on |
|---|---|---|---|
| 1 | p3 skeleton | startApp with p3's API v1 modules (shared, web, tui, worker), core plugins in the app's root, p3 CLI |
Chord slices 11-13 |
| 2 | Sessions and conversations | Durable on the Chord store, session lifecycle with delete, the conversation contract, subagents, history and handoff, code mode, shell commands | 1 |
| 3 | Session index | the index table, worker-to-server updates on metadata and idle changes, the session list, search over it | 2 |
| 4 | Session sync to Radius | outbox upload and trim, epochs, pull-back, split, files as blobs, continue on Radius and back | 2, 3, Radius side |
| 5 | Radius side | per-session SQLite DO, commit log and SQLite on R2, epochs, the sync handshake, hand relay | with 4 |
| 6 | Hands | daemon as an outbound service, enrollment by pairing, multiplexed protocol, reattach after disconnects, server hand pool, HarnessEnvironments kind, SSH as a hand kind |
1 |
| 7 | Web shell | p2's layout and flows rebuilt on slice 11 ("look and feel great"), themes | 1, slice 11 |
| 8 | TUI | p2's TUI on slice 11's sandbox, frame splitting, plugin assets path, keybindings and themes | 1, slice 11 |
| 9 | Models and settings | catalog, credentials, provider login flows, settings, prompt resources | 1 |
| 10 | Workspace | files, git, diffs, search, review comments, file index and @, through hands |
6 |
| 11 | Plugin API v1 and docs | the seven clusters published in @p3/app/v1, authoring guide, ported examples as the litmus (hello, permission-gate, todo, plan-mode, snake, doom) |
1, 2, 7, 8 |
| 12 | Access and status | owner, devices and pairing (app kit), notices, logs, status and presence | 1 |
Later: voice, terminals (channels and hands), artifacts, port forwards, chat mode and guest sharing, MCP and
tool_search.
Open Questions
- The proposed cut: base versus later per area (first table).
- Driver identity: epochs per session, handed out by Radius, with splits on conflict?
- The index with two writers: derived rows plus
{ epoch, seq }tags, or one of the other options? - Who syncs a closed session (the server reading its outbox, or starting its worker)?
- Files outside the store as blobs on R2, uploaded before their batch?
- Models and secrets for sessions continued on Radius: Radius's own models, or the owner's keys stored there?
- Hands: through Radius as a relay to the driver, and may one hand serve the local server and Radius at once?
- Do hands keep commands and terminals alive across disconnects (absorbing p2's terminal host)?
- Hand install and updates: signed binary, service install, version negotiation.
- Is SSH kept as a hand kind for machines without a service?
- Plugin API v1: the seven clusters as listed, and what is explicitly left out?
- Work package owners and order.
Sources
| Source | What |
|---|---|
/tmp/p3-prep/features.md |
p2's feature areas with files, sizes, tools, rough edges; the CLI, environment variables and data files |
/tmp/p3-prep/extensions.md |
all 78 extension examples, the capability matrix, the clusters with sketches, the hard cases |
/tmp/p3-prep/envs.md |
p2's environments and Radius link, Chord's sync pieces, the sync and hands sketches, open questions |
apps/p2/docs/p3-functions/ |
the functionality map (partly outdated) |
packages/chord/docs/next/ |
design, decisions (2026-11-02 meeting summary), app kit and plugin model, theming |