Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: LLMLarge Language Model. ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). Abstraction
Overview
pi-ai provides a single unified API for chat and image generation across 30+ LLMLarge Language Model. providers, abstracting away differences in wire protocols, authentication, streaming formats, tool-calling schemas, and token/cost accounting.
Consumers (the agent runtime, the coding agent, and third-party SDKSoftware Development Kit (the embeddable programmatic API). users) program against one Models collection and one event-stream contract instead of per-provider SDKs.
Stakeholders
| Stakeholder | Interest |
|---|---|
| Agent runtime (pi-agent-core) | A stable StreamFn contract and message types to drive the agent loop |
| Coding agent (pi-coding-agent) | Broad provider coverage, automatic auth resolution, and a model catalog |
| SDKSoftware Development Kit (the embeddable programmatic API). / extension authors | A clean public API to register custom providers and models |
| End users | Access to their chosen model and subscription with minimal setup |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FRFunctional Requirement.-01MustThe system shall expose a `Models` collection that routes model lookups and streams by owning provider. | Must | The system shall expose a Models collection that routes model lookups and streams by owning provider. |
| FRFunctional Requirement.-02MustThe system shall support the major wire protocols: Anthropic Messages, OpenAI Responses, OpenAI Completions, Google Generative AI, Google Vertex, Mistral Conversations, and Bedrock Converse Stream. | Must | The system shall support the major wire protocols: Anthropic Messages, OpenAI Responses, OpenAI Completions, Google Generative AI, Google Vertex, Mistral Conversations, and Bedrock Converse Stream. |
| FRFunctional Requirement.-03MustThe system shall stream assistant responses as an async-iterable event stream emitting `start`, text/thinking/toolcall deltas, `done`, and `error` events. | Must | The system shall stream assistant responses as an async-iterable event stream emitting start, text/thinking/toolcall deltas, done, and error events. |
| FRFunctional Requirement.-04Must"aborted"` and never throw out of a stream. | Must | The system shall encode all stream failures as terminal events with stopReason: "error" \ | "aborted" and never throw out of a stream. |
| FRFunctional Requirement.-05MustThe system shall resolve provider authentication via a credential store first (OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). or API keyApplication Programming Interface key (ambient provider authentication).), falling back to ambient env-var resolution only when nothing is stored. | Must | The system shall resolve provider authentication via a credential store first (OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). or API keyApplication Programming Interface key (ambient provider authentication).), falling back to ambient env-var resolution only when nothing is stored. |
| FRFunctional Requirement.-06MustThe system shall support OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). flows (PKCEProof Key for Code Exchange (OAuth flow used by pi-ai). and device code) for subscription-based providers. | Must | The system shall support OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). flows (PKCEProof Key for Code Exchange (OAuth flow used by pi-ai). and device code) for subscription-based providers. |
| FRFunctional Requirement.-07MustThe system shall represent tool definitions and tool calls using TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). schemas that are serializable and self-validating. | Must | The system shall represent tool definitions and tool calls using TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). schemas that are serializable and self-validating. |
| FRFunctional Requirement.-08MustThe system shall lazily load provider SDKs on first request rather than at import time. | Must | The system shall lazily load provider SDKs on first request rather than at import time. |
| FRFunctional Requirement.-09MustThe system shall generate its model catalog (`models.generated.ts`) from live upstream sources (models.dev, OpenRouter, Vercel AI Gateway), filtering to tool-capable models. | Must | The system shall generate its model catalog (models.generated.ts) from live upstream sources (models.dev, OpenRouter, Vercel AI Gateway), filtering to tool-capable models. |
| FRFunctional Requirement.-10ShouldThe system shall track token usage and cost per request. | Should | The system shall track token usage and cost per request. |
| FRFunctional Requirement.-11ShouldThe system shall support cross-provider handoffs, converting thinking blocks from foreign providers into tagged text. | Should | The system shall support cross-provider handoffs, converting thinking blocks from foreign providers into tagged text. |
| FRFunctional Requirement.-12ShouldThe system shall provide per-provider compatibility flags (e.g. `OpenAICompletionsCompat`) auto-detected from baseUrl and overridable per model. | Should | The system shall provide per-provider compatibility flags (e.g. OpenAICompletionsCompat) auto-detected from baseUrl and overridable per model. |
| FRFunctional Requirement.-13ShouldThe system shall provide a `faux` in-memory provider for deterministic testing. | Should | The system shall provide a faux in-memory provider for deterministic testing. |
| FRFunctional Requirement.-14MayThe system shall provide an image-generation API (`ImagesModels`) parallel to the chat API. | May | The system shall provide an image-generation API (ImagesModels) parallel to the chat API. |
| FRFunctional Requirement.-15MayThe system shall preserve a deprecated `/compat` entrypoint for the legacy global API during migration. | May | The system shall preserve a deprecated /compat entrypoint for the legacy global API during migration. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFRNon-Functional Requirement.-01MustThe root `index.ts` shall be side-effect free. | Must | Compatibility | The root index.ts shall be side-effect free. |
| NFRNon-Functional Requirement.-02MustSynchronous model reads (`getModels`, `getModel`) shall return last-known data without awaiting; `refresh()` shall be the explicit async verb. | Must | Performance | Synchronous model reads (getModels, getModel) shall return last-known data without awaiting; refresh() shall be the explicit async verb. |
| NFRNon-Functional Requirement.-03MustOAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). token refresh shall use double-checked locking so concurrent requests refresh at most once. | Must | Reliability | OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). token refresh shall use double-checked locking so concurrent requests refresh at most once. |
| NFRNon-Functional Requirement.-04MustThe system shall not silently fall back to ambient auth after a failed stored-credential refresh. | Must | Security | The system shall not silently fall back to ambient auth after a failed stored-credential refresh. |
| NFRNon-Functional Requirement.-05ShouldSDKSoftware Development Kit (the embeddable programmatic API). loading shall not block stream creation; setup may run behind a lazily returned stream. | Should | Performance | SDKSoftware Development Kit (the embeddable programmatic API). loading shall not block stream creation; setup may run behind a lazily returned stream. |
| NFRNon-Functional Requirement.-06ShouldProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). factories shall be thin wrappers over a shared `createProvider` helper. | Should | Maintainability | ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). factories shall be thin wrappers over a shared createProvider helper. |
Constraints
- Only tool-call-capable models are cataloged.
- Generated files (
models.generated.ts,*.models.ts) must never be hand-edited. - Erasable TypeScript syntax only; ESM only; Node
>=22.19.0.
Acceptance Criteria
Every FRFunctional Requirement. and NFRNon-Functional Requirement. shall have at least one acceptance criterion.
Order criteria by FRs first (sorted by ID), then NFRs (sorted by ID).
- FRFunctional Requirement.-01MustThe system shall expose a `Models` collection that routes model lookups and streams by owning provider.
- Given a
Modelscollection with multiple registered providers - When a consumer requests a model by
provider/id - Then the owning provider resolves the model and its stream behavior.
- Given a
- FRFunctional Requirement.-03MustThe system shall stream assistant responses as an async-iterable event stream emitting `start`, text/thinking/toolcall deltas, `done`, and `error` events.
- Given a configured provider and a prompt
- When the consumer calls the stream function
- Then it receives an
AssistantMessageEventStreamyielding ordered text/thinking/toolcall deltas and a terminaldoneevent.
- FRFunctional Requirement.-04Must"aborted"` and never throw out of a stream.
- Given a provider request that fails mid-stream
- When the failure occurs
- Then the stream emits an
errorevent with partial content and astopReasonoferrororabortedinstead of throwing.
- FRFunctional Requirement.-05MustThe system shall resolve provider authentication via a credential store first (OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). or API keyApplication Programming Interface key (ambient provider authentication).), falling back to ambient env-var resolution only when nothing is stored.
- Given a provider with both a stored credential and an ambient env var
- When auth is resolved
- Then the stored credential is used and the env var is ignored.
- FRFunctional Requirement.-07MustThe system shall represent tool definitions and tool calls using TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). schemas that are serializable and self-validating.
- Given a tool defined with a TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). parameter schema
- When the provider returns a tool call
- Then the arguments are parsed and available for validation without provider-specific handling.
- FRFunctional Requirement.-08MustThe system shall lazily load provider SDKs on first request rather than at import time.
- Given a fresh process importing the root entrypoint
- When no request has been made
- Then no provider SDKSoftware Development Kit (the embeddable programmatic API). (
@anthropic-ai/sdk,openai, etc.) is loaded.
- FRFunctional Requirement.-09MustThe system shall generate its model catalog (`models.generated.ts`) from live upstream sources (models.dev, OpenRouter, Vercel AI Gateway), filtering to tool-capable models.
- Given the model generator
- When run via
npm run generate-models - Then per-provider
*.models.tsfiles and the aggregatormodels.generated.tsare written, containing only tool-capable models.
- NFRNon-Functional Requirement.-01MustThe root `index.ts` shall be side-effect free.
- Given a consumer importing the root
index.ts - When the import completes
- Then no provider factories, generated catalogs, or OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). implementations have executed as side effects.
- Given a consumer importing the root
- NFRNon-Functional Requirement.-03MustOAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). token refresh shall use double-checked locking so concurrent requests refresh at most once.
- Given an expired OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). token and concurrent requests
- When both attempt to refresh
- Then exactly one network refresh occurs and both requests use the refreshed token.
Conflicts
None identified yet.
Open Questions
- What is the timeline and completion criteria for retiring the
/compatentrypoint once coding-agent'sModelManagermigration finishes? - Which providers, if any, are considered tier-1 (must-ship) versus community-maintained for the purposes of future feature work?
Specification: LLMLarge Language Model. ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). Abstraction
Overview
pi-ai is a provider-centric LLMLarge Language Model. abstraction.
Each provider owns its model catalog, auth, and stream behavior and delegates to one of a small set of shared wire-protocol ("API") implementations.
A Models collection routes by provider, streams flow through a unified AssistantMessageEventStream, and SDKs load lazily on first use.
Architecture
Consumer (Agent / SDKSoftware Development Kit (the embeddable programmatic API).)
|
v
+-------------------+ routes by provider id
| Models collection |----+ (createModels / builtinProviders)
+-------------------+ |
v
+-------------------+ owns auth + catalog
| ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). |
+---------+---------+
| delegates to
v
+-------------------+ wire protocol
| API impl (lazy) | (anthropic-messages,
+---------+---------+ openai-responses, ...)
| streams
v
+-------------------+
| AssistantMessage |
| EventStream |
+-------------------+
Data Models
ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`).
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | string | unique | ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). identifier (e.g. anthropic) |
| name | string | not null | Display name |
| baseUrl | string | optional | API base URL |
| auth | ProviderAuth | not null | API keyApplication Programming Interface key (ambient provider authentication). and/or OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). resolvers |
| models | Model[] | not null | Catalog of models |
| api | ApiFactory | not null | Wire-protocol factory |
Model
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | string | unique per provider | Model identifier |
| provider | string | FK -> ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`)..id | Owning provider |
| contextWindow | number | not null | Max context tokens |
| pricing | object | optional | Input/output/cache cost per million tokens |
| reasoning | object | optional | Supported thinking levels and budgets |
| compat | object | optional | ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). compatibility flags |
AssistantMessageEvent
| Field | Type | Constraints | Description |
|---|---|---|---|
| type | enum | not null | start, text_*, thinking_*, toolcall_*, done, error |
| partial | AssistantMessage | optional | Cumulative message at event time |
| contentIndex | number | optional | Associates delta with a content block |
| stopReason | enum | on done/error | stop, length, toolCall, error, aborted |
Tool
| Field | Type | Constraints | Description |
|---|---|---|---|
| name | string | not null | Tool name |
| description | string | not null | LLMLarge Language Model.-facing description |
| parameters | TSchema (TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating).) | not null | Serializable, self-validating schema |
API Contracts
Models.stream(model, context)
Request
| Field | Type | Required | Description |
|---|---|---|---|
| model | Model | yes | Target model |
| context | Context | yes | System prompt, messages, tools, reasoning level |
| apiKey | ModelAuth | optional | Explicit auth override |
Response: an AssistantMessageEventStream (async iterable) whose .result() resolves to the final AssistantMessage.
Error contract: failures are encoded as a final event with stopReason: "error" \| "aborted" and errorMessage; the function never throws.
Models.refresh()
Resolves the latest catalogs/auth; returns a promise. Synchronous readers (getModels/getModel) return last-known data without awaiting.
Sequences
Streaming a prompt
Consumer -> Models: stream(model, context)
Models -> ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`).: resolve auth (store first, then ambient)
ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). -> API impl: lazy-load SDKSoftware Development Kit (the embeddable programmatic API)., open stream
API impl -> Consumer: AssistantMessageEventStream
loop: yield start/text_delta/toolcall_delta ... done|error
Consumer awaits stream.result() -> AssistantMessage
OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). token refresh (double-checked locking)
Request -> resolveStoredOAuth: token expired?
yes -> acquire credentials.modify lock
-> re-check expiry (another request may have refreshed)
-> if still expired: refresh once, persist
-> release lock
no -> use cached token (zero locks)
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Routing unit | ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). (not API) | A provider owns auth + catalog; multiple providers share one wire protocol |
| SDKSoftware Development Kit (the embeddable programmatic API). loading | Lazy via .lazy.ts wrappers |
Keeps import side-effect-free and startup fast |
| Failure model | Encoded in stream | Callers always get a stream object; no try/catch around the call |
| Schema library | TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | JSON-serializable, self-validating, works across providers |
| Sync vs async reads | Sync last-known + explicit refresh() |
Avoids blocking the agent loop on network |
Risks and Unknowns
- The
/compatentrypoint duplicates the provider-centric API; its removal depends on the coding-agentModelManagermigration completing. - Generated catalogs depend on live upstream APIs (models.dev, OpenRouter, Vercel); outages during generation could stall releases.
- ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`).-specific compatibility quirks (token counting, cache headers, thinking-level support) require ongoing manual overrides in the generator.
Out of Scope
- The agent loop and tool execution (FEAT-0002).
- SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). persistence, compaction, and branching (FEAT-0006).
- The TUITerminal User Interface (the interactive mode, and the `pi-tui` library). and interactive experience (FEAT-0003, FEAT-0004).
Test Plan: LLMLarge Language Model. ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). Abstraction
Scope
Covers pi-ai provider abstraction, streaming, auth, token tracking, model catalog generation, and cross-provider compatibility. Does not cover agent loop, session, or TUITerminal User Interface (the interactive mode, and the `pi-tui` library)..
Unit Tests
Test files under packages/ai/test/ cover:
- ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). factories and auth resolution (providers.test.ts, oauth-auth.test.ts, oauth-device-code.test.ts, env-api-keys.test.ts, google-vertex-api-key-resolution.test.ts)
- Streaming protocol (stream.test.ts, anthropic-sse-parsing.test.ts, openai-completions-.test.ts, openai-responses-.test.ts)
- Tool calling (tool-call-id-normalization.test.ts, tool-call-without-result.test.ts, deferred-tools.test.ts)
- Token/cost tracking (tokens.test.ts, total-tokens.test.ts, cache-retention.test.ts, anthropic-cache-write-1h-cost.test.ts)
- Model catalog (providers.test.ts, firework-models.test.ts, together-models.test.ts, bedrock-models.test.ts, xiaomi-models.test.ts)
- Cross-provider handoff (cross-provider-handoff.test.ts)
- Error handling (error-body.test.ts, provider-error-body-passthrough.test.ts, retry.test.ts, abort.test.ts)
- Image support (image-tool-result.test.ts, images.test.ts, images-models.test.ts)
- Compatibility (compat-env.test.ts, anthropic-eager-tool-input-compat.test.ts, anthropic-temperature-compat.test.ts)
- OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). flows (oauth-auth.test.ts, oauth-device-code.test.ts, github-copilot-oauth.test.ts, openai-codex-oauth.test.ts, anthropic-oauth.test.ts)
- Lazy loading (lazy-module-load.test.ts)
- Context overflow (context-overflow.test.ts, overflow.test.ts)
- Thinking/reasoning (anthropic-adaptive-thinking-models.test.ts, anthropic-force-adaptive-thinking.test.ts, max-thinking.test.ts, google-thinking-disable.test.ts, mistral-reasoning-mode.test.ts)
- Various provider-specific compat (bedrock-.test.ts, google-.test.ts, azure-openai-base-url.test.ts, node-http-proxy.test.ts)
Integration Tests
E2E tests with live provider credentials (enabled when env vars are present) cover real streaming, auth, and tool-calling flows (anthropic-eager-tool-input-e2e.test.ts, anthropic-long-cache-retention-e2e.test.ts, openai-responses-reasoning-replay-e2e.test.ts, openai-codex-stream.test.ts, bedrock-thinking-payload.test.ts).
Edge Cases and Failure Scenarios
- Surrogate pairs in messages (unicode-surrogate.test.ts)
- ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). error body passthrough (provider-error-body-passthrough.test.ts, provider-error-body-regression.test.ts)
- Cache retention edge cases (cache-retention.test.ts)
- Empty tool results (openai-responses-empty-tool-result.test.ts)
- Partial JSON cleanup (openai-responses-partial-json-cleanup.test.ts)
- Various compat shims (anthropic-empty-thinking-signature-compat.test.ts, anthropic-tool-name-normalization.test.ts)
Test Infrastructure
- Faux providerAn in-memory scripted provider (`providers/faux.ts`) used for deterministic tests with no real API calls. (
providers/faux.ts) for deterministic, network-free testing - Model registry with mock/stub providers
- OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). test server for device code and PKCEProof Key for Code Exchange (OAuth flow used by pi-ai). flow testing
Coverage Matrix
| Requirement | Test Files |
|---|---|
| FRFunctional Requirement.-01MustThe system shall expose a `Models` collection that routes model lookups and streams by owning provider. (Models collection routing) | providers.test.ts, models-runtime.test.ts |
| FRFunctional Requirement.-02MustThe system shall support the major wire protocols: Anthropic Messages, OpenAI Responses, OpenAI Completions, Google Generative AI, Google Vertex, Mistral Conversations, and Bedrock Converse Stream. (Wire protocol support) | stream.test.ts, various anthropic/openai/bedrock/google test files |
| FRFunctional Requirement.-03MustThe system shall stream assistant responses as an async-iterable event stream emitting `start`, text/thinking/toolcall deltas, `done`, and `error` events. (Event stream) | stream.test.ts |
| FRFunctional Requirement.-04Must"aborted"` and never throw out of a stream. (Failure encoding) | error-body.test.ts, retry.test.ts, abort.test.ts |
| FRFunctional Requirement.-05MustThe system shall resolve provider authentication via a credential store first (OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). or API keyApplication Programming Interface key (ambient provider authentication).), falling back to ambient env-var resolution only when nothing is stored. (Auth resolution) | oauth-auth.test.ts, env-api-keys.test.ts, compat-env.test.ts |
| FRFunctional Requirement.-06MustThe system shall support OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). flows (PKCEProof Key for Code Exchange (OAuth flow used by pi-ai). and device code) for subscription-based providers. (OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). flows) | oauth-auth.test.ts, oauth-device-code.test.ts |
| FRFunctional Requirement.-07MustThe system shall represent tool definitions and tool calls using TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). schemas that are serializable and self-validating. (TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). tool schemas) | tool-call-id-normalization.test.ts, validation.test.ts |
| FRFunctional Requirement.-08MustThe system shall lazily load provider SDKs on first request rather than at import time. (Lazy SDKSoftware Development Kit (the embeddable programmatic API). loading) | lazy-module-load.test.ts |
| FRFunctional Requirement.-09MustThe system shall generate its model catalog (`models.generated.ts`) from live upstream sources (models.dev, OpenRouter, Vercel AI Gateway), filtering to tool-capable models. (Model catalog generation) | providers.test.ts, bedrock-models.test.ts, firework-models.test.ts |
| FRFunctional Requirement.-10ShouldThe system shall track token usage and cost per request. (Token/cost tracking) | tokens.test.ts, total-tokens.test.ts |
| FRFunctional Requirement.-11ShouldThe system shall support cross-provider handoffs, converting thinking blocks from foreign providers into tagged text. (Cross-provider handoff) | cross-provider-handoff.test.ts |
| FRFunctional Requirement.-12ShouldThe system shall provide per-provider compatibility flags (e.g. `OpenAICompletionsCompat`) auto-detected from baseUrl and overridable per model. (Compat flags) | various anthropic/openai compat test files |
| FRFunctional Requirement.-13ShouldThe system shall provide a `faux` in-memory provider for deterministic testing. (Faux providerAn in-memory scripted provider (`providers/faux.ts`) used for deterministic tests with no real API calls.) | faux-provider.test.ts |
| FRFunctional Requirement.-14MayThe system shall provide an image-generation API (`ImagesModels`) parallel to the chat API. (Image generation) | images.test.ts, images-models.test.ts |
| FRFunctional Requirement.-15MayThe system shall preserve a deprecated `/compat` entrypoint for the legacy global API during migration. (/compat entrypoint) | compat-env.test.ts |
| NFRNon-Functional Requirement.-01MustThe root `index.ts` shall be side-effect free. (Side-effect free root) | lazy-module-load.test.ts |
| NFRNon-Functional Requirement.-02MustSynchronous model reads (`getModels`, `getModel`) shall return last-known data without awaiting; `refresh()` shall be the explicit async verb. (Sync reads, async refresh) | models-runtime.test.ts |
| NFRNon-Functional Requirement.-03MustOAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). token refresh shall use double-checked locking so concurrent requests refresh at most once. (Double-checked OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). locking) | oauth-auth.test.ts |
requirements
- What is the timeline and completion criteria for retiring the
/compatentrypoint once coding-agent'sModelManagermigration finishes? - Which providers, if any, are considered tier-1 (must-ship) versus community-maintained for the purposes of future feature work?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. | The project: a minimal, self-extensible terminal coding agent harness and its libraries. |
| HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. | The coding agent runtime that wires the agent loop, tools, sessions, and UI together. |
| ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. | A TypeScript module with a default export function (pi: ExtensionAPI) that augments the agent with tools, commands, events, UI, or providers. |
| SkillAn on-demand capability package following the Agent Skills standard (`SKILL.md` + optional frontmatter), invoked as `/skill:name`. | An on-demand capability package following the Agent Skills standard (SKILL.md + optional frontmatter), invoked as /skill:name. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| pi packageA distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via `pi install`. | A distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via pi install. |
| SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). | A persistent, branchable conversation log stored as JSONL (SessionHeader, messages, compaction summaries, branch summaries). |
| BranchingTree-structured session forking (`/fork`, `/clone`, `/tree`) where each entry has `id`/`parentId`. | Tree-structured session forking (/fork, /clone, /tree) where each entry has id/parentId. |
| CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. | Lossy summarization of older session messages to reclaim context; original JSONL is preserved. |
| SteeringA queued message delivered to a streaming agent after the current tool batch completes. | A queued message delivered to a streaming agent after the current tool batch completes. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| Project trustA per-folder decision (`~/.pi/agent/trust.json`) gating whether project settings, resources, and extensions execute. | A per-folder decision (~/.pi/agent/trust.json) gating whether project settings, resources, and extensions execute. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Faux providerAn in-memory scripted provider (`providers/faux.ts`) used for deterministic tests with no real API calls. | An in-memory scripted provider (providers/faux.ts) used for deterministic tests with no real API calls. |
| CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. | The deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. |
| Remote sessionA pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. | A pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. |
| Session leaseA remote-session ownership handle (`exclusive` or `shared`) granted by `PiClient.acquireSession()`/`createSession()`; gates who may mutate or observe a session. | A remote-session ownership handle (exclusive or shared) granted by PiClient.acquireSession()/createSession(); gates who may mutate or observe a session. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| Byte transportThe minimal ordered byte-stream interface (`send`/`close`, inbound `onData`/`onClose`/`onError`) that `PiClient` uses; keeps pi-client Node-agnostic. | The minimal ordered byte-stream interface (send/close, inbound onData/onClose/onError) that PiClient uses; keeps pi-client Node-agnostic. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| Legacy supervisorThe renamed orchestrator code in pi-server (`server` CLI, child-process supervision, Radius, RPC stream bridge). | The renamed orchestrator code in pi-server (server CLICommand-Line Interface (the `pi` binary)., child-process supervision, Radius, RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). stream bridge). |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`. | Behavioral, model-backed checks for pi workflows run via vitest-evals with a real AgentSession. |
Technical Terms
| Term | Definition |
|---|---|
| ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). | The runtime unit owning a model catalog, auth, and stream behavior (e.g. anthropic, openai). |
| API implementationA wire-protocol backend shared by providers (e.g. `anthropic-messages`, `openai-responses`, `openai-completions`, `google-generative-ai`, `bedrock-converse-stream`). | A wire-protocol backend shared by providers (e.g. anthropic-messages, openai-responses, openai-completions, google-generative-ai, bedrock-converse-stream). |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
Agent |
pi-agent-core's stateful class owning the transcript and lifecycle (prompt, continue, abort). |
AgentHarness |
pi-agent-core's higher-level orchestrator wrapping Agent with sessions, compaction, skills, and provider hooks. |
agentLoop |
The low-level prompt-stream-tool-continue loop in pi-agent-core. |
AgentMessage |
pi-agent-core's app-extensible message union (via declaration merging); convertToLlm bridges to pi-ai Message. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| FramingThe pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBOR item. | The pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item. |
hello |
The first message a pi-client sends, carrying PROTOCOL_VERSION and a bearer token. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
| Differential renderingpi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. | pi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. |
| Synchronized outputTerminal escape sequence (`\x1b[?2026h..l`) used by pi-tui for atomic, flicker-free rendering. | Terminal escape sequence (\x1b[?2026h..l) used by pi-tui for atomic, flicker-free rendering. |
| Kitty keyboard protocolTerminal input protocol pi-tui negotiates for richer key reporting. | Terminal input protocol pi-tui negotiates for richer key reporting. |
| Lockstep versioningAll packages share one version and release together. | All packages share one version and release together. |
| Trusted publishingnpm publish via GitHub Actions OIDC (environment `npm-publish`); no local credentials required. | npm publish via GitHub Actions OIDCOpenID Connect (used for npm trusted publishing identity). (environment npm-publish); no local credentials required. |
| Shrinkwrap`packages/coding-agent/npm-shrinkwrap.json`, generated from the root lockfile to pin transitive deps for npm users. | packages/coding-agent/npm-shrinkwrap.json, generated from the root lockfile to pin transitive deps for npm users. |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| LLMLarge Language Model. | Large Language Model. |
| MCPModel Context Protocol (not built into core; extensions may add it). | Model Context Protocol (not built into core; extensions may add it). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). | Open Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). |
| PKCEProof Key for Code Exchange (OAuth flow used by pi-ai). | Proof Key for Code Exchange (OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). flow used by pi-ai). |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| FRFunctional Requirement. | Functional Requirement. |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). | Input Method Editor (pi-tui positions the hardware cursor for IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). candidate windows). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
| AGENTS.mdProject-specific rules file for humans and agents, read automatically from the repo root. | Project-specific rules file for humans and agents, read automatically from the repo root. |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Agent Runtime
Overview
pi-agent-core provides the stateful agent runtime that sits between the LLMLarge Language Model. abstraction (pi-ai) and application UIs.
It owns the transcript, runs the prompt-stream-tool-continue loop, executes tools (sequentially or in parallel), manages message queues (steering and follow-up), and exposes a higher-level AgentHarness with sessions, compaction, skills, and provider hooks.
Stakeholders
| Stakeholder | Interest |
|---|---|
| Coding agent (pi-coding-agent) | A reliable Agent/AgentHarness to drive prompts, tools, and session lifecycle |
| SDKSoftware Development Kit (the embeddable programmatic API). / embedding users | A transport-agnostic runtime they can point at any StreamFn |
| ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. authors | Typed hooks (tool_call, tool_result, before_provider_request, etc.) to observe and mutate behavior |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FRFunctional Requirement.-01MustThe system shall provide an `Agent` class owning the transcript, tools, model, and thinking level, with `prompt`, `continue`, `abort`, and `subscribe` methods. | Must | The system shall provide an Agent class owning the transcript, tools, model, and thinking level, with prompt, continue, abort, and subscribe methods. |
| FRFunctional Requirement.-02MustThe system shall run an agent loop that streams an assistant response, extracts tool calls, executes them, and continues until no more tool calls or queued messages remain. | Must | The system shall run an agent loop that streams an assistant response, extracts tool calls, executes them, and continues until no more tool calls or queued messages remain. |
| FRFunctional Requirement.-03MustThe system shall call an injectable `StreamFn` (default pi-ai `streamSimple`) to reach the LLMLarge Language Model., keeping the runtime transport-agnostic. | Must | The system shall call an injectable StreamFn (default pi-ai streamSimple) to reach the LLMLarge Language Model., keeping the runtime transport-agnostic. |
| FRFunctional Requirement.-04MustThe system shall execute tool calls either sequentially or in parallel based on config and per-tool `executionMode`, emitting `tool_execution_*` events in completion order while persisting tool results in source order. | Must | The system shall execute tool calls either sequentially or in parallel based on config and per-tool executionMode, emitting tool_execution_* events in completion order while persisting tool results in source order. |
| FRFunctional Requirement.-05MustThe system shall validate tool arguments before execution and support `beforeToolCall`/`afterToolCall` hooks that can block, override, or terminate a tool call. | Must | The system shall validate tool arguments before execution and support beforeToolCall/afterToolCall hooks that can block, override, or terminate a tool call. |
| FRFunctional Requirement.-06MustThe system shall support message queues for steering (delivered after the current tool batch) and follow-up (delivered after the agent stops). | Must | The system shall support message queues for steering (delivered after the current tool batch) and follow-up (delivered after the agent stops). |
| FRFunctional Requirement.-07MustThe system shall support abort via an `AbortController` whose signal flows to `streamFn` and tool `execute`. | Must | The system shall support abort via an AbortController whose signal flows to streamFn and tool execute. |
| FRFunctional Requirement.-08MustThe system shall emit a stable event taxonomy: `agent_start/end`, `turn_start/end`, `message_start/update/end`, `tool_execution_start/update/end`. | Must | The system shall emit a stable event taxonomy: agent_start/end, turn_start/end, message_start/update/end, tool_execution_start/update/end. |
| FRFunctional Requirement.-09MustThe system shall provide an `AgentHarness` wrapping `Agent` with sessions, compaction, skills, system-prompt building, and provider hooks. | Must | The system shall provide an AgentHarness wrapping Agent with sessions, compaction, skills, system-prompt building, and provider hooks. |
| FRFunctional Requirement.-10MustThe system shall support app-extensible `AgentMessage` types via declaration merging, with `convertToLlm` bridging to pi-ai messages and an optional `transformContext` hook. | Must | The system shall support app-extensible AgentMessage types via declaration merging, with convertToLlm bridging to pi-ai messages and an optional transformContext hook. |
| FRFunctional Requirement.-11ShouldThe system shall provide a `streamProxy` function for routing LLMLarge Language Model. calls through a server with bandwidth-reduced events. | Should | The system shall provide a streamProxy function for routing LLMLarge Language Model. calls through a server with bandwidth-reduced events. |
| FRFunctional Requirement.-12ShouldThe system shall include compaction utilities (`compact`, `shouldCompact`, `estimateTokens`, `findCutPoint`). | Should | The system shall include compaction utilities (compact, shouldCompact, estimateTokens, findCutPoint). |
| FRFunctional Requirement.-13ShouldThe system shall include branch-summarization utilities. | Should | The system shall include branch-summarization utilities. |
| FRFunctional Requirement.-14ShouldThe system shall provide a `NodeExecutionEnv` (filesystem + shell) via the `./node` entrypoint. | Should | The system shall provide a NodeExecutionEnv (filesystem + shell) via the ./node entrypoint. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFRNon-Functional Requirement.-01MustExactly one active run per `Agent` shall be allowed; concurrent `prompt` calls shall throw. | Must | Reliability | Exactly one active run per Agent shall be allowed; concurrent prompt calls shall throw. |
| NFRNon-Functional Requirement.-02MustRun failures shall be surfaced as a synthetic failure assistant message with `stopReason: "aborted" \ | Must | Reliability | Run failures shall be surfaced as a synthetic failure assistant message with stopReason: "aborted" \ | "error" followed by agent_end. |
| NFRNon-Functional Requirement.-03MustThe run shall not be considered idle until `agent_end` listeners have settled. | Must | Correctness | The run shall not be considered idle until agent_end listeners have settled. |
| NFRNon-Functional Requirement.-04MustCoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. modules shall be platform-agnostic; Node-specific code shall live behind the `./node` entrypoint. | Must | Portability | CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. modules shall be platform-agnostic; Node-specific code shall live behind the ./node entrypoint. |
| NFRNon-Functional Requirement.-05ShouldA stable, backend-independent error taxonomy (`FileErrorCode`, `ExecutionErrorCode`, `CompactionErrorCode`, `SessionErrorCode`) shall be used for fallible operations. | Should | Maintainability | A stable, backend-independent error taxonomy (FileErrorCode, ExecutionErrorCode, CompactionErrorCode, SessionErrorCode) shall be used for fallible operations. |
Constraints
- Built on
pi-aivia theStreamFnboundary; does not import provider SDKs directly. - Erasable TypeScript syntax only; ESM only.
Acceptance Criteria
Every FRFunctional Requirement. and NFRNon-Functional Requirement. shall have at least one acceptance criterion.
Order criteria by FRs first (sorted by ID), then NFRs (sorted by ID).
- FRFunctional Requirement.-02MustThe system shall run an agent loop that streams an assistant response, extracts tool calls, executes them, and continues until no more tool calls or queued messages remain.
- Given an agent with tools and a prompt that triggers tool calls
- When the agent loop runs to completion
- Then it streams the response, executes the tool calls, appends results, and continues until there are no more tool calls or queued messages.
- FRFunctional Requirement.-04MustThe system shall execute tool calls either sequentially or in parallel based on config and per-tool `executionMode`, emitting `tool_execution_*` events in completion order while persisting tool results in source order.
- Given a batch of parallel-capable tool calls plus one sequential tool
- When the batch executes
- Then the whole batch runs sequentially and
tool_execution_endevents fire in completion order, while persisted tool-result messages preserve assistant source order.
- FRFunctional Requirement.-05MustThe system shall validate tool arguments before execution and support `beforeToolCall`/`afterToolCall` hooks that can block, override, or terminate a tool call.
- Given a
beforeToolCallhook returning{ block: true } - When the tool is about to execute
- Then the call is blocked and an error tool result is recorded.
- Given a
- FRFunctional Requirement.-06MustThe system shall support message queues for steering (delivered after the current tool batch) and follow-up (delivered after the agent stops).
- Given a streaming agent and an incoming steering message
- When the current tool batch completes
- Then the steering message is delivered to the agent before it stops.
- FRFunctional Requirement.-07MustThe system shall support abort via an `AbortController` whose signal flows to `streamFn` and tool `execute`.
- Given an in-progress run
- When
abort()is called - Then the abort signal propagates to the active stream and executing tools.
- NFRNon-Functional Requirement.-01MustExactly one active run per `Agent` shall be allowed; concurrent `prompt` calls shall throw.
- Given an agent with an active run
- When
promptis called again concurrently - Then the second call throws rather than interleaving.
- NFRNon-Functional Requirement.-02MustRun failures shall be surfaced as a synthetic failure assistant message with `stopReason: "aborted" \
- Given a run that throws unexpectedly
- When the error is caught
- Then a synthetic failure assistant message with an appropriate
stopReasonis emitted, followed byagent_end.
Conflicts
None identified yet.
Open Questions
- Should
streamProxyremain a first-class supported deployment mode, or is it expected to migrate to an extension? - What is the long-term relationship between
AgentHarnessand the coding-agent's ownAgentSession(overlap, merge, or distinct layers)?
Specification: Agent Runtime
Overview
pi-agent-core is split into a low-level layer (Agent, agentLoop) and a high-level AgentHarness.
The low-level layer is transport-agnostic and delegates LLMLarge Language Model. calls to an injectable StreamFn.
The harness adds durable session storage, compaction, skills, system prompts, and a rich provider/session hook surface.
Architecture
+---------------------------------------------------+
| AgentHarness |
| sessions, compaction, skills, system-prompt, |
| provider/session hooks (before_provider_request, |
| tool_call, session_before_compact, ...) |
+-------------------------+-------------------------+
| owns
v
+---------------------------------------------------+
| Agent |
| transcript, tools, model/thinking, message |
| queues, lifecycle (prompt/continue/abort) |
+-------------------------+-------------------------+
| drives
v
+---------------------------------------------------+
| agentLoop |
| stream -> extract tools -> execute -> continue |
+-------------------------+-------------------------+
| calls
v
+---------------------+
| StreamFn (inject) | default: pi-ai streamSimple
+---------------------+ alt: streamProxy (server)
Data Models
AgentState
| Field | Type | Constraints | Description |
|---|---|---|---|
| systemPrompt | string | optional | Active system prompt |
| model | Model | not null | Active LLMLarge Language Model. model |
| thinkingLevel | ThinkingLevel | not null | Reasoning effort |
| tools | AgentTool[] | not null | Registered tools |
| messages | AgentMessage[] | not null | Transcript (app-extensible) |
| isStreaming | boolean | readonly | True while a run is active |
| streamingMessage | AssistantMessage | readonly | Partial message during stream |
| pendingToolCalls | Set | readonly | Tool calls in flight |
| errorMessage | string | readonly | Last run error, if any |
AgentTool
| Field | Type | Constraints | Description |
|---|---|---|---|
| name | string | not null | Tool name |
| description | string | not null | LLMLarge Language Model.-facing description |
| parameters | TSchema | not null | TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). parameter schema |
| label | string | optional | UI label |
| execute | function | not null | (id, args, signal, onUpdate) -> result |
| prepareArguments | function | optional | Pre-execution arg transform |
| executionMode | enum | optional | sequential or parallel |
AgentMessage
A union of pi-ai Message types plus app-injected custom message types via declaration merging.
convertToLlm bridges AgentMessage[] to pi-ai Message[] for the stream; transformContext optionally rewrites the AgentMessage[] before conversion.
AgentEvent
| Type | Emitted | Carries |
|---|---|---|
| agent_start / agent_end | run begin / end | - |
| turn_start / turn_end | each turn | - |
| message_start / update / end | stream lifecycle | partial/full assistant message |
| tool_execution_start / update / end | per tool | tool id, params, progress, result |
API Contracts
Agent.prompt(messages) / Agent.continue()
Request: AgentMessage[] for prompt; none for continue (resumes from current context; last message must be user/toolResult).
Response: an async subscription; results arrive via subscribe() events. Throws if an agent is already running.
StreamFn
| Field | Type | Required | Description |
|---|---|---|---|
| model | Model | yes | Target model |
| context | Context | yes | Messages, tools, system prompt, reasoning |
| apiKey | ModelAuth | optional | Explicit auth |
| signal | AbortSignal | optional | Cancellation |
Response: an AssistantMessageEventStream. Must never throw; failures are terminal stream events.
Sequences
Agent loop (single turn)
turn_start
-> transformContext (AgentMessage[])
-> convertToLlm (-> Message[])
-> StreamFn(context)
-> stream deltas -> message_update events
-> done -> message_end
-> extract tool calls
-> beforeToolCall (can block)
-> execute tools (sequential|parallel) -> tool_execution_* events
-> afterToolCall (can override/terminate)
-> append tool results to context
turn_end
-> drain steering messages? loop
-> else drain follow-up messages? outer loop
agent_end (after listeners settle)
Failure handling
run executor throws
-> handleRunFailure: synthesize assistant message (stopReason: aborted|error)
-> emit message_end, agent_end
-> finishRun: clear runtime state
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Transport boundary | Injectable StreamFn |
Enables direct, proxy, and custom backends without coupling to pi-ai |
| Message extensibility | Declaration-merged AgentMessage |
Apps add custom types without forking; convertToLlm is the bridge |
| Parallel tool execution | Completion-order events, source-order results | Parallelism for speed, deterministic transcript ordering |
| Idle definition | After agent_end listeners settle |
Listeners can perform async cleanup before idle |
| CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. vs Node | ./node entrypoint |
Keeps core platform-agnostic; Node fs/shell isolated |
Risks and Unknowns
- Overlap between
AgentHarnessand coding-agent'sAgentSessioncould lead to duplicated session/compaction logic. - The declaration-merge extensibility model is powerful but can make type errors hard to localize.
- Parallel tool execution ordering semantics are subtle; regressions here are hard to detect without targeted tests.
Out of Scope
- LLMLarge Language Model. provider abstraction and streaming protocol (FEAT-0001).
- Terminal rendering (FEAT-0003).
- Interactive TUITerminal User Interface (the interactive mode, and the `pi-tui` library)., slash commands, and the CLICommand-Line Interface (the `pi` binary). product (FEAT-0004).
- Coding-agent-specific session format and branching UI (FEAT-0006).
Test Plan: Agent Runtime
Scope
Covers pi-agent-core agent loop, tool execution, message queues, abort, events, and the AgentHarness layer including compaction, skills, sessions, and system prompts.
Unit Tests
Test files under packages/agent/test/ cover:
- Agent loop and state machine (agent.test.ts, agent-loop.test.ts)
- Abort and cancellation flow
- Tool execution (sequential/parallel)
- Message queue steering and follow-up
- AgentHarness session lifecycle (harness/session.test.ts, harness/agent-harness.test.ts, harness/agent-harness-stream.test.ts)
- CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. (harness/compaction.test.ts)
- Skills loading and injection (harness/skills.test.ts)
- Prompt templates (harness/prompt-templates.test.ts)
- System prompt building (harness/system-prompt.test.ts)
- Resource formatting (harness/resource-formatting.test.ts)
- Node execution environment (harness/nodejs-env.test.ts)
- Storage layer (harness/storage.test.ts)
- SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). UUID generation (harness/session-uuid.test.ts)
- Truncation utilities (harness/truncate.test.ts)
- Repo-level logic (harness/repo.test.ts)
Test Infrastructure
- Injectable
StreamFnwith controlled behavior (mock stream event sequences) - In-memory session storage for deterministic testing
NodeExecutionEnvfor filesystem and shell tool testing
Coverage Matrix
| Requirement | Test Files |
|---|---|
| FRFunctional Requirement.-01MustThe system shall provide an `Agent` class owning the transcript, tools, model, and thinking level, with `prompt`, `continue`, `abort`, and `subscribe` methods. (Agent class) | agent.test.ts |
| FRFunctional Requirement.-02MustThe system shall run an agent loop that streams an assistant response, extracts tool calls, executes them, and continues until no more tool calls or queued messages remain. (Agent loop) | agent-loop.test.ts |
| FRFunctional Requirement.-03MustThe system shall call an injectable `StreamFn` (default pi-ai `streamSimple`) to reach the LLMLarge Language Model., keeping the runtime transport-agnostic. (Injectable StreamFn) | agent-loop.test.ts |
| FRFunctional Requirement.-04MustThe system shall execute tool calls either sequentially or in parallel based on config and per-tool `executionMode`, emitting `tool_execution_*` events in completion order while persisting tool results in source order. (Sequential/parallel tool execution) | agent-loop.test.ts |
| FRFunctional Requirement.-05MustThe system shall validate tool arguments before execution and support `beforeToolCall`/`afterToolCall` hooks that can block, override, or terminate a tool call. (Tool hooks) | agent.test.ts |
| FRFunctional Requirement.-06MustThe system shall support message queues for steering (delivered after the current tool batch) and follow-up (delivered after the agent stops). (Message queues) | agent.test.ts |
| FRFunctional Requirement.-07MustThe system shall support abort via an `AbortController` whose signal flows to `streamFn` and tool `execute`. (Abort) | agent.test.ts |
| FRFunctional Requirement.-08MustThe system shall emit a stable event taxonomy: `agent_start/end`, `turn_start/end`, `message_start/update/end`, `tool_execution_start/update/end`. (Event taxonomy) | agent-loop.test.ts |
| FRFunctional Requirement.-09MustThe system shall provide an `AgentHarness` wrapping `Agent` with sessions, compaction, skills, system-prompt building, and provider hooks. (AgentHarness) | harness/agent-harness.test.ts |
| FRFunctional Requirement.-10MustThe system shall support app-extensible `AgentMessage` types via declaration merging, with `convertToLlm` bridging to pi-ai messages and an optional `transformContext` hook. (Extensible AgentMessage) | agent.test.ts |
| FRFunctional Requirement.-11ShouldThe system shall provide a `streamProxy` function for routing LLMLarge Language Model. calls through a server with bandwidth-reduced events. (Proxy streaming) | harness/agent-harness-stream.test.ts |
| FRFunctional Requirement.-12ShouldThe system shall include compaction utilities (`compact`, `shouldCompact`, `estimateTokens`, `findCutPoint`). (CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved.) | harness/compaction.test.ts |
| FRFunctional Requirement.-13ShouldThe system shall include branch-summarization utilities. (Branch summarization) | (covered in coding-agent test suite) |
| FRFunctional Requirement.-14ShouldThe system shall provide a `NodeExecutionEnv` (filesystem + shell) via the `./node` entrypoint. (NodeExecutionEnv) | harness/nodejs-env.test.ts |
| NFRNon-Functional Requirement.-01MustExactly one active run per `Agent` shall be allowed; concurrent `prompt` calls shall throw. (Single concurrent run) | agent.test.ts |
| NFRNon-Functional Requirement.-02MustRun failures shall be surfaced as a synthetic failure assistant message with `stopReason: "aborted" \ (Failure as assistant message) | agent.test.ts |
| NFRNon-Functional Requirement.-04MustCoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. modules shall be platform-agnostic; Node-specific code shall live behind the `./node` entrypoint. (Platform-agnostic core) | (module structure) |
requirements
- Should
streamProxyremain a first-class supported deployment mode, or is it expected to migrate to an extension? - What is the long-term relationship between
AgentHarnessand the coding-agent's ownAgentSession(overlap, merge, or distinct layers)?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. | The project: a minimal, self-extensible terminal coding agent harness and its libraries. |
| HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. | The coding agent runtime that wires the agent loop, tools, sessions, and UI together. |
| ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. | A TypeScript module with a default export function (pi: ExtensionAPI) that augments the agent with tools, commands, events, UI, or providers. |
| SkillAn on-demand capability package following the Agent Skills standard (`SKILL.md` + optional frontmatter), invoked as `/skill:name`. | An on-demand capability package following the Agent Skills standard (SKILL.md + optional frontmatter), invoked as /skill:name. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| pi packageA distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via `pi install`. | A distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via pi install. |
| SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). | A persistent, branchable conversation log stored as JSONL (SessionHeader, messages, compaction summaries, branch summaries). |
| BranchingTree-structured session forking (`/fork`, `/clone`, `/tree`) where each entry has `id`/`parentId`. | Tree-structured session forking (/fork, /clone, /tree) where each entry has id/parentId. |
| CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. | Lossy summarization of older session messages to reclaim context; original JSONL is preserved. |
| SteeringA queued message delivered to a streaming agent after the current tool batch completes. | A queued message delivered to a streaming agent after the current tool batch completes. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| Project trustA per-folder decision (`~/.pi/agent/trust.json`) gating whether project settings, resources, and extensions execute. | A per-folder decision (~/.pi/agent/trust.json) gating whether project settings, resources, and extensions execute. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Faux providerAn in-memory scripted provider (`providers/faux.ts`) used for deterministic tests with no real API calls. | An in-memory scripted provider (providers/faux.ts) used for deterministic tests with no real API calls. |
| CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. | The deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. |
| Remote sessionA pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. | A pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. |
| Session leaseA remote-session ownership handle (`exclusive` or `shared`) granted by `PiClient.acquireSession()`/`createSession()`; gates who may mutate or observe a session. | A remote-session ownership handle (exclusive or shared) granted by PiClient.acquireSession()/createSession(); gates who may mutate or observe a session. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| Byte transportThe minimal ordered byte-stream interface (`send`/`close`, inbound `onData`/`onClose`/`onError`) that `PiClient` uses; keeps pi-client Node-agnostic. | The minimal ordered byte-stream interface (send/close, inbound onData/onClose/onError) that PiClient uses; keeps pi-client Node-agnostic. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| Legacy supervisorThe renamed orchestrator code in pi-server (`server` CLI, child-process supervision, Radius, RPC stream bridge). | The renamed orchestrator code in pi-server (server CLICommand-Line Interface (the `pi` binary)., child-process supervision, Radius, RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). stream bridge). |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`. | Behavioral, model-backed checks for pi workflows run via vitest-evals with a real AgentSession. |
Technical Terms
| Term | Definition |
|---|---|
| ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). | The runtime unit owning a model catalog, auth, and stream behavior (e.g. anthropic, openai). |
| API implementationA wire-protocol backend shared by providers (e.g. `anthropic-messages`, `openai-responses`, `openai-completions`, `google-generative-ai`, `bedrock-converse-stream`). | A wire-protocol backend shared by providers (e.g. anthropic-messages, openai-responses, openai-completions, google-generative-ai, bedrock-converse-stream). |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
Agent |
pi-agent-core's stateful class owning the transcript and lifecycle (prompt, continue, abort). |
AgentHarness |
pi-agent-core's higher-level orchestrator wrapping Agent with sessions, compaction, skills, and provider hooks. |
agentLoop |
The low-level prompt-stream-tool-continue loop in pi-agent-core. |
AgentMessage |
pi-agent-core's app-extensible message union (via declaration merging); convertToLlm bridges to pi-ai Message. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| FramingThe pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBOR item. | The pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item. |
hello |
The first message a pi-client sends, carrying PROTOCOL_VERSION and a bearer token. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
| Differential renderingpi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. | pi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. |
| Synchronized outputTerminal escape sequence (`\x1b[?2026h..l`) used by pi-tui for atomic, flicker-free rendering. | Terminal escape sequence (\x1b[?2026h..l) used by pi-tui for atomic, flicker-free rendering. |
| Kitty keyboard protocolTerminal input protocol pi-tui negotiates for richer key reporting. | Terminal input protocol pi-tui negotiates for richer key reporting. |
| Lockstep versioningAll packages share one version and release together. | All packages share one version and release together. |
| Trusted publishingnpm publish via GitHub Actions OIDC (environment `npm-publish`); no local credentials required. | npm publish via GitHub Actions OIDCOpenID Connect (used for npm trusted publishing identity). (environment npm-publish); no local credentials required. |
| Shrinkwrap`packages/coding-agent/npm-shrinkwrap.json`, generated from the root lockfile to pin transitive deps for npm users. | packages/coding-agent/npm-shrinkwrap.json, generated from the root lockfile to pin transitive deps for npm users. |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| LLMLarge Language Model. | Large Language Model. |
| MCPModel Context Protocol (not built into core; extensions may add it). | Model Context Protocol (not built into core; extensions may add it). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). | Open Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). |
| PKCEProof Key for Code Exchange (OAuth flow used by pi-ai). | Proof Key for Code Exchange (OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). flow used by pi-ai). |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| FRFunctional Requirement. | Functional Requirement. |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). | Input Method Editor (pi-tui positions the hardware cursor for IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). candidate windows). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
| AGENTS.mdProject-specific rules file for humans and agents, read automatically from the repo root. | Project-specific rules file for humans and agents, read automatically from the repo root. |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Terminal UI Framework
Overview
pi-tui is a minimal terminal UI framework with differential rendering and synchronized output for flicker-free interactive CLICommand-Line Interface (the `pi` binary). applications.
It is custom-built (no React/Ink/VDOM): components are plain classes that return styled strings, and a hand-written renderer diffs the new frame against the previous one and writes minimal escape sequences.
It powers the coding agent's interactive mode and is reusable by any terminal application.
Stakeholders
| Stakeholder | Interest |
|---|---|
| Coding agent interactive mode | Primitive components (editor, text, markdown, lists, overlays) and flicker-free rendering |
| SDKSoftware Development Kit (the embeddable programmatic API). / library users | A terminal-agnostic component framework they can target |
| End users | Smooth, responsive terminal rendering across platforms and terminals |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FRFunctional Requirement.-01MustThe system shall provide a `Component` model where components implement `render(width): string[]` and optional `handleInput`. | Must | The system shall provide a Component model where components implement render(width): string[] and optional handleInput. |
| FRFunctional Requirement.-02MustThe system shall provide a `TUITerminal User Interface (the interactive mode, and the `pi-tui` library).` manager that composes components, manages focus, overlays, and drives rendering. | Must | The system shall provide a TUITerminal User Interface (the interactive mode, and the `pi-tui` library). manager that composes components, manages focus, overlays, and drives rendering. |
| FRFunctional Requirement.-03MustThe system shall perform differential rendering, writing only changed line ranges between frames. | Must | The system shall perform differential rendering, writing only changed line ranges between frames. |
| FRFunctional Requirement.-04MustThe system shall wrap updates in synchronized output escape sequences for atomic, flicker-free rendering. | Must | The system shall wrap updates in synchronized output escape sequences for atomic, flicker-free rendering. |
| FRFunctional Requirement.-05MustThe system shall throttle rendering to a capped frame rate and coalesce multiple invalidations per tick. | Must | The system shall throttle rendering to a capped frame rate and coalesce multiple invalidations per tick. |
| FRFunctional Requirement.-06MustThe system shall provide reusable components: `Editor`, `Text`, `TruncatedText`, `Input`, `Box`, `Markdown`, `SelectList`, `SettingsList`, `Loader`, `CancellableLoader`, `Image`, `Spacer`. | Must | The system shall provide reusable components: Editor, Text, TruncatedText, Input, Box, Markdown, SelectList, SettingsList, Loader, CancellableLoader, Image, Spacer. |
| FRFunctional Requirement.-07MustThe system shall provide an `Overlay` system with anchor-based, percentage, and absolute positioning, plus focus restore. | Must | The system shall provide an Overlay system with anchor-based, percentage, and absolute positioning, plus focus restore. |
| FRFunctional Requirement.-08MustThe system shall provide key handling with Kitty keyboard protocolTerminal input protocol pi-tui negotiates for richer key reporting. negotiation and legacy sequence fallback. | Must | The system shall provide key handling with Kitty keyboard protocolTerminal input protocol pi-tui negotiates for richer key reporting. negotiation and legacy sequence fallback. |
| FRFunctional Requirement.-09MustThe system shall provide column-aware string utilities (`visibleWidth`, `truncateToWidth`, `sliceByColumn`, `wrapTextWithAnsi`) handling ANSI codes and wide/CJKChinese, Japanese, Korean (terminal width handling for wide characters). characters. | Must | The system shall provide column-aware string utilities (visibleWidth, truncateToWidth, sliceByColumn, wrapTextWithAnsi) handling ANSI codes and wide/CJKChinese, Japanese, Korean (terminal width handling for wide characters). characters. |
| FRFunctional Requirement.-10MustThe system shall be terminal-agnostic via a `Terminal` interface, with `ProcessTerminal` for production. | Must | The system shall be terminal-agnostic via a Terminal interface, with ProcessTerminal for production. |
| FRFunctional Requirement.-11ShouldThe system shall support terminal image protocols (Kitty and iTerm2 inline images) with capability detection. | Should | The system shall support terminal image protocols (Kitty and iTerm2 inline images) with capability detection. |
| FRFunctional Requirement.-12ShouldThe system shall support IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). candidate windows by emitting a zero-width cursor marker and positioning the hardware cursor. | Should | The system shall support IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). candidate windows by emitting a zero-width cursor marker and positioning the hardware cursor. |
| FRFunctional Requirement.-13ShouldThe system shall provide a keybindings manager with default TUITerminal User Interface (the interactive mode, and the `pi-tui` library). keybindings. | Should | The system shall provide a keybindings manager with default TUITerminal User Interface (the interactive mode, and the `pi-tui` library). keybindings. |
| FRFunctional Requirement.-14ShouldThe system shall detect terminal color scheme (light/dark) via OSC 11. | Should | The system shall detect terminal color scheme (light/dark) via OSC 11. |
| FRFunctional Requirement.-15ShouldThe system shall provide fuzzy match/filter and autocomplete providers (slash commands + file paths). | Should | The system shall provide fuzzy match/filter and autocomplete providers (slash commands + file paths). |
| FRFunctional Requirement.-16MayThe system shall ship native addons for win32 and darwin to report modifier-key state. | May | The system shall ship native addons for win32 and darwin to report modifier-key state. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFRNon-Functional Requirement.-01MustComponents shall return lines that do not exceed the provided width; the framework shall error otherwise. | Must | Correctness | Components shall return lines that do not exceed the provided width; the framework shall error otherwise. |
| NFRNon-Functional Requirement.-02MustFirst render and full re-render strategies shall clear scrollback correctly; differential updates shall never leave stale content. | Must | Performance | First render and full re-render strategies shall clear scrollback correctly; differential updates shall never leave stale content. |
| NFRNon-Functional Requirement.-03MustEach rendered line shall append a full SGR reset and OSC 8 hyperlink reset so styles do not carry across lines. | Must | Compatibility | Each rendered line shall append a full SGR reset and OSC 8 hyperlink reset so styles do not carry across lines. |
| NFRNon-Functional Requirement.-04ShouldThe framework shall be testable against a virtual terminal (xterm headless) for deterministic render assertions. | Should | Testability | The framework shall be testable against a virtual terminal (xterm headless) for deterministic render assertions. |
| NFRNon-Functional Requirement.-05ShouldRendering shall be capped at approximately 60 fps (`MIN_RENDER_INTERVAL_MS = 16`). | Should | Performance | Rendering shall be capped at approximately 60 fps (MIN_RENDER_INTERVAL_MS = 16). |
Constraints
- No React, no Ink, no virtual DOM; imperative component model.
- Dependencies kept minimal (
get-east-asian-width,marked). - Erasable TypeScript syntax only; ESM only.
Acceptance Criteria
Every FRFunctional Requirement. and NFRNon-Functional Requirement. shall have at least one acceptance criterion.
Order criteria by FRs first (sorted by ID), then NFRs (sorted by ID).
- FRFunctional Requirement.-03MustThe system shall perform differential rendering, writing only changed line ranges between frames.
- Given a previous frame and a new frame differing on a contiguous range of lines
- When a render is requested
- Then only the changed range is written, with the cursor moved to the first changed line and clear-to-end applied.
- FRFunctional Requirement.-04MustThe system shall wrap updates in synchronized output escape sequences for atomic, flicker-free rendering.
- Given any render update
- When the update is written to the terminal
- Then the bytes are wrapped in synchronized output (
\x1b[?2026h...\x1b[?2026l).
- FRFunctional Requirement.-07MustThe system shall provide an `Overlay` system with anchor-based, percentage, and absolute positioning, plus focus restore.
- Given a focused overlay that is temporarily replaced by another overlay
- When the replacement releases focus
- Then the original overlay reclaims focus via the focus-restore state machine.
- FRFunctional Requirement.-09MustThe system shall provide column-aware string utilities (`visibleWidth`, `truncateToWidth`, `sliceByColumn`, `wrapTextWithAnsi`) handling ANSI codes and wide/CJKChinese, Japanese, Korean (terminal width handling for wide characters). characters.
- Given a string containing ANSI codes and wide CJKChinese, Japanese, Korean (terminal width handling for wide characters). characters
- When
visibleWidthandtruncateToWidthare applied - Then the visible width and truncation account for display columns, not raw character count.
- NFRNon-Functional Requirement.-01MustComponents shall return lines that do not exceed the provided width; the framework shall error otherwise.
- Given a component that returns a line longer than
width - When rendered
- Then the framework raises an error rather than overflowing the terminal.
- Given a component that returns a line longer than
- NFRNon-Functional Requirement.-03MustEach rendered line shall append a full SGR reset and OSC 8 hyperlink reset so styles do not carry across lines.
- Given consecutive rendered lines with different styling
- When composited
- Then each line resets SGR state so no style leaks into the next line.
Conflicts
None identified yet.
Open Questions
- Is the native modifier-key addon (
native-modifiers.ts) expected to gain a Linux path, or is the protocol fallback sufficient there? - What is the policy for adding new built-in components versus leaving them to consumers?
Specification: Terminal UI Framework
Overview
pi-tui is an imperative terminal UI framework.
Components return arrays of pre-styled strings; the TUITerminal User Interface (the interactive mode, and the `pi-tui` library). manager diffs the new array against the previous frame and emits minimal escape sequences wrapped in synchronized output.
There is no virtual DOM; callers mutate components then request a render.
Architecture
+-------------------+ mutate + requestRender
| Application code |-------------------------+
+-------------------+ |
| uses |
v v
+-------------------+ render(width) +-------------+
| Components |------------------>| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). |
| (Editor, Text, | | (manager) |
| Markdown, ...) | +------+------+
+-------------------+ | composites overlays
| diffs vs previousLines
v
+---------------------+
| differential write |
| (sync output, ~60fps)|
+----------+----------+
|
v
+---------------------+
| Terminal interface |
| (ProcessTerminal / |
| VirtualTerminal) |
+---------------------+
Data Models
Component
| Field | Type | Constraints | Description |
|---|---|---|---|
| render | (width: number) => string[] | required | Returns styled lines, each <= width |
| handleInput | (data) => boolean | optional | Returns true if input consumed |
| wantsKeyRelease | boolean | optional | Claims key release |
| invalidate | () => void | required | Marks the component dirty |
OverlayOptions
| Field | Type | Constraints | Description |
|---|---|---|---|
| anchor | object | optional | Positioning anchor |
| margins | object | optional | Edge margins |
| visible | () => boolean | optional | Visibility callback |
| nonCapturing | boolean | optional | Does not capture focus |
Key
| Field | Type | Constraints | Description |
|---|---|---|---|
| key | string | optional | Logical key name |
| ctrl/alt/shift | boolean | optional | Modifiers |
| paste | boolean | optional | Bracketed paste flag |
API Contracts
TUITerminal User Interface (the interactive mode, and the `pi-tui` library)..start() / TUITerminal User Interface (the interactive mode, and the `pi-tui` library)..stop()
Begins/ends raw mode, Kitty keyboard negotiation, and the render loop.
requestRender() schedules a throttled render; setFocus, showOverlay/hideOverlay manage composition.
Keybindings
matchesKey(keyData, binding) compares parsed keys against configurable bindings.
Defaults live in TUI_KEYBINDINGS; consumers override via setKeybindings.
Sequences
Differential render
application -> component.invalidate() / TUITerminal User Interface (the interactive mode, and the `pi-tui` library)..requestRender()
TUITerminal User Interface (the interactive mode, and the `pi-tui` library). (next tick, if >= MIN_RENDER_INTERVAL_MS):
render each focused component -> lines[]
composite overlays into lines[]
extract cursor marker -> compute hardware cursor position
if first render: output all lines (no scrollback clear)
elif full re-render: clear screen + home + clear scrollback; delete Kitty images
else (differential):
find first/last changed line vs previousLines
move cursor to first changed line; clear to end
write only changed range
wrap all output in synchronized output escapes
handle appended/deleted lines and viewport scroll
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Rendering model | Imperative, string-line diff | No VDOM overhead; minimal escape output |
| Atomicity | Synchronized outputTerminal escape sequence (`\x1b[?2026h..l`) used by pi-tui for atomic, flicker-free rendering. (?2026h/l) |
Flicker-free even across slow connections |
| Throttling | ~60fps with nextTick coalescing | Bounding CPU while staying responsive |
| IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). cursor | Zero-width APC marker | Keeps fake cursor while positioning hardware cursor for IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). |
| Line resets | Full SGR + OSC 8 reset per line | Prevents style/hyperlink leakage across lines |
| Terminal abstraction | Terminal interface |
ProcessTerminal prod, VirtualTerminal tests |
Risks and Unknowns
- Differential renderingpi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. correctness depends on accurate change detection; subtle bugs (Kitty image ranges, overlay compositing above viewport) require regression tests.
- Terminal compatibility variance (Kitty support, synchronized output, color scheme reporting) means fallback paths must be exercised.
- The overlay focus-restore state machine has many states ("eligible"/"blocked") and is a likely source of regressions.
Out of Scope
- LLMLarge Language Model. streaming and provider abstraction (FEAT-0001).
- Agent loop and tool execution (FEAT-0002).
- The interactive coding agent product built on top of this framework (FEAT-0004).
Test Plan: Terminal UI Framework
Scope
Covers pi-tui rendering pipeline, components, key handling, terminal abstraction, overlays, and differential rendering.
Unit Tests
Test files under packages/tui/test/ cover:
- Differential render engine (tui-render.test.ts, tui-shrink.test.ts, tui-cell-size-input.test.ts, tui-overlay-style-leak.test.ts)
- Editor component (editor.test.ts)
- Input handling (input.test.ts, keys.test.ts)
- Keybindings (keybindings.test.ts)
- Markdown rendering (markdown.test.ts)
- SelectList component (select-list.test.ts, truncated-text.test.ts)
- Overlay system (overlay-options.test.ts, overlay-short-content.test.ts, overlay-non-capturing.test.ts)
- Autocomplete (autocomplete.test.ts)
- Fuzzy matching (fuzzy.test.ts)
- Terminal abstraction (terminal.test.ts, terminal-colors.test.ts, terminal-image.test.ts)
- String utilities (truncate-to-width.test.ts, wrap-ansi.test.ts, tab-width.test.ts, word-navigation.test.ts)
- Stdin buffer (stdin-buffer.test.ts)
- Regression tests (regression-overlay-cjk-boundary.test.ts, regression-regional-indicator-width.test.ts, bug-regression-isimageline-startswith-bug.test.ts)
Integration Tests
Terminal tests run against VirtualTerminal for deterministic render assertions without a real terminal.
Edge Cases and Failure Scenarios
- CJKChinese, Japanese, Korean (terminal width handling for wide characters). character boundary in overlays
- Regional indicator (flag) width
- Tab width handling
- Image protocol capability detection fallback
- Non-capturing overlay focus management
- Short overlay content positioning
Test Infrastructure
VirtualTerminalfor headless render assertions- Mock terminal color scheme (OSC 11)
- Kitty keyboard protocolTerminal input protocol pi-tui negotiates for richer key reporting. negotiation simulation
Coverage Matrix
| Requirement | Test Files |
|---|---|
| FRFunctional Requirement.-01MustThe system shall provide a `Component` model where components implement `render(width): string[]` and optional `handleInput`. (Component model) | editor.test.ts, input.test.ts, markdown.test.ts |
| FRFunctional Requirement.-02MustThe system shall provide a `TUITerminal User Interface (the interactive mode, and the `pi-tui` library).` manager that composes components, manages focus, overlays, and drives rendering. (TUITerminal User Interface (the interactive mode, and the `pi-tui` library). manager) | tui-render.test.ts |
| FRFunctional Requirement.-03MustThe system shall perform differential rendering, writing only changed line ranges between frames. (Differential renderingpi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences.) | tui-render.test.ts |
| FRFunctional Requirement.-04MustThe system shall wrap updates in synchronized output escape sequences for atomic, flicker-free rendering. (Synchronized outputTerminal escape sequence (`\x1b[?2026h..l`) used by pi-tui for atomic, flicker-free rendering.) | tui-render.test.ts |
| FRFunctional Requirement.-05MustThe system shall throttle rendering to a capped frame rate and coalesce multiple invalidations per tick. (Frame throttling) | tui-render.test.ts |
| FRFunctional Requirement.-06MustThe system shall provide reusable components: `Editor`, `Text`, `TruncatedText`, `Input`, `Box`, `Markdown`, `SelectList`, `SettingsList`, `Loader`, `CancellableLoader`, `Image`, `Spacer`. (Built-in components) | editor.test.ts, markdown.test.ts, select-list.test.ts |
| FRFunctional Requirement.-07MustThe system shall provide an `Overlay` system with anchor-based, percentage, and absolute positioning, plus focus restore. (Overlay system) | overlay-*.test.ts |
| FRFunctional Requirement.-08MustThe system shall provide key handling with Kitty keyboard protocolTerminal input protocol pi-tui negotiates for richer key reporting. negotiation and legacy sequence fallback. (Key handling) | keys.test.ts, keybindings.test.ts |
| FRFunctional Requirement.-09MustThe system shall provide column-aware string utilities (`visibleWidth`, `truncateToWidth`, `sliceByColumn`, `wrapTextWithAnsi`) handling ANSI codes and wide/CJKChinese, Japanese, Korean (terminal width handling for wide characters). characters. (Column-aware string utils) | truncate-to-width.test.ts, wrap-ansi.test.ts, tab-width.test.ts |
| FRFunctional Requirement.-10MustThe system shall be terminal-agnostic via a `Terminal` interface, with `ProcessTerminal` for production. (Terminal interface) | terminal.test.ts |
| FRFunctional Requirement.-11ShouldThe system shall support terminal image protocols (Kitty and iTerm2 inline images) with capability detection. (Terminal images) | terminal-image.test.ts |
| FRFunctional Requirement.-12ShouldThe system shall support IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). candidate windows by emitting a zero-width cursor marker and positioning the hardware cursor. (IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). support) | (covered by tui-render cursor positioning) |
| FRFunctional Requirement.-13ShouldThe system shall provide a keybindings manager with default TUITerminal User Interface (the interactive mode, and the `pi-tui` library). keybindings. (Keybindings manager) | keybindings.test.ts |
| FRFunctional Requirement.-15ShouldThe system shall provide fuzzy match/filter and autocomplete providers (slash commands + file paths). (Fuzzy match/autocomplete) | fuzzy.test.ts, autocomplete.test.ts |
| NFRNon-Functional Requirement.-01MustComponents shall return lines that do not exceed the provided width; the framework shall error otherwise. (Width validation) | tui-render.test.ts |
| NFRNon-Functional Requirement.-03MustEach rendered line shall append a full SGR reset and OSC 8 hyperlink reset so styles do not carry across lines. (SGR resets per line) | tui-render.test.ts |
| NFRNon-Functional Requirement.-04ShouldThe framework shall be testable against a virtual terminal (xterm headless) for deterministic render assertions. (Virtual terminal testing) | terminal.test.ts |
requirements
- Is the native modifier-key addon (
native-modifiers.ts) expected to gain a Linux path, or is the protocol fallback sufficient there? - What is the policy for adding new built-in components versus leaving them to consumers?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. | The project: a minimal, self-extensible terminal coding agent harness and its libraries. |
| HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. | The coding agent runtime that wires the agent loop, tools, sessions, and UI together. |
| ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. | A TypeScript module with a default export function (pi: ExtensionAPI) that augments the agent with tools, commands, events, UI, or providers. |
| SkillAn on-demand capability package following the Agent Skills standard (`SKILL.md` + optional frontmatter), invoked as `/skill:name`. | An on-demand capability package following the Agent Skills standard (SKILL.md + optional frontmatter), invoked as /skill:name. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| pi packageA distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via `pi install`. | A distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via pi install. |
| SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). | A persistent, branchable conversation log stored as JSONL (SessionHeader, messages, compaction summaries, branch summaries). |
| BranchingTree-structured session forking (`/fork`, `/clone`, `/tree`) where each entry has `id`/`parentId`. | Tree-structured session forking (/fork, /clone, /tree) where each entry has id/parentId. |
| CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. | Lossy summarization of older session messages to reclaim context; original JSONL is preserved. |
| SteeringA queued message delivered to a streaming agent after the current tool batch completes. | A queued message delivered to a streaming agent after the current tool batch completes. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| Project trustA per-folder decision (`~/.pi/agent/trust.json`) gating whether project settings, resources, and extensions execute. | A per-folder decision (~/.pi/agent/trust.json) gating whether project settings, resources, and extensions execute. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Faux providerAn in-memory scripted provider (`providers/faux.ts`) used for deterministic tests with no real API calls. | An in-memory scripted provider (providers/faux.ts) used for deterministic tests with no real API calls. |
| CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. | The deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. |
| Remote sessionA pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. | A pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. |
| Session leaseA remote-session ownership handle (`exclusive` or `shared`) granted by `PiClient.acquireSession()`/`createSession()`; gates who may mutate or observe a session. | A remote-session ownership handle (exclusive or shared) granted by PiClient.acquireSession()/createSession(); gates who may mutate or observe a session. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| Byte transportThe minimal ordered byte-stream interface (`send`/`close`, inbound `onData`/`onClose`/`onError`) that `PiClient` uses; keeps pi-client Node-agnostic. | The minimal ordered byte-stream interface (send/close, inbound onData/onClose/onError) that PiClient uses; keeps pi-client Node-agnostic. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| Legacy supervisorThe renamed orchestrator code in pi-server (`server` CLI, child-process supervision, Radius, RPC stream bridge). | The renamed orchestrator code in pi-server (server CLICommand-Line Interface (the `pi` binary)., child-process supervision, Radius, RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). stream bridge). |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`. | Behavioral, model-backed checks for pi workflows run via vitest-evals with a real AgentSession. |
Technical Terms
| Term | Definition |
|---|---|
| ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). | The runtime unit owning a model catalog, auth, and stream behavior (e.g. anthropic, openai). |
| API implementationA wire-protocol backend shared by providers (e.g. `anthropic-messages`, `openai-responses`, `openai-completions`, `google-generative-ai`, `bedrock-converse-stream`). | A wire-protocol backend shared by providers (e.g. anthropic-messages, openai-responses, openai-completions, google-generative-ai, bedrock-converse-stream). |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
Agent |
pi-agent-core's stateful class owning the transcript and lifecycle (prompt, continue, abort). |
AgentHarness |
pi-agent-core's higher-level orchestrator wrapping Agent with sessions, compaction, skills, and provider hooks. |
agentLoop |
The low-level prompt-stream-tool-continue loop in pi-agent-core. |
AgentMessage |
pi-agent-core's app-extensible message union (via declaration merging); convertToLlm bridges to pi-ai Message. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| FramingThe pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBOR item. | The pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item. |
hello |
The first message a pi-client sends, carrying PROTOCOL_VERSION and a bearer token. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
| Differential renderingpi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. | pi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. |
| Synchronized outputTerminal escape sequence (`\x1b[?2026h..l`) used by pi-tui for atomic, flicker-free rendering. | Terminal escape sequence (\x1b[?2026h..l) used by pi-tui for atomic, flicker-free rendering. |
| Kitty keyboard protocolTerminal input protocol pi-tui negotiates for richer key reporting. | Terminal input protocol pi-tui negotiates for richer key reporting. |
| Lockstep versioningAll packages share one version and release together. | All packages share one version and release together. |
| Trusted publishingnpm publish via GitHub Actions OIDC (environment `npm-publish`); no local credentials required. | npm publish via GitHub Actions OIDCOpenID Connect (used for npm trusted publishing identity). (environment npm-publish); no local credentials required. |
| Shrinkwrap`packages/coding-agent/npm-shrinkwrap.json`, generated from the root lockfile to pin transitive deps for npm users. | packages/coding-agent/npm-shrinkwrap.json, generated from the root lockfile to pin transitive deps for npm users. |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| LLMLarge Language Model. | Large Language Model. |
| MCPModel Context Protocol (not built into core; extensions may add it). | Model Context Protocol (not built into core; extensions may add it). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). | Open Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). |
| PKCEProof Key for Code Exchange (OAuth flow used by pi-ai). | Proof Key for Code Exchange (OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). flow used by pi-ai). |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| FRFunctional Requirement. | Functional Requirement. |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). | Input Method Editor (pi-tui positions the hardware cursor for IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). candidate windows). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
| AGENTS.mdProject-specific rules file for humans and agents, read automatically from the repo root. | Project-specific rules file for humans and agents, read automatically from the repo root. |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Interactive Coding Agent
Overview
pi-coding-agent is the CLICommand-Line Interface (the `pi` binary). product: a minimal terminal coding harness built around four core tools (read, bash, edit, write), designed to be extended.
It wires pi-ai, pi-agent-core, and pi-tui into an AgentSession that drives prompts, manages models and thinking levels, executes built-in tools, and renders an interactive TUITerminal User Interface (the interactive mode, and the `pi-tui` library)..
It also exposes print, JSON, and RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). modes plus an embeddable SDKSoftware Development Kit (the embeddable programmatic API)..
Stakeholders
| Stakeholder | Interest |
|---|---|
| End users (developers) | A reliable interactive coding workflow with model choice and responsive UI |
| ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. / SDKSoftware Development Kit (the embeddable programmatic API). authors | A stable SDKSoftware Development Kit (the embeddable programmatic API). surface and clear extension boundaries |
| Maintainers | A minimal core that stays small; features belong in extensions when possible |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FRFunctional Requirement.-01MustThe system shall provide a `pi` CLICommand-Line Interface (the `pi` binary). with an interactive TUITerminal User Interface (the interactive mode, and the `pi-tui` library). as the default mode. | Must | The system shall provide a pi CLICommand-Line Interface (the `pi` binary). with an interactive TUITerminal User Interface (the interactive mode, and the `pi-tui` library). as the default mode. |
| FRFunctional Requirement.-02MustThe system shall provide four default built-in tools: `read`, `bash`, `edit`, `write`, and a read-only tool set (`read`, `grep`, `find`, `ls`). | Must | The system shall provide four default built-in tools: read, bash, edit, write, and a read-only tool set (read, grep, find, ls). |
| FRFunctional Requirement.-03MustThe system shall provide an `AgentSession` core that drives the prompt loop, manages model and thinking level, runs compaction, executes bash, and exports HTML. | Must | The system shall provide an AgentSession core that drives the prompt loop, manages model and thinking level, runs compaction, executes bash, and exports HTML. |
| FRFunctional Requirement.-04MustThe system shall resolve model selection via `provider/id:thinking` patterns, scoped model cycling, and in-TUITerminal User Interface (the interactive mode, and the `pi-tui` library). `/model` and `/scoped-models` commands. | Must | The system shall resolve model selection via provider/id:thinking patterns, scoped model cycling, and in-TUITerminal User Interface (the interactive mode, and the `pi-tui` library). /model and /scoped-models commands. |
| FRFunctional Requirement.-05MustThe system shall provide built-in slash commands (`settings`, `model`, `export`, `fork`, `tree`, `login`, `logout`, `new`, `compact`, `resume`, `reload`, `quit`, and others). | Must | The system shall provide built-in slash commands (settings, model, export, fork, tree, login, logout, new, compact, resume, reload, quit, and others). |
| FRFunctional Requirement.-06MustThe system shall support run modes: interactive (default), print (`-p`), JSON (`--mode json`), RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). (`--mode rpc`), and SDKSoftware Development Kit (the embeddable programmatic API).. | Must | The system shall support run modes: interactive (default), print (-p), JSON (--mode json), RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). (--mode rpc), and SDKSoftware Development Kit (the embeddable programmatic API).. |
| FRFunctional Requirement.-07MustThe system shall provide global and project settings (`~/.pi/agent/settings.json`, `.pi/settings.json`) with file locking and deep merge, plus keybindings. | Must | The system shall provide global and project settings (~/.pi/agent/settings.json, .pi/settings.json) with file locking and deep merge, plus keybindings. |
| FRFunctional Requirement.-08MustThe system shall gate project resources (settings, extensions, context files) behind a project-trust decision. | Must | The system shall gate project resources (settings, extensions, context files) behind a project-trust decision. |
| FRFunctional Requirement.-09MustThe system shall discover and load resources (extensions, skills, prompt templates, themes, AGENTS.mdProject-specific rules file for humans and agents, read automatically from the repo root./CLAUDE.md) from global, project, and package sources. | Must | The system shall discover and load resources (extensions, skills, prompt templates, themes, AGENTS.mdProject-specific rules file for humans and agents, read automatically from the repo root./CLAUDE.md) from global, project, and package sources. |
| FRFunctional Requirement.-10MustThe system shall build the system prompt from base prompt, project context, skills, date, and cwd. | Must | The system shall build the system prompt from base prompt, project context, skills, date, and cwd. |
| FRFunctional Requirement.-11ShouldThe system shall support theme loading (JSON themes, light/dark auto, hot-reload). | Should | The system shall support theme loading (JSON themes, light/dark auto, hot-reload). |
| FRFunctional Requirement.-12ShouldThe system shall provide update-check and install-telemetry endpoints, disable-able via env flags or `--offline`. | Should | The system shall provide update-check and install-telemetry endpoints, disable-able via env flags or --offline. |
| FRFunctional Requirement.-13ShouldThe system shall support first-time setup flow (theme + analytics opt-in) behind an experimental flag. | Should | The system shall support first-time setup flow (theme + analytics opt-in) behind an experimental flag. |
| FRFunctional Requirement.-14MayThe system shall provide a `pi install/remove/update/list/config` package manager for pi packages (npm/git). | May | The system shall provide a pi install/remove/update/list/config package manager for pi packages (npm/git). |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFRNon-Functional Requirement.-01MustThe package shall target Node `>=22.19.0` and use erasable TypeScript syntax only. | Must | Compatibility | The package shall target Node >=22.19.0 and use erasable TypeScript syntax only. |
| NFRNon-Functional Requirement.-02MustA hot-swap runtime shall tear down and recreate cwd-bound services when switching sessions or cwds. | Must | Reliability | A hot-swap runtime shall tear down and recreate cwd-bound services when switching sessions or cwds. |
| NFRNon-Functional Requirement.-03MustThe system shall not include an in-process sandbox; containerization is delegated to external sandboxes (documented). | Must | Security | The system shall not include an in-process sandbox; containerization is delegated to external sandboxes (documented). |
| NFRNon-Functional Requirement.-04ShouldStartup shall defer non-essential network operations when `--offline` / `PI_OFFLINE=1` is set. | Should | Performance | Startup shall defer non-essential network operations when --offline / PI_OFFLINE=1 is set. |
| NFRNon-Functional Requirement.-05ShouldThe published CLICommand-Line Interface (the `pi` binary). shall include a generated npm shrinkwrap pinning transitive deps. | Should | Maintainability | The published CLICommand-Line Interface (the `pi` binary). shall include a generated npm shrinkwrap pinning transitive deps. |
Constraints
- CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. is minimal: no built-in MCPModel Context Protocol (not built into core; extensions may add it)., sub-agents, plan mode, or permission popups (extensions provide these).
- Direct dependencies pinned to exact versions; lockfile is ground truth.
- New issues/PRs from new contributors are auto-closed by default (maintainer gate).
Acceptance Criteria
Every FRFunctional Requirement. and NFRNon-Functional Requirement. shall have at least one acceptance criterion.
Order criteria by FRs first (sorted by ID), then NFRs (sorted by ID).
- FRFunctional Requirement.-02MustThe system shall provide four default built-in tools: `read`, `bash`, `edit`, `write`, and a read-only tool set (`read`, `grep`, `find`, `ls`).
- Given a default agent session
- When tools are registered
- Then
read,bash,edit,writeare available, and the read-only set restricts toread,grep,find,ls.
- FRFunctional Requirement.-03MustThe system shall provide an `AgentSession` core that drives the prompt loop, manages model and thinking level, runs compaction, executes bash, and exports HTML.
- Given a user prompt
- When submitted to
AgentSession.prompt() - Then it expands commands/templates, emits lifecycle events, streams the response, executes tools, handles retries/compaction, and persists entries to the session file.
- FRFunctional Requirement.-06MustThe system shall support run modes: interactive (default), print (`-p`), JSON (`--mode json`), RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). (`--mode rpc`), and SDKSoftware Development Kit (the embeddable programmatic API)..
- Given the CLICommand-Line Interface (the `pi` binary). invoked with
-p,--mode json,--mode rpc, or no flag - When it starts
- Then it runs in print, JSON, RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode)., or interactive mode respectively.
- Given the CLICommand-Line Interface (the `pi` binary). invoked with
- FRFunctional Requirement.-08MustThe system shall gate project resources (settings, extensions, context files) behind a project-trust decision.
- Given an untrusted project
- When the agent starts
- Then project resources are not loaded until a trust decision is recorded (or
--approveis passed non-interactively).
- NFRNon-Functional Requirement.-02MustA hot-swap runtime shall tear down and recreate cwd-bound services when switching sessions or cwds.
- Given an active interactive session
- When the user switches sessions or cwd via
/forkor/tree - Then the runtime tears down and recreates cwd-bound services without leaking state.
Conflicts
None identified yet.
Open Questions
- Should the experimental first-time setup flow graduate to default behavior, and on what timeline?
- What is the policy for promoting a widely-used extension into a built-in tool or command?
Specification: Interactive Coding Agent
Overview
pi-coding-agent composes the three libraries into a CLICommand-Line Interface (the `pi` binary). product.
main() parses args, resolves project trust, builds a SessionManager and AgentSessionRuntime, then dispatches to a run mode.
The AgentSession is the central class shared by all modes; InteractiveMode renders events via pi-tui.
Architecture
cli.ts -> main() -------------------------------+
args parse, migrations, trust resolve |
createSessionManager, createAgentSessionRuntime|
| |
v |
+-------------------+ owns session + |
| AgentSession | cwd-bound services |
| Runtime | (hot-swap on |
+---------+---------+ fork/switch/tree) |
| |
v |
+-------------------+ delegates to |
| AgentSession |----> pi-agent-core Agent |
| (prompt loop, |----> ModelRegistry (auth)|
| compaction, bash,|----> pi-ai streamSimple |
| tools, export) | |
+---------+---------+ |
| events |
v |
+------+------+------+ |
| | | |
v v v |
Interactive Print RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). <-- modes dispatch-+
Mode (pi-tui) (text/json)
Data Models
Settings (excerpt)
| Field | Type | Description |
|---|---|---|
| defaultModel | string | provider/id:thinking |
| transport | enum | sse / websocket / auto |
| compaction | object | Thresholds and behavior |
| extensions/skills/prompts/themes | string[] | Enabled resource globs |
| telemetry/analytics | object | Opt-in/out flags |
| projectTrust | object | Trust defaults |
ResourceLoader sources
| Source | Path | Trust-gated |
|---|---|---|
| Global user | ~/.pi/agent/, ~/.agents/ |
No |
| Project | .pi/, .agents/, AGENTS.mdProject-specific rules file for humans and agents, read automatically from the repo root., CLAUDE.md |
Yes |
| Package | installed pi packages | Per-source |
Tool sets
| Set | Tools |
|---|---|
| Default (4) | read, bash, edit, write |
| Read-only | read, grep, find, ls |
API Contracts
SDKSoftware Development Kit (the embeddable programmatic API).: createAgentSession()
Factory building an AgentSession wired with a resource loader, model registry, settings, and extension runner.
Exposes prompt, subscribe, state, abort, setModel, setThinkingLevel, compact, fork.
CLICommand-Line Interface (the `pi` binary). flags (excerpt)
| Flag | Effect |
|---|---|
-p / --prompt |
Print mode (single-shot) |
--mode json / --mode rpc |
JSON event stream / RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). protocol |
-c / -r / --fork / --session |
Continue / resume / fork / specific session |
-t / -xt / -nbt / -nt |
Tool allowlist / exclude / no-builtin / none |
--model / --models |
Model / scoped cycling set |
--approve / --no-approve |
Non-interactive project trust |
--offline |
Disable startup network ops |
Sequences
Interactive prompt
user input -> AgentSession.prompt()
-> slash command? extension command or builtin
-> emit input event (extensions may transform/intercept)
-> expand skill/template
-> streaming? queue steer/follow-up
-> validate model + auth
-> check pre-prompt compaction
-> before_agent_start (extensions modify system prompt / inject messages)
-> Agent.prompt() -> agentLoop -> streamFn -> pi-ai
-> stream deltas, execute tools, emit events
-> _handlePostAgentRun: retry/compact/queued messages loop
-> append entries to SessionManager JSONL
InteractiveMode renders events incrementally via pi-tui
Run mode dispatch
main() -> resolveAppMode(args)
interactive -> AgentSessionRuntime + InteractiveMode (TUITerminal User Interface (the interactive mode, and the `pi-tui` library).)
print -> runPrintMode (text or json)
rpc -> runRpcMode (JSONL stdin/stdout)
(SDKSoftware Development Kit (the embeddable programmatic API).) -> createAgentSession() used programmatically
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Central class | AgentSession shared by all modes |
One code path for prompt lifecycle across modes |
| Runtime hot-swap | AgentSessionRuntime recreates services |
Clean state on fork/switch/tree without leaking cwd-bound resources |
| CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. tools | Four only | Minimal core; more tools via extensions |
| No in-process sandbox | Delegate to external sandboxes | CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. stays small; security boundary is the OS/container |
| Resource discovery | Global + project + package, trust-gated | User controls what project code executes |
| Modes | Interactive default; print/json/rpc alternatives | One binary serves TUITerminal User Interface (the interactive mode, and the `pi-tui` library). users, scripts, and embeddings |
Risks and Unknowns
InteractiveModeis a very large module (~193KB); changes there are high-churn and hard to review.- Project trustA per-folder decision (`~/.pi/agent/trust.json`) gating whether project settings, resources, and extensions execute. is a security-critical path; regressions could execute untrusted code.
- The package manager and resource discovery span many source locations; collision and precedence rules must stay consistent.
Out of Scope
- ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). abstraction internals (FEAT-0001).
- Agent loop internals (FEAT-0002).
- TUITerminal User Interface (the interactive mode, and the `pi-tui` library). rendering internals (FEAT-0003).
- ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers./skills API surface (FEAT-0005).
- SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). format and branching internals (FEAT-0006).
Test Plan: Interactive Coding Agent
Scope
Covers the pi-coding-agent CLICommand-Line Interface (the `pi` binary). product: AgentSession, tools, modes (interactive, print, JSON, RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode).), settings, resource loading, project trust, and the SDKSoftware Development Kit (the embeddable programmatic API)..
Unit Tests
Test files under packages/coding-agent/test/ cover:
- AgentSession core (agent-session-.test.ts files for retry, concurrent, stats, dynamic-provider, dynamic-tools, runtime-events, auto-compaction-queue)
- Bash execution (bash-close-hang-windows.test.ts, bash-execution-width.test.ts)
- Built-in tools (tools.test.ts, edit-tool-legacy-input.test.ts, edit-tool-no-full-redraw.test.ts)
- CLICommand-Line Interface (the `pi` binary). args (args.test.ts)
- Config/settings (config.test.ts, settings-manager.test.ts, settings-manager-bug.test.ts, config-value-migration.test.ts)
- Resource loader (resource-loader.test.ts)
- System prompt building (system-prompt.test.ts)
- Run modes (print-mode.test.ts, rpc.test.ts, rpc-jsonl.test.ts, rpc-client-clone.test.ts, rpc-client-process-exit.test.ts, rpc-prompt-response-semantics.test.ts)
- SDKSoftware Development Kit (the embeddable programmatic API). (sdk-session-manager.test.ts, sdk-skills.test.ts, sdk-stream-options.test.ts, sdk-openrouter-attribution.test.ts)
- Trust manager (trust-manager.test.ts, trust-selector.test.ts)
- Theme handling (theme-detection.test.ts, theme-export.test.ts, theme-picker.test.ts, export-html-xss.test.ts)
- Interactive mode (interactive-mode-anthropic-warning.test.ts, interactive-mode-clone-command.test.ts, interactive-mode-compaction.test.ts, interactive-mode-import-command.test.ts, interactive-mode-startup-input.test.ts, interactive-mode-status.test.ts, interactive-mode-suspend.test.ts)
- Image processing (image-process.test.ts, image-processing.test.ts, image-resize-callers.test.ts)
- Clipboard handling (clipboard.test.ts, clipboard-native.test.ts, clipboard-image.test.ts, clipboard-image-bmp-conversion.test.ts)
- Version check (version-check.test.ts, pi-user-agent.test.ts)
- Suite tests (suite/agent-session-.test.ts) using the harness + faux provider
Integration Tests
- Suite tests under
test/suite/use the coding-agent test harness with a faux provider for full prompt-loop testing without real API calls - Issue regression tests under
test/suite/regressions/cover specific bugs and edge cases
Test Infrastructure
- HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. (
test/suite/harness.ts) provides a managed AgentSession + faux provider - In-memory session manager for deterministic testing
- Controlled bash process lifecycle
Coverage Matrix
| Requirement | Test Files |
|---|---|
| FRFunctional Requirement.-01MustThe system shall provide a `pi` CLICommand-Line Interface (the `pi` binary). with an interactive TUITerminal User Interface (the interactive mode, and the `pi-tui` library). as the default mode. (CLICommand-Line Interface (the `pi` binary). interactive default) | args.test.ts |
| FRFunctional Requirement.-02MustThe system shall provide four default built-in tools: `read`, `bash`, `edit`, `write`, and a read-only tool set (`read`, `grep`, `find`, `ls`). (Built-in tools) | tools.test.ts, edit-tool-*.test.ts |
| FRFunctional Requirement.-03MustThe system shall provide an `AgentSession` core that drives the prompt loop, manages model and thinking level, runs compaction, executes bash, and exports HTML. (AgentSession core) | agent-session-.test.ts, suite/agent-session-.test.ts |
| FRFunctional Requirement.-04MustThe system shall resolve model selection via `provider/id:thinking` patterns, scoped model cycling, and in-TUITerminal User Interface (the interactive mode, and the `pi-tui` library). `/model` and `/scoped-models` commands. (Model selection) | model-registry.test.ts, model-resolver.test.ts |
| FRFunctional Requirement.-05MustThe system shall provide built-in slash commands (`settings`, `model`, `export`, `fork`, `tree`, `login`, `logout`, `new`, `compact`, `resume`, `reload`, `quit`, and others). (Slash commands) | tools.test.ts, interactive-mode-*.test.ts |
| FRFunctional Requirement.-06MustThe system shall support run modes: interactive (default), print (`-p`), JSON (`--mode json`), RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). (`--mode rpc`), and SDKSoftware Development Kit (the embeddable programmatic API).. (Run modes) | print-mode.test.ts, rpc.test.ts |
| FRFunctional Requirement.-07MustThe system shall provide global and project settings (`~/.pi/agent/settings.json`, `.pi/settings.json`) with file locking and deep merge, plus keybindings. (Settings) | config.test.ts, settings-manager.test.ts |
| FRFunctional Requirement.-08MustThe system shall gate project resources (settings, extensions, context files) behind a project-trust decision. (Project trustA per-folder decision (`~/.pi/agent/trust.json`) gating whether project settings, resources, and extensions execute.) | trust-manager.test.ts, trust-selector.test.ts |
| FRFunctional Requirement.-09MustThe system shall discover and load resources (extensions, skills, prompt templates, themes, AGENTS.mdProject-specific rules file for humans and agents, read automatically from the repo root./CLAUDE.md) from global, project, and package sources. (Resource discovery) | resource-loader.test.ts |
| FRFunctional Requirement.-10MustThe system shall build the system prompt from base prompt, project context, skills, date, and cwd. (System prompt building) | system-prompt.test.ts |
| FRFunctional Requirement.-11ShouldThe system shall support theme loading (JSON themes, light/dark auto, hot-reload). (Themes) | theme-detection.test.ts, theme-export.test.ts |
| FRFunctional Requirement.-12ShouldThe system shall provide update-check and install-telemetry endpoints, disable-able via env flags or `--offline`. (Update/telemetry) | version-check.test.ts |
| NFRNon-Functional Requirement.-02MustA hot-swap runtime shall tear down and recreate cwd-bound services when switching sessions or cwds. (Hot-swap runtime) | agent-session-runtime-events.test.ts, suite/agent-session-runtime.test.ts |
requirements
- Should the experimental first-time setup flow graduate to default behavior, and on what timeline?
- What is the policy for promoting a widely-used extension into a built-in tool or command?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. | The project: a minimal, self-extensible terminal coding agent harness and its libraries. |
| HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. | The coding agent runtime that wires the agent loop, tools, sessions, and UI together. |
| ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. | A TypeScript module with a default export function (pi: ExtensionAPI) that augments the agent with tools, commands, events, UI, or providers. |
| SkillAn on-demand capability package following the Agent Skills standard (`SKILL.md` + optional frontmatter), invoked as `/skill:name`. | An on-demand capability package following the Agent Skills standard (SKILL.md + optional frontmatter), invoked as /skill:name. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| pi packageA distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via `pi install`. | A distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via pi install. |
| SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). | A persistent, branchable conversation log stored as JSONL (SessionHeader, messages, compaction summaries, branch summaries). |
| BranchingTree-structured session forking (`/fork`, `/clone`, `/tree`) where each entry has `id`/`parentId`. | Tree-structured session forking (/fork, /clone, /tree) where each entry has id/parentId. |
| CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. | Lossy summarization of older session messages to reclaim context; original JSONL is preserved. |
| SteeringA queued message delivered to a streaming agent after the current tool batch completes. | A queued message delivered to a streaming agent after the current tool batch completes. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| Project trustA per-folder decision (`~/.pi/agent/trust.json`) gating whether project settings, resources, and extensions execute. | A per-folder decision (~/.pi/agent/trust.json) gating whether project settings, resources, and extensions execute. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Faux providerAn in-memory scripted provider (`providers/faux.ts`) used for deterministic tests with no real API calls. | An in-memory scripted provider (providers/faux.ts) used for deterministic tests with no real API calls. |
| CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. | The deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. |
| Remote sessionA pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. | A pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. |
| Session leaseA remote-session ownership handle (`exclusive` or `shared`) granted by `PiClient.acquireSession()`/`createSession()`; gates who may mutate or observe a session. | A remote-session ownership handle (exclusive or shared) granted by PiClient.acquireSession()/createSession(); gates who may mutate or observe a session. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| Byte transportThe minimal ordered byte-stream interface (`send`/`close`, inbound `onData`/`onClose`/`onError`) that `PiClient` uses; keeps pi-client Node-agnostic. | The minimal ordered byte-stream interface (send/close, inbound onData/onClose/onError) that PiClient uses; keeps pi-client Node-agnostic. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| Legacy supervisorThe renamed orchestrator code in pi-server (`server` CLI, child-process supervision, Radius, RPC stream bridge). | The renamed orchestrator code in pi-server (server CLICommand-Line Interface (the `pi` binary)., child-process supervision, Radius, RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). stream bridge). |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`. | Behavioral, model-backed checks for pi workflows run via vitest-evals with a real AgentSession. |
Technical Terms
| Term | Definition |
|---|---|
| ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). | The runtime unit owning a model catalog, auth, and stream behavior (e.g. anthropic, openai). |
| API implementationA wire-protocol backend shared by providers (e.g. `anthropic-messages`, `openai-responses`, `openai-completions`, `google-generative-ai`, `bedrock-converse-stream`). | A wire-protocol backend shared by providers (e.g. anthropic-messages, openai-responses, openai-completions, google-generative-ai, bedrock-converse-stream). |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
Agent |
pi-agent-core's stateful class owning the transcript and lifecycle (prompt, continue, abort). |
AgentHarness |
pi-agent-core's higher-level orchestrator wrapping Agent with sessions, compaction, skills, and provider hooks. |
agentLoop |
The low-level prompt-stream-tool-continue loop in pi-agent-core. |
AgentMessage |
pi-agent-core's app-extensible message union (via declaration merging); convertToLlm bridges to pi-ai Message. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| FramingThe pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBOR item. | The pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item. |
hello |
The first message a pi-client sends, carrying PROTOCOL_VERSION and a bearer token. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
| Differential renderingpi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. | pi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. |
| Synchronized outputTerminal escape sequence (`\x1b[?2026h..l`) used by pi-tui for atomic, flicker-free rendering. | Terminal escape sequence (\x1b[?2026h..l) used by pi-tui for atomic, flicker-free rendering. |
| Kitty keyboard protocolTerminal input protocol pi-tui negotiates for richer key reporting. | Terminal input protocol pi-tui negotiates for richer key reporting. |
| Lockstep versioningAll packages share one version and release together. | All packages share one version and release together. |
| Trusted publishingnpm publish via GitHub Actions OIDC (environment `npm-publish`); no local credentials required. | npm publish via GitHub Actions OIDCOpenID Connect (used for npm trusted publishing identity). (environment npm-publish); no local credentials required. |
| Shrinkwrap`packages/coding-agent/npm-shrinkwrap.json`, generated from the root lockfile to pin transitive deps for npm users. | packages/coding-agent/npm-shrinkwrap.json, generated from the root lockfile to pin transitive deps for npm users. |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| LLMLarge Language Model. | Large Language Model. |
| MCPModel Context Protocol (not built into core; extensions may add it). | Model Context Protocol (not built into core; extensions may add it). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). | Open Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). |
| PKCEProof Key for Code Exchange (OAuth flow used by pi-ai). | Proof Key for Code Exchange (OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). flow used by pi-ai). |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| FRFunctional Requirement. | Functional Requirement. |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). | Input Method Editor (pi-tui positions the hardware cursor for IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). candidate windows). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
| AGENTS.mdProject-specific rules file for humans and agents, read automatically from the repo root. | Project-specific rules file for humans and agents, read automatically from the repo root. |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. and Skills Platform
Overview
The extension platform is how pi stays minimal while remaining extensible. TypeScript extensions register custom tools, commands, event handlers, UI primitives, providers, and autocomplete; skills package on-demand capabilities via the Agent Skills standard; prompt templates offer reusable prompt snippets. Together they let users and packages reshape nearly every part of the agent without forking the core.
Stakeholders
| Stakeholder | Interest |
|---|---|
| ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. authors | A rich, stable ExtensionAPI and clear loading rules |
| Skills authors | A standard SKILL.md format and predictable invocation |
| End users | Safe, discoverable ways to add capabilities (install, trust) |
| Maintainers | A small, well-considered hook surface that does not bloat core |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FRFunctional Requirement.-01MustThe system shall load TypeScript extensions via a default export `function (pi: ExtensionAPI)` using cached, lazy module loading. | Must | The system shall load TypeScript extensions via a default export function (pi: ExtensionAPI) using cached, lazy module loading. |
| FRFunctional Requirement.-02MustThe system shall allow extensions to register and replace tools (built-ins included). | Must | The system shall allow extensions to register and replace tools (built-ins included). |
| FRFunctional Requirement.-03MustThe system shall allow extensions to register slash commands invoked as `/name`. | Must | The system shall allow extensions to register slash commands invoked as /name. |
| FRFunctional Requirement.-04MustThe system shall provide event handlers for input, tool_call, tool_result, message lifecycle, turn/agent lifecycle, session lifecycle, compaction, provider request/response, project_trust, and resources_discover. | Must | The system shall provide event handlers for input, tool_call, tool_result, message lifecycle, turn/agent lifecycle, session lifecycle, compaction, provider request/response, project_trust, and resources_discover. |
| FRFunctional Requirement.-05MustThe system shall allow extensions to render UI primitives: selectors, confirmations, inputs, notifications, status line, widgets, custom footer/header/editor/overlay, and raw terminal input. | Must | The system shall allow extensions to render UI primitives: selectors, confirmations, inputs, notifications, status line, widgets, custom footer/header/editor/overlay, and raw terminal input. |
| FRFunctional Requirement.-06MustThe system shall allow extensions to define keyboard shortcuts, CLICommand-Line Interface (the `pi` binary). flags, autocomplete providers, and message renderers. | Must | The system shall allow extensions to define keyboard shortcuts, CLICommand-Line Interface (the `pi` binary). flags, autocomplete providers, and message renderers. |
| FRFunctional Requirement.-07MustThe system shall allow extensions to perform session control actions (setActiveTools, setModel, setThinkingLevel, abort, compact, fork). | Must | The system shall allow extensions to perform session control actions (setActiveTools, setModel, setThinkingLevel, abort, compact, fork). |
| FRFunctional Requirement.-08MustThe system shall load Agent Skills from `SKILL.md` files (global, project parent-walk, or packages), invoked as `/skill:name`, injected into the system prompt on demand. | Must | The system shall load Agent Skills from SKILL.md files (global, project parent-walk, or packages), invoked as /skill:name, injected into the system prompt on demand. |
| FRFunctional Requirement.-09MustThe system shall load prompt templates (Markdown with `{{variable}}` expansion) invoked as `/templatename`. | Must | The system shall load prompt templates (Markdown with {{variable}} expansion) invoked as /templatename. |
| FRFunctional Requirement.-10MustThe system shall load resources from global, project, and package sources with project-trust gating. | Must | The system shall load resources from global, project, and package sources with project-trust gating. |
| FRFunctional Requirement.-11ShouldThe system shall support a package manager (`pi install/remove/update/list`) for distributing extensions, skills, prompts, and themes via npm or git. | Should | The system shall support a package manager (pi install/remove/update/list) for distributing extensions, skills, prompts, and themes via npm or git. |
| FRFunctional Requirement.-12ShouldThe system shall support custom providers via `~/.pi/agent/models.json` or extensions for custom APIs/OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot).. | Should | The system shall support custom providers via ~/.pi/agent/models.json or extensions for custom APIs/OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot).. |
| FRFunctional Requirement.-13MayThe system shall allow extensions to register custom compaction and summarization behavior. | May | The system shall allow extensions to register custom compaction and summarization behavior. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFRNon-Functional Requirement.-01MustProject-sourced extensions shall not execute until a project-trust decision is recorded. | Must | Security | Project-sourced extensions shall not execute until a project-trust decision is recorded. |
| NFRNon-Functional Requirement.-02MustExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. module loading shall be cached so a module is not re-evaluated per use. | Must | Reliability | ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. module loading shall be cached so a module is not re-evaluated per use. |
| NFRNon-Functional Requirement.-03MustThe legacy pi-ai root API used by extensions shall be aliased to `/compat` at runtime so existing extensions keep working. | Must | Compatibility | The legacy pi-ai root API used by extensions shall be aliased to /compat at runtime so existing extensions keep working. |
| NFRNon-Functional Requirement.-04ShouldThe hook surface shall be well-considered; new hooks require maintainer discussion to avoid unmaintainable complexity. | Should | Maintainability | The hook surface shall be well-considered; new hooks require maintainer discussion to avoid unmaintainable complexity. |
| NFRNon-Functional Requirement.-05ShouldExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. event dispatch shall not block the agent loop on the happy path. | Should | Performance | ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. event dispatch shall not block the agent loop on the happy path. |
Constraints
- Extensions are TypeScript modules loaded via
jiti. - Skills follow the external Agent Skills standard (
agentskills.io). - New hooks that bloat core are rejected; the bar is "well considered and discussed".
Acceptance Criteria
Every FRFunctional Requirement. and NFRNon-Functional Requirement. shall have at least one acceptance criterion.
Order criteria by FRs first (sorted by ID), then NFRs (sorted by ID).
- FRFunctional Requirement.-02MustThe system shall allow extensions to register and replace tools (built-ins included).
- Given an extension calling
pi.registerTool(...) - When the agent runs
- Then the custom tool is available and may replace a built-in of the same name.
- Given an extension calling
- FRFunctional Requirement.-04MustThe system shall provide event handlers for input, tool_call, tool_result, message lifecycle, turn/agent lifecycle, session lifecycle, compaction, provider request/response, project_trust, and resources_discover.
- Given an extension with a
tool_callhandler - When a tool is invoked
- Then the handler receives the call and may observe or mutate it.
- Given an extension with a
- FRFunctional Requirement.-08MustThe system shall load Agent Skills from `SKILL.md` files (global, project parent-walk, or packages), invoked as `/skill:name`, injected into the system prompt on demand.
- Given a
SKILL.mdplaced in a discoverable skills directory - When the user invokes
/skill:name - Then the skill content is injected into the system prompt for that session.
- Given a
- FRFunctional Requirement.-10MustThe system shall load resources from global, project, and package sources with project-trust gating.
- Given an untrusted project with
.pi/resources - When the agent starts
- Then project-sourced extensions, skills, and prompts are not loaded until trust is granted.
- Given an untrusted project with
- NFRNon-Functional Requirement.-01MustProject-sourced extensions shall not execute until a project-trust decision is recorded.
- Given a project-sourced extension
- When the project is not trusted
- Then the extension's default export is never executed.
- NFRNon-Functional Requirement.-03MustThe legacy pi-ai root API used by extensions shall be aliased to `/compat` at runtime so existing extensions keep working.
- Given an extension importing from the pi-ai root
- When loaded after the
/compatmigration - Then the import resolves to the compat entrypoint without code changes.
Conflicts
None identified yet.
Open Questions
- What is the stabilization criteria for the
ExtensionAPI(currently documented in a 104KB extensions.md), and which parts are considered stable versus experimental? - How should extension-provided permissions/sandboxing hooks interact with the documented external-sandbox patterns?
Specification: ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. and Skills Platform
Overview
Three resource types extend pi: extensions (TypeScript modules), skills (SKILL.md packages), and prompt templates ({{var}} Markdown).
DefaultResourceLoader discovers them from global/project/package sources with trust gating; ExtensionRunner dispatches lifecycle and event hooks; skills and templates are injected into the system prompt on demand.
Architecture
DefaultResourceLoader
discover: global (~/.pi/agent, ~/.agents)
project (.pi, .agents, AGENTS.mdProject-specific rules file for humans and agents, read automatically from the repo root./CLAUDE.md) -- trust gated
packages (installed)
|
v
+--------------------+ cached, lazy (jiti)
| ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. loader |----> default export (pi: ExtensionAPI)
+---------+----------+
|
v
+--------------------+
| ExtensionRunner | lifecycle + event dispatch
| (on/emit hooks) | (input, tool_call, tool_result,
+--------------------+ message_*, session_*, provider_*, ...)
|
v
AgentSession / InteractiveMode (consume hooks, expose UI context)
Skills & Prompt Templates
loaded on demand (/skill:name, /templatename)
-> injected into system prompt or expanded into user message
Data Models
ExtensionAPI (excerpt)
| Capability | Method | Description |
|---|---|---|
| Tools | registerTool, defineTool |
Add or replace tools |
| Commands | registerCommand |
Add slash command /name |
| Events | on(event, handler) |
Subscribe to lifecycle hooks |
| UI | ExtensionUIContext |
Selectors, confirmations, inputs, widgets, overlays |
| Shortcuts | ExtensionShortcut |
Register key bindings |
| Flags | ExtensionFlag |
Register CLICommand-Line Interface (the `pi` binary). flags |
| SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). control | ExtensionContextActions |
setActiveTools, setModel, compact, fork, abort |
Event surface (categories)
| Category | Events |
|---|---|
| Input | input |
| Agent lifecycle | before_agent_start, agent_start/end, turn_start/end |
| Messages | message_start/update/end |
| Tools | tool_call, tool_result |
| Sessions | session_start/shutdown, session_before_compact/compact/fork/switch/tree |
| ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). | before_provider_request, after_provider_response |
| Context | context, resources_discover, project_trust, user_bash |
SkillAn on-demand capability package following the Agent Skills standard (`SKILL.md` + optional frontmatter), invoked as `/skill:name`.
| Field | Type | Constraints | Description |
|---|---|---|---|
| name | string | from /skill: invocation |
SkillAn on-demand capability package following the Agent Skills standard (`SKILL.md` + optional frontmatter), invoked as `/skill:name`. identifier |
| path | string | file path | Location of SKILL.md |
| frontmatter | object | optional | Metadata |
| body | string | not null | Markdown injected into prompt |
API Contracts
/skill:name
Injects the skill's SKILL.md body into the system prompt context for the session.
/templatename
Expands {{variable}} placeholders in the Markdown template into a user message.
pi.install(name) / pi.remove(name) / pi.update(name)
Package manager operations over npm or git sources, resolving to resource directories (extensions, skills, prompts, themes, models).
Sequences
Tool call through extension hooks
agent -> tool_call event
-> beforeToolCall (extension can {block: true})
-> if not blocked: tool.execute(args)
-> tool_execution_update events (extensions observe)
-> afterToolCall (extension can override content/details/isError/terminate)
-> tool_result event
Resource discovery (trust-gated)
DefaultResourceLoader.discover()
for each source (global, project, package):
if project source and not trusted: skip
collect extensions, skills, prompts, themes, context files
-> resources_discover event (extensions may augment)
-> registry populated
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Language | TypeScript via jiti | Type-safe, cached, lazy |
| Skills standard | Agent Skills (SKILL.md) |
Cross-agent interoperability |
| Hook surface | Curated event taxonomy | Power without unbounded core growth |
| Trust model | Project sources gated | Prevent untrusted code execution by default |
| Compat alias | pi-ai root -> /compat |
Existing extensions keep working |
Risks and Unknowns
- The
ExtensionAPIis large and evolving; breaking changes affect the ecosystem. - Event handler ordering and interaction effects across multiple extensions can be hard to predict.
- Project trustA per-folder decision (`~/.pi/agent/trust.json`) gating whether project settings, resources, and extensions execute. bypass (e.g. via a malicious package source) is a security risk to monitor.
Out of Scope
- The core built-in tools and agent loop (FEAT-0002, FEAT-0004).
- SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). persistence and branching (FEAT-0006).
- LLMLarge Language Model. provider auth internals (FEAT-0001).
Test Plan: ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. and Skills Platform
Scope
Covers extension loading, hook dispatch, skills, prompt templates, package manager, and the ExtensionAPI surface.
Unit Tests
Test files under packages/coding-agent/test/ cover:
- ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. runner (extensions-runner.test.ts, compaction-extensions.test.ts, compaction-extensions-example.test.ts)
- ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. discovery (extensions-discovery.test.ts)
- ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. input events (extensions-input-event.test.ts)
- Skills (skills.test.ts, sdk-skills.test.ts)
- Prompt templates (prompt-templates.test.ts)
- Package manager (package-manager.test.ts, package-manager-ssh.test.ts, package-command-paths.test.ts)
- Git integration (git-update.test.ts, git-ssh-url.test.ts, git-merge-and-resolve-extension.test.ts)
- Plan mode extension (plan-mode-extension.test.ts, plan-mode-utils.test.ts)
- Trigger compact extension (trigger-compact-extension.test.ts)
- Suite regressions (extension-factory-cache.test.ts, 6162-extension-active-tools-next-turn.test.ts, 6260-inline-extension-naming.test.ts, 5433-extension-oauth-prompt-input.test.ts, 2835-tools-allowlist-filters-extension-tools.test.ts, 5080-signal-shutdown-extension-cleanup.test.ts)
Test Infrastructure
- In-memory extension module loading with mock default exports
- Fake
ExtensionAPIandExtensionUIContextfor dispatch testing - Test extension files in
examples/extensions/
Coverage Matrix
| Requirement | Test Files |
|---|---|
| FRFunctional Requirement.-01MustThe system shall load TypeScript extensions via a default export `function (pi: ExtensionAPI)` using cached, lazy module loading. (ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. loading) | extensions-runner.test.ts |
| FRFunctional Requirement.-02MustThe system shall allow extensions to register and replace tools (built-ins included). (Tool registration/replacement) | extensions-runner.test.ts, 2835-regression |
| FRFunctional Requirement.-03MustThe system shall allow extensions to register slash commands invoked as `/name`. (Slash commands) | extensions-runner.test.ts |
| FRFunctional Requirement.-04MustThe system shall provide event handlers for input, tool_call, tool_result, message lifecycle, turn/agent lifecycle, session lifecycle, compaction, provider request/response, project_trust, and resources_discover. (Event handlers) | extensions-runner.test.ts, extensions-input-event.test.ts |
| FRFunctional Requirement.-05MustThe system shall allow extensions to render UI primitives: selectors, confirmations, inputs, notifications, status line, widgets, custom footer/header/editor/overlay, and raw terminal input. (UI primitives) | (sdk docs) |
| FRFunctional Requirement.-07MustThe system shall allow extensions to perform session control actions (setActiveTools, setModel, setThinkingLevel, abort, compact, fork). (SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). control) | extensions-runner.test.ts |
| FRFunctional Requirement.-08MustThe system shall load Agent Skills from `SKILL.md` files (global, project parent-walk, or packages), invoked as `/skill:name`, injected into the system prompt on demand. (Skills loading) | skills.test.ts, sdk-skills.test.ts |
| FRFunctional Requirement.-09MustThe system shall load prompt templates (Markdown with `{{variable}}` expansion) invoked as `/templatename`. (Prompt templates) | prompt-templates.test.ts |
| FRFunctional Requirement.-10MustThe system shall load resources from global, project, and package sources with project-trust gating. (Resource loading) | extensions-discovery.test.ts |
| FRFunctional Requirement.-11ShouldThe system shall support a package manager (`pi install/remove/update/list`) for distributing extensions, skills, prompts, and themes via npm or git. (Package manager) | package-manager.test.ts |
| NFRNon-Functional Requirement.-01MustProject-sourced extensions shall not execute until a project-trust decision is recorded. (Trust-gated extensions) | (project trust tests) |
| NFRNon-Functional Requirement.-02MustExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. module loading shall be cached so a module is not re-evaluated per use. (Cached module loading) | extension-factory-cache.test.ts |
requirements
- What is the stabilization criteria for the
ExtensionAPI(currently documented in a 104KB extensions.md), and which parts are considered stable versus experimental? - How should extension-provided permissions/sandboxing hooks interact with the documented external-sandbox patterns?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. | The project: a minimal, self-extensible terminal coding agent harness and its libraries. |
| HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. | The coding agent runtime that wires the agent loop, tools, sessions, and UI together. |
| ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. | A TypeScript module with a default export function (pi: ExtensionAPI) that augments the agent with tools, commands, events, UI, or providers. |
| SkillAn on-demand capability package following the Agent Skills standard (`SKILL.md` + optional frontmatter), invoked as `/skill:name`. | An on-demand capability package following the Agent Skills standard (SKILL.md + optional frontmatter), invoked as /skill:name. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| pi packageA distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via `pi install`. | A distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via pi install. |
| SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). | A persistent, branchable conversation log stored as JSONL (SessionHeader, messages, compaction summaries, branch summaries). |
| BranchingTree-structured session forking (`/fork`, `/clone`, `/tree`) where each entry has `id`/`parentId`. | Tree-structured session forking (/fork, /clone, /tree) where each entry has id/parentId. |
| CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. | Lossy summarization of older session messages to reclaim context; original JSONL is preserved. |
| SteeringA queued message delivered to a streaming agent after the current tool batch completes. | A queued message delivered to a streaming agent after the current tool batch completes. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| Project trustA per-folder decision (`~/.pi/agent/trust.json`) gating whether project settings, resources, and extensions execute. | A per-folder decision (~/.pi/agent/trust.json) gating whether project settings, resources, and extensions execute. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Faux providerAn in-memory scripted provider (`providers/faux.ts`) used for deterministic tests with no real API calls. | An in-memory scripted provider (providers/faux.ts) used for deterministic tests with no real API calls. |
| CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. | The deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. |
| Remote sessionA pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. | A pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. |
| Session leaseA remote-session ownership handle (`exclusive` or `shared`) granted by `PiClient.acquireSession()`/`createSession()`; gates who may mutate or observe a session. | A remote-session ownership handle (exclusive or shared) granted by PiClient.acquireSession()/createSession(); gates who may mutate or observe a session. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| Byte transportThe minimal ordered byte-stream interface (`send`/`close`, inbound `onData`/`onClose`/`onError`) that `PiClient` uses; keeps pi-client Node-agnostic. | The minimal ordered byte-stream interface (send/close, inbound onData/onClose/onError) that PiClient uses; keeps pi-client Node-agnostic. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| Legacy supervisorThe renamed orchestrator code in pi-server (`server` CLI, child-process supervision, Radius, RPC stream bridge). | The renamed orchestrator code in pi-server (server CLICommand-Line Interface (the `pi` binary)., child-process supervision, Radius, RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). stream bridge). |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`. | Behavioral, model-backed checks for pi workflows run via vitest-evals with a real AgentSession. |
Technical Terms
| Term | Definition |
|---|---|
| ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). | The runtime unit owning a model catalog, auth, and stream behavior (e.g. anthropic, openai). |
| API implementationA wire-protocol backend shared by providers (e.g. `anthropic-messages`, `openai-responses`, `openai-completions`, `google-generative-ai`, `bedrock-converse-stream`). | A wire-protocol backend shared by providers (e.g. anthropic-messages, openai-responses, openai-completions, google-generative-ai, bedrock-converse-stream). |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
Agent |
pi-agent-core's stateful class owning the transcript and lifecycle (prompt, continue, abort). |
AgentHarness |
pi-agent-core's higher-level orchestrator wrapping Agent with sessions, compaction, skills, and provider hooks. |
agentLoop |
The low-level prompt-stream-tool-continue loop in pi-agent-core. |
AgentMessage |
pi-agent-core's app-extensible message union (via declaration merging); convertToLlm bridges to pi-ai Message. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| FramingThe pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBOR item. | The pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item. |
hello |
The first message a pi-client sends, carrying PROTOCOL_VERSION and a bearer token. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
| Differential renderingpi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. | pi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. |
| Synchronized outputTerminal escape sequence (`\x1b[?2026h..l`) used by pi-tui for atomic, flicker-free rendering. | Terminal escape sequence (\x1b[?2026h..l) used by pi-tui for atomic, flicker-free rendering. |
| Kitty keyboard protocolTerminal input protocol pi-tui negotiates for richer key reporting. | Terminal input protocol pi-tui negotiates for richer key reporting. |
| Lockstep versioningAll packages share one version and release together. | All packages share one version and release together. |
| Trusted publishingnpm publish via GitHub Actions OIDC (environment `npm-publish`); no local credentials required. | npm publish via GitHub Actions OIDCOpenID Connect (used for npm trusted publishing identity). (environment npm-publish); no local credentials required. |
| Shrinkwrap`packages/coding-agent/npm-shrinkwrap.json`, generated from the root lockfile to pin transitive deps for npm users. | packages/coding-agent/npm-shrinkwrap.json, generated from the root lockfile to pin transitive deps for npm users. |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| LLMLarge Language Model. | Large Language Model. |
| MCPModel Context Protocol (not built into core; extensions may add it). | Model Context Protocol (not built into core; extensions may add it). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). | Open Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). |
| PKCEProof Key for Code Exchange (OAuth flow used by pi-ai). | Proof Key for Code Exchange (OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). flow used by pi-ai). |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| FRFunctional Requirement. | Functional Requirement. |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). | Input Method Editor (pi-tui positions the hardware cursor for IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). candidate windows). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
| AGENTS.mdProject-specific rules file for humans and agents, read automatically from the repo root. | Project-specific rules file for humans and agents, read automatically from the repo root. |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). Persistence and BranchingTree-structured session forking (`/fork`, `/clone`, `/tree`) where each entry has `id`/`parentId`.
Overview
Sessions make pi's conversations durable, resumable, and branchable.
SessionManager persists tree-structured JSONL logs (messages, model/thinking changes, compaction summaries, branch summaries); users can resume, fork, clone, and navigate session trees; compaction reclaims context lossily while preserving the original log.
Stakeholders
| Stakeholder | Interest |
|---|---|
| End users | Resume past work, branch explorations without losing history, manage context length |
| ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. authors | SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). lifecycle hooks (compact, fork, switch, tree) to observe and augment |
| Maintainers | A stable, migratable on-disk format with clear versioning |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FRFunctional Requirement.-01MustThe system shall persist sessions as JSONL with tree-structured entries (`id`/`parentId` per entry) under a versioned format. | Must | The system shall persist sessions as JSONL with tree-structured entries (id/parentId per entry) under a versioned format. |
| FRFunctional Requirement.-02MustThe system shall support session entry types: messages, thinking-level changes, model changes, compaction summaries, branch summaries, labels, custom messages, and session info. | Must | The system shall support session entry types: messages, thinking-level changes, model changes, compaction summaries, branch summaries, labels, custom messages, and session info. |
| FRFunctional Requirement.-03MustThe system shall support resume (`-r`, `-c`, `--session`, `/resume`), new (`/new`), fork (`/fork`, `--fork`), clone (`/clone`), and tree navigation (`/tree`). | Must | The system shall support resume (-r, -c, --session, /resume), new (/new), fork (/fork, --fork), clone (/clone), and tree navigation (/tree). |
| FRFunctional Requirement.-04MustThe system shall support branching where forking from a previous user message creates a new session file and the original log is preserved. | Must | The system shall support branching where forking from a previous user message creates a new session file and the original log is preserved. |
| FRFunctional Requirement.-05MustThe system shall support compaction (manual via `/compact [prompt]` or automatic on threshold/overflow) that summarizes older messages while keeping recent ones. | Must | The system shall support compaction (manual via /compact [prompt] or automatic on threshold/overflow) that summarizes older messages while keeping recent ones. |
| FRFunctional Requirement.-06MustThe system shall preserve the original JSONL file through compaction (compaction is lossy but non-destructive to the source log). | Must | The system shall preserve the original JSONL file through compaction (compaction is lossy but non-destructive to the source log). |
| FRFunctional Requirement.-07MustThe system shall hot-swap the runtime when switching sessions or cwds, tearing down and recreating cwd-bound services. | Must | The system shall hot-swap the runtime when switching sessions or cwds, tearing down and recreating cwd-bound services. |
| FRFunctional Requirement.-08MustThe system shall emit session lifecycle hooks (`session_before_compact/compact`, `session_before_fork/fork`, `session_before_switch/switch`, `session_before_tree/tree`) extensions can observe. | Must | The system shall emit session lifecycle hooks (session_before_compact/compact, session_before_fork/fork, session_before_switch/switch, session_before_tree/tree) extensions can observe. |
| FRFunctional Requirement.-09ShouldThe system shall provide compaction post-token estimates and branch summarization. | Should | The system shall provide compaction post-token estimates and branch summarization. |
| FRFunctional Requirement.-10ShouldThe system shall support session export to HTML. | Should | The system shall support session export to HTML. |
| FRFunctional Requirement.-11ShouldThe system shall migrate older session format versions to the current version on load. | Should | The system shall migrate older session format versions to the current version on load. |
| FRFunctional Requirement.-12MayThe system shall support session labels and bookmarks for navigation. | May | The system shall support session labels and bookmarks for navigation. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFRNon-Functional Requirement.-01MustThe original session JSONL shall never be destructively modified by compaction or branching. | Must | Reliability | The original session JSONL shall never be destructively modified by compaction or branching. |
| NFRNon-Functional Requirement.-02MustThe format version shall be explicit (`CURRENT_SESSION_VERSION`) with documented migration steps. | Must | Compatibility | The format version shall be explicit (CURRENT_SESSION_VERSION) with documented migration steps. |
| NFRNon-Functional Requirement.-03ShouldSessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). append and read operations shall remain efficient for large session files. | Should | Performance | SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). append and read operations shall remain efficient for large session files. |
| NFRNon-Functional Requirement.-04ShouldCompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. events shall carry reason and retry metadata for extension consumers. | Should | Observability | CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. events shall carry reason and retry metadata for extension consumers. |
Constraints
- Sessions are local JSONL files; no server-side session store.
progress.mdandstate.yml(workflow state) are never committed; session JSONL is user data.- CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. is lossy by design.
Acceptance Criteria
Every FRFunctional Requirement. and NFRNon-Functional Requirement. shall have at least one acceptance criterion.
Order criteria by FRs first (sorted by ID), then NFRs (sorted by ID).
- FRFunctional Requirement.-01MustThe system shall persist sessions as JSONL with tree-structured entries (`id`/`parentId` per entry) under a versioned format.
- Given a session with multiple turns and a branch
- When serialized to disk
- Then the JSONL contains tree-structured entries each with
idandparentIdforming a valid tree.
- FRFunctional Requirement.-05MustThe system shall support compaction (manual via `/compact [prompt]` or automatic on threshold/overflow) that summarizes older messages while keeping recent ones.
- Given a session exceeding the compaction threshold
- When compaction runs
- Then older messages are summarized, recent messages are retained, and a compaction summary entry is appended.
- FRFunctional Requirement.-06MustThe system shall preserve the original JSONL file through compaction (compaction is lossy but non-destructive to the source log).
- Given a session that has been compacted
- When inspecting the JSONL file
- Then the original message entries remain present alongside the compaction summary.
- FRFunctional Requirement.-07MustThe system shall hot-swap the runtime when switching sessions or cwds, tearing down and recreating cwd-bound services.
- Given an interactive session
- When the user runs
/treeand switches branches - Then the runtime tears down cwd-bound services and recreates them for the target branch without leaking state.
- NFRNon-Functional Requirement.-01MustThe original session JSONL shall never be destructively modified by compaction or branching.
- Given any compaction or fork operation
- When it completes
- Then the source session JSONL file is byte-for-byte unchanged except for appended entries.
Conflicts
None identified yet.
Open Questions
- Is there a plan to support remote/session-sync, or will sessions remain strictly local files?
- What is the policy for pruning or archiving very large session files over time?
Specification: SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). Persistence and BranchingTree-structured session forking (`/fork`, `/clone`, `/tree`) where each entry has `id`/`parentId`.
Overview
Sessions are JSONL files with a tree-structured entry stream.
SessionManager owns append/read/migration; AgentSessionRuntime hot-swaps the active session and its cwd-bound services on fork/switch/tree navigation; compaction runs in AgentSession with hooks for extensions.
Architecture
AgentSession
prompt loop appends entries ------+
compaction summarizes |
branch summarization |
| v
| +-------------------+
+----------------->| SessionManager |
| JSONL append/read |
| tree traversal |
| migration (v1->N) |
+---------+---------+
| hot-swap
v
+------------------------+
| AgentSessionRuntime |
| owns active session + |
| cwd-bound services |
| fork()/switch()/ |
| navigate() |
+------------------------+
Data Models
SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). JSONL entry (union, tree-structured)
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | string | required | Unique entry id |
| parentId | string | nullable | Parent entry id (null = root) |
| type | enum | required | message, thinkingChange, modelChange, compaction, branchSummary, label, custom, sessionInfo |
| ... | varies | per type | Type-specific payload |
SessionHeader
| Field | Type | Description |
|---|---|---|
| version | number | Format version (CURRENT_SESSION_VERSION) |
| sessionId | string | Stable session identifier |
| cwd | string | Working directory at creation |
CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. summary entry
| Field | Type | Description |
|---|---|---|
| reason | enum | manual, threshold, overflow, retry |
| willRetry | boolean | Whether compaction will retry |
| summary | string | Summarized older context |
| cutPoint | string | Entry id where cut occurred |
API Contracts
SessionManager.append(entry) / read()
Append appends a JSONL line; read loads and indexes the tree for traversal, listing, and migration.
Runtime: fork() / switch(id) / navigate(direction)
Each tears down the current runtime, emits the corresponding session_before_* event, and recreates services bound to the target session/cwd.
/compact [prompt]
Triggers compaction: emits session_before_compact, summarizes older messages, retains recent ones, appends a compaction summary, emits session_compact.
Sequences
CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. flow
threshold exceeded OR /compact
-> session_before_compact event (extensions may modify/inject)
-> findCutPoint (token estimation)
-> summarize messages before cut
-> keep recent messages
-> append compaction summary entry to JSONL
-> post-token estimate
-> session_compact event (with reason, willRetry)
-> if willRetry: agent.continue()
BranchingTree-structured session forking (`/fork`, `/clone`, `/tree`) where each entry has `id`/`parentId`. flow
/fork <message-id>
-> session_before_fork event
-> create new session file from entries up to message-id
-> runtime.switch() to new session
-> session_fork event
(original session file unchanged)
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Format | JSONL, tree-structured | Append-only, diff/stream friendly, supports branching |
| CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. | Lossy, non-destructive | Reclaims context while preserving source history |
| Migration | Explicit version + on-load migration | Supports evolving format without breaking old sessions |
| Hot-swap | Runtime recreation | Clean state across sessions/cwds |
| Hook surface | session_before_*/session_* pairs |
Extensions observe and augment lifecycle |
Risks and Unknowns
- Large session files (multi-MB) affect resume and read performance; pruning policy is undefined.
- Migration logic must handle every prior version; a missed step corrupts old sessions.
- CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. quality (what gets summarized) directly affects agent performance and is hard to evaluate automatically.
Out of Scope
- The interactive TUITerminal User Interface (the interactive mode, and the `pi-tui` library). rendering of sessions (FEAT-0004).
- The agent loop and tool execution (FEAT-0002).
- ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. API internals beyond session hooks (FEAT-0005).
Test Plan: SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). Persistence and BranchingTree-structured session forking (`/fork`, `/clone`, `/tree`) where each entry has `id`/`parentId`.
Scope
Covers session format, JSONL persistence, tree-structured entries, branching, compaction, migration, export, and session lifecycle.
Unit Tests
Test files under packages/coding-agent/test/ cover:
- SessionManager operations (session-manager/save-entry.test.ts, session-manager/file-operations.test.ts, session-manager/build-context.test.ts, session-manager/labels.test.ts, session-manager/tree-traversal.test.ts, session-manager/migration.test.ts, session-manager/custom-session-id.test.ts)
- SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). branching (agent-session-branching.test.ts)
- SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). compaction (agent-session-compaction.test.ts, compaction.test.ts, compaction-serialization.test.ts, compaction-summary-reasoning.test.ts)
- SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). tree navigation (agent-session-tree-navigation.test.ts, tree-selector.test.ts)
- SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). file handling (session-file-invalid.test.ts, session-id-readonly.test.ts, session-info-modified-timestamp.test.ts, session-cwd.test.ts, session-selector-search.test.ts, session-selector-rename.test.ts, session-selector-path-delete.test.ts)
- SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). lifecycle events (suite/agent-session-queue.test.ts)
- HTML export (export-html-whitespace.test.ts, export-html-skill-block.test.ts)
- Startup session naming (startup-session-name.test.ts)
- First-time setup fork (first-time-setup-fork.test.ts)
- Format resume command (format-resume-command.test.ts)
- Suite regressions (6324-branch-summary-ambient-auth.test.ts, 3686-session-name-event.test.ts, 3688-tree-cancel-compacting.test.ts, 5943-session-start-notify.test.ts, 5996-session-name-newlines.test.ts, 2860-replaced-session-context.test.ts, pre-prompt-compaction-no-continue.test.ts)
Test Infrastructure
- In-memory session storage for deterministic testing
- Simulated session files for migration testing
- Tree-structured JSONL helpers
Coverage Matrix
| Requirement | Test Files |
|---|---|
| FRFunctional Requirement.-01MustThe system shall persist sessions as JSONL with tree-structured entries (`id`/`parentId` per entry) under a versioned format. (JSONL tree format) | session-manager/save-entry.test.ts |
| FRFunctional Requirement.-02MustThe system shall support session entry types: messages, thinking-level changes, model changes, compaction summaries, branch summaries, labels, custom messages, and session info. (Entry types) | session-manager/save-entry.test.ts |
| FRFunctional Requirement.-03MustThe system shall support resume (`-r`, `-c`, `--session`, `/resume`), new (`/new`), fork (`/fork`, `--fork`), clone (`/clone`), and tree navigation (`/tree`). (Resume/fork/clone/tree) | agent-session-branching.test.ts, agent-session-tree-navigation.test.ts |
| FRFunctional Requirement.-04MustThe system shall support branching where forking from a previous user message creates a new session file and the original log is preserved. (BranchingTree-structured session forking (`/fork`, `/clone`, `/tree`) where each entry has `id`/`parentId`.) | agent-session-branching.test.ts |
| FRFunctional Requirement.-05MustThe system shall support compaction (manual via `/compact [prompt]` or automatic on threshold/overflow) that summarizes older messages while keeping recent ones. (CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved.) | agent-session-compaction.test.ts, compaction.test.ts, compaction-serialization.test.ts |
| FRFunctional Requirement.-06MustThe system shall preserve the original JSONL file through compaction (compaction is lossy but non-destructive to the source log). (Non-destructive compaction) | compact test files |
| FRFunctional Requirement.-07MustThe system shall hot-swap the runtime when switching sessions or cwds, tearing down and recreating cwd-bound services. (Runtime hot-swap) | agent-session-runtime-events.test.ts |
| FRFunctional Requirement.-08MustThe system shall emit session lifecycle hooks (`session_before_compact/compact`, `session_before_fork/fork`, `session_before_switch/switch`, `session_before_tree/tree`) extensions can observe. (SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). lifecycle hooks) | (suite harness tests) |
| FRFunctional Requirement.-09ShouldThe system shall provide compaction post-token estimates and branch summarization. (CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. estimates) | compaction-summary-reasoning.test.ts |
| FRFunctional Requirement.-10ShouldThe system shall support session export to HTML. (HTML export) | export-html-*.test.ts |
| FRFunctional Requirement.-11ShouldThe system shall migrate older session format versions to the current version on load. (Migration) | session-manager/migration.test.ts |
| NFRNon-Functional Requirement.-01MustThe original session JSONL shall never be destructively modified by compaction or branching. (Non-destructive original) | compaction.test.ts |
| NFRNon-Functional Requirement.-04ShouldCompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. events shall carry reason and retry metadata for extension consumers. (CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. observability) | compaction-summary-reasoning.test.ts |
requirements
- Is there a plan to support remote/session-sync, or will sessions remain strictly local files?
- What is the policy for pruning or archiving very large session files over time?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. | The project: a minimal, self-extensible terminal coding agent harness and its libraries. |
| HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. | The coding agent runtime that wires the agent loop, tools, sessions, and UI together. |
| ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. | A TypeScript module with a default export function (pi: ExtensionAPI) that augments the agent with tools, commands, events, UI, or providers. |
| SkillAn on-demand capability package following the Agent Skills standard (`SKILL.md` + optional frontmatter), invoked as `/skill:name`. | An on-demand capability package following the Agent Skills standard (SKILL.md + optional frontmatter), invoked as /skill:name. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| pi packageA distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via `pi install`. | A distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via pi install. |
| SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). | A persistent, branchable conversation log stored as JSONL (SessionHeader, messages, compaction summaries, branch summaries). |
| BranchingTree-structured session forking (`/fork`, `/clone`, `/tree`) where each entry has `id`/`parentId`. | Tree-structured session forking (/fork, /clone, /tree) where each entry has id/parentId. |
| CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. | Lossy summarization of older session messages to reclaim context; original JSONL is preserved. |
| SteeringA queued message delivered to a streaming agent after the current tool batch completes. | A queued message delivered to a streaming agent after the current tool batch completes. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| Project trustA per-folder decision (`~/.pi/agent/trust.json`) gating whether project settings, resources, and extensions execute. | A per-folder decision (~/.pi/agent/trust.json) gating whether project settings, resources, and extensions execute. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Faux providerAn in-memory scripted provider (`providers/faux.ts`) used for deterministic tests with no real API calls. | An in-memory scripted provider (providers/faux.ts) used for deterministic tests with no real API calls. |
| CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. | The deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. |
| Remote sessionA pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. | A pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. |
| Session leaseA remote-session ownership handle (`exclusive` or `shared`) granted by `PiClient.acquireSession()`/`createSession()`; gates who may mutate or observe a session. | A remote-session ownership handle (exclusive or shared) granted by PiClient.acquireSession()/createSession(); gates who may mutate or observe a session. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| Byte transportThe minimal ordered byte-stream interface (`send`/`close`, inbound `onData`/`onClose`/`onError`) that `PiClient` uses; keeps pi-client Node-agnostic. | The minimal ordered byte-stream interface (send/close, inbound onData/onClose/onError) that PiClient uses; keeps pi-client Node-agnostic. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| Legacy supervisorThe renamed orchestrator code in pi-server (`server` CLI, child-process supervision, Radius, RPC stream bridge). | The renamed orchestrator code in pi-server (server CLICommand-Line Interface (the `pi` binary)., child-process supervision, Radius, RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). stream bridge). |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`. | Behavioral, model-backed checks for pi workflows run via vitest-evals with a real AgentSession. |
Technical Terms
| Term | Definition |
|---|---|
| ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). | The runtime unit owning a model catalog, auth, and stream behavior (e.g. anthropic, openai). |
| API implementationA wire-protocol backend shared by providers (e.g. `anthropic-messages`, `openai-responses`, `openai-completions`, `google-generative-ai`, `bedrock-converse-stream`). | A wire-protocol backend shared by providers (e.g. anthropic-messages, openai-responses, openai-completions, google-generative-ai, bedrock-converse-stream). |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
Agent |
pi-agent-core's stateful class owning the transcript and lifecycle (prompt, continue, abort). |
AgentHarness |
pi-agent-core's higher-level orchestrator wrapping Agent with sessions, compaction, skills, and provider hooks. |
agentLoop |
The low-level prompt-stream-tool-continue loop in pi-agent-core. |
AgentMessage |
pi-agent-core's app-extensible message union (via declaration merging); convertToLlm bridges to pi-ai Message. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| FramingThe pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBOR item. | The pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item. |
hello |
The first message a pi-client sends, carrying PROTOCOL_VERSION and a bearer token. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
| Differential renderingpi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. | pi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. |
| Synchronized outputTerminal escape sequence (`\x1b[?2026h..l`) used by pi-tui for atomic, flicker-free rendering. | Terminal escape sequence (\x1b[?2026h..l) used by pi-tui for atomic, flicker-free rendering. |
| Kitty keyboard protocolTerminal input protocol pi-tui negotiates for richer key reporting. | Terminal input protocol pi-tui negotiates for richer key reporting. |
| Lockstep versioningAll packages share one version and release together. | All packages share one version and release together. |
| Trusted publishingnpm publish via GitHub Actions OIDC (environment `npm-publish`); no local credentials required. | npm publish via GitHub Actions OIDCOpenID Connect (used for npm trusted publishing identity). (environment npm-publish); no local credentials required. |
| Shrinkwrap`packages/coding-agent/npm-shrinkwrap.json`, generated from the root lockfile to pin transitive deps for npm users. | packages/coding-agent/npm-shrinkwrap.json, generated from the root lockfile to pin transitive deps for npm users. |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| LLMLarge Language Model. | Large Language Model. |
| MCPModel Context Protocol (not built into core; extensions may add it). | Model Context Protocol (not built into core; extensions may add it). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). | Open Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). |
| PKCEProof Key for Code Exchange (OAuth flow used by pi-ai). | Proof Key for Code Exchange (OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). flow used by pi-ai). |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| FRFunctional Requirement. | Functional Requirement. |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). | Input Method Editor (pi-tui positions the hardware cursor for IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). candidate windows). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
| AGENTS.mdProject-specific rules file for humans and agents, read automatically from the repo root. | Project-specific rules file for humans and agents, read automatically from the repo root. |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Orchestrator
Overview
@earendil-works/pi-orchestrator is an experimental daemon and CLICommand-Line Interface (the `pi` binary). that manages multiple pi-coding-agent instances as child processes, providing a supervision layer for spawning, monitoring, stopping, and communicating with pi instances via IPC. It supports a Radius presence integration for remote machine registration and an RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). stream bridge for session event forwarding. The package is under active development and its API is not yet stable.
Stakeholders
| Stakeholder | Interest |
|---|---|
| Operators / automation users | Spawn and manage multiple pi instances from scripts or orchestration tooling |
| Maintainers | A supervision layer for multi-instance pi deployments without modifying the coding-agent core |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FRFunctional Requirement.-01MustThe system shall provide a `serve` daemon command that starts a Unix socket IPC server for managing pi instances. | Must | The system shall provide a serve daemon command that starts a Unix socket IPC server for managing pi instances. |
| FRFunctional Requirement.-02MustThe system shall support spawning new pi-coding-agent RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). instances with a specified cwd and optional label. | Must | The system shall support spawning new pi-coding-agent RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). instances with a specified cwd and optional label. |
| FRFunctional Requirement.-03MustThe system shall support listing all managed instances with their status (starting, online, stopping, stopped, error). | Must | The system shall support listing all managed instances with their status (starting, online, stopping, stopped, error). |
| FRFunctional Requirement.-04MustThe system shall support querying the status of a specific instance by ID. | Must | The system shall support querying the status of a specific instance by ID. |
| FRFunctional Requirement.-05MustThe system shall support stopping a specific instance by ID, cleaning up its resources. | Must | The system shall support stopping a specific instance by ID, cleaning up its resources. |
| FRFunctional Requirement.-06MustThe system shall support sending RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). commands to a running instance and receiving responses. | Must | The system shall support sending RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). commands to a running instance and receiving responses. |
| FRFunctional Requirement.-07MustThe system shall support opening an RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). stream to an instance for bidirectional JSONL communication (commands, session events, extension UI requests). | Must | The system shall support opening an RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). stream to an instance for bidirectional JSONL communication (commands, session events, extension UI requests). |
| FRFunctional Requirement.-08ShouldThe system shall persist instance records to disk for recovery after restart. | Should | The system shall persist instance records to disk for recovery after restart. |
| FRFunctional Requirement.-09ShouldThe system shall support Radius presence registration for remote machine discovery. | Should | The system shall support Radius presence registration for remote machine discovery. |
| FRFunctional Requirement.-10ShouldThe system shall recover instances on restart by marking previously-online instances as stopped and cleaning up Radius connections. | Should | The system shall recover instances on restart by marking previously-online instances as stopped and cleaning up Radius connections. |
| FRFunctional Requirement.-11ShouldThe system shall support session metadata synchronization (sessionId, sessionFile) after commands that can change the session identity. | Should | The system shall support session metadata synchronization (sessionId, sessionFile) after commands that can change the session identity. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFRNon-Functional Requirement.-01MustInstance lifecycle transitions shall be consistent: starting -> online -> stopping -> stopped, with error as a terminal failure state. | Must | Correctness | Instance lifecycle transitions shall be consistent: starting -> online -> stopping -> stopped, with error as a terminal failure state. |
| NFRNon-Functional Requirement.-02MustUnexpected RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). process exit shall mark the instance as error and clean up resources (Radius disconnect, subscriber notification). | Must | Reliability | Unexpected RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). process exit shall mark the instance as error and clean up resources (Radius disconnect, subscriber notification). |
| NFRNon-Functional Requirement.-03ShouldIPC shall use local Unix sockets only; no network exposure by default. | Should | Security | IPC shall use local Unix sockets only; no network exposure by default. |
Constraints
- Experimental; the CLICommand-Line Interface (the `pi` binary)., APIs, and behavior are not yet stable and may change without notice.
- Depends on
@earendil-works/pi-coding-agentfor RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). protocol types and process management. - Unix socket IPC only (no Windows named pipe support at this stage).
Acceptance Criteria
- FRFunctional Requirement.-01MustThe system shall provide a `serve` daemon command that starts a Unix socket IPC server for managing pi instances.
- Given the orchestrator CLICommand-Line Interface (the `pi` binary).
- When
orchestrator serveis run - Then a Unix socket IPC server starts, listening on the configured socket path.
- FRFunctional Requirement.-02MustThe system shall support spawning new pi-coding-agent RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). instances with a specified cwd and optional label.
- Given a running serve daemon
- When
orchestrator spawn --cwd /pathis called - Then a new pi-coding-agent RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). instance is spawned and its ID is returned.
- FRFunctional Requirement.-06MustThe system shall support sending RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). commands to a running instance and receiving responses.
- Given a running instance
- When
orchestrator rpc <id> '{"type":"get_state"}'is called - Then the response from the pi instance is returned.
- NFRNon-Functional Requirement.-02MustUnexpected RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). process exit shall mark the instance as error and clean up resources (Radius disconnect, subscriber notification).
- Given a running instance whose RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). process crashes
- When the exit is detected
- Then the instance status is set to error and persisted.
Conflicts
None identified yet.
Open Questions
- What is the stabilization path for the orchestrator, and should its APIs be aligned with the coding-agent's SDKSoftware Development Kit (the embeddable programmatic API). or be independent?
- Should the orchestrator gain built-in support for non-Unix platforms (Windows named pipes, TCP with auth)?
Specification: Orchestrator
Overview
The orchestrator is a daemon (orchestrator serve) that exposes a Unix socket IPC server. A CLICommand-Line Interface (the `pi` binary). client communicates with the daemon to spawn, list, status-check, stop, and send RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). commands to pi-coding-agent instances. Each instance is a child process running pi --mode rpc. The supervisor persists instance records to JSON and optionally registers with a Radius presence service for remote machine discovery.
Architecture
+-------------------+ +----------------------+
| orchestrator CLICommand-Line Interface (the `pi` binary). | IPC | orchestrator serve |
| (list, spawn, |<-------->| (Unix socket daemon) |
| status, stop, | | |
| rpc, rpc-stream) | +----------+-----------+
+-------------------+ |
| manages
v
+---------------------------+
| OrchestratorSupervisor |
| - spawnInstance() |
| - stopInstance() |
| - handleRpc() |
| - openRpcStream() |
+----+---------------------+
| owns
v
+-----------------------------+
| RpcProcessInstance (child) |
| pi --mode rpc |
| Unix socket IPC |
+-----------------------------+
Data Models
InstanceRecord
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | string | UUID | Unique instance identifier |
| status | InstanceStatus | enum | starting, online, stopping, stopped, error |
| cwd | string | not null | Working directory |
| createdAt | string | ISO 8601 | Creation timestamp |
| lastSeenAt | string | optional | Last update timestamp |
| label | string | optional | Human-readable label |
| sessionId | string | optional | Current pi session ID |
| sessionFile | string | optional | Current pi session file path |
| radiusPiId | string | optional | Radius presence registration ID |
IPC Protocol (excerpt)
| Request type | Payload | Response type |
|---|---|---|
| spawn | cwd, label? | spawn_result |
| list | — | list_result (InstanceSummary[]) |
| status | instanceId | status_result |
| stop | instanceId | stop_result |
| rpc | instanceId, command | rpc_result (RpcResponse) |
| rpc_stream | instanceId | rpc_ready (then bidirectional stream) |
API Contracts
orchestrator serve
Starts the daemon. Recovers persisted instances, starts Radius presence if configured, and listens on a Unix socket.
orchestrator spawn --cwd [--label
Spawns a child pi-coding-agent process in RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). mode, registers with Radius, and returns the instance ID.
orchestrator rpc
Sends a single RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). command to the instance and returns the response. SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). metadata is refreshed after mutating commands (new_session, switch_session, fork, clone, set_session_name, prompt).
orchestrator rpc-stream
Opens a bidirectional stream: the CLICommand-Line Interface (the `pi` binary). forwards stdin JSONL to the instance as RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). commands and extension UI responses, and writes instance events to stdout.
Sequences
Instance spawn flow
orchestrator CLICommand-Line Interface (the `pi` binary). -> serve daemon: spawn {cwd, label}
serve -> supervisor.spawnInstance()
supervisor: create InstanceRecord (status: starting)
supervisor: createRpcProcessInstance (child pi --mode rpc)
supervisor: syncInstanceRecord (get_state)
supervisor: radiusPresence.registerPi()
supervisor: setStatus(online)
serve -> orchestrator CLICommand-Line Interface (the `pi` binary).: spawn_result {instance}
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| IPC transport | Unix socket | Local-only, fast, no network overhead |
| Child process | pi --mode rpc | Reuses existing coding-agent RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). protocol |
| Persistence | JSON file (instances.json) | Simple, portable, no database dependency |
| SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). sync | On-demand after mutating commands | Avoids unnecessary get_state calls on every RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). |
| Experimental status | Explicit disclaimer | Allows API evolution without commitment |
Risks and Unknowns
- Experimental package: the API surface may change or be removed without notice. Consumers should pin a specific version.
- No test suite exists for the orchestrator (no test files found).
- Unix socket only: no Windows support at this stage.
- Radius presence integration depends on an external service; behavior when the service is unreachable needs hardening.
Out of Scope
- The pi-coding-agent RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). protocol itself (FEAT-0004).
- SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). persistence and branching (FEAT-0006).
- ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. API internals (FEAT-0005).
requirements
- What is the stabilization path for the orchestrator, and should its APIs be aligned with the coding-agent's SDK or be independent?
- Should the orchestrator gain built-in support for non-Unix platforms (Windows named pipes, TCP with auth)?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. | The project: a minimal, self-extensible terminal coding agent harness and its libraries. |
| HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. | The coding agent runtime that wires the agent loop, tools, sessions, and UI together. |
| ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. | A TypeScript module with a default export function (pi: ExtensionAPI) that augments the agent with tools, commands, events, UI, or providers. |
| SkillAn on-demand capability package following the Agent Skills standard (`SKILL.md` + optional frontmatter), invoked as `/skill:name`. | An on-demand capability package following the Agent Skills standard (SKILL.md + optional frontmatter), invoked as /skill:name. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| pi packageA distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via `pi install`. | A distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via pi install. |
| SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). | A persistent, branchable conversation log stored as JSONL (SessionHeader, messages, compaction summaries, branch summaries). |
| BranchingTree-structured session forking (`/fork`, `/clone`, `/tree`) where each entry has `id`/`parentId`. | Tree-structured session forking (/fork, /clone, /tree) where each entry has id/parentId. |
| CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. | Lossy summarization of older session messages to reclaim context; original JSONL is preserved. |
| SteeringA queued message delivered to a streaming agent after the current tool batch completes. | A queued message delivered to a streaming agent after the current tool batch completes. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| Project trustA per-folder decision (`~/.pi/agent/trust.json`) gating whether project settings, resources, and extensions execute. | A per-folder decision (~/.pi/agent/trust.json) gating whether project settings, resources, and extensions execute. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Faux providerAn in-memory scripted provider (`providers/faux.ts`) used for deterministic tests with no real API calls. | An in-memory scripted provider (providers/faux.ts) used for deterministic tests with no real API calls. |
| CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. | The deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. |
| Remote sessionA pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. | A pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. |
| Session leaseA remote-session ownership handle (`exclusive` or `shared`) granted by `PiClient.acquireSession()`/`createSession()`; gates who may mutate or observe a session. | A remote-session ownership handle (exclusive or shared) granted by PiClient.acquireSession()/createSession(); gates who may mutate or observe a session. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| Byte transportThe minimal ordered byte-stream interface (`send`/`close`, inbound `onData`/`onClose`/`onError`) that `PiClient` uses; keeps pi-client Node-agnostic. | The minimal ordered byte-stream interface (send/close, inbound onData/onClose/onError) that PiClient uses; keeps pi-client Node-agnostic. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| Legacy supervisorThe renamed orchestrator code in pi-server (`server` CLI, child-process supervision, Radius, RPC stream bridge). | The renamed orchestrator code in pi-server (server CLICommand-Line Interface (the `pi` binary)., child-process supervision, Radius, RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). stream bridge). |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`. | Behavioral, model-backed checks for pi workflows run via vitest-evals with a real AgentSession. |
Technical Terms
| Term | Definition |
|---|---|
| ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). | The runtime unit owning a model catalog, auth, and stream behavior (e.g. anthropic, openai). |
| API implementationA wire-protocol backend shared by providers (e.g. `anthropic-messages`, `openai-responses`, `openai-completions`, `google-generative-ai`, `bedrock-converse-stream`). | A wire-protocol backend shared by providers (e.g. anthropic-messages, openai-responses, openai-completions, google-generative-ai, bedrock-converse-stream). |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
Agent |
pi-agent-core's stateful class owning the transcript and lifecycle (prompt, continue, abort). |
AgentHarness |
pi-agent-core's higher-level orchestrator wrapping Agent with sessions, compaction, skills, and provider hooks. |
agentLoop |
The low-level prompt-stream-tool-continue loop in pi-agent-core. |
AgentMessage |
pi-agent-core's app-extensible message union (via declaration merging); convertToLlm bridges to pi-ai Message. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| FramingThe pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBOR item. | The pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item. |
hello |
The first message a pi-client sends, carrying PROTOCOL_VERSION and a bearer token. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
| Differential renderingpi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. | pi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. |
| Synchronized outputTerminal escape sequence (`\x1b[?2026h..l`) used by pi-tui for atomic, flicker-free rendering. | Terminal escape sequence (\x1b[?2026h..l) used by pi-tui for atomic, flicker-free rendering. |
| Kitty keyboard protocolTerminal input protocol pi-tui negotiates for richer key reporting. | Terminal input protocol pi-tui negotiates for richer key reporting. |
| Lockstep versioningAll packages share one version and release together. | All packages share one version and release together. |
| Trusted publishingnpm publish via GitHub Actions OIDC (environment `npm-publish`); no local credentials required. | npm publish via GitHub Actions OIDCOpenID Connect (used for npm trusted publishing identity). (environment npm-publish); no local credentials required. |
| Shrinkwrap`packages/coding-agent/npm-shrinkwrap.json`, generated from the root lockfile to pin transitive deps for npm users. | packages/coding-agent/npm-shrinkwrap.json, generated from the root lockfile to pin transitive deps for npm users. |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| LLMLarge Language Model. | Large Language Model. |
| MCPModel Context Protocol (not built into core; extensions may add it). | Model Context Protocol (not built into core; extensions may add it). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). | Open Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). |
| PKCEProof Key for Code Exchange (OAuth flow used by pi-ai). | Proof Key for Code Exchange (OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). flow used by pi-ai). |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| FRFunctional Requirement. | Functional Requirement. |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). | Input Method Editor (pi-tui positions the hardware cursor for IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). candidate windows). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
| AGENTS.mdProject-specific rules file for humans and agents, read automatically from the repo root. | Project-specific rules file for humans and agents, read automatically from the repo root. |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Remote SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). Protocol
Overview
@earendil-works/pi-protocol defines a runtime-neutral wire protocol for remote pi sessions.
It provides schemas, types, CBORConcise Binary Object Representation, the payload format for pi protocol messages. encoding/decoding, and byte-stream framing so that a client and a server can exchange messages over any ordered byte transport.
The protocol is versioned (currently 2) and message-correlated (request/response envelopes plus server event envelopes), with authoritative server/session snapshots and transient progress events.
Stakeholders
| Stakeholder | Interest |
|---|---|
Remote sessionA pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. client (pi-client) |
A validated, framed message API plus incremental decoders that tolerate arbitrary fragmentation or coalescing |
SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). server (pi-server) |
Matching encode/decode on the server side with an authoritative snapshot model |
| Transport authors | A wire format that is transport-agnostic (streams, sockets, custom byte transports) |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FRFunctional Requirement.-01MustThe system shall define runtime-neutral schemas and types for all protocol messages (client and server). | Must | The system shall define runtime-neutral schemas and types for all protocol messages (client and server). |
| FRFunctional Requirement.-02MustThe system shall encode messages as one definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item framed by a four-byte unsigned big-endian payload length. | Must | The system shall encode messages as one definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item framed by a four-byte unsigned big-endian payload length. |
| FRFunctional Requirement.-03MustThe system shall provide `encodeClientMessage()` and `encodeServerMessage()` that validate a message and return a complete framed `Uint8Array`. | Must | The system shall provide encodeClientMessage() and encodeServerMessage() that validate a message and return a complete framed Uint8Array. |
| FRFunctional Requirement.-04MustThe system shall provide incremental decoders that accept arbitrary fragmentation or coalescing of framed bytes. | Must | The system shall provide incremental decoders that accept arbitrary fragmentation or coalescing of framed bytes. |
| FRFunctional Requirement.-05MustThe system shall require the first client message to be `hello`, carrying `PROTOCOL_VERSION` and a bearer token. | Must | The system shall require the first client message to be hello, carrying PROTOCOL_VERSION and a bearer token. |
| FRFunctional Requirement.-06MustThe system shall correlate subsequent messages by request/response envelopes and server event envelopes. | Must | The system shall correlate subsequent messages by request/response envelopes and server event envelopes. |
| FRFunctional Requirement.-07MustThe system shall expose a version constant (`PROTOCOL_VERSION`) used by both client and server. | Must | The system shall expose a version constant (PROTOCOL_VERSION) used by both client and server. |
| FRFunctional Requirement.-08ShouldThe system shall treat server and session snapshots as authoritative, and progress events as transient UI hints. | Should | The system shall treat server and session snapshots as authoritative, and progress events as transient UI hints. |
| FRFunctional Requirement.-09ShouldThe system shall ship a self-contained CBORConcise Binary Object Representation, the payload format for pi protocol messages. encoder/decoder (no external CBORConcise Binary Object Representation, the payload format for pi protocol messages. dependency). | Should | The system shall ship a self-contained CBORConcise Binary Object Representation, the payload format for pi protocol messages. encoder/decoder (no external CBORConcise Binary Object Representation, the payload format for pi protocol messages. dependency). |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFRNon-Functional Requirement.-01MustThe protocol shall be versioned so incompatible wire changes bump `PROTOCOL_VERSION`. | Must | Compatibility | The protocol shall be versioned so incompatible wire changes bump PROTOCOL_VERSION. |
| NFRNon-Functional Requirement.-02MustThe `hello` handshake shall carry a bearer token before any session operations are allowed. | Must | Security | The hello handshake shall carry a bearer token before any session operations are allowed. |
| NFRNon-Functional Requirement.-03ShouldFramingThe pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBOR item. shall use a fixed-length length prefix for cheap incremental parsing. | Should | Performance | FramingThe pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBOR item. shall use a fixed-length length prefix for cheap incremental parsing. |
| NFRNon-Functional Requirement.-04ShouldThe package shall be runtime-neutral with no Node-specific imports. | Should | Portability | The package shall be runtime-neutral with no Node-specific imports. |
Constraints
- Depends on
typeboxfor schema definitions. - Protocol version
2is the current wire layout; version1is not preserved. - Experimental; the message surface may change without notice.
Acceptance Criteria
- FRFunctional Requirement.-02MustThe system shall encode messages as one definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item framed by a four-byte unsigned big-endian payload length.
- Given a protocol message
- When encoded to bytes
- Then the result is a four-byte big-endian length prefix followed by one definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item.
- FRFunctional Requirement.-04MustThe system shall provide incremental decoders that accept arbitrary fragmentation or coalescing of framed bytes.
- Given a framed byte stream delivered in arbitrary chunk boundaries
- When fed to the incremental decoder
- Then the original messages are recovered exactly.
- FRFunctional Requirement.-05MustThe system shall require the first client message to be `hello`, carrying `PROTOCOL_VERSION` and a bearer token.
- Given a client that has not sent
hello - When it sends a session operation
- Then the server rejects the message until a valid
hellowith a matching token is received.
- Given a client that has not sent
- NFRNon-Functional Requirement.-04ShouldThe package shall be runtime-neutral with no Node-specific imports.
- Given the package source
- When scanned for Node imports
- Then none are present.
Conflicts
None identified yet.
Open Questions
- Should the protocol expose a negotiation mechanism for future wire-version migration beyond bumping
PROTOCOL_VERSION?
Specification: Remote SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). Protocol
Overview
pi-protocol is the wire boundary for remote pi sessions.
Messages are schema-validated objects encoded as a single definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item and framed with a four-byte big-endian length prefix.
The package ships a self-contained CBORConcise Binary Object Representation, the payload format for pi protocol messages. encoder/decoder, incremental decoders tolerant of arbitrary fragmentation, and a validated message API (encodeClientMessage/encodeServerMessage).
Architecture
Client messages Server messages
+----------------+ +----------------+
| hello (v2) | | session events |
| request/ |<------->| server events |
| response env. | framed | response env. |
+--------+-------+ CBORConcise Binary Object Representation, the payload format for pi protocol messages. +--------+-------+
| |
v v
length-prefixed framed bytes incremental decoders
The protocol is runtime-neutral: no Node-specific imports.
framing.ts owns the length-prefix layout; cbor/ owns encode/decode; schemas.ts owns the message definitions; codec.ts ties validation + framing into the message API.
Data Models
ClientHello
| Field | Type | Constraints | Description |
|---|---|---|---|
| type | string | "hello" |
Message discriminator |
| version | number | PROTOCOL_VERSION |
Protocol version |
| token | string | not null | Bearer token for authentication |
Framed message
| Field | Type | Constraints | Description |
|---|---|---|---|
| length | uint32 | big-endian | Byte length of the CBORConcise Binary Object Representation, the payload format for pi protocol messages. payload |
| payload | CBORConcise Binary Object Representation, the payload format for pi protocol messages. | definite-length | One schema-validated message |
API Contracts
encodeClientMessage(message)
Request
| Field | Type | Required | Description |
|---|---|---|---|
| message | ClientMessage | yes | Message to validate and frame |
Response (200 OK)
| Field | Type | Description |
|---|---|---|
| Uint8Array | framed bytes | Complete framed message ready to send |
createServerMessageDecoder()
Request
None; incremental state is held in the returned decoder.
Response (200 OK)
| Field | Type | Description |
|---|---|---|
| decoder | object | Accepts arbitrary chunks and emits decoded messages |
Sequences
Hello handshake
Client → encodeClientMessage(hello) → transport
Server → decoder → validate hello (version + token) → accept connection
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Payload format | CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Compact, schema-validated, binary-safe |
| FramingThe pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBOR item. | 4-byte BE length prefix | Cheap incremental parsing, exact boundaries |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. implementation | Self-contained encoder/decoder | No runtime dependency, full control |
| Message validation | typebox schemas | Shared schema + validate pipeline |
| Versioning | PROTOCOL_VERSION constant |
Single source of truth for wire compatibility |
Risks and Unknowns
- Experimental protocol surface may change; consumers must pin the package.
- Snapshot-authoritative semantics depend on server cooperation; progress events must never be reduced into authoritative state.
Out of Scope
- The client implementation itself (
pi-client). - The server implementation (
pi-server). - Transport plumbing (sockets, WebSocket adapters) beyond the framing/decoder layer.
Test Plan: Remote SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). Protocol
Scope
Tests cover CBORConcise Binary Object Representation, the payload format for pi protocol messages. encoding/decoding correctness, length-prefixed framing, incremental decoding across fragmentation boundaries, and the validated message API (encodeClientMessage/encodeServerMessage) plus the hello handshake.
Transport-level behavior is out of scope (covered by pi-client/pi-server).
Unit Tests
| ID | Description | Input | Expected Output |
|---|---|---|---|
| TC-1 | CBORConcise Binary Object Representation, the payload format for pi protocol messages. encode/decode round-trips representative values | Mixed JS values | Lossless round-trip |
| TC-2 | Encoded messages carry a four-byte BE length prefix then one definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item | A hello message |
Length prefix matches CBORConcise Binary Object Representation, the payload format for pi protocol messages. payload length |
| TC-3 | Incremental decoder recovers messages across arbitrary fragmentation | Framed bytes split at many offsets | Original messages recovered exactly |
Integration Tests
| ID | Description | Preconditions | Expected Outcome |
|---|---|---|---|
| TC-4 | encodeClientMessage validates and frames a valid client message |
Valid message object | Complete framed Uint8Array returned |
| TC-5 | Decoder tolerates coalescing of multiple framed messages in one chunk | Multiple encoded messages concatenated | All messages emitted in order |
Edge Cases and Failure Scenarios
| ID | Scenario | Expected Behavior |
|---|---|---|
| TC-6 | Fragment boundary inside the length prefix | Decoder waits for the full prefix before parsing |
| TC-7 | Fragment boundary inside the CBORConcise Binary Object Representation, the payload format for pi protocol messages. payload | Decoder buffers until the full payload arrives |
| TC-8 | Invalid message shape | encodeClientMessage rejects rather than emitting malformed bytes |
Test Infrastructure
- Pure function tests; no network, sockets, or timers.
- Ran with Vitest (
packages/protocol/test/).
Coverage Matrix
| Requirement | Test Cases |
|---|---|
| FRFunctional Requirement.-01MustThe system shall define runtime-neutral schemas and types for all protocol messages (client and server). | TC-1, TC-2 |
| FRFunctional Requirement.-02MustThe system shall encode messages as one definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item framed by a four-byte unsigned big-endian payload length. | TC-2, TC-5 |
| FRFunctional Requirement.-03MustThe system shall provide `encodeClientMessage()` and `encodeServerMessage()` that validate a message and return a complete framed `Uint8Array`. | TC-4 |
| FRFunctional Requirement.-04MustThe system shall provide incremental decoders that accept arbitrary fragmentation or coalescing of framed bytes. | TC-3, TC-5, TC-6, TC-7 |
| FRFunctional Requirement.-05MustThe system shall require the first client message to be `hello`, carrying `PROTOCOL_VERSION` and a bearer token. | TC-8 |
| FRFunctional Requirement.-09ShouldThe system shall ship a self-contained CBORConcise Binary Object Representation, the payload format for pi protocol messages. encoder/decoder (no external CBORConcise Binary Object Representation, the payload format for pi protocol messages. dependency). | TC-1 |
requirements
- Should the protocol expose a negotiation mechanism for future wire-version migration beyond bumping
PROTOCOL_VERSION?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. | The project: a minimal, self-extensible terminal coding agent harness and its libraries. |
| HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. | The coding agent runtime that wires the agent loop, tools, sessions, and UI together. |
| ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. | A TypeScript module with a default export function (pi: ExtensionAPI) that augments the agent with tools, commands, events, UI, or providers. |
| SkillAn on-demand capability package following the Agent Skills standard (`SKILL.md` + optional frontmatter), invoked as `/skill:name`. | An on-demand capability package following the Agent Skills standard (SKILL.md + optional frontmatter), invoked as /skill:name. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| pi packageA distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via `pi install`. | A distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via pi install. |
| SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). | A persistent, branchable conversation log stored as JSONL (SessionHeader, messages, compaction summaries, branch summaries). |
| BranchingTree-structured session forking (`/fork`, `/clone`, `/tree`) where each entry has `id`/`parentId`. | Tree-structured session forking (/fork, /clone, /tree) where each entry has id/parentId. |
| CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. | Lossy summarization of older session messages to reclaim context; original JSONL is preserved. |
| SteeringA queued message delivered to a streaming agent after the current tool batch completes. | A queued message delivered to a streaming agent after the current tool batch completes. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| Project trustA per-folder decision (`~/.pi/agent/trust.json`) gating whether project settings, resources, and extensions execute. | A per-folder decision (~/.pi/agent/trust.json) gating whether project settings, resources, and extensions execute. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Faux providerAn in-memory scripted provider (`providers/faux.ts`) used for deterministic tests with no real API calls. | An in-memory scripted provider (providers/faux.ts) used for deterministic tests with no real API calls. |
| CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. | The deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. |
| Remote sessionA pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. | A pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. |
| Session leaseA remote-session ownership handle (`exclusive` or `shared`) granted by `PiClient.acquireSession()`/`createSession()`; gates who may mutate or observe a session. | A remote-session ownership handle (exclusive or shared) granted by PiClient.acquireSession()/createSession(); gates who may mutate or observe a session. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| Byte transportThe minimal ordered byte-stream interface (`send`/`close`, inbound `onData`/`onClose`/`onError`) that `PiClient` uses; keeps pi-client Node-agnostic. | The minimal ordered byte-stream interface (send/close, inbound onData/onClose/onError) that PiClient uses; keeps pi-client Node-agnostic. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| Legacy supervisorThe renamed orchestrator code in pi-server (`server` CLI, child-process supervision, Radius, RPC stream bridge). | The renamed orchestrator code in pi-server (server CLICommand-Line Interface (the `pi` binary)., child-process supervision, Radius, RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). stream bridge). |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`. | Behavioral, model-backed checks for pi workflows run via vitest-evals with a real AgentSession. |
Technical Terms
| Term | Definition |
|---|---|
| ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). | The runtime unit owning a model catalog, auth, and stream behavior (e.g. anthropic, openai). |
| API implementationA wire-protocol backend shared by providers (e.g. `anthropic-messages`, `openai-responses`, `openai-completions`, `google-generative-ai`, `bedrock-converse-stream`). | A wire-protocol backend shared by providers (e.g. anthropic-messages, openai-responses, openai-completions, google-generative-ai, bedrock-converse-stream). |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
Agent |
pi-agent-core's stateful class owning the transcript and lifecycle (prompt, continue, abort). |
AgentHarness |
pi-agent-core's higher-level orchestrator wrapping Agent with sessions, compaction, skills, and provider hooks. |
agentLoop |
The low-level prompt-stream-tool-continue loop in pi-agent-core. |
AgentMessage |
pi-agent-core's app-extensible message union (via declaration merging); convertToLlm bridges to pi-ai Message. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| FramingThe pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBOR item. | The pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item. |
hello |
The first message a pi-client sends, carrying PROTOCOL_VERSION and a bearer token. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
| Differential renderingpi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. | pi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. |
| Synchronized outputTerminal escape sequence (`\x1b[?2026h..l`) used by pi-tui for atomic, flicker-free rendering. | Terminal escape sequence (\x1b[?2026h..l) used by pi-tui for atomic, flicker-free rendering. |
| Kitty keyboard protocolTerminal input protocol pi-tui negotiates for richer key reporting. | Terminal input protocol pi-tui negotiates for richer key reporting. |
| Lockstep versioningAll packages share one version and release together. | All packages share one version and release together. |
| Trusted publishingnpm publish via GitHub Actions OIDC (environment `npm-publish`); no local credentials required. | npm publish via GitHub Actions OIDCOpenID Connect (used for npm trusted publishing identity). (environment npm-publish); no local credentials required. |
| Shrinkwrap`packages/coding-agent/npm-shrinkwrap.json`, generated from the root lockfile to pin transitive deps for npm users. | packages/coding-agent/npm-shrinkwrap.json, generated from the root lockfile to pin transitive deps for npm users. |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| LLMLarge Language Model. | Large Language Model. |
| MCPModel Context Protocol (not built into core; extensions may add it). | Model Context Protocol (not built into core; extensions may add it). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). | Open Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). |
| PKCEProof Key for Code Exchange (OAuth flow used by pi-ai). | Proof Key for Code Exchange (OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). flow used by pi-ai). |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| FRFunctional Requirement. | Functional Requirement. |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). | Input Method Editor (pi-tui positions the hardware cursor for IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). candidate windows). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
| AGENTS.mdProject-specific rules file for humans and agents, read automatically from the repo root. | Project-specific rules file for humans and agents, read automatically from the repo root. |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: Remote SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). Client
Overview
@earendil-works/pi-client is a transport-neutral client for remote pi sessions.
PiClient exchanges length-prefixed CBORConcise Binary Object Representation, the payload format for pi protocol messages. messages through a small ByteTransport interface, so it works over Unix sockets, WebSockets, or any ordered byte transport without Node-specific imports.
It provides session acquisition with exclusive/shared leases, session creation/attachment, snapshot subscriptions, and request correlation by ID.
Stakeholders
| Stakeholder | Interest |
|---|---|
Coding agent (pi-coding-agent) |
Attach to remote pi sessions (create, resume, subscribe) from the CLICommand-Line Interface (the `pi` binary). |
| Server operators | A client that authenticates with a bearer token and honors exclusive/shared session ownership |
| Transport authors | A minimal ByteTransport contract that is easy to implement |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FRFunctional Requirement.-01MustThe system shall provide a `PiClient` that connects to a server via a `ByteTransportFactory` and authenticates with a bearer token. | Must | The system shall provide a PiClient that connects to a server via a ByteTransportFactory and authenticates with a bearer token. |
| FRFunctional Requirement.-02MustThe system shall support creating a session (`createSession({ cwd })`) and returning a new session lease. | Must | The system shall support creating a session (createSession({ cwd })) and returning a new session lease. |
| FRFunctional Requirement.-03MustThe system shall support attaching to an existing session (`attachSession()`) as a shared acquisition. | Must | The system shall support attaching to an existing session (attachSession()) as a shared acquisition. |
| FRFunctional Requirement.-04MustThe system shall support acquiring sessions with explicit lease modes: `{ mode: "exclusive" }` or `{ mode: "shared" }`. | Must | The system shall support acquiring sessions with explicit lease modes: { mode: "exclusive" } or { mode: "shared" }. |
| FRFunctional Requirement.-05MustThe system shall enforce ownership: exclusive acquisition fails while any lease exists; shared acquisition fails while an exclusive lease exists. | Must | The system shall enforce ownership: exclusive acquisition fails while any lease exists; shared acquisition fails while an exclusive lease exists. |
| FRFunctional Requirement.-06MustThe system shall correlate requests by ID and deliver responses to the right caller. | Must | The system shall correlate requests by ID and deliver responses to the right caller. |
| FRFunctional Requirement.-07MustThe system shall publish session snapshots to subscribers and list sessions (`listSessions()`). | Must | The system shall publish session snapshots to subscribers and list sessions (listSessions()). |
| FRFunctional Requirement.-08ShouldThe system shall support `reconnect()` after disconnection (no automatic reconnect). | Should | The system shall support reconnect() after disconnection (no automatic reconnect). |
| FRFunctional Requirement.-09ShouldThe system shall expose cached session summaries from the latest snapshot. | Should | The system shall expose cached session summaries from the latest snapshot. |
| FRFunctional Requirement.-10ShouldThe system shall drive a session with `session.prompt(...)` and stream events to subscribers. | Should | The system shall drive a session with session.prompt(...) and stream events to subscribers. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFRNon-Functional Requirement.-01MustThe package shall have no Node-specific imports; all byte movement goes through `ByteTransport`. | Must | Portability | The package shall have no Node-specific imports; all byte movement goes through ByteTransport. |
| NFRNon-Functional Requirement.-02MustA transport factory must create a fresh transport for every connection attempt. | Must | Correctness | A transport factory must create a fresh transport for every connection attempt. |
| NFRNon-Functional Requirement.-03MustThe client shall send the bearer token only through the `hello` handshake. | Must | Security | The client shall send the bearer token only through the hello handshake. |
| NFRNon-Functional Requirement.-04ShouldOn transport close/error the client shall surface the failure to callers and support explicit reconnect. | Should | Reliability | On transport close/error the client shall surface the failure to callers and support explicit reconnect. |
Constraints
- No automatic reconnect; callers must call
reconnect(). - One connection can attach several sessions.
- Experimental; API may change without notice.
Acceptance Criteria
- FRFunctional Requirement.-01MustThe system shall provide a `PiClient` that connects to a server via a `ByteTransportFactory` and authenticates with a bearer token.
- Given a
PiClientwith a token and transport factory - When
connect()is called - Then the client authenticates via
helloand becomes ready.
- Given a
- FRFunctional Requirement.-04MustThe system shall support acquiring sessions with explicit lease modes: `{ mode: "exclusive" }` or `{ mode: "shared" }`.
- Given a session with no active leases
- When an exclusive acquisition is requested
- Then an exclusive lease is returned and shared acquisitions now fail.
- FRFunctional Requirement.-05MustThe system shall enforce ownership: exclusive acquisition fails while any lease exists; shared acquisition fails while an exclusive lease exists.
- Given an exclusive lease already held
- When a second exclusive acquisition is requested
- Then
PiSessionOwnershipErroris thrown.
- FRFunctional Requirement.-06MustThe system shall correlate requests by ID and deliver responses to the right caller.
- Given multiple in-flight requests
- When responses arrive out of order
- Then each caller receives the response matching its request ID.
- NFRNon-Functional Requirement.-01MustThe package shall have no Node-specific imports; all byte movement goes through `ByteTransport`.
- Given the package source
- When scanned for Node imports
- Then none are present.
Conflicts
None identified yet.
Open Questions
- Should automatic reconnect with backoff be added, or does explicit
reconnect()suffice?
Specification: Remote SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). Client
Overview
pi-client is the transport-neutral remote-session client.
PiClient connects through a ByteTransportFactory, authenticates via the protocol hello handshake, and exchanges request/response envelopes with the server.
SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). access is mediated by SessionLease objects with exclusive/shared ownership semantics.
Architecture
+----------------------+
| PiClient |
| - connect() |
| - createSession() |
| - attachSession() |
| - acquireSession() |
| - listSessions() |
| - reconnect() |
+---------+------------+
| ByteTransportFactory (one fresh transport per attempt)
v
+----------------------+
| ByteTransport |
| send(chunk), close() |
| onData/onClose/onErr |
+----------------------+
connection.ts owns transport lifecycle, state.ts the connection/session state machine, session-handle.ts the lease model, promise.ts request correlation, and client.ts the public PiClient API.
Data Models
SessionLease
| Field | Type | Constraints | Description |
|---|---|---|---|
| mode | "exclusive" \| "shared" |
not null | Ownership mode |
| session | session handle | not null | Underlying remote session |
| release | function | — | Releases the lease |
Leases cannot be constructed directly; they come from createSession() (exclusive) or acquireSession()/attachSession().
API Contracts
client.createSession({ cwd })
Request
| Field | Type | Required | Description |
|---|---|---|---|
| cwd | string | yes | Working directory for the session |
Response (200 OK)
| Field | Type | Description |
|---|---|---|
| lease | SessionLease | New exclusive lease for the created session |
Error Responses
| Status | Code | Description |
|---|---|---|
| 409 | SESSION_OWNERSHIP | Ownership conflict during acquisition |
client.acquireSession({ mode })
Request
| Field | Type | Required | Description |
|---|---|---|---|
| mode | "exclusive" | "shared" | yes | Desired lease mode |
Response (200 OK)
| Field | Type | Description |
|---|---|---|
| lease | SessionLease | Lease in the requested mode |
Error Responses
| Status | Code | Description |
|---|---|---|
| 409 | SESSION_OWNERSHIP | Exclusive requested while a lease exists, or shared while exclusive held |
Sequences
SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). create flow
PiClient → encodeClientMessage(create_session) → server
server → session created → response snapshot
PiClient → SessionLease (exclusive) → caller
Ownership enforcement
caller acquires exclusive (ok)
caller attempts shared → PiSessionOwnershipError
caller releases exclusive
caller acquires shared (ok)
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Transport abstraction | ByteTransportFactory |
Fresh transport per attempt; keeps client Node-agnostic |
| Ownership | SessionLease exclusive/shared |
Guards mutation vs. observation; single coordinator |
| Request correlation | per-request ID + promises | Correct handling of out-of-order responses |
| Snapshot model | authoritative snapshots, transient progress | No optimistic mutation of authoritative state |
Risks and Unknowns
- No automatic reconnect; long-lived connections must manage
reconnect()explicitly. - Experimental API may change without notice.
Out of Scope
- The wire protocol (
pi-protocol). - The server (
pi-server). - Concrete transports beyond the
ByteTransportcontract.
Test Plan: Remote SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). Client
Scope
Tests cover connection lifecycle, session acquisition and ownership enforcement, request/response correlation, state transitions, and disposal semantics.
Transport-level integration uses a Unix-socket transport (unix.ts); core client logic is transport-neutral.
Unit Tests
| ID | Description | Input | Expected Output |
|---|---|---|---|
| TC-1 | Connection establishes via ByteTransportFactory and authenticates |
Valid factory + token | Client reaches connected/ready state |
| TC-2 | Ownership enforcement: exclusive then shared acquisition | SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). with exclusive lease held | Shared acquisition throws PiSessionOwnershipError |
| TC-3 | Ownership enforcement: exclusive while a lease exists | SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). with any lease held | Exclusive acquisition throws PiSessionOwnershipError |
| TC-4 | Request correlation delivers each response to its caller | Concurrent in-flight requests | Each caller receives its own response |
| TC-5 | State machine transitions on connect/close/error | Sequence of transport events | Correct state transitions |
Integration Tests
| ID | Description | Preconditions | Expected Outcome |
|---|---|---|---|
| TC-6 | Unix-socket transport end-to-end session create | Running server on a Unix socket | createSession({ cwd }) returns an exclusive lease |
| TC-7 | SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). subscribe/event delivery over the socket | Active session | Subscribers receive snapshots/events |
| TC-8 | Disposal releases leases and cleans up | Leases held | Disposal closes connection and releases resources |
Edge Cases and Failure Scenarios
| ID | Scenario | Expected Behavior |
|---|---|---|
| TC-9 | Transport factory returns a stale/closed transport | Fresh transport is created per attempt |
| TC-10 | Disconnection without explicit reconnect | Client surfaces failure; no auto-reconnect |
| TC-11 | Duplicate release of a lease | Release is idempotent, no double-free |
Test Infrastructure
packages/client/test/support.tsprovides shared transport/test harness helpers.test/unix.test.tsexercises the concrete Unix-socket transport.- Ran with Vitest.
Coverage Matrix
| Requirement | Test Cases |
|---|---|
| FRFunctional Requirement.-01MustThe system shall provide a `PiClient` that connects to a server via a `ByteTransportFactory` and authenticates with a bearer token. | TC-1, TC-6 |
| FRFunctional Requirement.-02MustThe system shall support creating a session (`createSession({ cwd })`) and returning a new session lease. | TC-6 |
| FRFunctional Requirement.-04MustThe system shall support acquiring sessions with explicit lease modes: `{ mode: "exclusive" }` or `{ mode: "shared" }`. | TC-2, TC-3 |
| FRFunctional Requirement.-05MustThe system shall enforce ownership: exclusive acquisition fails while any lease exists; shared acquisition fails while an exclusive lease exists. | TC-2, TC-3 |
| FRFunctional Requirement.-06MustThe system shall correlate requests by ID and deliver responses to the right caller. | TC-4 |
| FRFunctional Requirement.-07MustThe system shall publish session snapshots to subscribers and list sessions (`listSessions()`). | TC-7 |
| NFRNon-Functional Requirement.-02MustA transport factory must create a fresh transport for every connection attempt. | TC-9 |
| NFRNon-Functional Requirement.-04ShouldOn transport close/error the client shall surface the failure to callers and support explicit reconnect. | TC-10 |
requirements
- Should automatic reconnect with backoff be added, or does explicit
reconnect()suffice?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. | The project: a minimal, self-extensible terminal coding agent harness and its libraries. |
| HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. | The coding agent runtime that wires the agent loop, tools, sessions, and UI together. |
| ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. | A TypeScript module with a default export function (pi: ExtensionAPI) that augments the agent with tools, commands, events, UI, or providers. |
| SkillAn on-demand capability package following the Agent Skills standard (`SKILL.md` + optional frontmatter), invoked as `/skill:name`. | An on-demand capability package following the Agent Skills standard (SKILL.md + optional frontmatter), invoked as /skill:name. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| pi packageA distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via `pi install`. | A distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via pi install. |
| SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). | A persistent, branchable conversation log stored as JSONL (SessionHeader, messages, compaction summaries, branch summaries). |
| BranchingTree-structured session forking (`/fork`, `/clone`, `/tree`) where each entry has `id`/`parentId`. | Tree-structured session forking (/fork, /clone, /tree) where each entry has id/parentId. |
| CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. | Lossy summarization of older session messages to reclaim context; original JSONL is preserved. |
| SteeringA queued message delivered to a streaming agent after the current tool batch completes. | A queued message delivered to a streaming agent after the current tool batch completes. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| Project trustA per-folder decision (`~/.pi/agent/trust.json`) gating whether project settings, resources, and extensions execute. | A per-folder decision (~/.pi/agent/trust.json) gating whether project settings, resources, and extensions execute. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Faux providerAn in-memory scripted provider (`providers/faux.ts`) used for deterministic tests with no real API calls. | An in-memory scripted provider (providers/faux.ts) used for deterministic tests with no real API calls. |
| CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. | The deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. |
| Remote sessionA pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. | A pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. |
| Session leaseA remote-session ownership handle (`exclusive` or `shared`) granted by `PiClient.acquireSession()`/`createSession()`; gates who may mutate or observe a session. | A remote-session ownership handle (exclusive or shared) granted by PiClient.acquireSession()/createSession(); gates who may mutate or observe a session. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| Byte transportThe minimal ordered byte-stream interface (`send`/`close`, inbound `onData`/`onClose`/`onError`) that `PiClient` uses; keeps pi-client Node-agnostic. | The minimal ordered byte-stream interface (send/close, inbound onData/onClose/onError) that PiClient uses; keeps pi-client Node-agnostic. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| Legacy supervisorThe renamed orchestrator code in pi-server (`server` CLI, child-process supervision, Radius, RPC stream bridge). | The renamed orchestrator code in pi-server (server CLICommand-Line Interface (the `pi` binary)., child-process supervision, Radius, RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). stream bridge). |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`. | Behavioral, model-backed checks for pi workflows run via vitest-evals with a real AgentSession. |
Technical Terms
| Term | Definition |
|---|---|
| ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). | The runtime unit owning a model catalog, auth, and stream behavior (e.g. anthropic, openai). |
| API implementationA wire-protocol backend shared by providers (e.g. `anthropic-messages`, `openai-responses`, `openai-completions`, `google-generative-ai`, `bedrock-converse-stream`). | A wire-protocol backend shared by providers (e.g. anthropic-messages, openai-responses, openai-completions, google-generative-ai, bedrock-converse-stream). |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
Agent |
pi-agent-core's stateful class owning the transcript and lifecycle (prompt, continue, abort). |
AgentHarness |
pi-agent-core's higher-level orchestrator wrapping Agent with sessions, compaction, skills, and provider hooks. |
agentLoop |
The low-level prompt-stream-tool-continue loop in pi-agent-core. |
AgentMessage |
pi-agent-core's app-extensible message union (via declaration merging); convertToLlm bridges to pi-ai Message. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| FramingThe pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBOR item. | The pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item. |
hello |
The first message a pi-client sends, carrying PROTOCOL_VERSION and a bearer token. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
| Differential renderingpi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. | pi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. |
| Synchronized outputTerminal escape sequence (`\x1b[?2026h..l`) used by pi-tui for atomic, flicker-free rendering. | Terminal escape sequence (\x1b[?2026h..l) used by pi-tui for atomic, flicker-free rendering. |
| Kitty keyboard protocolTerminal input protocol pi-tui negotiates for richer key reporting. | Terminal input protocol pi-tui negotiates for richer key reporting. |
| Lockstep versioningAll packages share one version and release together. | All packages share one version and release together. |
| Trusted publishingnpm publish via GitHub Actions OIDC (environment `npm-publish`); no local credentials required. | npm publish via GitHub Actions OIDCOpenID Connect (used for npm trusted publishing identity). (environment npm-publish); no local credentials required. |
| Shrinkwrap`packages/coding-agent/npm-shrinkwrap.json`, generated from the root lockfile to pin transitive deps for npm users. | packages/coding-agent/npm-shrinkwrap.json, generated from the root lockfile to pin transitive deps for npm users. |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| LLMLarge Language Model. | Large Language Model. |
| MCPModel Context Protocol (not built into core; extensions may add it). | Model Context Protocol (not built into core; extensions may add it). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). | Open Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). |
| PKCEProof Key for Code Exchange (OAuth flow used by pi-ai). | Proof Key for Code Exchange (OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). flow used by pi-ai). |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| FRFunctional Requirement. | Functional Requirement. |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). | Input Method Editor (pi-tui positions the hardware cursor for IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). candidate windows). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
| AGENTS.mdProject-specific rules file for humans and agents, read automatically from the repo root. | Project-specific rules file for humans and agents, read automatically from the repo root. |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). Server
Overview
@earendil-works/pi-server provides the experimental PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). session server for remote pi sessions.
It is token-authenticated, listens on listeners (including a createUnixServer preset), manages live sessions via LiveSessionManager, publishes server and session snapshots, and ships a testing harness for building servers/clients in tests.
The legacy child-process supervisor (server CLICommand-Line Interface (the `pi` binary).) coexists in the same package under legacy/.
Stakeholders
| Stakeholder | Interest |
|---|---|
| Remote sessionA pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. users | Host a pi session server that accepts authenticated clients over Unix sockets |
Coding agent (pi-coding-agent) |
A server-side counterpart to PiClient for remote sessions |
| Test authors | A testing harness for driving server/client pairs in tests |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FRFunctional Requirement.-01MustThe system shall provide a `PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`).` class that serves authenticated remote sessions over listeners. | Must | The system shall provide a PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). class that serves authenticated remote sessions over listeners. |
| FRFunctional Requirement.-02MustThe system shall require a bearer token before accepting session operations (validated in the `hello` handshake). | Must | The system shall require a bearer token before accepting session operations (validated in the hello handshake). |
| FRFunctional Requirement.-03MustThe system shall support a Unix socket transport preset (`createUnixServer`) with a configurable socket path. | Must | The system shall support a Unix socket transport preset (createUnixServer) with a configurable socket path. |
| FRFunctional Requirement.-04MustThe system shall manage live sessions through a `LiveSessionManager`. | Must | The system shall manage live sessions through a LiveSessionManager. |
| FRFunctional Requirement.-05MustThe system shall publish authoritative server snapshots and session snapshots to connected clients. | Must | The system shall publish authoritative server snapshots and session snapshots to connected clients. |
| FRFunctional Requirement.-06MustThe system shall route client requests (create/open/list sessions, prompt, subscribe) through the protocol. | Must | The system shall route client requests (create/open/list sessions, prompt, subscribe) through the protocol. |
| FRFunctional Requirement.-07ShouldThe system shall expose a `PiSessionBackend` interface decoupling the server from concrete session storage. | Should | The system shall expose a PiSessionBackend interface decoupling the server from concrete session storage. |
| FRFunctional Requirement.-08ShouldThe system shall ship a `testing` entrypoint with in-memory server/client helpers for tests. | Should | The system shall ship a testing entrypoint with in-memory server/client helpers for tests. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFRNon-Functional Requirement.-01MustAccess shall require the configured bearer token; no anonymous sessions. | Must | Security | Access shall require the configured bearer token; no anonymous sessions. |
| NFRNon-Functional Requirement.-02MustThe server shall remain functional across client connect/disconnect cycles. | Must | Reliability | The server shall remain functional across client connect/disconnect cycles. |
| NFRNon-Functional Requirement.-03ShouldThe `legacy` supervisor CLICommand-Line Interface (the `pi` binary). and exports shall remain available while the new server is additive. | Should | Compatibility | The legacy supervisor CLICommand-Line Interface (the `pi` binary). and exports shall remain available while the new server is additive. |
| NFRNon-Functional Requirement.-04ShouldServer behavior shall be covered by a conformance suite plus per-component tests. | Should | Maintainability | Server behavior shall be covered by a conformance suite plus per-component tests. |
Constraints
- Experimental; APIs may change or be removed without notice.
- Unix socket transport only for the built-in preset.
- Same package hosts both the new
PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`).and the legacy supervisor (serverCLICommand-Line Interface (the `pi` binary).).
Acceptance Criteria
- FRFunctional Requirement.-01MustThe system shall provide a `PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`).` class that serves authenticated remote sessions over listeners.
- Given a
PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`).with a backend and a Unix listener - When started
- Then it accepts connections on the configured socket path.
- Given a
- FRFunctional Requirement.-02MustThe system shall require a bearer token before accepting session operations (validated in the `hello` handshake).
- Given a connection without a valid token
- When it sends a session operation
- Then the server rejects it.
- FRFunctional Requirement.-05MustThe system shall publish authoritative server snapshots and session snapshots to connected clients.
- Given connected clients and a live session
- When session state changes
- Then authoritative snapshots are published to subscribers.
- NFRNon-Functional Requirement.-01MustAccess shall require the configured bearer token; no anonymous sessions.
- Given no configured token client
- When connecting
- Then the handshake fails.
Conflicts
None identified yet.
Open Questions
- Will the legacy supervisor eventually be removed in favor of
PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`).alone?
Specification: SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). Server
Overview
pi-server hosts the experimental PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). session server.
A PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). is constructed with a PiSessionBackend (session storage) and one or more listeners; createUnixServer wires a Unix socket transport.
Connections authenticate via the protocol hello token, then exchange request/response and event envelopes.
LiveSessionManager tracks active sessions and publishes snapshots.
Architecture
+----------------------------+
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). |
| - backend (PiSessionBackend)|
| - listeners |
+------------+---------------+
| accepts connections (e.g. Unix socket via createUnixServer)
v
+------------+---------------+
| connection.ts |
| hello/auth, protocol.ts |
+------------+---------------+
|
v
+------------+---------------+
| LiveSessionManager |
| sessions.ts, snapshots.ts |
+----------------------------+
server.ts is the entry point; listener.ts defines listeners; transports/unix/ the Unix transport; sessions.ts/snapshots.ts live-session and snapshot management; testing/ in-memory server/client helpers.
Data Models
PiSessionBackend
| Method | Signature | Description |
|---|---|---|
| listSessions | () => Promise<SessionSummary[]> |
List known sessions |
| listModels | () => Promise<ModelInfo[]> |
List available models |
| createSession | (options) => Promise<SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries).> |
Create and open a session |
| openSession | (sessionId) => Promise<SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries).> |
Open an existing session |
API Contracts
createUnixServer(backend, options)
Request
| Field | Type | Required | Description |
|---|---|---|---|
| backend | PiSessionBackend | yes | SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). storage adapter |
| options.token | string | yes | Bearer token |
| options.path | string | yes | Unix socket path |
Response (200 OK)
| Field | Type | Description |
|---|---|---|
| server | PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | Started server instance |
Sequences
Client connect
Client → createUnixServer → server.start()
Client connects (Unix socket)
Client → hello {version, token} → server validates → session established
Client → createSession/openSession → LiveSessionManager → snapshots → client
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Auth | bearer token in hello |
Reuses protocol handshake, no extra round trips |
| SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). management | LiveSessionManager |
Single owner of live session lifecycle |
| Snapshot publication | server + session snapshots | Clients render from authoritative state |
| Backend abstraction | PiSessionBackend |
Decouples transport from session storage |
| Package layout | new PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). + legacy/ supervisor |
Additive migration; legacy CLICommand-Line Interface (the `pi` binary). kept |
Risks and Unknowns
- Experimental; the API may change or be removed without notice.
- Only a Unix socket preset ships; other transports are future work.
- Relationship to the legacy supervisor is transitional.
Out of Scope
- The wire protocol (
pi-protocol). - The client (
pi-client). - SQLite session persistence (separate feature in
pi-storage-sqlite-node).
Test Plan: SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). Server
Scope
Tests cover server startup and listeners, protocol handling, live-session management, snapshot publication, Unix-socket connectivity, and a conformance suite exercising the server as a client would.
Legacy supervisorThe renamed orchestrator code in pi-server (`server` CLI, child-process supervision, Radius, RPC stream bridge). behavior is not covered here (separate feature, p1-orchestrator).
Unit Tests
| ID | Description | Input | Expected Output |
|---|---|---|---|
| TC-1 | LiveSessionManager creates and tracks sessions | Create/open requests | Sessions tracked; snapshots updated |
| TC-2 | Snapshot publication reflects authoritative state | State change on a live session | Subscribers receive new snapshot |
| TC-3 | Protocol routing handles client requests | Request envelopes | Correct responses routed |
Integration Tests
| ID | Description | Preconditions | Expected Outcome |
|---|---|---|---|
| TC-4 | Server starts and accepts Unix-socket connections | createUnixServer on a temp path |
Connections accepted |
| TC-5 | End-to-end session create over Unix socket | Running server + client | SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). created and snapshots flow |
| TC-6 | Conformance suite drives server as a client | testing harness |
Behavior matches the protocol contract |
Edge Cases and Failure Scenarios
| ID | Scenario | Expected Behavior |
|---|---|---|
| TC-7 | Connection with missing/invalid token | Rejected before session operations |
| TC-8 | Client disconnect mid-session | Live session cleaned up; server remains up |
| TC-9 | Concurrent client connections | Sessions isolated per client lease |
Test Infrastructure
test/fixturesprovides shared setup.testing/entrypoint provides in-memory server/client harness.- Ran with Vitest (
packages/server/test/).
Coverage Matrix
| Requirement | Test Cases |
|---|---|
| FRFunctional Requirement.-01MustThe system shall provide a `PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`).` class that serves authenticated remote sessions over listeners. | TC-4, TC-5 |
| FRFunctional Requirement.-02MustThe system shall require a bearer token before accepting session operations (validated in the `hello` handshake). | TC-7 |
| FRFunctional Requirement.-03MustThe system shall support a Unix socket transport preset (`createUnixServer`) with a configurable socket path. | TC-4 |
| FRFunctional Requirement.-04MustThe system shall manage live sessions through a `LiveSessionManager`. | TC-1 |
| FRFunctional Requirement.-05MustThe system shall publish authoritative server snapshots and session snapshots to connected clients. | TC-2 |
| FRFunctional Requirement.-06MustThe system shall route client requests (create/open/list sessions, prompt, subscribe) through the protocol. | TC-3, TC-5 |
| FRFunctional Requirement.-08ShouldThe system shall ship a `testing` entrypoint with in-memory server/client helpers for tests. | TC-6 |
| NFRNon-Functional Requirement.-02MustThe server shall remain functional across client connect/disconnect cycles. | TC-8, TC-9 |
requirements
- Will the legacy supervisor eventually be removed in favor of
PiServeralone?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. | The project: a minimal, self-extensible terminal coding agent harness and its libraries. |
| HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. | The coding agent runtime that wires the agent loop, tools, sessions, and UI together. |
| ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. | A TypeScript module with a default export function (pi: ExtensionAPI) that augments the agent with tools, commands, events, UI, or providers. |
| SkillAn on-demand capability package following the Agent Skills standard (`SKILL.md` + optional frontmatter), invoked as `/skill:name`. | An on-demand capability package following the Agent Skills standard (SKILL.md + optional frontmatter), invoked as /skill:name. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| pi packageA distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via `pi install`. | A distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via pi install. |
| SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). | A persistent, branchable conversation log stored as JSONL (SessionHeader, messages, compaction summaries, branch summaries). |
| BranchingTree-structured session forking (`/fork`, `/clone`, `/tree`) where each entry has `id`/`parentId`. | Tree-structured session forking (/fork, /clone, /tree) where each entry has id/parentId. |
| CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. | Lossy summarization of older session messages to reclaim context; original JSONL is preserved. |
| SteeringA queued message delivered to a streaming agent after the current tool batch completes. | A queued message delivered to a streaming agent after the current tool batch completes. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| Project trustA per-folder decision (`~/.pi/agent/trust.json`) gating whether project settings, resources, and extensions execute. | A per-folder decision (~/.pi/agent/trust.json) gating whether project settings, resources, and extensions execute. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Faux providerAn in-memory scripted provider (`providers/faux.ts`) used for deterministic tests with no real API calls. | An in-memory scripted provider (providers/faux.ts) used for deterministic tests with no real API calls. |
| CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. | The deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. |
| Remote sessionA pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. | A pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. |
| Session leaseA remote-session ownership handle (`exclusive` or `shared`) granted by `PiClient.acquireSession()`/`createSession()`; gates who may mutate or observe a session. | A remote-session ownership handle (exclusive or shared) granted by PiClient.acquireSession()/createSession(); gates who may mutate or observe a session. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| Byte transportThe minimal ordered byte-stream interface (`send`/`close`, inbound `onData`/`onClose`/`onError`) that `PiClient` uses; keeps pi-client Node-agnostic. | The minimal ordered byte-stream interface (send/close, inbound onData/onClose/onError) that PiClient uses; keeps pi-client Node-agnostic. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| Legacy supervisorThe renamed orchestrator code in pi-server (`server` CLI, child-process supervision, Radius, RPC stream bridge). | The renamed orchestrator code in pi-server (server CLICommand-Line Interface (the `pi` binary)., child-process supervision, Radius, RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). stream bridge). |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`. | Behavioral, model-backed checks for pi workflows run via vitest-evals with a real AgentSession. |
Technical Terms
| Term | Definition |
|---|---|
| ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). | The runtime unit owning a model catalog, auth, and stream behavior (e.g. anthropic, openai). |
| API implementationA wire-protocol backend shared by providers (e.g. `anthropic-messages`, `openai-responses`, `openai-completions`, `google-generative-ai`, `bedrock-converse-stream`). | A wire-protocol backend shared by providers (e.g. anthropic-messages, openai-responses, openai-completions, google-generative-ai, bedrock-converse-stream). |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
Agent |
pi-agent-core's stateful class owning the transcript and lifecycle (prompt, continue, abort). |
AgentHarness |
pi-agent-core's higher-level orchestrator wrapping Agent with sessions, compaction, skills, and provider hooks. |
agentLoop |
The low-level prompt-stream-tool-continue loop in pi-agent-core. |
AgentMessage |
pi-agent-core's app-extensible message union (via declaration merging); convertToLlm bridges to pi-ai Message. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| FramingThe pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBOR item. | The pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item. |
hello |
The first message a pi-client sends, carrying PROTOCOL_VERSION and a bearer token. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
| Differential renderingpi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. | pi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. |
| Synchronized outputTerminal escape sequence (`\x1b[?2026h..l`) used by pi-tui for atomic, flicker-free rendering. | Terminal escape sequence (\x1b[?2026h..l) used by pi-tui for atomic, flicker-free rendering. |
| Kitty keyboard protocolTerminal input protocol pi-tui negotiates for richer key reporting. | Terminal input protocol pi-tui negotiates for richer key reporting. |
| Lockstep versioningAll packages share one version and release together. | All packages share one version and release together. |
| Trusted publishingnpm publish via GitHub Actions OIDC (environment `npm-publish`); no local credentials required. | npm publish via GitHub Actions OIDCOpenID Connect (used for npm trusted publishing identity). (environment npm-publish); no local credentials required. |
| Shrinkwrap`packages/coding-agent/npm-shrinkwrap.json`, generated from the root lockfile to pin transitive deps for npm users. | packages/coding-agent/npm-shrinkwrap.json, generated from the root lockfile to pin transitive deps for npm users. |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| LLMLarge Language Model. | Large Language Model. |
| MCPModel Context Protocol (not built into core; extensions may add it). | Model Context Protocol (not built into core; extensions may add it). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). | Open Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). |
| PKCEProof Key for Code Exchange (OAuth flow used by pi-ai). | Proof Key for Code Exchange (OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). flow used by pi-ai). |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| FRFunctional Requirement. | Functional Requirement. |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). | Input Method Editor (pi-tui positions the hardware cursor for IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). candidate windows). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
| AGENTS.mdProject-specific rules file for humans and agents, read automatically from the repo root. | Project-specific rules file for humans and agents, read automatically from the repo root. |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: SQLite SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). Storage
Overview
@earendil-works/pi-storage-sqlite-node provides a node:sqlite storage backend for pi-agent-core sessions.
It ships the SqliteDatabase adapter, a SqliteSessionRepository, schema migrations (including branch-tips tracking), materialized session projections, and an optional full-text search backend.
Sessions can be created, opened, and queried with text search against a single canonical SQLite database.
Stakeholders
| Stakeholder | Interest |
|---|---|
| Agent users | Fast, queryable session persistence with search |
pi-agent-core integrators |
A drop-in SQLite backend behind the existing session repository interface |
| Search consumers | Optional FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). search over session content |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FRFunctional Requirement.-01MustThe system shall provide a `SqliteDatabase` adapter over `node:sqlite`. | Must | The system shall provide a SqliteDatabase adapter over node:sqlite. |
| FRFunctional Requirement.-02MustThe system shall provide a `SqliteSessionRepository` implementing the agent session repository interface. | Must | The system shall provide a SqliteSessionRepository implementing the agent session repository interface. |
| FRFunctional Requirement.-03MustThe system shall create and open sessions with a cwd, and persist session data durably. | Must | The system shall create and open sessions with a cwd, and persist session data durably. |
| FRFunctional Requirement.-04MustThe system shall run schema migrations on open (including `001_initial` and `002_branch_tips`). | Must | The system shall run schema migrations on open (including 001_initial and 002_branch_tips). |
| FRFunctional Requirement.-05ShouldThe system shall maintain materialized views for session summaries, entries, and sequences. | Should | The system shall maintain materialized views for session summaries, entries, and sequences. |
| FRFunctional Requirement.-06ShouldThe system shall provide an optional FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). search backend (`createSqliteSessionSearch`) for text queries. | Should | The system shall provide an optional FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). search backend (createSqliteSessionSearch) for text queries. |
| FRFunctional Requirement.-07ShouldThe system shall share one lazy database connection across repository and search. | Should | The system shall share one lazy database connection across repository and search. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFRNon-Functional Requirement.-01MustMigrations shall be idempotent and version-tracked. | Must | Correctness | Migrations shall be idempotent and version-tracked. |
| NFRNon-Functional Requirement.-02MustThe repository shall lazily own one shared connection; errors surface cleanly. | Must | Reliability | The repository shall lazily own one shared connection; errors surface cleanly. |
| NFRNon-Functional Requirement.-03ShouldSearch shall be a query-only projection over the canonical database. | Should | Performance | Search shall be a query-only projection over the canonical database. |
| NFRNon-Functional Requirement.-04ShouldNode `>=22.19.0` with built-in `node:sqlite`; no native build step. | Should | Portability | Node >=22.19.0 with built-in node:sqlite; no native build step. |
Constraints
- Requires Node's built-in
node:sqlite(Node >= 22.19.0). - Tested through
packages/agent/test/harness/; the package itself carries no test files.
Acceptance Criteria
- FRFunctional Requirement.-02MustThe system shall provide a `SqliteSessionRepository` implementing the agent session repository interface.
- Given an open
SqliteSessionRepository - When
repository.create({ cwd })is called - Then a new session is created and persisted.
- Given an open
- FRFunctional Requirement.-04MustThe system shall run schema migrations on open (including `001_initial` and `002_branch_tips`).
- Given a fresh database file
- When the repository is opened
- Then migrations
001_initialand002_branch_tipsare applied.
- FRFunctional Requirement.-06ShouldThe system shall provide an optional FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). search backend (`createSqliteSessionSearch`) for text queries.
- Given a session with content
- When
search.search({ text: "needle" })runs - Then matching session entries are returned.
- NFRNon-Functional Requirement.-01MustMigrations shall be idempotent and version-tracked.
- Given an already-migrated database
- When the repository is opened again
- Then no migration is re-applied and no error is thrown.
Conflicts
None identified yet.
Open Questions
- Should the SQLite backend become the default session store, or remain an opt-in alternative to JSONL?
Specification: SQLite SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). Storage
Overview
pi-storage-sqlite-node implements a node:sqlite backend for agent sessions.
A SqliteSessionRepository implements the agent session-repository contract, backed by a single lazy shared connection.
Schema migrations set up the canonical tables plus materialized projections (session summaries, entries, sequences, branch tips), and an independent search backend provides FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). queries over the same database.
Architecture
+-------------------------------+
| SqliteSessionRepository |
| (implements session repo) |
+--------------+----------------+
| node:sqlite (lazy single connection)
v
+--------------+----------------+
| SQLite database |
| migrations 001_initial, |
| 002_branch_tips |
| storage/: sessions, entries, |
| sequences, materialized, |
| branch-cache |
+-------------------------------+
^
+--------------+----------------+
| createSqliteSessionSearch |
| (FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). search, query-only) |
+-------------------------------+
sqlite/repo.ts is the repository; sqlite/migrations.ts + sqlite/migrations/*.sql the schema; sqlite/storage/ the table/storage modules; sqlite/search-backend.ts the FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). search; sqlite/types.ts the shared types.
Data Models
SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). table (excerpt, from 001_initial.sql)
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | text/uuid | PK | SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). identifier |
| cwd | text | not null | Working directory |
| created_at | int | not null | Creation timestamp |
| ... | Full session schema from migration |
Branch tips (from 002_branch_tips.sql)
Tracks the tip of each session branch to support fast branch navigation and summaries.
API Contracts
new SqliteSessionRepository(options)
Request
| Field | Type | Required | Description |
|---|---|---|---|
| options | object | yes | Path/database + session schema options |
Response (200 OK)
| Field | Type | Description |
|---|---|---|
| repository | SqliteSessionRepository | Async-disposable repository |
createSqliteSessionSearch(options)
Request
| Field | Type | Required | Description |
|---|---|---|---|
| options | object | yes | Database + search options |
Response (200 OK)
| Field | Type | Description |
|---|---|---|
| search | object | .search({ text }) returning matching entries |
Sequences
Open + migrate
new SqliteSessionRepository → open database → run pending migrations
→ materialize views → ready
Search
createSqliteSessionSearch → query-only projection over same database
→ search({ text }) → hits
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Driver | node:sqlite |
No native dependency, Node >= 22.19.0 |
| Connection | single lazy shared connection | Simplicity; repository owns lifecycle |
| Migrations | versioned .sql files |
Deterministic schema evolution |
| Projections | materialized views + branch tips | Fast reads without touching raw logs |
| Search | separate query-only backend | No write coupling between repo and search |
Risks and Unknowns
node:sqliteis a recent Node API; version floor must be honored.- FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). behavior and query semantics are experimental.
Out of Scope
- The remote-session protocol/client/server (separate features).
- The JSONL session manager in the coding agent.
Test Plan: SQLite SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). Storage
Scope
Tests cover migrations, the SQLite session repository, branch-cache behavior, search queries, and backend equivalence against the agent session contract.
Tests live in packages/agent/test/harness/ because the package itself carries no test files.
Unit Tests
| ID | Description | Input | Expected Output |
|---|---|---|---|
| TC-1 | Migrations apply idempotently and in order | Fresh database, then reopen | 001_initial + 002_branch_tips applied once |
| TC-2 | Repository create/open persists sessions | create({ cwd }) |
SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). persisted and reopenable |
| TC-3 | Branch-cache materialization tracks branch tips | BranchingTree-structured session forking (`/fork`, `/clone`, `/tree`) where each entry has `id`/`parentId`. operations | Tips maintained correctly |
Integration Tests
| ID | Description | Preconditions | Expected Outcome |
|---|---|---|---|
| TC-4 | SQLite backend matches the session-backend contract | HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. driving the repo | Same observable behavior as the contract |
| TC-5 | Branch queries over SQLite return correct branch data | Sessions with branches | Correct branch results |
Edge Cases and Failure Scenarios
| ID | Scenario | Expected Behavior |
|---|---|---|
| TC-6 | Reopen an already-migrated database | No re-migration, no error |
| TC-7 | Search over sessions with no FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). content | Empty result set, no crash |
Test Infrastructure
- Ran with Vitest from
packages/agent/test/harness/: sqlite-migrations.test.ts,sqlite-branch-cache.test.ts,sqlite-node.test.ts,branch-query.test.ts,session-backends.test.ts.- Requires Node
>=22.19.0fornode:sqlite.
Coverage Matrix
| Requirement | Test Cases |
|---|---|
| FRFunctional Requirement.-01MustThe system shall provide a `SqliteDatabase` adapter over `node:sqlite`. | TC-4 |
| FRFunctional Requirement.-02MustThe system shall provide a `SqliteSessionRepository` implementing the agent session repository interface. | TC-2, TC-4 |
| FRFunctional Requirement.-03MustThe system shall create and open sessions with a cwd, and persist session data durably. | TC-2 |
| FRFunctional Requirement.-04MustThe system shall run schema migrations on open (including `001_initial` and `002_branch_tips`). | TC-1, TC-6 |
| FRFunctional Requirement.-05ShouldThe system shall maintain materialized views for session summaries, entries, and sequences. | TC-3 |
| FRFunctional Requirement.-06ShouldThe system shall provide an optional FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). search backend (`createSqliteSessionSearch`) for text queries. | TC-7 |
| NFRNon-Functional Requirement.-01MustMigrations shall be idempotent and version-tracked. | TC-1, TC-6 |
requirements
- Should the SQLite backend become the default session store, or remain an opt-in alternative to JSONL?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. | The project: a minimal, self-extensible terminal coding agent harness and its libraries. |
| HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. | The coding agent runtime that wires the agent loop, tools, sessions, and UI together. |
| ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. | A TypeScript module with a default export function (pi: ExtensionAPI) that augments the agent with tools, commands, events, UI, or providers. |
| SkillAn on-demand capability package following the Agent Skills standard (`SKILL.md` + optional frontmatter), invoked as `/skill:name`. | An on-demand capability package following the Agent Skills standard (SKILL.md + optional frontmatter), invoked as /skill:name. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| pi packageA distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via `pi install`. | A distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via pi install. |
| SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). | A persistent, branchable conversation log stored as JSONL (SessionHeader, messages, compaction summaries, branch summaries). |
| BranchingTree-structured session forking (`/fork`, `/clone`, `/tree`) where each entry has `id`/`parentId`. | Tree-structured session forking (/fork, /clone, /tree) where each entry has id/parentId. |
| CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. | Lossy summarization of older session messages to reclaim context; original JSONL is preserved. |
| SteeringA queued message delivered to a streaming agent after the current tool batch completes. | A queued message delivered to a streaming agent after the current tool batch completes. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| Project trustA per-folder decision (`~/.pi/agent/trust.json`) gating whether project settings, resources, and extensions execute. | A per-folder decision (~/.pi/agent/trust.json) gating whether project settings, resources, and extensions execute. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Faux providerAn in-memory scripted provider (`providers/faux.ts`) used for deterministic tests with no real API calls. | An in-memory scripted provider (providers/faux.ts) used for deterministic tests with no real API calls. |
| CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. | The deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. |
| Remote sessionA pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. | A pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. |
| Session leaseA remote-session ownership handle (`exclusive` or `shared`) granted by `PiClient.acquireSession()`/`createSession()`; gates who may mutate or observe a session. | A remote-session ownership handle (exclusive or shared) granted by PiClient.acquireSession()/createSession(); gates who may mutate or observe a session. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| Byte transportThe minimal ordered byte-stream interface (`send`/`close`, inbound `onData`/`onClose`/`onError`) that `PiClient` uses; keeps pi-client Node-agnostic. | The minimal ordered byte-stream interface (send/close, inbound onData/onClose/onError) that PiClient uses; keeps pi-client Node-agnostic. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| Legacy supervisorThe renamed orchestrator code in pi-server (`server` CLI, child-process supervision, Radius, RPC stream bridge). | The renamed orchestrator code in pi-server (server CLICommand-Line Interface (the `pi` binary)., child-process supervision, Radius, RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). stream bridge). |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`. | Behavioral, model-backed checks for pi workflows run via vitest-evals with a real AgentSession. |
Technical Terms
| Term | Definition |
|---|---|
| ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). | The runtime unit owning a model catalog, auth, and stream behavior (e.g. anthropic, openai). |
| API implementationA wire-protocol backend shared by providers (e.g. `anthropic-messages`, `openai-responses`, `openai-completions`, `google-generative-ai`, `bedrock-converse-stream`). | A wire-protocol backend shared by providers (e.g. anthropic-messages, openai-responses, openai-completions, google-generative-ai, bedrock-converse-stream). |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
Agent |
pi-agent-core's stateful class owning the transcript and lifecycle (prompt, continue, abort). |
AgentHarness |
pi-agent-core's higher-level orchestrator wrapping Agent with sessions, compaction, skills, and provider hooks. |
agentLoop |
The low-level prompt-stream-tool-continue loop in pi-agent-core. |
AgentMessage |
pi-agent-core's app-extensible message union (via declaration merging); convertToLlm bridges to pi-ai Message. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| FramingThe pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBOR item. | The pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item. |
hello |
The first message a pi-client sends, carrying PROTOCOL_VERSION and a bearer token. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
| Differential renderingpi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. | pi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. |
| Synchronized outputTerminal escape sequence (`\x1b[?2026h..l`) used by pi-tui for atomic, flicker-free rendering. | Terminal escape sequence (\x1b[?2026h..l) used by pi-tui for atomic, flicker-free rendering. |
| Kitty keyboard protocolTerminal input protocol pi-tui negotiates for richer key reporting. | Terminal input protocol pi-tui negotiates for richer key reporting. |
| Lockstep versioningAll packages share one version and release together. | All packages share one version and release together. |
| Trusted publishingnpm publish via GitHub Actions OIDC (environment `npm-publish`); no local credentials required. | npm publish via GitHub Actions OIDCOpenID Connect (used for npm trusted publishing identity). (environment npm-publish); no local credentials required. |
| Shrinkwrap`packages/coding-agent/npm-shrinkwrap.json`, generated from the root lockfile to pin transitive deps for npm users. | packages/coding-agent/npm-shrinkwrap.json, generated from the root lockfile to pin transitive deps for npm users. |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| LLMLarge Language Model. | Large Language Model. |
| MCPModel Context Protocol (not built into core; extensions may add it). | Model Context Protocol (not built into core; extensions may add it). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). | Open Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). |
| PKCEProof Key for Code Exchange (OAuth flow used by pi-ai). | Proof Key for Code Exchange (OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). flow used by pi-ai). |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| FRFunctional Requirement. | Functional Requirement. |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). | Input Method Editor (pi-tui positions the hardware cursor for IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). candidate windows). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
| AGENTS.mdProject-specific rules file for humans and agents, read automatically from the repo root. | Project-specific rules file for humans and agents, read automatically from the repo root. |
Pipeline Progress ?⚪ not started not yet begun✏️ draft initial version🔍 in review under review🚧 in progress actively worked on⛔ blocked waiting on dependency✅ done completed⏭️ skipped not applicable
Requirements: EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`.
Overview
@earendil-works/pi-evals provides behavioral, model-backed evals for PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. workflows.
EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`. adapt a real AgentSession to vitest-evals, run it in isolated temporary project and agent directories, and attach native PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. session artifacts.
They measure end-to-end behavior to compare prompts, tools, skills, models, or other harness configurations.
Stakeholders
| Stakeholder | Interest |
|---|---|
| Maintainers | Behavioral regression signals for PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. workflows across model/harness configurations |
| Contributors | A harness for validating extensions, prompts, skills, and tools |
| Model/tool researchers | Apples-to-apples comparisons of prompt/model/tool configs |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FRFunctional Requirement.-01MustThe system shall adapt a real `AgentSession` to the `vitest-evals` harness interface. | Must | The system shall adapt a real AgentSession to the vitest-evals harness interface. |
| FRFunctional Requirement.-02MustThe system shall run each eval in an isolated temporary project directory and agent directory. | Must | The system shall run each eval in an isolated temporary project directory and agent directory. |
| FRFunctional Requirement.-03MustThe system shall attach native PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. session artifacts to eval results. | Must | The system shall attach native PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. session artifacts to eval results. |
| FRFunctional Requirement.-04MustThe system shall be runnable from the repo root via `npm run eval` with a provider/model default. | Must | The system shall be runnable from the repo root via npm run eval with a provider/model default. |
| FRFunctional Requirement.-05ShouldThe system shall support filtering evals by file or by test name (Vitest forwarding). | Should | The system shall support filtering evals by file or by test name (Vitest forwarding). |
| FRFunctional Requirement.-06ShouldThe system shall ship a set of core evals (e.g. smoke, extensions). | Should | The system shall ship a set of core evals (e.g. smoke, extensions). |
| FRFunctional Requirement.-07ShouldThe system shall allow harnesses to configure their own model, overriding the default. | Should | The system shall allow harnesses to configure their own model, overriding the default. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFRNon-Functional Requirement.-01MustEvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`. shall be isolated (temp dirs) so runs do not touch the real workspace. | Must | Reliability | EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`. shall be isolated (temp dirs) so runs do not touch the real workspace. |
| NFRNon-Functional Requirement.-02MustEvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`. shall not require real provider keys beyond the user's normal PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. auth. | Must | Security | EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`. shall not require real provider keys beyond the user's normal PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. auth. |
| NFRNon-Functional Requirement.-03ShouldResults shall include session artifacts so failures are diagnosable. | Should | Maintainability | Results shall include session artifacts so failures are diagnosable. |
Constraints
- Requires a configured provider/model (CLICommand-Line Interface (the `pi` binary). values or
PI_PROVIDER/PI_MODEL). - Requires the normal PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries.
ModelRuntimeauth (subscription credentials or API-key env vars). - EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`. are model-backed; they cost tokens and are not part of the default
test.shsuite.
Acceptance Criteria
- FRFunctional Requirement.-01MustThe system shall adapt a real `AgentSession` to the `vitest-evals` harness interface.
- Given a working PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. installation with a provider configured
- When
npm run evalruns - Then an
AgentSessionruns inside thevitest-evalsharness in an isolated temp dir.
- FRFunctional Requirement.-02MustThe system shall run each eval in an isolated temporary project directory and agent directory.
- Given an eval run
- When it executes
- Then the project and agent directories are temporary and isolated.
- FRFunctional Requirement.-04MustThe system shall be runnable from the repo root via `npm run eval` with a provider/model default.
- Given a repo with a provider/model configured
- When
npm run eval -- --provider openai --model gpt-5.6-solruns - Then evals execute with that default.
- FRFunctional Requirement.-05ShouldThe system shall support filtering evals by file or by test name (Vitest forwarding).
- Given many evals
- When
npm run eval -- src/extensions.eval.tsor-t "<name>"is used - Then only the matching evals run.
Conflicts
None identified yet.
Open Questions
- Should evals be integrated into CI with a pinned model, or stay local/on-demand only?
Specification: EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`.
Overview
pi-evals turns real PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. agent runs into behavioral evals.
A pi-harness.ts adapter exposes a real AgentSession to the vitest-evals harness, running it inside isolated temporary project and agent directories.
CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. evals (smoke, extensions) plus harness-table, artifacts, and summary utilities support measuring and comparing workflow behavior.
Architecture
npm run eval (scripts/run-evals.mjs)
|
v
+---------------------+
| vitest-evals |
| (harness runner) |
+----------+----------+
| adapts a real AgentSession
v
+---------------------+
| src/pi-harness.ts |
| (isolated temp |
| project + agent) |
+----------+----------+
|
v
+---------------------+
| AgentSession |
| (real pi agent) |
+---------------------+
src/extensions.eval.ts and src/smoke.eval.ts are core evals; src/vitest-evals/ provides the harness primitives (harness-table, artifacts, summary).
Data Models
Eval artifact
| Field | Type | Constraints | Description |
|---|---|---|---|
| session | native pi session | attached | The AgentSession transcript/artifacts from the run |
| result | vitest-evals result | — | HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. verdict and observations |
API Contracts
npm run eval [-- --provider <p> --model <m>]
Request
| Field | Type | Required | Description |
|---|---|---|---|
| provider | string | yes* | Default provider (* unless every harness sets its own model) |
| model | string | yes* | Default model |
Response (200 OK)
| Field | Type | Description |
|---|---|---|
| exit code | number | Vitest exit code for the eval run |
Sequences
Run an eval
npm run eval → run-evals.mjs → vitest-evals → pi-harness
→ create temp project + agent dirs → adapt AgentSession
→ run workflow → attach session artifacts → record result
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. | vitest-evals |
Reuses Vitest runner, filtering, and reporting |
| Isolation | temp project + agent dirs per eval | No side effects on the real workspace |
| Model defaults | CLICommand-Line Interface (the `pi` binary). args or PI_PROVIDER/PI_MODEL |
CLICommand-Line Interface (the `pi` binary). wins; harnesses may override per eval |
| Auth | normal ModelRuntime |
Subscription + env API keys, no new secret handling |
Risks and Unknowns
- Model-backed evals are non-deterministic and cost tokens; thresholds need care.
- Auth availability (subscription vs. API keyApplication Programming Interface key (ambient provider authentication).) affects whether evals can run locally.
Out of Scope
- The remote-session stack (protocol/client/server).
- SQLite session storage.
- The interactive coding agent itself (covered by other features).
Test Plan: EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`.
Scope
Tests cover the pi-harness adapter, the vitest-evals harness primitives (harness-table, artifacts, summary), and the harness contract.
Model-backed eval runs themselves (smoke, extensions) are on-demand and excluded from the default unit suite.
Unit Tests
| ID | Description | Input | Expected Output |
|---|---|---|---|
| TC-1 | pi-harness adapts an AgentSession to the harness interface |
HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. options | A valid harness instance |
| TC-2 | HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. table summarizes multiple harness configurations | Table of harnesses | Correct summary output |
| TC-3 | Artifact collection attaches session artifacts | Completed run | Artifacts recorded |
Integration Tests
| ID | Description | Preconditions | Expected Outcome |
|---|---|---|---|
| TC-4 | vitest-evals summary aggregates results across evals |
Multiple eval results | Aggregated summary with pass/fail |
Edge Cases and Failure Scenarios
| ID | Scenario | Expected Behavior |
|---|---|---|
| TC-5 | No provider/model configured | Eval run fails with a clear error |
| TC-6 | HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. without a model selection and no default | Run reports a configuration error |
Test Infrastructure
- Ran with Vitest (
packages/evals/test/): pi-harness.test.ts,vitest-evals/artifacts.test.ts,vitest-evals/harness-table.test.ts,vitest-evals/summary.test.ts.- CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. evals live in
src/(smoke.eval.ts,extensions.eval.ts) and run vianpm run evalwith a provider/model.
Coverage Matrix
| Requirement | Test Cases |
|---|---|
| FRFunctional Requirement.-01MustThe system shall adapt a real `AgentSession` to the `vitest-evals` harness interface. | TC-1 |
| FRFunctional Requirement.-07ShouldThe system shall allow harnesses to configure their own model, overriding the default. | TC-2 |
| NFRNon-Functional Requirement.-03ShouldResults shall include session artifacts so failures are diagnosable. | TC-3, TC-4 |
| FRFunctional Requirement.-04MustThe system shall be runnable from the repo root via `npm run eval` with a provider/model default. | TC-5 |
requirements
- Should evals be integrated into CI with a pinned model, or stay local/on-demand only?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. | The project: a minimal, self-extensible terminal coding agent harness and its libraries. |
| HarnessThe coding agent runtime that wires the agent loop, tools, sessions, and UI together. | The coding agent runtime that wires the agent loop, tools, sessions, and UI together. |
| ExtensionA TypeScript module with a default export `function (pi: ExtensionAPI)` that augments the agent with tools, commands, events, UI, or providers. | A TypeScript module with a default export function (pi: ExtensionAPI) that augments the agent with tools, commands, events, UI, or providers. |
| SkillAn on-demand capability package following the Agent Skills standard (`SKILL.md` + optional frontmatter), invoked as `/skill:name`. | An on-demand capability package following the Agent Skills standard (SKILL.md + optional frontmatter), invoked as /skill:name. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| pi packageA distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via `pi install`. | A distributable bundle (npm or git) of extensions, skills, prompts, themes, or custom providers, installed via pi install. |
| SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). | A persistent, branchable conversation log stored as JSONL (SessionHeader, messages, compaction summaries, branch summaries). |
| BranchingTree-structured session forking (`/fork`, `/clone`, `/tree`) where each entry has `id`/`parentId`. | Tree-structured session forking (/fork, /clone, /tree) where each entry has id/parentId. |
| CompactionLossy summarization of older session messages to reclaim context; original JSONL is preserved. | Lossy summarization of older session messages to reclaim context; original JSONL is preserved. |
| SteeringA queued message delivered to a streaming agent after the current tool batch completes. | A queued message delivered to a streaming agent after the current tool batch completes. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| Project trustA per-folder decision (`~/.pi/agent/trust.json`) gating whether project settings, resources, and extensions execute. | A per-folder decision (~/.pi/agent/trust.json) gating whether project settings, resources, and extensions execute. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Faux providerAn in-memory scripted provider (`providers/faux.ts`) used for deterministic tests with no real API calls. | An in-memory scripted provider (providers/faux.ts) used for deterministic tests with no real API calls. |
| CoreThe deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. | The deliberately minimal set of built-in capabilities (four tools); features outside core must be extensions. |
| Remote sessionA pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. | A pi session driven over a transport (e.g. Unix socket) via the pi protocol instead of a local JSONL file. |
| Session leaseA remote-session ownership handle (`exclusive` or `shared`) granted by `PiClient.acquireSession()`/`createSession()`; gates who may mutate or observe a session. | A remote-session ownership handle (exclusive or shared) granted by PiClient.acquireSession()/createSession(); gates who may mutate or observe a session. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| Byte transportThe minimal ordered byte-stream interface (`send`/`close`, inbound `onData`/`onClose`/`onError`) that `PiClient` uses; keeps pi-client Node-agnostic. | The minimal ordered byte-stream interface (send/close, inbound onData/onClose/onError) that PiClient uses; keeps pi-client Node-agnostic. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| Legacy supervisorThe renamed orchestrator code in pi-server (`server` CLI, child-process supervision, Radius, RPC stream bridge). | The renamed orchestrator code in pi-server (server CLICommand-Line Interface (the `pi` binary)., child-process supervision, Radius, RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). stream bridge). |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| EvalsBehavioral, model-backed checks for pi workflows run via `vitest-evals` with a real `AgentSession`. | Behavioral, model-backed checks for pi workflows run via vitest-evals with a real AgentSession. |
Technical Terms
| Term | Definition |
|---|---|
| ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). | The runtime unit owning a model catalog, auth, and stream behavior (e.g. anthropic, openai). |
| API implementationA wire-protocol backend shared by providers (e.g. `anthropic-messages`, `openai-responses`, `openai-completions`, `google-generative-ai`, `bedrock-converse-stream`). | A wire-protocol backend shared by providers (e.g. anthropic-messages, openai-responses, openai-completions, google-generative-ai, bedrock-converse-stream). |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
Agent |
pi-agent-core's stateful class owning the transcript and lifecycle (prompt, continue, abort). |
AgentHarness |
pi-agent-core's higher-level orchestrator wrapping Agent with sessions, compaction, skills, and provider hooks. |
agentLoop |
The low-level prompt-stream-tool-continue loop in pi-agent-core. |
AgentMessage |
pi-agent-core's app-extensible message union (via declaration merging); convertToLlm bridges to pi-ai Message. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| FramingThe pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBOR item. | The pi protocol's wire layout: four-byte big-endian payload length followed by one definite-length CBORConcise Binary Object Representation, the payload format for pi protocol messages. item. |
hello |
The first message a pi-client sends, carrying PROTOCOL_VERSION and a bearer token. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
| Differential renderingpi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. | pi-tui's technique of diffing a new line array against the previous frame and writing minimal escape sequences. |
| Synchronized outputTerminal escape sequence (`\x1b[?2026h..l`) used by pi-tui for atomic, flicker-free rendering. | Terminal escape sequence (\x1b[?2026h..l) used by pi-tui for atomic, flicker-free rendering. |
| Kitty keyboard protocolTerminal input protocol pi-tui negotiates for richer key reporting. | Terminal input protocol pi-tui negotiates for richer key reporting. |
| Lockstep versioningAll packages share one version and release together. | All packages share one version and release together. |
| Trusted publishingnpm publish via GitHub Actions OIDC (environment `npm-publish`); no local credentials required. | npm publish via GitHub Actions OIDCOpenID Connect (used for npm trusted publishing identity). (environment npm-publish); no local credentials required. |
| Shrinkwrap`packages/coding-agent/npm-shrinkwrap.json`, generated from the root lockfile to pin transitive deps for npm users. | packages/coding-agent/npm-shrinkwrap.json, generated from the root lockfile to pin transitive deps for npm users. |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| LLMLarge Language Model. | Large Language Model. |
| MCPModel Context Protocol (not built into core; extensions may add it). | Model Context Protocol (not built into core; extensions may add it). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). | Open Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). |
| PKCEProof Key for Code Exchange (OAuth flow used by pi-ai). | Proof Key for Code Exchange (OAuthOpen Authorization (used for subscription-based provider login: Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot). flow used by pi-ai). |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| FRFunctional Requirement. | Functional Requirement. |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). | Input Method Editor (pi-tui positions the hardware cursor for IMEInput Method Editor (pi-tui positions the hardware cursor for IME candidate windows). candidate windows). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
| AGENTS.mdProject-specific rules file for humans and agents, read automatically from the repo root. | Project-specific rules file for humans and agents, read automatically from the repo root. |