Object model
The nouns and verbs of the whole runtime: Pi, our extension packages, Pi core, and every frontend. The glossary owns the names, one or two sentences each. This page holds how they relate, their lifecycles, the verbs that change them, and where each comes from in Pi. The TUI’s object model keeps only what belongs to the TUI: its regions, components, keys, and visual rules. Why we took Pi’s model is in decision 0006.
Code and tool text still carry some older words. Where the code differs lists them, with the gaps between today’s behavior and this model.
Sources and rules
Section titled “Sources and rules”Pi has two models, and this one reads both:
- Pi’s coding agent, 1.3.0. What we run: session files, the entry tree, extension events, and RPC. Sources: Pi’s
docs/how-pi-works.md,sessions.md,session-format.md,message-types.md,extensions.md,json.md,rpc.md, andrpc-commands.md, and pi-agent-core’s README. - pi-durable, 1.2.0. Pi’s experimental durable harness (
packages/durable, read at upstream3daa202), with a normative spec. It is Pi’s newest model, and the only one that names inputs, answers, owned work, and background boundaries. The coding agent does not use it yet.
Rules:
- Where Pi has a word, use it with Pi’s meaning.
- Where the two Pi models differ, take durable’s word for what it defines: input, answer, run, task, outcome, and owner. Take the coding agent’s word for the host we run: session, entry tree, branch, delivery, events, and RPC.
- One exception: we say async task where durable says background task.
- Define a my-pi concept as an addition to Pi’s model, in Pi’s nouns, not as a parallel model.
- Presentation nouns derive from domain nouns and own no lifecycle.
- Adopting durable’s words does not adopt its API. Our code stays on the coding agent.
At a glance
Section titled “At a glance”Session: a tree of entries; the active branch is the history; runs with an Agent User turn: opened by a user's input; holds runs until its final answer Input: submitted, queued, placed as a user entry, answered by a Run Run: Turns, from an input to its answer; each turn is one assistant message and its tool calls Tool call: may launch an Async task (a shell task, a subagent) Async task: its origin is the user turn whose run launched it Subagent: owns a child Session Async task ends: an outcome, and a result it delivers to the session Wake: a delivered result that triggers a run, which belongs to the origin's user turn User turn done: its final answer is the last answer once nothing it launched can wake itA noun whose Pi source says “none” is my-pi’s own, defined in Pi’s terms.
Session and history
Section titled “Session and history”| Noun | Meaning | Pi source |
|---|---|---|
| Session | One saved conversation: a file of entries in a tree, with an id, a name, and a working directory. What the user resumes, forks, and names. | coding agent sessions.md; durable calls it a conversation |
| Entry | One immutable record in a session: a message, a model or thinking change, a compaction, a branch summary, a label, session info, or a custom entry. | session-format.md; durable Entry |
| Message | An entry’s model-facing content: user, assistant, tool result, custom, bash execution, compaction summary, or branch summary. | message-types.md |
| Active branch | The path from the root to the current entry. It supplies the history of the next model request. | how-pi-works.md |
| Tree, fork, clone | /tree moves within a session; /fork starts a new session from an earlier user message; /clone copies the active branch into a new session. | sessions.md; durable has only fork, which creates a conversation |
| Compaction | A summary entry that replaces older history in later model requests. The entries stay. | compaction.md; durable pi.compaction |
| Custom entry, custom message | Extension data kept out of the model’s context, and extension content sent in it. | session-format.md; durable write submissions |
| Transcript | The entries of the active branch, as recorded. What is drawn is the transcript view. | durable: “a conversation is a transcript”; the coding agent uses the word for both the record and the screen |
| Context | What the model receives for one request: the system prompt, the active branch’s messages after compaction, and the tools. | how-pi-works.md#context; durable §2.1 |
Execution
Section titled “Execution”| Noun | Meaning | Pi source |
|---|---|---|
| Input | Something submitted for the model to answer: a prompt, a steer, a follow-up, or a wake. It is placed as a user or custom message entry. | durable input submission; coding agent prompt, steer, followUp, sendMessage with triggerTurn |
| Queue | Inputs waiting for a boundary: steers, placed after the current tool round, and follow-ups, placed once the run answers. | coding agent queue_update, clear_queue; durable inbox |
| Turn | One assistant response and the tool calls it makes. | all three sources agree |
| Run | The turns from an input to its answer. A steer joins the current run; a follow-up starts the next one. | durable §1; the coding agent calls agent_start to agent_end a “low-level run” |
| Model request | One provider request. A turn makes one; retries and compaction make more. Say “model request”, not “request” alone. | coding agent, durable |
| Tool call | One invocation in an assistant message. It runs after its message ends. A tool can call other tools as nested calls. | message-types.md; durable §7.3 |
| Answer | The assistant message that ends a run without tool calls. It answers the run’s input and every steer that joined the run. | durable: a done submission names its answer entry |
| User turn | One request to the agent and all the work for it, until its final answer. “User” is Pi’s user role, whoever the agent answers: a person, a frontend, a parent agent, a Goal, or a supervisor. It holds the steers that join its runs and the runs its async tasks’ wakes start. “Turn” alone stays Pi’s turn. | none: Pi’s turn is one model response; durable’s run ends at the first answer |
| Final answer | A user turn’s last answer, once nothing it launched can wake the session. | durable’s answer, extended by the user turn |
| Busy, idle | A session is busy while a run is active, including from one queued run to the next. It is idle once Pi will not continue on its own. Becoming idle is settling. The coding agent’s span up to agent_settled is busy, not a run, and agent_start to agent_end is an event pair, not a noun. | durable “busy”; coding agent agent_settled |
Work and ownership
Section titled “Work and ownership”| Noun | Meaning | Pi source |
|---|---|---|
| Task | A unit of work with an owner and an outcome: a tool call or an async task. | durable Task: every task has an owner, its conversation or another task |
| Owner | What a task belongs to. Aborting an owner aborts what it owns, and an owner stays busy while what it owns runs. | durable §5.4 |
| Outcome | How a task ended: completed, failed, or aborted. | durable TaskOutcome |
| Async task | A task launched by a call that returns at once. It runs outside its session’s busy and abort scope, so the session can settle and take new input, and Esc does not reach it. Its result arrives later. | durable background task, { background: true } |
| Shell task | An async task that runs one shell process group: the bg tool’s object, with its output file. | none |
| Persistent job | A shell task that runs until stopped, such as a server. It never holds its origin waiting. | none |
| Subagent | An async task that owns a child session launched from an agent definition. In a headless parent, the launch waits for the child instead, so it is ordinary tool work there. | durable example 23, persistent background subagents |
| Child | A session owned by a task in another session: a subagent’s or a supervised one. | durable task-owned conversation |
| Result | What an async task hands back when it ends: its outcome and output, as a message to its session. | coding agent tool result; durable task result |
| Wake | A delivered result that triggers a run: an input from an async task rather than the user. Its run belongs to its origin’s user turn. | durable example 23: a background reporter posts the answer back as a follow-up input |
| Origin | The user turn whose run launched an async task. | durable derives it from ownership: tool call, then run, then input |
| Wake-capable | An async task whose result will wake the session: not persistent, with delivery and its run trigger on. | none |
| Settle report | An entry a child appends after each settle: its user turn is waiting on wake-capable work, or done. Its parent ends the subagent only on done. | none |
Configuration and extension
Section titled “Configuration and extension”| Noun | Meaning | Pi source |
|---|---|---|
| Agent | What a session runs with: model, thinking level, extensions, tools, instructions, and working directory. | durable pi.agent |
| Agent definition | A named partial agent for a child: its roster line and added instructions. | none |
| Model, provider, thinking level | As Pi defines them. | coding agent, pi-ai |
| Extension | A module Pi loads that registers tools, commands, event handlers, providers, renderers, and UI. Extension package and entrypoint are my-pi’s words for how we ship them. | coding agent; durable “a named bundle” |
| Tool, command, skill, prompt template, theme | As Pi defines them. | coding agent |
| Settings | Run policy read at each use. | settings.md; durable HarnessSettings |
| Mode | How Pi runs: interactive, print, JSON, or RPC. | how-pi-works.md#interfaces |
my-pi’s own
Section titled “my-pi’s own”These keep their meanings, now defined over the nouns above: goal; coordinator; supervisor project, with its units, attempts, lease, and inbox; primary checkout; worktree; work root, the directory a session’s tools operate in, which worktree enter sets without replacing the session; aside, a fork opened in another pane; pane agent; gateway profile; frontend; and Pi core. The glossary defines all but worktree.
Presentation
Section titled “Presentation”These derive from the nouns above and own no lifecycle.
| Noun | Derived from |
|---|---|
| Transcript view | The transcript as a frontend or display draws it. Display hides rows of the view, never entries. |
| Segment | The transcript view’s span of one user turn. A steer can start another segment inside it, to keep the text drawn before the steer. |
| Status block, status row | The running user turn, else the newest one that waits; the row also names the other user turns’ waits. |
| Answer summary | A done user turn’s one-line record: its active time and tool calls. |
| Notice | A transient UI message, Pi’s ctx.ui.notify. A result is not a notice. |
| Block | One drawn transcript entry in my-tui. |
| Dialog, status entry, widget, footer | Extension UI, as Pi defines it. |
Retired words are listed in the glossary, each under Avoid in the entry for the word that replaced it.
Eight verbs, grouped by who does them. Steer and follow-up are modes of submit, not verbs.
| Actor | Verb | Acts on | Meaning | Pi | my-pi surface |
|---|---|---|---|---|---|
| user, extension, task | submit | input, as a prompt, steer, or follow-up | Hand an input to a session: a prompt when idle; a steer or follow-up when busy. | RPC prompt with streamingBehavior; sendUserMessage; durable submit with whenBusy | Enter; Alt+Enter |
| user | withdraw | queued input | Take it back out of the queue. | RPC clear_queue; durable submission.abort() | Alt+Up; Esc returns it to the draft |
| user, model | abort | the run, or one async task | Stop it and the work it owns. Aborting the run withdraws queued inputs; async tasks continue. | RPC abort; durable abort() | Esc (app.interrupt); bg stop, subagent stop |
| user | replace | session | Swap the running session for another: resume, new, fork, or clone. | session_before_switch; RPC switch_session, new_session, fork, clone | /resume, /new, /fork |
| user | configure | agent | Change the model, thinking level, or tools. | set_model, set_thinking_level; durable configure() | Ctrl+L, Shift+Tab |
| model, through a tool | launch | async task | Start an async task; the call returns at once. | durable createTask with background: true | bg run, subagent start |
| async task | deliver | its result, as a follow-up, except a subagent’s result in a headless parent or a nesting child, or from a launch that fails, which steers; a subagent’s ping or ask, as a steer | Send the result to its session. A delivery that triggers a run is a wake; one that does not is appended. A result withdrawn from the queue, by a withdraw or an abort, is delivered again once; after an abort it joins the next run instead of starting one. | sendMessage with deliverAs and triggerTurn | shell tasks, subagents |
| extension | append | entry | Record an entry the model does not answer: a custom entry, or a custom message without a trigger. | appendEntry, sendMessage; durable write | settle reports, status records |
What the verbs absorb:
- Steer and follow up are modes of submit, as they are Pi’s
streamingBehavior,deliverAs, andwhenBusyvalues. - Notify, wake, and report are deliver. Wake stays a noun, and a child’s report to its parent is its delivered result.
- Stop is abort applied to one async task; the tool action keeps the name
stop. - Revise is no act of its own: a wake’s run belongs to its origin’s user turn, so its answer becomes that user turn’s latest answer. That is answer continuation.
- Place, answer, and settle are transitions Pi performs, so they live in the lifecycles below.
- Compact, name, and
/treestay Pi’s session commands, not domain verbs.
Lifecycles
Section titled “Lifecycles”Transitions are Pi’s; the verbs above cause some of them.
Durable’s submission states:
submit ─▶ queued ──place──▶ placed ──answer──▶ done │ │ └──withdraw────────┴──abort, failure──▶ unanswered (aborted, failed, reset)An input submitted to an idle session is placed at once; done carries the answer entry.
User turn
Section titled “User turn”My-pi’s addition, over the runs it holds. What starts one:
| Input | Effect |
|---|---|
| A prompt or follow-up: from a person, a frontend, or a parent agent sending its child a task | starts a user turn |
Goal’s start and resume, sent after the user’s /goal, and an arranged Goal wake | starts a user turn |
| A supervisor wake | starts a user turn |
| A steer | joins the running user turn |
| An async task’s result | runs in its origin’s user turn, never a new one |
| A stall retry | continues the user turn whose run it recovers |
open ─▶ running ──a run answers──▶ waiting ──nothing left that can wake it──▶ done ▲ │ │ │ └──abort, failure──▶ unanswered └────a wake starts a run──┘- A user turn is running while one of its runs is active, and waiting once a run has answered while a wake-capable async task it launched is live. Its latest answer is provisional meanwhile, and the session may be idle or running another user turn.
- A wake starts a run in its origin’s user turn, and that run’s answer becomes the user turn’s latest answer.
- With nothing left that can wake it, the user turn is done, and its last answer is its final answer. A user turn that launched nothing wake-capable is done at its first answer.
- A child’s settle report is this state for the child’s own user turn:
waitingordone.
Durable’s task states are pending, running, waiting, completing, then terminal, with the outcome completed, failed, aborted, orphaned, or faulted. An async task delivers its result as it completes, and is terminal once that result is in the session, or at once when it delivers none.
| Ours | Durable outcome |
|---|---|
shell task completed; subagent completed | completed |
shell task failed (exit code, timeout, output cap, signal); subagent failed | failed |
shell task killed; subagent cancelled | aborted |
subagent timed-out, idle | failed, with our reason; both are local additions |
The async-task service lists our tasks the same way: running; completing from the known outcome until the result is in the session; then terminal, at once for a task that delivers none.
Session
Section titled “Session”A session is busy or idle, and it settles when it becomes idle. Herdr’s working, blocked, and idle are a different reading, of the pane, and stay separate: architecture keeps the rule against one universal busy flag.
Where the code differs
Section titled “Where the code differs”The model is ahead of the code in these places:
- An aborted user turn that still waits keeps waiting. In the model it ends unanswered at the abort, and a later wake from one of its async tasks reopens it. The code leaves it waiting, so that wake runs in it and one answer summary covers both. A wake whose origin is not open, such as a
/bgtask’s, starts a user turn of its own. - Segments follow component order, not user turns. A run that a hidden message starts, such as a Goal or supervisor wake, while an earlier user turn waits draws in that user turn’s segment, because no drawn row marks where the new user turn starts. Live answer ids keep each user turn’s latest good answer visible even when segments overlap; a wake’s revision draws where its run runs, below any later user turn. The transcript view hides the answer it replaced only while user-turns remembers those ids, in memory: a reload falls back to component order.
- A result in another user turn’s run revises nothing. A result that an abort drops joins the next run, which usually belongs to another user turn; that run answers it, and the origin’s earlier answer stays its final answer.
- “Answer” has two readings. Display’s draw rule shows a segment’s last assistant message with visible text. The model’s answer is the message that ends a run without tool calls, which can be thinking-only or empty.
- The async-task service keeps an older reading. It lists a Goal-owned subagent as wake-capable, though Goal’s guarded wake can decline to start a run.
Some names keep older words, on purpose or until a rename:
| Concept | Name in code or session files today |
|---|---|
| A shell task’s result | the bg tool’s notifyOnCompletion parameter |
| User turn | the turn title of tui-display’s status block |