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 |
|---|---|
| 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. |
| 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. |
| ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. | The facet/service composition runtime (packages/chord) where facets declare, provide, and consume singleton or keyed services with replicated state. |
| 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. |
| 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. |
| Durable runtimeThe Pico storage contract separating transcript scope (`conversation`), executable work (`task`), and persisted JSON (`document`) with monotonic commit sequences. | The PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. storage contract separating transcript scope (conversation), executable work (task), and persisted JSON (document) with monotonic commit sequences. |
| 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. |
| 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. |
| FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. | A setup unit with an id and setup(env) that declares service dependencies, provides implementations, and owns lifecycle callbacks in a ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. host. |
| 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. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| 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. |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. | The detached in-memory Storage implementation used for tests and reference PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. behavior with no persistence. |
| 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. |
| 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. |
| PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. | The durable agent harness (packages/durable, spec pico-v5.md) persisting conversations, tasks, and documents as atomic storage commits. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| PluginA versioned bundle identity (`FacetBundlePlugin` with `id`/`version`) packaging one or more Chord facets for distribution and loading. | A versioned bundle identity (FacetBundlePlugin with id/version) packaging one or more ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. facets for distribution and loading. |
| 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. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| 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. |
| Replicated stateAn immutable-value shared state handle (`ReplicatedState`/`MutableReplicatedState`) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. | An immutable-value shared state handle (ReplicatedState/MutableReplicatedState) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Service (singleton/keyed) | A stable typed contract identity (Service<T> with string id) provided once (singleton) or per-key (keyed) and consumed via use/observe. |
| 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). |
| 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. |
| 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. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| 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. |
Technical Terms
| Term | Definition |
|---|---|
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. |
| 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). |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
ByteTransportFactory |
A factory creating a fresh connected, authenticated ByteTransport from ByteTransportHandlers, with exactly one terminal handler expected. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| 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. |
| 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. |
| 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. |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
| 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). |
| 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. |
| Span/Event taxonomyThe versioned telemetry schema (`defineTelemetrySchema`) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. | The versioned telemetry schema (defineTelemetrySchema) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
| 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. |
TelemetryContext / TelemetrySpan |
The span-creation scope (TelemetryContext.startSpan) and its active unit (TelemetrySpan, itself a context) carrying attributes, events, and status. |
| 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. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| 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. |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| FRFunctional Requirement. | Functional Requirement. |
| 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). |
| 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). |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| 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). |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| 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). |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
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 |
|---|---|
| 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. |
| 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. |
| ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. | The facet/service composition runtime (packages/chord) where facets declare, provide, and consume singleton or keyed services with replicated state. |
| 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. |
| 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. |
| Durable runtimeThe Pico storage contract separating transcript scope (`conversation`), executable work (`task`), and persisted JSON (`document`) with monotonic commit sequences. | The PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. storage contract separating transcript scope (conversation), executable work (task), and persisted JSON (document) with monotonic commit sequences. |
| 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. |
| 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. |
| FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. | A setup unit with an id and setup(env) that declares service dependencies, provides implementations, and owns lifecycle callbacks in a ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. host. |
| 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. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| 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. |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. | The detached in-memory Storage implementation used for tests and reference PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. behavior with no persistence. |
| 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. |
| 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. |
| PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. | The durable agent harness (packages/durable, spec pico-v5.md) persisting conversations, tasks, and documents as atomic storage commits. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| PluginA versioned bundle identity (`FacetBundlePlugin` with `id`/`version`) packaging one or more Chord facets for distribution and loading. | A versioned bundle identity (FacetBundlePlugin with id/version) packaging one or more ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. facets for distribution and loading. |
| 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. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| 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. |
| Replicated stateAn immutable-value shared state handle (`ReplicatedState`/`MutableReplicatedState`) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. | An immutable-value shared state handle (ReplicatedState/MutableReplicatedState) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Service (singleton/keyed) | A stable typed contract identity (Service<T> with string id) provided once (singleton) or per-key (keyed) and consumed via use/observe. |
| 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). |
| 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. |
| 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. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| 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. |
Technical Terms
| Term | Definition |
|---|---|
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. |
| 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). |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
ByteTransportFactory |
A factory creating a fresh connected, authenticated ByteTransport from ByteTransportHandlers, with exactly one terminal handler expected. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| 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. |
| 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. |
| 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. |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
| 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). |
| 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. |
| Span/Event taxonomyThe versioned telemetry schema (`defineTelemetrySchema`) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. | The versioned telemetry schema (defineTelemetrySchema) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
| 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. |
TelemetryContext / TelemetrySpan |
The span-creation scope (TelemetryContext.startSpan) and its active unit (TelemetrySpan, itself a context) carrying attributes, events, and status. |
| 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. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| 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. |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| FRFunctional Requirement. | Functional Requirement. |
| 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). |
| 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). |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| 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). |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| 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). |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
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 |
|---|---|
| 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. |
| 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. |
| ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. | The facet/service composition runtime (packages/chord) where facets declare, provide, and consume singleton or keyed services with replicated state. |
| 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. |
| 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. |
| Durable runtimeThe Pico storage contract separating transcript scope (`conversation`), executable work (`task`), and persisted JSON (`document`) with monotonic commit sequences. | The PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. storage contract separating transcript scope (conversation), executable work (task), and persisted JSON (document) with monotonic commit sequences. |
| 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. |
| 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. |
| FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. | A setup unit with an id and setup(env) that declares service dependencies, provides implementations, and owns lifecycle callbacks in a ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. host. |
| 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. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| 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. |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. | The detached in-memory Storage implementation used for tests and reference PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. behavior with no persistence. |
| 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. |
| 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. |
| PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. | The durable agent harness (packages/durable, spec pico-v5.md) persisting conversations, tasks, and documents as atomic storage commits. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| PluginA versioned bundle identity (`FacetBundlePlugin` with `id`/`version`) packaging one or more Chord facets for distribution and loading. | A versioned bundle identity (FacetBundlePlugin with id/version) packaging one or more ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. facets for distribution and loading. |
| 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. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| 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. |
| Replicated stateAn immutable-value shared state handle (`ReplicatedState`/`MutableReplicatedState`) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. | An immutable-value shared state handle (ReplicatedState/MutableReplicatedState) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Service (singleton/keyed) | A stable typed contract identity (Service<T> with string id) provided once (singleton) or per-key (keyed) and consumed via use/observe. |
| 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). |
| 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. |
| 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. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| 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. |
Technical Terms
| Term | Definition |
|---|---|
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. |
| 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). |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
ByteTransportFactory |
A factory creating a fresh connected, authenticated ByteTransport from ByteTransportHandlers, with exactly one terminal handler expected. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| 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. |
| 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. |
| 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. |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
| 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). |
| 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. |
| Span/Event taxonomyThe versioned telemetry schema (`defineTelemetrySchema`) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. | The versioned telemetry schema (defineTelemetrySchema) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
| 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. |
TelemetryContext / TelemetrySpan |
The span-creation scope (TelemetryContext.startSpan) and its active unit (TelemetrySpan, itself a context) carrying attributes, events, and status. |
| 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. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| 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. |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| FRFunctional Requirement. | Functional Requirement. |
| 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). |
| 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). |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| 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). |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| 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). |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
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 |
|---|---|
| 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. |
| 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. |
| ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. | The facet/service composition runtime (packages/chord) where facets declare, provide, and consume singleton or keyed services with replicated state. |
| 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. |
| 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. |
| Durable runtimeThe Pico storage contract separating transcript scope (`conversation`), executable work (`task`), and persisted JSON (`document`) with monotonic commit sequences. | The PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. storage contract separating transcript scope (conversation), executable work (task), and persisted JSON (document) with monotonic commit sequences. |
| 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. |
| 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. |
| FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. | A setup unit with an id and setup(env) that declares service dependencies, provides implementations, and owns lifecycle callbacks in a ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. host. |
| 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. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| 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. |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. | The detached in-memory Storage implementation used for tests and reference PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. behavior with no persistence. |
| 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. |
| 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. |
| PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. | The durable agent harness (packages/durable, spec pico-v5.md) persisting conversations, tasks, and documents as atomic storage commits. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| PluginA versioned bundle identity (`FacetBundlePlugin` with `id`/`version`) packaging one or more Chord facets for distribution and loading. | A versioned bundle identity (FacetBundlePlugin with id/version) packaging one or more ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. facets for distribution and loading. |
| 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. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| 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. |
| Replicated stateAn immutable-value shared state handle (`ReplicatedState`/`MutableReplicatedState`) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. | An immutable-value shared state handle (ReplicatedState/MutableReplicatedState) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Service (singleton/keyed) | A stable typed contract identity (Service<T> with string id) provided once (singleton) or per-key (keyed) and consumed via use/observe. |
| 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). |
| 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. |
| 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. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| 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. |
Technical Terms
| Term | Definition |
|---|---|
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. |
| 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). |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
ByteTransportFactory |
A factory creating a fresh connected, authenticated ByteTransport from ByteTransportHandlers, with exactly one terminal handler expected. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| 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. |
| 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. |
| 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. |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
| 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). |
| 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. |
| Span/Event taxonomyThe versioned telemetry schema (`defineTelemetrySchema`) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. | The versioned telemetry schema (defineTelemetrySchema) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
| 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. |
TelemetryContext / TelemetrySpan |
The span-creation scope (TelemetryContext.startSpan) and its active unit (TelemetrySpan, itself a context) carrying attributes, events, and status. |
| 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. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| 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. |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| FRFunctional Requirement. | Functional Requirement. |
| 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). |
| 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). |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| 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). |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| 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). |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
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 |
|---|---|
| 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. |
| 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. |
| ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. | The facet/service composition runtime (packages/chord) where facets declare, provide, and consume singleton or keyed services with replicated state. |
| 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. |
| 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. |
| Durable runtimeThe Pico storage contract separating transcript scope (`conversation`), executable work (`task`), and persisted JSON (`document`) with monotonic commit sequences. | The PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. storage contract separating transcript scope (conversation), executable work (task), and persisted JSON (document) with monotonic commit sequences. |
| 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. |
| 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. |
| FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. | A setup unit with an id and setup(env) that declares service dependencies, provides implementations, and owns lifecycle callbacks in a ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. host. |
| 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. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| 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. |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. | The detached in-memory Storage implementation used for tests and reference PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. behavior with no persistence. |
| 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. |
| 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. |
| PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. | The durable agent harness (packages/durable, spec pico-v5.md) persisting conversations, tasks, and documents as atomic storage commits. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| PluginA versioned bundle identity (`FacetBundlePlugin` with `id`/`version`) packaging one or more Chord facets for distribution and loading. | A versioned bundle identity (FacetBundlePlugin with id/version) packaging one or more ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. facets for distribution and loading. |
| 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. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| 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. |
| Replicated stateAn immutable-value shared state handle (`ReplicatedState`/`MutableReplicatedState`) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. | An immutable-value shared state handle (ReplicatedState/MutableReplicatedState) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Service (singleton/keyed) | A stable typed contract identity (Service<T> with string id) provided once (singleton) or per-key (keyed) and consumed via use/observe. |
| 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). |
| 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. |
| 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. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| 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. |
Technical Terms
| Term | Definition |
|---|---|
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. |
| 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). |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
ByteTransportFactory |
A factory creating a fresh connected, authenticated ByteTransport from ByteTransportHandlers, with exactly one terminal handler expected. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| 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. |
| 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. |
| 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. |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
| 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). |
| 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. |
| Span/Event taxonomyThe versioned telemetry schema (`defineTelemetrySchema`) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. | The versioned telemetry schema (defineTelemetrySchema) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
| 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. |
TelemetryContext / TelemetrySpan |
The span-creation scope (TelemetryContext.startSpan) and its active unit (TelemetrySpan, itself a context) carrying attributes, events, and status. |
| 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. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| 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. |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| FRFunctional Requirement. | Functional Requirement. |
| 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). |
| 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). |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| 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). |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| 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). |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
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 |
|---|---|
| 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. |
| 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. |
| ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. | The facet/service composition runtime (packages/chord) where facets declare, provide, and consume singleton or keyed services with replicated state. |
| 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. |
| 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. |
| Durable runtimeThe Pico storage contract separating transcript scope (`conversation`), executable work (`task`), and persisted JSON (`document`) with monotonic commit sequences. | The PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. storage contract separating transcript scope (conversation), executable work (task), and persisted JSON (document) with monotonic commit sequences. |
| 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. |
| 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. |
| FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. | A setup unit with an id and setup(env) that declares service dependencies, provides implementations, and owns lifecycle callbacks in a ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. host. |
| 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. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| 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. |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. | The detached in-memory Storage implementation used for tests and reference PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. behavior with no persistence. |
| 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. |
| 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. |
| PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. | The durable agent harness (packages/durable, spec pico-v5.md) persisting conversations, tasks, and documents as atomic storage commits. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| PluginA versioned bundle identity (`FacetBundlePlugin` with `id`/`version`) packaging one or more Chord facets for distribution and loading. | A versioned bundle identity (FacetBundlePlugin with id/version) packaging one or more ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. facets for distribution and loading. |
| 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. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| 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. |
| Replicated stateAn immutable-value shared state handle (`ReplicatedState`/`MutableReplicatedState`) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. | An immutable-value shared state handle (ReplicatedState/MutableReplicatedState) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Service (singleton/keyed) | A stable typed contract identity (Service<T> with string id) provided once (singleton) or per-key (keyed) and consumed via use/observe. |
| 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). |
| 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. |
| 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. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| 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. |
Technical Terms
| Term | Definition |
|---|---|
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. |
| 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). |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
ByteTransportFactory |
A factory creating a fresh connected, authenticated ByteTransport from ByteTransportHandlers, with exactly one terminal handler expected. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| 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. |
| 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. |
| 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. |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
| 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). |
| 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. |
| Span/Event taxonomyThe versioned telemetry schema (`defineTelemetrySchema`) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. | The versioned telemetry schema (defineTelemetrySchema) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
| 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. |
TelemetryContext / TelemetrySpan |
The span-creation scope (TelemetryContext.startSpan) and its active unit (TelemetrySpan, itself a context) carrying attributes, events, and status. |
| 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. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| 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. |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| FRFunctional Requirement. | Functional Requirement. |
| 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). |
| 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). |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| 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). |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| 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). |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
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 |
|---|---|
| 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. |
| 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. |
| ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. | The facet/service composition runtime (packages/chord) where facets declare, provide, and consume singleton or keyed services with replicated state. |
| 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. |
| 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. |
| Durable runtimeThe Pico storage contract separating transcript scope (`conversation`), executable work (`task`), and persisted JSON (`document`) with monotonic commit sequences. | The PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. storage contract separating transcript scope (conversation), executable work (task), and persisted JSON (document) with monotonic commit sequences. |
| 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. |
| 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. |
| FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. | A setup unit with an id and setup(env) that declares service dependencies, provides implementations, and owns lifecycle callbacks in a ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. host. |
| 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. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| 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. |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. | The detached in-memory Storage implementation used for tests and reference PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. behavior with no persistence. |
| 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. |
| 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. |
| PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. | The durable agent harness (packages/durable, spec pico-v5.md) persisting conversations, tasks, and documents as atomic storage commits. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| PluginA versioned bundle identity (`FacetBundlePlugin` with `id`/`version`) packaging one or more Chord facets for distribution and loading. | A versioned bundle identity (FacetBundlePlugin with id/version) packaging one or more ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. facets for distribution and loading. |
| 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. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| 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. |
| Replicated stateAn immutable-value shared state handle (`ReplicatedState`/`MutableReplicatedState`) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. | An immutable-value shared state handle (ReplicatedState/MutableReplicatedState) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Service (singleton/keyed) | A stable typed contract identity (Service<T> with string id) provided once (singleton) or per-key (keyed) and consumed via use/observe. |
| 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). |
| 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. |
| 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. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| 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. |
Technical Terms
| Term | Definition |
|---|---|
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. |
| 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). |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
ByteTransportFactory |
A factory creating a fresh connected, authenticated ByteTransport from ByteTransportHandlers, with exactly one terminal handler expected. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| 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. |
| 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. |
| 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. |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
| 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). |
| 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. |
| Span/Event taxonomyThe versioned telemetry schema (`defineTelemetrySchema`) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. | The versioned telemetry schema (defineTelemetrySchema) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
| 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. |
TelemetryContext / TelemetrySpan |
The span-creation scope (TelemetryContext.startSpan) and its active unit (TelemetrySpan, itself a context) carrying attributes, events, and status. |
| 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. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| 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. |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| FRFunctional Requirement. | Functional Requirement. |
| 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). |
| 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). |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| 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). |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| 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). |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
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 |
|---|---|
| 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. |
| 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. |
| ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. | The facet/service composition runtime (packages/chord) where facets declare, provide, and consume singleton or keyed services with replicated state. |
| 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. |
| 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. |
| Durable runtimeThe Pico storage contract separating transcript scope (`conversation`), executable work (`task`), and persisted JSON (`document`) with monotonic commit sequences. | The PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. storage contract separating transcript scope (conversation), executable work (task), and persisted JSON (document) with monotonic commit sequences. |
| 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. |
| 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. |
| FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. | A setup unit with an id and setup(env) that declares service dependencies, provides implementations, and owns lifecycle callbacks in a ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. host. |
| 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. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| 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. |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. | The detached in-memory Storage implementation used for tests and reference PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. behavior with no persistence. |
| 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. |
| 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. |
| PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. | The durable agent harness (packages/durable, spec pico-v5.md) persisting conversations, tasks, and documents as atomic storage commits. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| PluginA versioned bundle identity (`FacetBundlePlugin` with `id`/`version`) packaging one or more Chord facets for distribution and loading. | A versioned bundle identity (FacetBundlePlugin with id/version) packaging one or more ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. facets for distribution and loading. |
| 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. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| 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. |
| Replicated stateAn immutable-value shared state handle (`ReplicatedState`/`MutableReplicatedState`) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. | An immutable-value shared state handle (ReplicatedState/MutableReplicatedState) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Service (singleton/keyed) | A stable typed contract identity (Service<T> with string id) provided once (singleton) or per-key (keyed) and consumed via use/observe. |
| 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). |
| 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. |
| 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. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| 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. |
Technical Terms
| Term | Definition |
|---|---|
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. |
| 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). |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
ByteTransportFactory |
A factory creating a fresh connected, authenticated ByteTransport from ByteTransportHandlers, with exactly one terminal handler expected. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| 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. |
| 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. |
| 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. |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
| 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). |
| 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. |
| Span/Event taxonomyThe versioned telemetry schema (`defineTelemetrySchema`) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. | The versioned telemetry schema (defineTelemetrySchema) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
| 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. |
TelemetryContext / TelemetrySpan |
The span-creation scope (TelemetryContext.startSpan) and its active unit (TelemetrySpan, itself a context) carrying attributes, events, and status. |
| 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. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| 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. |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| FRFunctional Requirement. | Functional Requirement. |
| 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). |
| 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). |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| 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). |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| 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). |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
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 |
|---|---|
| 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. |
| 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. |
| ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. | The facet/service composition runtime (packages/chord) where facets declare, provide, and consume singleton or keyed services with replicated state. |
| 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. |
| 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. |
| Durable runtimeThe Pico storage contract separating transcript scope (`conversation`), executable work (`task`), and persisted JSON (`document`) with monotonic commit sequences. | The PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. storage contract separating transcript scope (conversation), executable work (task), and persisted JSON (document) with monotonic commit sequences. |
| 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. |
| 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. |
| FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. | A setup unit with an id and setup(env) that declares service dependencies, provides implementations, and owns lifecycle callbacks in a ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. host. |
| 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. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| 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. |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. | The detached in-memory Storage implementation used for tests and reference PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. behavior with no persistence. |
| 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. |
| 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. |
| PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. | The durable agent harness (packages/durable, spec pico-v5.md) persisting conversations, tasks, and documents as atomic storage commits. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| PluginA versioned bundle identity (`FacetBundlePlugin` with `id`/`version`) packaging one or more Chord facets for distribution and loading. | A versioned bundle identity (FacetBundlePlugin with id/version) packaging one or more ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. facets for distribution and loading. |
| 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. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| 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. |
| Replicated stateAn immutable-value shared state handle (`ReplicatedState`/`MutableReplicatedState`) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. | An immutable-value shared state handle (ReplicatedState/MutableReplicatedState) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Service (singleton/keyed) | A stable typed contract identity (Service<T> with string id) provided once (singleton) or per-key (keyed) and consumed via use/observe. |
| 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). |
| 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. |
| 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. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| 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. |
Technical Terms
| Term | Definition |
|---|---|
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. |
| 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). |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
ByteTransportFactory |
A factory creating a fresh connected, authenticated ByteTransport from ByteTransportHandlers, with exactly one terminal handler expected. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| 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. |
| 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. |
| 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. |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
| 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). |
| 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. |
| Span/Event taxonomyThe versioned telemetry schema (`defineTelemetrySchema`) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. | The versioned telemetry schema (defineTelemetrySchema) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
| 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. |
TelemetryContext / TelemetrySpan |
The span-creation scope (TelemetryContext.startSpan) and its active unit (TelemetrySpan, itself a context) carrying attributes, events, and status. |
| 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. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| 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. |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| FRFunctional Requirement. | Functional Requirement. |
| 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). |
| 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). |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| 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). |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| 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). |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
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 supervisor 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 |
|---|---|
| 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. |
| 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. |
| ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. | The facet/service composition runtime (packages/chord) where facets declare, provide, and consume singleton or keyed services with replicated state. |
| 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. |
| 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. |
| Durable runtimeThe Pico storage contract separating transcript scope (`conversation`), executable work (`task`), and persisted JSON (`document`) with monotonic commit sequences. | The PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. storage contract separating transcript scope (conversation), executable work (task), and persisted JSON (document) with monotonic commit sequences. |
| 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. |
| 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. |
| FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. | A setup unit with an id and setup(env) that declares service dependencies, provides implementations, and owns lifecycle callbacks in a ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. host. |
| 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. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| 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. |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. | The detached in-memory Storage implementation used for tests and reference PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. behavior with no persistence. |
| 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. |
| 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. |
| PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. | The durable agent harness (packages/durable, spec pico-v5.md) persisting conversations, tasks, and documents as atomic storage commits. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| PluginA versioned bundle identity (`FacetBundlePlugin` with `id`/`version`) packaging one or more Chord facets for distribution and loading. | A versioned bundle identity (FacetBundlePlugin with id/version) packaging one or more ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. facets for distribution and loading. |
| 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. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| 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. |
| Replicated stateAn immutable-value shared state handle (`ReplicatedState`/`MutableReplicatedState`) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. | An immutable-value shared state handle (ReplicatedState/MutableReplicatedState) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Service (singleton/keyed) | A stable typed contract identity (Service<T> with string id) provided once (singleton) or per-key (keyed) and consumed via use/observe. |
| 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). |
| 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. |
| 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. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| 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. |
Technical Terms
| Term | Definition |
|---|---|
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. |
| 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). |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
ByteTransportFactory |
A factory creating a fresh connected, authenticated ByteTransport from ByteTransportHandlers, with exactly one terminal handler expected. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| 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. |
| 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. |
| 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. |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
| 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). |
| 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. |
| Span/Event taxonomyThe versioned telemetry schema (`defineTelemetrySchema`) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. | The versioned telemetry schema (defineTelemetrySchema) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
| 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. |
TelemetryContext / TelemetrySpan |
The span-creation scope (TelemetryContext.startSpan) and its active unit (TelemetrySpan, itself a context) carrying attributes, events, and status. |
| 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. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| 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. |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| FRFunctional Requirement. | Functional Requirement. |
| 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). |
| 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). |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| 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). |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| 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). |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
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 |
|---|---|
| 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. |
| 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. |
| ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. | The facet/service composition runtime (packages/chord) where facets declare, provide, and consume singleton or keyed services with replicated state. |
| 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. |
| 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. |
| Durable runtimeThe Pico storage contract separating transcript scope (`conversation`), executable work (`task`), and persisted JSON (`document`) with monotonic commit sequences. | The PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. storage contract separating transcript scope (conversation), executable work (task), and persisted JSON (document) with monotonic commit sequences. |
| 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. |
| 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. |
| FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. | A setup unit with an id and setup(env) that declares service dependencies, provides implementations, and owns lifecycle callbacks in a ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. host. |
| 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. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| 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. |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. | The detached in-memory Storage implementation used for tests and reference PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. behavior with no persistence. |
| 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. |
| 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. |
| PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. | The durable agent harness (packages/durable, spec pico-v5.md) persisting conversations, tasks, and documents as atomic storage commits. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| PluginA versioned bundle identity (`FacetBundlePlugin` with `id`/`version`) packaging one or more Chord facets for distribution and loading. | A versioned bundle identity (FacetBundlePlugin with id/version) packaging one or more ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. facets for distribution and loading. |
| 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. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| 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. |
| Replicated stateAn immutable-value shared state handle (`ReplicatedState`/`MutableReplicatedState`) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. | An immutable-value shared state handle (ReplicatedState/MutableReplicatedState) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Service (singleton/keyed) | A stable typed contract identity (Service<T> with string id) provided once (singleton) or per-key (keyed) and consumed via use/observe. |
| 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). |
| 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. |
| 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. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| 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. |
Technical Terms
| Term | Definition |
|---|---|
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. |
| 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). |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
ByteTransportFactory |
A factory creating a fresh connected, authenticated ByteTransport from ByteTransportHandlers, with exactly one terminal handler expected. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| 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. |
| 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. |
| 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. |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
| 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). |
| 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. |
| Span/Event taxonomyThe versioned telemetry schema (`defineTelemetrySchema`) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. | The versioned telemetry schema (defineTelemetrySchema) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
| 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. |
TelemetryContext / TelemetrySpan |
The span-creation scope (TelemetryContext.startSpan) and its active unit (TelemetrySpan, itself a context) carrying attributes, events, and status. |
| 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. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| 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. |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| FRFunctional Requirement. | Functional Requirement. |
| 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). |
| 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). |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| 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). |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| 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). |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
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 |
|---|---|
| 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. |
| 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. |
| ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. | The facet/service composition runtime (packages/chord) where facets declare, provide, and consume singleton or keyed services with replicated state. |
| 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. |
| 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. |
| Durable runtimeThe Pico storage contract separating transcript scope (`conversation`), executable work (`task`), and persisted JSON (`document`) with monotonic commit sequences. | The PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. storage contract separating transcript scope (conversation), executable work (task), and persisted JSON (document) with monotonic commit sequences. |
| 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. |
| 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. |
| FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. | A setup unit with an id and setup(env) that declares service dependencies, provides implementations, and owns lifecycle callbacks in a ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. host. |
| 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. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| 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. |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. | The detached in-memory Storage implementation used for tests and reference PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. behavior with no persistence. |
| 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. |
| 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. |
| PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. | The durable agent harness (packages/durable, spec pico-v5.md) persisting conversations, tasks, and documents as atomic storage commits. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| PluginA versioned bundle identity (`FacetBundlePlugin` with `id`/`version`) packaging one or more Chord facets for distribution and loading. | A versioned bundle identity (FacetBundlePlugin with id/version) packaging one or more ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. facets for distribution and loading. |
| 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. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| 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. |
| Replicated stateAn immutable-value shared state handle (`ReplicatedState`/`MutableReplicatedState`) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. | An immutable-value shared state handle (ReplicatedState/MutableReplicatedState) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Service (singleton/keyed) | A stable typed contract identity (Service<T> with string id) provided once (singleton) or per-key (keyed) and consumed via use/observe. |
| 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). |
| 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. |
| 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. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| 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. |
Technical Terms
| Term | Definition |
|---|---|
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. |
| 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). |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
ByteTransportFactory |
A factory creating a fresh connected, authenticated ByteTransport from ByteTransportHandlers, with exactly one terminal handler expected. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| 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. |
| 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. |
| 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. |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
| 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). |
| 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. |
| Span/Event taxonomyThe versioned telemetry schema (`defineTelemetrySchema`) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. | The versioned telemetry schema (defineTelemetrySchema) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
| 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. |
TelemetryContext / TelemetrySpan |
The span-creation scope (TelemetryContext.startSpan) and its active unit (TelemetrySpan, itself a context) carrying attributes, events, and status. |
| 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. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| 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. |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| FRFunctional Requirement. | Functional Requirement. |
| 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). |
| 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). |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| 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). |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| 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). |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
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: Durable runtimeThe Pico storage contract separating transcript scope (`conversation`), executable work (`task`), and persisted JSON (`document`) with monotonic commit sequences.
Overview
@earendil-works/pi-durable provides the durable conversation, task, and document runtime for PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries..
It implements the PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. model in which a SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). atomically commits immutable entries, full task records, and ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state.-tracked documents.
The package currently ships the durable record contracts, the Storage persistence boundary, and the detached in-memory MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. reference implementation.
The root conversation always uses the reserved ROOT_CONVERSATION_ID (1), and fresh IDs are minted from 2 upward.
The normative design sequence lives in packages/durable/docs/pico-v5.md, pico-v5-handoff.md, and pico-v5-chord-usage.md.
Stakeholders
| Stakeholder | Interest |
|---|---|
| SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). implementers | A stable record and storage contract for building the PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). mutation line. |
| Storage backend authors | A conformance baseline (MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence.) for serialization-backed stores. |
| Contributors | Typed record contracts that fail fast at compile time on invalid state transitions. |
Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Requirement |
|---|---|---|
| FRFunctional Requirement.-1MustThe system shall model conversations as immutable records with an ID, an optional fork parent edge, and an optional task owner edge. | Must | The system shall model conversations as immutable records with an ID, an optional fork parent edge, and an optional task owner edge. |
| FRFunctional Requirement.-2MustThe system shall model entries as immutable transcript events with kind, optional model messages, optional JSON data, optional head marker, optional context edits, and optional producing task ID. | Must | The system shall model entries as immutable transcript events with kind, optional model messages, optional JSON data, optional head marker, optional context edits, and optional producing task ID. |
| FRFunctional Requirement.-3MustThe system shall model host inputs through the queued, placed, done, and unanswered lifecycle states with conversation-scoped request ID deduplication. | Must | The system shall model host inputs through the queued, placed, done, and unanswered lifecycle states with conversation-scoped request ID deduplication. |
| FRFunctional Requirement.-4MustThe system shall model tasks as pending, running, and terminal states with completed, failed, aborted, orphaned, and faulted outcomes. | Must | The system shall model tasks as pending, running, and terminal states with completed, failed, aborted, orphaned, and faulted outcomes. |
| FRFunctional Requirement.-5MustThe system shall model document incarnations with session, conversation, and task scopes plus history and fork policies. | Must | The system shall model document incarnations with session, conversation, and task scopes plus history and fork policies. |
| FRFunctional Requirement.-6MustThe system shall expose a `Storage` boundary with atomic commit, global ID minting, conversation lookup and scan, entry lookup with commit sequence, head-marker search, fork-aware entry scan, task lookup and filtered scan, input lookup by ID and request key, and close. | Must | The system shall expose a Storage boundary with atomic commit, global ID minting, conversation lookup and scan, entry lookup with commit sequence, head-marker search, fork-aware entry scan, task lookup and filtered scan, input lookup by ID and request key, and close. |
| FRFunctional Requirement.-7MustThe system shall provide `MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence.` as a detached in-memory `Storage` with atomic commits, sorted indexes, opaque cursors, fork-aware history scans, a single global ID namespace, and post-close rejection. | Must | The system shall provide MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. as a detached in-memory Storage with atomic commits, sorted indexes, opaque cursors, fork-aware history scans, a single global ID namespace, and post-close rejection. |
| FRFunctional Requirement.-8MustThe system shall reserve `ROOT_CONVERSATION_ID` (`1`) for the root conversation and start minted IDs at `2`. | Must | The system shall reserve ROOT_CONVERSATION_ID (1) for the root conversation and start minted IDs at 2. |
| FRFunctional Requirement.-9MustThe system shall enforce omit and replace context-edit discriminators so omissions carry no messages and replacements always carry messages. | Must | The system shall enforce omit and replace context-edit discriminators so omissions carry no messages and replacements always carry messages. |
| FRFunctional Requirement.-10ShouldThe system shall expose pagination (`Page`, `Cursor`), scan filters (`EntryQuery`, `TaskQuery`), and the atomic `StorageWrite` table-mutation union. | Should | The system shall expose pagination (Page, Cursor), scan filters (EntryQuery, TaskQuery), and the atomic StorageWrite table-mutation union. |
Non-Functional Requirements
Order rows by priority: Must first, then Should, then May.
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFRNon-Functional Requirement.-1MustCommits shall be atomic so a failed batch persists none of its writes. | Must | Reliability | Commits shall be atomic so a failed batch persists none of its writes. |
| NFRNon-Functional Requirement.-2MustStored and returned values shall be detached clones that never pollute object prototypes. | Must | Security | Stored and returned values shall be detached clones that never pollute object prototypes. |
| NFRNon-Functional Requirement.-3MustThe package shall use erasable TypeScript with top-level imports only and no `any`. | Must | Maintainability | The package shall use erasable TypeScript with top-level imports only and no any. |
| NFRNon-Functional Requirement.-4ShouldIn-memory reads and scans shall use sorted-index seeks rather than full-table sorts. | Should | Performance | In-memory reads and scans shall use sorted-index seeks rather than full-table sorts. |
Constraints
MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence.is a conformance reference, not a persistent backend.- Storage trusts the owning SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). for semantic validity of records, references, ancestry, and transitions.
- The package has no HTTP surface and therefore ships no
api.yaml.
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.-1MustThe system shall model conversations as immutable records with an ID, an optional fork parent edge, and an optional task owner edge.
gherkin @FRFunctional Requirement.-1MustThe system shall model conversations as immutable records with an ID, an optional fork parent edge, and an optional task owner edge. Scenario: create and read a forked conversation Given a storage with a root conversation When a child conversation with a parent edge is committed Then reading the child returns the exact parent edge -
FRFunctional Requirement.-2MustThe system shall model entries as immutable transcript events with kind, optional model messages, optional JSON data, optional head marker, optional context edits, and optional producing task ID.
gherkin @FRFunctional Requirement.-2MustThe system shall model entries as immutable transcript events with kind, optional model messages, optional JSON data, optional head marker, optional context edits, and optional producing task ID. Scenario: commit and read an immutable entry Given a storage with a root conversation When an entry with kind, model messages, and data is committed Then reading the entry returns the same kind, messages, and data with its commit sequence -
FRFunctional Requirement.-3MustThe system shall model host inputs through the queued, placed, done, and unanswered lifecycle states with conversation-scoped request ID deduplication.
gherkin @FRFunctional Requirement.-3MustThe system shall model host inputs through the queued, placed, done, and unanswered lifecycle states with conversation-scoped request ID deduplication. Scenario: advance an input through its lifecycle Given a queued input with a request ID When the input is replaced by placed and then done records Then lookups by ID and by request key return the latest record -
FRFunctional Requirement.-4MustThe system shall model tasks as pending, running, and terminal states with completed, failed, aborted, orphaned, and faulted outcomes.
gherkin @FRFunctional Requirement.-4MustThe system shall model tasks as pending, running, and terminal states with completed, failed, aborted, orphaned, and faulted outcomes. Scenario: run a task to a terminal outcome Given a pending task with a checkpoint When the task is replaced by running and then terminal records Then the stored task carries the terminal outcome and no live checkpoint -
FRFunctional Requirement.-5MustThe system shall model document incarnations with session, conversation, and task scopes plus history and fork policies.
gherkin @FRFunctional Requirement.-5MustThe system shall model document incarnations with session, conversation, and task scopes plus history and fork policies. Scenario: declare scoped document incarnations Given the document record contracts When a conversation document declares rewindable history with as-of fork Then the type system accepts it and rejects session documents with conversation policies -
FRFunctional Requirement.-6MustThe system shall expose a `Storage` boundary with atomic commit, global ID minting, conversation lookup and scan, entry lookup with commit sequence, head-marker search, fork-aware entry scan, task lookup and filtered scan, input lookup by ID and request key, and close.
gherkin @FRFunctional Requirement.-6MustThe system shall expose a `Storage` boundary with atomic commit, global ID minting, conversation lookup and scan, entry lookup with commit sequence, head-marker search, fork-aware entry scan, task lookup and filtered scan, input lookup by ID and request key, and close. Scenario: exercise the full storage boundary Given an empty storage When conversations, entries, tasks, and inputs are committed and scanned Then every lookup, filtered scan, and cursor page returns the committed records -
FRFunctional Requirement.-7MustThe system shall provide `MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence.` as a detached in-memory `Storage` with atomic commits, sorted indexes, opaque cursors, fork-aware history scans, a single global ID namespace, and post-close rejection.
gherkin @FRFunctional Requirement.-7MustThe system shall provide `MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence.` as a detached in-memory `Storage` with atomic commits, sorted indexes, opaque cursors, fork-aware history scans, a single global ID namespace, and post-close rejection. Scenario: scan deep fork history newest-first Given a grandchild conversation forked through two ancestor caps When entries are scanned newest-first with cursors Then results cross each ancestor cap in order and excluded entries never appear -
FRFunctional Requirement.-8MustThe system shall reserve `ROOT_CONVERSATION_ID` (`1`) for the root conversation and start minted IDs at `2`.
gherkin @FRFunctional Requirement.-8MustThe system shall reserve `ROOT_CONVERSATION_ID` (`1`) for the root conversation and start minted IDs at `2`. Scenario: reserve the root conversation ID Given a fresh storage When the first ID is minted and the root conversation is committed Then the minted ID is 2 and recommitting ID 1 is rejected -
FRFunctional Requirement.-9MustThe system shall enforce omit and replace context-edit discriminators so omissions carry no messages and replacements always carry messages.
gherkin @FRFunctional Requirement.-9MustThe system shall enforce omit and replace context-edit discriminators so omissions carry no messages and replacements always carry messages. Scenario: enforce context-edit discriminators Given the context-edit contracts When a replacement without messages or an omission with messages is assigned Then the type system rejects the assignment -
FRFunctional Requirement.-10ShouldThe system shall expose pagination (`Page`, `Cursor`), scan filters (`EntryQuery`, `TaskQuery`), and the atomic `StorageWrite` table-mutation union.
gherkin @FRFunctional Requirement.-10ShouldThe system shall expose pagination (`Page`, `Cursor`), scan filters (`EntryQuery`, `TaskQuery`), and the atomic `StorageWrite` table-mutation union. Scenario: paginate scans with opaque cursors Given three conversations in storage When the first page of size two is read and continued with its cursor Then the second page returns only the remaining conversation -
NFRNon-Functional Requirement.-1MustCommits shall be atomic so a failed batch persists none of its writes.
gherkin @NFRNon-Functional Requirement.-1MustCommits shall be atomic so a failed batch persists none of its writes. Scenario: roll back a failed mixed-table batch Given committed entry, task, and input records When a batch containing a duplicate conversation ID is committed Then the commit rejects and none of the batch writes are visible -
NFRNon-Functional Requirement.-2MustStored and returned values shall be detached clones that never pollute object prototypes.
gherkin @NFRNon-Functional Requirement.-2MustStored and returned values shall be detached clones that never pollute object prototypes. Scenario: resist prototype pollution through stored JSON Given an entry carrying `__proto__` and `constructor` keys When the entry is committed and read back twice Then the reads keep `Object.prototype` and the stored values stay unchanged -
NFRNon-Functional Requirement.-3MustThe package shall use erasable TypeScript with top-level imports only and no `any`.
gherkin @NFRNon-Functional Requirement.-3MustThe package shall use erasable TypeScript with top-level imports only and no `any`. Scenario: hold the erasable TypeScript boundary Given the package sources and tests When the root check runs Then no strip-only violations are reported for the package -
NFRNon-Functional Requirement.-4ShouldIn-memory reads and scans shall use sorted-index seeks rather than full-table sorts.
gherkin @NFRNon-Functional Requirement.-4ShouldIn-memory reads and scans shall use sorted-index seeks rather than full-table sorts. Scenario: seek sorted indexes on scan Given entries committed out of ID order When entries are scanned newest-first Then results arrive in ID order without caller-side sorting
Conflicts
None identified yet.
Open Questions
- Which serialization-backed store becomes the first persistent
StoragebeyondMemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence.? - Should document deltas ship inside this package or stay in the ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. package with usage docs only?
- Does the SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries).-level commit path need a published conformance suite shared by every backend?
Specification: Durable runtimeThe Pico storage contract separating transcript scope (`conversation`), executable work (`task`), and persisted JSON (`document`) with monotonic commit sequences.
Overview
The package implements the PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. durable-record layer as pure TypeScript contracts plus one detached in-memory store.
Record types encode every lifecycle discriminator as a union so invalid states fail at compile time.
MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. keeps sorted per-table indexes and clones across the ownership boundary so it behaves like a serialization-backed store.
Architecture
types.ts owns all record contracts and the Storage interface.
memory-storage.ts implements Storage with Map tables and sorted ID arrays.
index.ts re-exports the public surface (MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence., ROOT_CONVERSATION_ID, and the record types).
The SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). (not yet in this package) serializes commits and supplies semantic validity.
flowchart LR
SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). --> Storage
Storage --> MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence.
types --> MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence.
types --> SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries).
Data Models
Conversation record
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | Id | PK, not null, globally unique | Transcript scope identity. |
| parent.conversationId | Id | FK when present | Fork source conversation. |
| parent.at | Id | Inclusive cap when present | Newest inherited parent entry. |
| owner.conversationId | Id | FK when present | Creator conversation for abort and idle waits. |
| owner.taskId | Id | FK when present | Creator task for abort and idle waits. |
Entry record
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | Id | PK, immutable, never reused | Global transcript event identity. |
| conversationId | Id | FK, not null | Owning conversation. |
| kind | string | Not null | Application-defined entry discriminator. |
| model | readonly Message[] | Optional | Messages contributed to model context. |
| data | JsonValue | Optional | Payload for views, plugins, or bookkeeping. |
| head | Id | Optional | First entry of the active context selected by this entry. |
| edits | readonly ContextEdit[] | Optional | Context-only overrides of earlier visible entries. |
| byTaskId | Id | Optional | Task that appended this entry. |
Task record
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | Id | PK, globally unique | Task identity. |
| conversationId | Id | FK, not null | Attaching conversation. |
| kind | string | Not null | Registered task definition name. |
| version | number | Not null | Definition version for input and checkpoint migration. |
| input | I | Retained while live or terminal | Original task input. |
| after | readonly Id[] | Possibly empty | Tasks that must be terminal before ordinary execution. |
| background | boolean | Not null | Exclusion from idle waits and conversation aborts. |
| abortRequested | boolean | Not null | Durable abort mark checked before run-mode progress commits. |
| state | TaskState | Pending, running, or terminal | Complete durable execution state. |
| memos | Record |
Live tasks only | Small first-writer-wins values. |
| outcome | TaskOutcome | Terminal tasks only | Completed, failed, aborted, orphaned, or faulted receipt. |
Document record
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | Id | PK, never reused across incarnations | Unique incarnation identity. |
| kind | string | Not null | Registered document kind. |
| key | string | Optional | Family member key, absent for singletons. |
| createdAt | Seq | Stamped by storage | Creating commit sequence. |
| retiredAt | Seq | Absent while current | Retiring commit sequence. |
| scope | session, conversation, or task | Not null | Ownership scope with conversation ID or task ID where applicable. |
| history | latest or rewindable | Conversation scope only | Retention policy for as-of reads. |
| fork | current, initial, or asOf | Conversation scope only | Initialization policy, constrained by history choice. |
Input record
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | Id | PK, globally unique | Admitted input identity. |
| conversationId | Id | FK, not null | Owning conversation. |
| requestId | string | Optional, conversation-scoped | Host deduplication key. |
| status | queued, placed, done, or unanswered | Not null | Lifecycle state. |
| entry | Id | Placed and done, optional for unanswered | Transcript entry created at placement. |
| answer | Id | Done only | Assistant answer entry. |
| reason | string | Unanswered only | Machine-readable terminal explanation. |
| detail | JsonValue | Optional | Structured diagnostics for unanswered inputs and stored errors. |
API Contracts
The package exposes a TypeScript interface, not an HTTP surface, so there is no api.yaml.
The Storage boundary is: commit, mintId, conversation, scanConversations, entry, findLatestHeadMarker, scanEntries, task, scanTasks, input, inputByRequest, and close.
Cursor pagination returns { items, next } where next is an opaque { after } continuation.
findLatestHeadMarker walks the conversation ancestry so forks resolve markers through each parent cap.
Sequences
Atomic mixed-table commit
sequenceDiagram
autonumber
participant S as SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries).
participant M as MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence.
participant T as Tables
S->>M: commit(writes)
M->>M: clone writes
M->>M: check immutable IDs
M->>T: apply conversation, entry, task, input writes
M->>M: advance nextId and nextSeq
M-->>S: seq
Fork-aware entry scan
sequenceDiagram
autonumber
participant S as SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries).
participant M as MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence.
participant T as Tables
S->>M: scanEntries(query, cursor, limit)
M->>T: newest-first walk of own entries
M->>T: descend to parent capped at parent.at
M-->>S: page with opaque cursor
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| ID space | One global number namespace starting at 2 with 1 reserved |
A single namespace lets one commit span tables without cross-table collisions. |
| Detachment | Deep clone on write and on read | Cloning matches serialization-backed ownership so conformance transfers to real backends. |
| Immutability enforcement | Reject duplicate conversation and entry IDs, reject cross-table ID reuse | The store guarantees IDs are never reused after a committed write. |
| Fork history | Ancestry walk capped at each parent.at |
Caps give deterministic newest-first visibility without copying parent entries. |
| Document scope split | SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries)., conversation-scoped, and task-scoped variants in one union | The union keeps history and fork policies exactly where they apply. |
Risks and Unknowns
- A future persistent backend may interpret cursor shape or commit ordering differently than
MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence.. - Prototype-safe cloning adds per-record cost that needs measuring once real workloads exist.
- Document deltas and sources currently live outside this package, so the full PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. commit path is not yet exercisable here.
Out of Scope
- The SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). mutation line and task scheduler.
- Persistent storage backends such as SQLite.
- ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. document delta mechanics, covered by the ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. package and
pico-v5-chord-usage.md. - HTTP endpoints and OpenAPI contracts.
Test Plan: Durable runtimeThe Pico storage contract separating transcript scope (`conversation`), executable work (`task`), and persisted JSON (`document`) with monotonic commit sequences.
Scope
This plan covers the PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. record contracts and MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. behavior in packages/durable.
It is derived from test/memory-storage.test.ts (twelve behavior cases) and test/types.test.ts (one compile-time discriminator case).
Out of scope are the SessionA persistent, branchable conversation log stored as JSONL (`SessionHeader`, messages, compaction summaries, branch summaries). mutation line, persistent backends, and ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. delta mechanics.
Unit Tests
| ID | Description | Input | Expected Output |
|---|---|---|---|
| TC-1 | Root conversation reserves ID 1 and rejects duplicates | Mint ID, commit root, recommit root | First mint is 2, duplicate commit throws already belongs to conversation. |
| TC-2 | Mixed-table batch commits atomically and rolls back on failure | Entry, task, and input writes followed by a batch with a duplicate conversation ID | First batch visible with its sequence, failed batch throws, none of its writes visible, next commit gets initialSeq + 1. |
| TC-3 | Retained writes and returned records are detached | Mutated caller objects and mutated read results | Stored entry data, task checkpoint, and input detail keep original values. |
| TC-4 | Prototype-like JSON keys clone without changing prototypes | Entry data with __proto__, constructor, and toString keys |
Reads keep Object.prototype, keep own __proto__ data, and global prototype stays clean. |
| TC-5 | Out-of-order entry IDs index in sorted order | Entries 30, 10, and marker 20 committed together | Newest-first scan returns 30, 20, 10 and latest head marker is 20. |
| TC-6 | Entry cursor continues below its last item after a newer commit | Two-page newest-first scan with an append between pages | Second page returns only the oldest entry with no further cursor. |
| TC-7 | Conversations paginate ascending by opaque cursor | Root plus two conversations committed out of order | First page returns root and second with { after } cursor, second page returns third. |
| TC-8 | Deep fork history scans newest-first through every ancestor cap | Three-level fork with caps, exclusions, and head markers | Pages return grandchild, child-cap, and root entries in order with correct markers and commit sequences. |
| TC-9 | Task records replace fully and page through filtered scans | Three tasks with memo, background, and abort flags advanced to terminal | Latest record returned, pending pages split 1 plus 1, terminal and background filters match. |
| TC-10 | Request IDs index per conversation and input records replace fully | Same key in two conversations plus placed transition | Per-conversation lookup resolves, replacement visible by ID and by key. |
| TC-11 | Global ID namespace rejects cross-table reuse and exhausted minting | Explicit ID 100, cross-table task write, MAX_SAFE_INTEGER entry |
Cross-table write throws already belongs to entry, minting past the safe integer throws. |
| TC-12 | Closed storage rejects every operation | Commit and reads after close |
Commit, lookup, and mint all throw closed. |
| TC-13 | Type discriminators accept valid shapes and reject invalid ones | Omit, replace, pending, terminal, document, outcome, and input shapes | Valid shapes compile, thirteen invalid shapes each raise @ts-expect-error. |
Integration Tests
| ID | Description | Preconditions | Expected Outcome |
|---|---|---|---|
| TC-14 | Entry commit sequence ties reads to their commit | Root conversation exists | Each entry read carries the sequence of its creating commit. |
| TC-15 | Head-marker search resolves through fork ancestry | Three-level fork with markers at each level | Current and historical markers resolve, pre-history cutoff returns undefined. |
| TC-16 | Unknown conversations fail fast on history reads | No conversation with the queried ID | Entry scan and head-marker search throw Unknown conversation. |
End-to-End Tests
| ID | Description | Steps | Expected Outcome |
|---|---|---|---|
| TC-17 | Package check passes for sources and tests | Run the root check | No erasable-TypeScript or import-hygiene violations for the package. |
Edge Cases and Failure Scenarios
| ID | Scenario | Expected Behavior |
|---|---|---|
| TC-18 | Same input written twice in one batch | Commit rejects the batch and persists nothing. |
| TC-19 | Task write reuses an entry ID | Commit throws already belongs to entry. |
| TC-20 | Cursor continuation after interleaved newer commit | Older page resumes strictly below the first page cursor. |
Test Infrastructure
- Vitest suite in
packages/durable/test/usingBACKGROUND_CONTEXTas the storage context. - Small local factories for pending tasks and entries keep each case isolated with a fresh
MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence.. - Type-level assertions use
expectTypeOfplus@ts-expect-errorfor the thirteen invalid shapes. - Targeted runs avoid the full suite:
node ../../node_modules/vitest/dist/cli.js --run test/memory-storage.test.ts.
Coverage Matrix
| Requirement | Test Cases |
|---|---|
| FRFunctional Requirement.-1MustThe system shall model conversations as immutable records with an ID, an optional fork parent edge, and an optional task owner edge. | TC-8, TC-15, TC-16 |
| FRFunctional Requirement.-2MustThe system shall model entries as immutable transcript events with kind, optional model messages, optional JSON data, optional head marker, optional context edits, and optional producing task ID. | TC-5, TC-6, TC-8, TC-14 |
| FRFunctional Requirement.-3MustThe system shall model host inputs through the queued, placed, done, and unanswered lifecycle states with conversation-scoped request ID deduplication. | TC-10, TC-18 |
| FRFunctional Requirement.-4MustThe system shall model tasks as pending, running, and terminal states with completed, failed, aborted, orphaned, and faulted outcomes. | TC-9 |
| FRFunctional Requirement.-5MustThe system shall model document incarnations with session, conversation, and task scopes plus history and fork policies. | TC-13 |
| FRFunctional Requirement.-6MustThe system shall expose a `Storage` boundary with atomic commit, global ID minting, conversation lookup and scan, entry lookup with commit sequence, head-marker search, fork-aware entry scan, task lookup and filtered scan, input lookup by ID and request key, and close. | TC-2, TC-7, TC-9, TC-10, TC-14, TC-15, TC-16 |
| FRFunctional Requirement.-7MustThe system shall provide `MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence.` as a detached in-memory `Storage` with atomic commits, sorted indexes, opaque cursors, fork-aware history scans, a single global ID namespace, and post-close rejection. | TC-1, TC-2, TC-3, TC-4, TC-5, TC-6, TC-7, TC-8, TC-11, TC-12, TC-20 |
| FRFunctional Requirement.-8MustThe system shall reserve `ROOT_CONVERSATION_ID` (`1`) for the root conversation and start minted IDs at `2`. | TC-1, TC-11 |
| FRFunctional Requirement.-9MustThe system shall enforce omit and replace context-edit discriminators so omissions carry no messages and replacements always carry messages. | TC-13 |
| FRFunctional Requirement.-10ShouldThe system shall expose pagination (`Page`, `Cursor`), scan filters (`EntryQuery`, `TaskQuery`), and the atomic `StorageWrite` table-mutation union. | TC-6, TC-7, TC-9 |
| NFRNon-Functional Requirement.-1MustCommits shall be atomic so a failed batch persists none of its writes. | TC-2 |
| NFRNon-Functional Requirement.-2MustStored and returned values shall be detached clones that never pollute object prototypes. | TC-3, TC-4 |
| NFRNon-Functional Requirement.-3MustThe package shall use erasable TypeScript with top-level imports only and no `any`. | TC-13, TC-17 |
| NFRNon-Functional Requirement.-4ShouldIn-memory reads and scans shall use sorted-index seeks rather than full-table sorts. | TC-5, TC-8 |
requirements
- Which serialization-backed store becomes the first persistent
StoragebeyondMemoryStorage? - Should document deltas ship inside this package or stay in the Chord package with usage docs only?
- Does the Session-level commit path need a published conformance suite shared by every backend?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| 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. |
| 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. |
| ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. | The facet/service composition runtime (packages/chord) where facets declare, provide, and consume singleton or keyed services with replicated state. |
| 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. |
| 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. |
| Durable runtimeThe Pico storage contract separating transcript scope (`conversation`), executable work (`task`), and persisted JSON (`document`) with monotonic commit sequences. | The PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. storage contract separating transcript scope (conversation), executable work (task), and persisted JSON (document) with monotonic commit sequences. |
| 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. |
| 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. |
| FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. | A setup unit with an id and setup(env) that declares service dependencies, provides implementations, and owns lifecycle callbacks in a ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. host. |
| 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. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| 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. |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. | The detached in-memory Storage implementation used for tests and reference PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. behavior with no persistence. |
| 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. |
| 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. |
| PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. | The durable agent harness (packages/durable, spec pico-v5.md) persisting conversations, tasks, and documents as atomic storage commits. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| PluginA versioned bundle identity (`FacetBundlePlugin` with `id`/`version`) packaging one or more Chord facets for distribution and loading. | A versioned bundle identity (FacetBundlePlugin with id/version) packaging one or more ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. facets for distribution and loading. |
| 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. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| 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. |
| Replicated stateAn immutable-value shared state handle (`ReplicatedState`/`MutableReplicatedState`) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. | An immutable-value shared state handle (ReplicatedState/MutableReplicatedState) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Service (singleton/keyed) | A stable typed contract identity (Service<T> with string id) provided once (singleton) or per-key (keyed) and consumed via use/observe. |
| 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). |
| 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. |
| 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. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| 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. |
Technical Terms
| Term | Definition |
|---|---|
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. |
| 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). |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
ByteTransportFactory |
A factory creating a fresh connected, authenticated ByteTransport from ByteTransportHandlers, with exactly one terminal handler expected. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| 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. |
| 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. |
| 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. |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
| 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). |
| 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. |
| Span/Event taxonomyThe versioned telemetry schema (`defineTelemetrySchema`) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. | The versioned telemetry schema (defineTelemetrySchema) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
| 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. |
TelemetryContext / TelemetrySpan |
The span-creation scope (TelemetryContext.startSpan) and its active unit (TelemetrySpan, itself a context) carrying attributes, events, and status. |
| 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. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| 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. |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| FRFunctional Requirement. | Functional Requirement. |
| 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). |
| 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). |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| 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). |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| 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). |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
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: ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. Composition Runtime
Overview
ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. is a standalone application-composition runtime that assembles applications from plugins whose facets declare, provide, and consume typed services with replicated state across local and remote boundaries.
Stakeholders
| Stakeholder | Interest |
|---|---|
| Application host authors | Compose worker, TUITerminal User Interface (the interactive mode, and the `pi-tui` library)., and WebUI environments from one plugin model |
| PluginA versioned bundle identity (`FacetBundlePlugin` with `id`/`version`) packaging one or more Chord facets for distribution and loading. authors | Declare services once and run each facet where it belongs |
| Agent developers | Build extensions without owning transport, framing, or lifecycle |
Functional Requirements
| ID | Priority | Requirement |
|---|---|---|
| FRFunctional Requirement.-1MustThe system shall let plugins declare facets as synchronous setup units that state the services they provide and require. | Must | The system shall let plugins declare facets as synchronous setup units that state the services they provide and require. |
| FRFunctional Requirement.-2MustThe system shall validate the complete facet dependency graph and reject missing dependencies, duplicate providers, cycles, and asynchronous setup. | Must | The system shall validate the complete facet dependency graph and reject missing dependencies, duplicate providers, cycles, and asynchronous setup. |
| FRFunctional Requirement.-3MustThe system shall support singleton services with one provider behind a stable consumer-facing handle. | Must | The system shall support singleton services with one provider behind a stable consumer-facing handle. |
| FRFunctional Requirement.-4MustThe system shall support keyed services with dynamically spawned and retired per-key instances. | Must | The system shall support keyed services with dynamically spawned and retired per-key instances. |
| FRFunctional Requirement.-5MustThe system shall bind services after graph validation, activate providers before consumers, and dispose resources in reverse dependency order. | Must | The system shall bind services after graph validation, activate providers before consumers, and dispose resources in reverse dependency order. |
| FRFunctional Requirement.-6MustThe system shall provide replicated state where producers mutate a tracked state proxy and publish while consumers receive immutable values. | Must | The system shall provide replicated state where producers mutate a tracked state proxy and publish while consumers receive immutable values. |
| FRFunctional Requirement.-7MustThe system shall expose remotely consumable services through an application-supplied transport carrying strict-JSON calls and subscriptions. | Must | The system shall expose remotely consumable services through an application-supplied transport carrying strict-JSON calls and subscriptions. |
| FRFunctional Requirement.-8MustThe system shall track and coalesce JSON delta operations with durable base batches and validation of untrusted operations at apply time. | Must | The system shall track and coalesce JSON delta operations with durable base batches and validation of untrusted operations at apply time. |
| FRFunctional Requirement.-9ShouldThe system shall reload facets by replacing singletons without an unavailable interval and giving keyed replacements fresh generations. | Should | The system shall reload facets by replacing singletons without an unavailable interval and giving keyed replacements fresh generations. |
| FRFunctional Requirement.-10ShouldThe system shall bundle facet entries into content-addressed artifacts and load them with integrity verification in an isolated module scope. | Should | The system shall bundle facet entries into content-addressed artifacts and load them with integrity verification in an isolated module scope. |
| FRFunctional Requirement.-11ShouldThe system shall provide a Go-like context carrying cancellation and invocation-scoped values through operations. | Should | The system shall provide a Go-like context carrying cancellation and invocation-scoped values through operations. |
Non-Functional Requirements
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFRNon-Functional Requirement.-1MustThe system shall ship as a standalone package with no dependency on any other PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. workspace package. | Must | Portability | The system shall ship as a standalone package with no dependency on any other PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. workspace package. |
| NFRNon-Functional Requirement.-2MustThe system shall validate every value crossing the remote boundary as strict JSON. | Must | Security | The system shall validate every value crossing the remote boundary as strict JSON. |
| NFRNon-Functional Requirement.-3ShouldThe system shall mark replicas unready on disconnect or replacement and rehydrate them before delivering new updates. | Should | Reliability | The system shall mark replicas unready on disconnect or replacement and rehydrate them before delivering new updates. |
| NFRNon-Functional Requirement.-4ShouldThe system shall flush at most one decoded operation batch per publication with independent path-codec state per client stream. | Should | Performance | The system shall flush at most one decoded operation batch per publication with independent path-codec state per client stream. |
Constraints
- ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. prescribes no transport, framing, routing, or application wire envelope.
- Process-local services accept unrestricted contracts and are never published remotely.
- ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. never installs dependencies and never runs package lifecycle scripts.
Acceptance Criteria
-
FRFunctional Requirement.-1MustThe system shall let plugins declare facets as synchronous setup units that state the services they provide and require.
gherkin @FRFunctional Requirement.-1MustThe system shall let plugins declare facets as synchronous setup units that state the services they provide and require. Scenario: facet declares its service shape synchronously Given a plugin facet with a synchronous setup function When the host collects provided and required services Then the facet shape is registered without running any async work -
FRFunctional Requirement.-2MustThe system shall validate the complete facet dependency graph and reject missing dependencies, duplicate providers, cycles, and asynchronous setup.
gherkin @FRFunctional Requirement.-2MustThe system shall validate the complete facet dependency graph and reject missing dependencies, duplicate providers, cycles, and asynchronous setup. Scenario: invalid dependency graphs are rejected before binding Given facets with a missing dependency, a duplicate provider, or a dependency cycle When the host validates the complete dependency graph Then activation fails with a descriptive error and no service is bound -
FRFunctional Requirement.-3MustThe system shall support singleton services with one provider behind a stable consumer-facing handle.
gherkin @FRFunctional Requirement.-3MustThe system shall support singleton services with one provider behind a stable consumer-facing handle. Scenario: singleton facade survives provider replacement Given a consumer holding a singleton service handle When the providing facet is replaced by a reload Then the same handle routes to the new provider without an unavailable interval -
FRFunctional Requirement.-4MustThe system shall support keyed services with dynamically spawned and retired per-key instances.
gherkin @FRFunctional Requirement.-4MustThe system shall support keyed services with dynamically spawned and retired per-key instances. Scenario: keyed instances spawn and retire independently Given a keyed service owned by one facet When the owner spawns two keys and retires one Then observers see both instances appear and only the retired key close -
FRFunctional Requirement.-5MustThe system shall bind services after graph validation, activate providers before consumers, and dispose resources in reverse dependency order.
gherkin @FRFunctional Requirement.-5MustThe system shall bind services after graph validation, activate providers before consumers, and dispose resources in reverse dependency order. Scenario: activation and disposal follow dependency order Given a provider facet and a consumer facet depending on it When the host activates the graph and later disposes it Then the provider activates first and disposes last -
FRFunctional Requirement.-6MustThe system shall provide replicated state where producers mutate a tracked state proxy and publish while consumers receive immutable values.
gherkin @FRFunctional Requirement.-6MustThe system shall provide replicated state where producers mutate a tracked state proxy and publish while consumers receive immutable values. Scenario: published mutations reach subscribers as immutable values Given a producer mutating its tracked state proxy When the producer publishes with a context Then each subscriber receives a complete immutable value reflecting the mutation -
FRFunctional Requirement.-7MustThe system shall expose remotely consumable services through an application-supplied transport carrying strict-JSON calls and subscriptions.
gherkin @FRFunctional Requirement.-7MustThe system shall expose remotely consumable services through an application-supplied transport carrying strict-JSON calls and subscriptions. Scenario: remote calls cross an application-supplied transport Given a remotely exposable service and a transport adapter When a consumer invokes a method and subscribes to its state Then arguments, results, snapshots, and updates all cross as strict JSON -
FRFunctional Requirement.-8MustThe system shall track and coalesce JSON delta operations with durable base batches and validation of untrusted operations at apply time.
gherkin @FRFunctional Requirement.-8MustThe system shall track and coalesce JSON delta operations with durable base batches and validation of untrusted operations at apply time. Scenario: deltas coalesce and untrusted ops are validated Given tracked plain JSON with buffered mutations When the tracker flushes and a replica applies the batch Then the replica converges and malformed operations are rejected -
FRFunctional Requirement.-9ShouldThe system shall reload facets by replacing singletons without an unavailable interval and giving keyed replacements fresh generations.
gherkin @FRFunctional Requirement.-9ShouldThe system shall reload facets by replacing singletons without an unavailable interval and giving keyed replacements fresh generations. Scenario: reload keeps singleton consumers connected Given active singleton consumers on a running host When a candidate generation activates and cuts over successfully Then singleton handles stay connected while keyed replacements get fresh generations -
FRFunctional Requirement.-10ShouldThe system shall bundle facet entries into content-addressed artifacts and load them with integrity verification in an isolated module scope.
gherkin @FRFunctional Requirement.-10ShouldThe system shall bundle facet entries into content-addressed artifacts and load them with integrity verification in an isolated module scope. Scenario: bundles verify integrity and isolate module scope Given a content-addressed facet bundle built from a plugin package When the loader verifies SHA-256 and compiles the entry in a VM scope Then corrupt entries fail to load and host externals resolve through the restricted require -
FRFunctional Requirement.-11ShouldThe system shall provide a Go-like context carrying cancellation and invocation-scoped values through operations.
gherkin @FRFunctional Requirement.-11ShouldThe system shall provide a Go-like context carrying cancellation and invocation-scoped values through operations. Scenario: cancellation and values flow through context Given an operation invoked with a cancellable context carrying a typed value When the caller cancels or the callee reads the value Then waiting stops promptly and the callee observes the carried value -
NFRNon-Functional Requirement.-1MustThe system shall ship as a standalone package with no dependency on any other PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. workspace package.
gherkin @NFRNon-Functional Requirement.-1MustThe system shall ship as a standalone package with no dependency on any other PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. workspace package. Scenario: package boundary stays PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries.-free Given the ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. package source tree When the boundary test scans its imports and file references Then no import reaches any other PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. workspace package or file outside ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. -
NFRNon-Functional Requirement.-2MustThe system shall validate every value crossing the remote boundary as strict JSON.
gherkin @NFRNon-Functional Requirement.-2MustThe system shall validate every value crossing the remote boundary as strict JSON. Scenario: non-JSON values are rejected at the boundary Given a candidate value containing undefined, functions, or other non-JSON data When the adapter validates it with the strict-JSON check Then the value is rejected without normalization -
NFRNon-Functional Requirement.-3ShouldThe system shall mark replicas unready on disconnect or replacement and rehydrate them before delivering new updates.
gherkin @NFRNon-Functional Requirement.-3ShouldThe system shall mark replicas unready on disconnect or replacement and rehydrate them before delivering new updates. Scenario: disconnected replicas rehydrate before updating Given a subscribed replica whose provider disconnects When the provider returns and publishes new state Then the replica reports unready until rehydration completes -
NFRNon-Functional Requirement.-4ShouldThe system shall flush at most one decoded operation batch per publication with independent path-codec state per client stream.
gherkin @NFRNon-Functional Requirement.-4ShouldThe system shall flush at most one decoded operation batch per publication with independent path-codec state per client stream. Scenario: one publication produces one batch per stream Given a producer with two remote client streams on one state When the producer publishes once Then each stream encodes exactly one operation batch with its own path-codec state
Conflicts
None identified yet.
Open Questions
- Should symmetric RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). peers ship as the first optional transport over the remote-service boundary?
- Should the reserved
$chord.*service namespace become a permanent part of the ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. contract? - Do delta batches need a canonical or minimal form beyond the current convergence guarantee?
Specification: ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. Composition Runtime
Overview
The runtime models each plugin as synchronous facet setup functions over a typed service graph, resolves that graph in a facet kernel, and replicates tracked JSON state to local and remote subscribers through a transport-independent wire grammar.
Architecture
flowchart LR
subgraph PluginA versioned bundle identity (`FacetBundlePlugin` with `id`/`version`) packaging one or more Chord facets for distribution and loading.
F1[FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. A]
F2[FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. B]
end
K[FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. Kernel]
P[Service ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`).]
S[Replicated State]
T[Remote Transport Adapter]
F1 --> K
F2 --> K
K --> P
P --> S
P --> T
T --> C[Remote Consumer]
Data Models
FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host.
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | string | PK, not null | Stable identity of one facet within its plugin |
| setup | function | synchronous, not null | Declares provided and required services against the facet environment |
Service
| Field | Type | Constraints | Description |
|---|---|---|---|
| id | string | PK, not null, $chord.* reserved |
Stable identity of one shared TypeScript service contract |
| local | boolean | not null, defaults false | Marks process-local contracts that are never published remotely |
| mode | singleton or keyed | not null | Selects one provider versus dynamic per-key instances |
Replicated State
| Field | Type | Constraints | Description |
|---|---|---|---|
| state | tracked proxy | producer-only writes | Mutable view that records operations for the next publication |
| value | immutable snapshot or undefined | consumer-visible | Latest hydrated value, never mutated by later updates |
| sequence | number | monotonically increasing | Orders hydrate and update deliveries per subscription |
Service Subscription Snapshot
| Field | Type | Constraints | Description |
|---|---|---|---|
| serviceId | string | not null | Service the snapshot describes |
| mode | singleton or keyed | not null | ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`). mode the snapshot was taken under |
| instances | array | possibly empty | Per-instance member lists with state sequences and operation batches |
API Contracts
There is no HTTP surface in this feature, so no api.yaml exists. The normative contract is the TypeScript surface exported from the package root plus the context, delta, bundler, and node entry points.
Sequences
PluginA versioned bundle identity (`FacetBundlePlugin` with `id`/`version`) packaging one or more Chord facets for distribution and loading. bind, activate, publish, replicate
sequenceDiagram
autonumber
participant F as FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host.
participant K as FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. Kernel
participant P as Service ProviderThe runtime unit owning a model catalog, auth, and stream behavior (e.g. `anthropic`, `openai`).
participant C as Consumer
F->>K: setup(env): provide and require services
K->>K: validate dependency graph
K->>P: bind stable service handles
K->>F: onActivate in dependency order
F->>P: mutate tracked state and publish(context)
P->>P: flush one operation batch
P->>C: hydrate or update with immutable value
C-->>P: subscribe for further updates
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Package coupling | Standalone with zero PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. workspace dependencies | Lets unrelated applications adopt ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. without the PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. monorepo |
| Remote boundary | Transport-independent wire grammar with application-supplied adapters | Keeps ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. neutral on framing, routing, and envelopes while enforcing strict JSON |
| State replication | Proxy-tracked mutations with per-stream path codecs | Preserves string and array operations compactly while isolating client streams |
| Module loading | esbuild bundles verified by SHA-256 and compiled with node VM | Gives content-addressed reloadable generations without Node module-cache coupling |
| Context model | Explicit Go-like context parameter on operations | Carries cancellation and scoped values without ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. depending on either domain |
Risks and Unknowns
- Symmetric RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). peers are planned but not yet specified as the reference transport implementation.
- Delta batches guarantee convergence without a canonical or minimal form, which may surprise bandwidth-sensitive adapters.
- The
$chord.*service-ID reservation is enforced in code but its long-term namespace policy is undecided.
Out of Scope
- Any concrete transport, framing, routing, or envelope implementation.
- A canonical or minimal delta-batch encoding.
- PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries.-specific hosts, TUIs, or agent workers built on top of ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state..
Test Plan: ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. Composition Runtime
Scope
The suite exercises facets, singleton and keyed services, replicated state, delta tracking, the remote-service wire boundary, facet bundling and loading, context, and strict-JSON validation. The suite covers ten test files under packages/chord/test with roughly one hundred eighty individual cases. Out of scope is any real transport, PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. host integration, and performance benchmarking beyond the delta traversal bench.
Unit Tests
| ID | Description | Input | Expected Output |
|---|---|---|---|
| TC-1 | Delta tracker records intent, root ops, and whole-value replacement | Mutations through the tracked proxy | Correct operation batches with a complete base batch on first flush |
| TC-2 | Immutable apply preserves prior revisions while converging replicas | Operation batches against a base value | New revision equals the producer value and old revisions are unchanged |
| TC-3 | Unsafe array indices, malformed ops, and boundary escapes are rejected | Hostile or out-of-range operations | Descriptive apply-time errors with no state corruption |
| TC-4 | Path codec interns paths and property tests round-trip random ops | Random tracked mutations | Decoded batches match the producer value on every seed |
| TC-5 | Delta payloads are deeply detached from producer mutations | Shared and subsequently edited producer objects | Published snapshots stay independent of later producer edits |
| TC-6 | Tracker proxies survive garbage-collection pressure | Mutations interleaved with GC jobs | No lost operations and no retained-worker failures |
| TC-7 | Singleton provide, consume, replacement, and disposal behave stably | FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. graphs with singleton providers | Stable facades, buffered-update replay, and facade cleanup on disposal |
| TC-8 | Replicated stateAn immutable-value shared state handle (`ReplicatedState`/`MutableReplicatedState`) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. hydrates, buffers racing updates, and survives rebinds | Publish, rebind, and hydration races | Cold replicas hydrate and buffered updates arrive exactly once |
| TC-16 | Context layers values and propagates cancellation | Nested contexts with typed keys | Child isolation, parent inheritance, and prompt cancellation |
| TC-17 | Strict-JSON validation accepts JSON and rejects the rest | JSON and non-JSON candidate values | True for strict JSON and false otherwise with no normalization |
Integration Tests
| ID | Description | Preconditions | Expected Outcome |
|---|---|---|---|
| TC-9 | FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. host validates graphs and orders activation and disposal | Facets with chained dependencies | Providers activate before consumers and dispose in reverse order |
| TC-10 | Keyed services route through the host and stay local when marked local | Keyed providers with local and remote contracts | Remote keyed calls route through the provider while local ones never leave the process |
| TC-11 | Loaders combine in order and dispose generations in reverse | Multiple facet loaders with resources | Load-order facets and reverse-order disposal with cleanup on partial failure |
| TC-12 | Wire grammar encodes control calls and isolates per-state codecs | Service catalogue, subscribe, and unsubscribe calls | Valid calls encode and malformed snapshots or updates are rejected |
| TC-13 | Remote endpoints publish provider subscriptions and clean them up | A provider endpoint with active subscriptions | Updates publish while subscribed and resources release on close |
End-to-End Tests
| ID | Description | Steps | Expected Outcome |
|---|---|---|---|
| TC-14 | Package boundary keeps ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. free of PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. dependencies | Run the boundary test over the ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. tree | No import reaches another PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. workspace package or outside file |
| TC-15 | FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. bundles build, load, reload, and reject corrupt entries | Bundle a plugin package then load its entries | Fresh reloadable generations load and corrupt entries fail fast |
Edge Cases and Failure Scenarios
| ID | Scenario | Expected Behavior |
|---|---|---|
| TC-9E | Missing dependencies, cycles, duplicate providers, async setup | Host rejects the graph before binding any service |
| TC-11E | Replacement activation or post-cutover cleanup fails | Old generation stays active or the host terminates deterministically |
| TC-15E | Invalid entry configuration or corrupt bundle bytes | Build or load fails without touching the previous output generation |
Test Infrastructure
- Vitest runs the suite from packages/chord with a loopback service transport helper.
- A dedicated retention worker exercises tracker proxies under garbage-collection pressure.
- Content-addressed bundle fixtures are built with esbuild and loaded through the Node VM loader.
Coverage Matrix
| Requirement | Test Cases |
|---|---|
| FRFunctional Requirement.-1MustThe system shall let plugins declare facets as synchronous setup units that state the services they provide and require. | TC-9, TC-11 |
| FRFunctional Requirement.-2MustThe system shall validate the complete facet dependency graph and reject missing dependencies, duplicate providers, cycles, and asynchronous setup. | TC-9, TC-9E |
| FRFunctional Requirement.-3MustThe system shall support singleton services with one provider behind a stable consumer-facing handle. | TC-7 |
| FRFunctional Requirement.-4MustThe system shall support keyed services with dynamically spawned and retired per-key instances. | TC-10 |
| FRFunctional Requirement.-5MustThe system shall bind services after graph validation, activate providers before consumers, and dispose resources in reverse dependency order. | TC-9 |
| FRFunctional Requirement.-6MustThe system shall provide replicated state where producers mutate a tracked state proxy and publish while consumers receive immutable values. | TC-7, TC-8 |
| FRFunctional Requirement.-7MustThe system shall expose remotely consumable services through an application-supplied transport carrying strict-JSON calls and subscriptions. | TC-12, TC-13 |
| FRFunctional Requirement.-8MustThe system shall track and coalesce JSON delta operations with durable base batches and validation of untrusted operations at apply time. | TC-1, TC-2, TC-3, TC-4, TC-5, TC-6 |
| FRFunctional Requirement.-9ShouldThe system shall reload facets by replacing singletons without an unavailable interval and giving keyed replacements fresh generations. | TC-11, TC-11E |
| FRFunctional Requirement.-10ShouldThe system shall bundle facet entries into content-addressed artifacts and load them with integrity verification in an isolated module scope. | TC-15, TC-15E |
| FRFunctional Requirement.-11ShouldThe system shall provide a Go-like context carrying cancellation and invocation-scoped values through operations. | TC-16 |
| NFRNon-Functional Requirement.-1MustThe system shall ship as a standalone package with no dependency on any other PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. workspace package. | TC-14 |
| NFRNon-Functional Requirement.-2MustThe system shall validate every value crossing the remote boundary as strict JSON. | TC-12, TC-17 |
| NFRNon-Functional Requirement.-3ShouldThe system shall mark replicas unready on disconnect or replacement and rehydrate them before delivering new updates. | TC-8 |
| NFRNon-Functional Requirement.-4ShouldThe system shall flush at most one decoded operation batch per publication with independent path-codec state per client stream. | TC-4, TC-8 |
requirements
- Should symmetric RPC peers ship as the first optional transport over the remote-service boundary?
- Should the reserved
$chord.*service namespace become a permanent part of the Chord contract? - Do delta batches need a canonical or minimal form beyond the current convergence guarantee?
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| 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. |
| 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. |
| ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. | The facet/service composition runtime (packages/chord) where facets declare, provide, and consume singleton or keyed services with replicated state. |
| 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. |
| 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. |
| Durable runtimeThe Pico storage contract separating transcript scope (`conversation`), executable work (`task`), and persisted JSON (`document`) with monotonic commit sequences. | The PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. storage contract separating transcript scope (conversation), executable work (task), and persisted JSON (document) with monotonic commit sequences. |
| 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. |
| 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. |
| FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. | A setup unit with an id and setup(env) that declares service dependencies, provides implementations, and owns lifecycle callbacks in a ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. host. |
| 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. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| 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. |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. | The detached in-memory Storage implementation used for tests and reference PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. behavior with no persistence. |
| 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. |
| 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. |
| PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. | The durable agent harness (packages/durable, spec pico-v5.md) persisting conversations, tasks, and documents as atomic storage commits. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| PluginA versioned bundle identity (`FacetBundlePlugin` with `id`/`version`) packaging one or more Chord facets for distribution and loading. | A versioned bundle identity (FacetBundlePlugin with id/version) packaging one or more ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. facets for distribution and loading. |
| 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. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| 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. |
| Replicated stateAn immutable-value shared state handle (`ReplicatedState`/`MutableReplicatedState`) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. | An immutable-value shared state handle (ReplicatedState/MutableReplicatedState) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Service (singleton/keyed) | A stable typed contract identity (Service<T> with string id) provided once (singleton) or per-key (keyed) and consumed via use/observe. |
| 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). |
| 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. |
| 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. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| 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. |
Technical Terms
| Term | Definition |
|---|---|
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. |
| 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). |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
ByteTransportFactory |
A factory creating a fresh connected, authenticated ByteTransport from ByteTransportHandlers, with exactly one terminal handler expected. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| 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. |
| 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. |
| 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. |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
| 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). |
| 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. |
| Span/Event taxonomyThe versioned telemetry schema (`defineTelemetrySchema`) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. | The versioned telemetry schema (defineTelemetrySchema) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
| 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. |
TelemetryContext / TelemetrySpan |
The span-creation scope (TelemetryContext.startSpan) and its active unit (TelemetrySpan, itself a context) carrying attributes, events, and status. |
| 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. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| 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. |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| FRFunctional Requirement. | Functional Requirement. |
| 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). |
| 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). |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| 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). |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| 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). |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |
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: Telemetry Contracts
Overview
The pi-telemetry package defines vendor-neutral telemetry contracts for explicit callback-managed spans. TelemetryContext exposes startSpan for creating child spans around callbacks. TelemetrySpan exposes addEvent and setAttributes and setStatus for recording diagnostics. Typed schema utilities define serializable span and event and attribute vocabularies with inferred TypeScript types. Noop and in-memory backends provide passive defaults and process-local reference recording without exporters.
Stakeholders
| Stakeholder | Interest |
|---|---|
| PiThe project: a minimal, self-extensible terminal coding agent harness and its libraries. package maintainer | Needs explicit telemetry propagation without global state. |
| Backend adapter author | Needs a stable contract with observable conformance semantics. |
| Application operator | Needs diagnostic spans without business behavior changes. |
Functional Requirements
| ID | Priority | Requirement |
|---|---|---|
| FRFunctional Requirement.-1MustThe system shall provide TelemetryContext with startSpan that admits the callback synchronously exactly once. | Must | The system shall provide TelemetryContext with startSpan that admits the callback synchronously exactly once. |
| FRFunctional Requirement.-2MustThe system shall provide TelemetrySpan with setAttributes that merges attribute bags with last-defined-wins semantics. | Must | The system shall provide TelemetrySpan with setAttributes that merges attribute bags with last-defined-wins semantics. |
| FRFunctional Requirement.-3MustThe system shall provide TelemetrySpan with addEvent that records ordered named events with attributes. | Must | The system shall provide TelemetrySpan with addEvent that records ordered named events with attributes. |
| FRFunctional Requirement.-4MustThe system shall provide TelemetrySpan with setStatus that records explicit ok or error outcomes with last-write-wins semantics. | Must | The system shall provide TelemetrySpan with setStatus that records explicit ok or error outcomes with last-write-wins semantics. |
| FRFunctional Requirement.-5MustThe system shall treat normal completion as ok and throws or rejections as errors unless an explicit status was set. | Must | The system shall treat normal completion as ok and throws or rejections as errors unless an explicit status was set. |
| FRFunctional Requirement.-6MustThe system shall provide a shared noop context that invokes callbacks without inspecting or retaining payloads. | Must | The system shall provide a shared noop context that invokes callbacks without inspecting or retaining payloads. |
| FRFunctional Requirement.-7MustThe system shall provide an in-memory context that records detached span snapshots in span-start order. | Must | The system shall provide an in-memory context that records detached span snapshots in span-start order. |
| FRFunctional Requirement.-8MustThe system shall provide serializable schema helpers that infer exact start and end and event attribute types. | Must | The system shall provide serializable schema helpers that infer exact start and end and event attribute types. |
| FRFunctional Requirement.-9MustThe system shall provide a typed span starter that binds a parent context to one or more schema vocabularies. | Must | The system shall provide a typed span starter that binds a parent context to one or more schema vocabularies. |
Non-Functional Requirements
| ID | Priority | Category | Requirement |
|---|---|---|---|
| NFRNon-Functional Requirement.-1MustThe system shall keep recording methods synchronous and passive and non-throwing. | Must | Reliability | The system shall keep recording methods synchronous and passive and non-throwing. |
| NFRNon-Functional Requirement.-2MustThe system shall remain runtime-neutral without ambient context APIs. | Must | Portability | The system shall remain runtime-neutral without ambient context APIs. |
| NFRNon-Functional Requirement.-3ShouldThe system shall keep noop overhead negligible by sharing one frozen inert span. | Should | Performance | The system shall keep noop overhead negligible by sharing one frozen inert span. |
| NFRNon-Functional Requirement.-4MustThe system shall restrict attribute values to primitive scalars and arrays and avoid sensitive payloads by default. | Must | Security | The system shall restrict attribute values to primitive scalars and arrays and avoid sensitive payloads by default. |
Constraints
- The package shall not include exporters or global current-span state.
- Backend-specific identifiers and buffering and flushing belong to adapters.
- Schema objects shall remain JSON-serializable data without runtime validation.
Acceptance Criteria
Every FRFunctional Requirement. and NFRNon-Functional Requirement. has at least one executable criterion below.
-
FRFunctional Requirement.-1MustThe system shall provide TelemetryContext with startSpan that admits the callback synchronously exactly once.
gherkin @FRFunctional Requirement.-1MustThe system shall provide TelemetryContext with startSpan that admits the callback synchronously exactly once. Scenario: synchronous single admission preserves result Given a fresh TelemetryContext When startSpan is called with a callback returning a value Then the callback runs synchronously exactly once and the returned promise resolves with the same value -
FRFunctional Requirement.-2MustThe system shall provide TelemetrySpan with setAttributes that merges attribute bags with last-defined-wins semantics.
gherkin @FRFunctional Requirement.-2MustThe system shall provide TelemetrySpan with setAttributes that merges attribute bags with last-defined-wins semantics. Scenario: attribute merging keeps last defined values Given a span with start attributes When setAttributes is called repeatedly with overlapping keys Then later defined values replace earlier values and undefined entries are ignored -
FRFunctional Requirement.-3MustThe system shall provide TelemetrySpan with addEvent that records ordered named events with attributes.
gherkin @FRFunctional Requirement.-3MustThe system shall provide TelemetrySpan with addEvent that records ordered named events with attributes. Scenario: events are recorded in call order Given an open span When addEvent is called twice with distinct names Then the snapshot contains both events in call order with their attributes -
FRFunctional Requirement.-4MustThe system shall provide TelemetrySpan with setStatus that records explicit ok or error outcomes with last-write-wins semantics.
gherkin @FRFunctional Requirement.-4MustThe system shall provide TelemetrySpan with setStatus that records explicit ok or error outcomes with last-write-wins semantics. Scenario: explicit status uses last-write-wins semantics Given an open span When setStatus is called with error and then with ok Then the final snapshot status is ok -
FRFunctional Requirement.-5MustThe system shall treat normal completion as ok and throws or rejections as errors unless an explicit status was set.
gherkin @FRFunctional Requirement.-5MustThe system shall treat normal completion as ok and throws or rejections as errors unless an explicit status was set. Scenario: throw without explicit status becomes error Given a fresh TelemetryContext When the callback throws synchronously without setting a status Then the returned promise rejects with the identical value and the span status is error -
FRFunctional Requirement.-6MustThe system shall provide a shared noop context that invokes callbacks without inspecting or retaining payloads.
gherkin @FRFunctional Requirement.-6MustThe system shall provide a shared noop context that invokes callbacks without inspecting or retaining payloads. Scenario: noop context stays inert Given the shared noop context When startSpan runs nested callbacks that record attributes and events Then values pass through unchanged and nested spans reuse the same frozen span -
FRFunctional Requirement.-7MustThe system shall provide an in-memory context that records detached span snapshots in span-start order.
gherkin @FRFunctional Requirement.-7MustThe system shall provide an in-memory context that records detached span snapshots in span-start order. Scenario: in-memory context returns detached snapshots Given an in-memory context with one completed span When getSpans is called and the caller mutates the result Then a subsequent getSpans call still returns the original recorded values -
FRFunctional Requirement.-8MustThe system shall provide serializable schema helpers that infer exact start and end and event attribute types.
gherkin @FRFunctional Requirement.-8MustThe system shall provide serializable schema helpers that infer exact start and end and event attribute types. Scenario: schema helper infers exact attribute types Given a serializable schema with closed string sets When TypeScript infers start attributes for a declared span Then missing required attributes and unknown keys and out-of-set values are rejected at compile time -
FRFunctional Requirement.-9MustThe system shall provide a typed span starter that binds a parent context to one or more schema vocabularies.
gherkin @FRFunctional Requirement.-9MustThe system shall provide a typed span starter that binds a parent context to one or more schema vocabularies. Scenario: typed starter binds children to their parent span Given a typed starter over operation and request schemas When an operation span starts a nested request span Then the recorded child carries the parent identifier and the callback result passes through -
NFRNon-Functional Requirement.-1MustThe system shall keep recording methods synchronous and passive and non-throwing.
gherkin @NFRNon-Functional Requirement.-1MustThe system shall keep recording methods synchronous and passive and non-throwing. Scenario: unreadable payloads never break business work Given recording payloads that throw on property reads When setAttributes and addEvent and setStatus are called with those payloads Then no call throws and the business callback still executes exactly once -
NFRNon-Functional Requirement.-2MustThe system shall remain runtime-neutral without ambient context APIs.
gherkin @NFRNon-Functional Requirement.-2MustThe system shall remain runtime-neutral without ambient context APIs. Scenario: explicit propagation works without ambient state Given parent spans passed only as context arguments When nested and concurrent children run Then parentage is recorded correctly without ambient context APIs -
NFRNon-Functional Requirement.-3ShouldThe system shall keep noop overhead negligible by sharing one frozen inert span.
gherkin @NFRNon-Functional Requirement.-3ShouldThe system shall keep noop overhead negligible by sharing one frozen inert span. Scenario: noop path performs no recording work Given the shared noop context When many spans run with attributes and events Then callbacks execute directly against one shared frozen span -
NFRNon-Functional Requirement.-4MustThe system shall restrict attribute values to primitive scalars and arrays and avoid sensitive payloads by default.
gherkin @NFRNon-Functional Requirement.-4MustThe system shall restrict attribute values to primitive scalars and arrays and avoid sensitive payloads by default. Scenario: attribute surface stays primitive-only Given the AttributeValue type definition When a schema declares attribute types Then only scalar and array primitives are expressible without payload fields
Conflicts
No conflicts were identified.
Open Questions
No open questions remain because the contracts are implemented and documented.
Specification: Telemetry Contracts
Overview
The package implements explicit callback-managed telemetry with generic adapter semantics. Schemas act as compile-time vocabularies while adapters translate generic calls to backend concepts at runtime. Reference backends cover disabled telemetry and process-local capture without exporters.
Architecture
Adapter implementations bridge one generic surface to many backends while pi code always passes parents explicitly. The typed starter binds a parent context to schema vocabularies without retaining schemas at runtime.
flowchart LR
App --> Ctx[TelemetryContext]
Ctx --> Span[TelemetrySpan]
Span --> Noop[Noop backend]
Span --> Mem[In-memory backend]
Span --> Adapters[External adapters]
Schemas[Typed schemas] --> Starter[Typed starter]
Starter --> Ctx
Data Models
SpanOptions
| Field | Type | Constraints | Description |
|---|---|---|---|
| name | string | required | Open span name |
| attributes | SpanAttributes | optional | Open start attribute bag |
SpanAttributes and AttributeValue
AttributeValue supports string and number and boolean scalars plus readonly arrays of each scalar. SpanAttributes maps names to values where undefined entries are ignored. Merging copies values so later reads cannot mutate recorded state through aliases.
SpanStatus
| Variant | Fields | Description |
|---|---|---|
| ok | none | Normal completion marker |
| error | optional name and message | Failure marker with optional error details |
Automatic settlement uses ok for normal returns and error for throws or rejections. Explicit setStatus calls override automatic settlement with last-write-wins semantics.
RecordedTelemetrySpan
| Field | Type | Description |
|---|---|---|
| id | number | Deterministic span identifier |
| parentId | number or null | Parent identifier with null for roots |
| name | string | Recorded span name |
| attributes | SpanAttributes | Merged attribute bag |
| events | array of RecordedTelemetryEvent | Ordered event list |
| status | SpanStatus | Final outcome |
| settled | boolean | Settlement marker |
| endSequence | number or undefined | Deterministic end ordering assigned at settlement |
Snapshots are detached copies returned in span-start order without timestamps.
RecordedTelemetryEvent
| Field | Type | Description |
|---|---|---|
| name | string | Event name |
| attributes | SpanAttributes | Event attribute bag |
TelemetrySchemaDefinition
| Field | Type | Description |
|---|---|---|
| version | number | Schema version marker |
| spans | record of TelemetrySpanDefinition | Declared span vocabulary |
Schemas are ordinary JSON-serializable data with no runtime validation behavior.
TelemetrySpanDefinition
| Field | Type | Description |
|---|---|---|
| description | string | Span purpose |
| parents | TelemetryParentDefinition | Descriptive parent rule |
| startAttributes | record of definitions | Attributes normally known at span creation |
| endAttributes | record of definitions | Optional completion enrichment recorded later |
| events | optional record of TelemetryEventDefinition | Declared event vocabulary |
| status | default plus errorWhen | Documented outcome rule |
Start and end attributes share one backend attribute bag despite their separate declaration timing.
TelemetryAttributeDefinition
Supported types are string and number and boolean plus string arrays and number arrays and boolean arrays. Scalar definitions accept closed value sets while array definitions accept closed element value sets. Metadata covers description and examples and sensitivity and cardinality markers. Start and event attribute definitions add a required flag while end attributes stay optional.
TelemetryEventDefinition
| Field | Type | Description |
|---|---|---|
| description | string | Event purpose |
| attributes | record of definitions | Declared event attribute vocabulary |
TelemetryParentDefinition
| Kind | Meaning |
|---|---|
| any | Root span or any caller span |
| root_or_external | Root span or caller-owned span outside the schema |
| spans | Only the listed schema spans |
Parent metadata is descriptive and is not enforced at runtime.
API Contracts
No HTTP API exists because the package exposes TypeScript interfaces only. Normative types live beside the reference backends in the telemetry source tree.
| Export | Purpose |
|---|---|
| TelemetryContext | Starts callback-managed child spans |
| TelemetrySpan | Records attributes and events and status while acting as a child context |
| NOOP_TELEMETRY_CONTEXT | Shared passive context for disabled telemetry |
| InMemoryTelemetryContext | Reference adapter with process-local recording |
| defineTelemetrySchema | Typed identity helper for serializable schema data |
| createTypedSpanStarter | Binds a parent context to one or more schema vocabularies |
Sequences
Callback span lifecycle
sequenceDiagram
autonumber
participant App as App
participant Ctx as Context
participant Span as Span
App->>Ctx: startSpan(options, callback)
Ctx->>Span: create child span
App->>Span: addEvent and setAttributes
App->>Span: setStatus optional
Ctx->>Span: settle on callback resolve or reject
Ctx-->>App: promise of callback result
The startSpan call creates a child span and invokes the callback synchronously exactly once. The call keeps the span open until a returned promise settles. Recording calls merge into the span while the callback is active. Settlement applies automatic status only when no explicit status was recorded. Calls made after settlement are inert and never throw.
Typed nested spans
The typed starter resolves a span name to its owning schema at compile time. Its callback receives a schema-scoped span plus a child starter bound to the callback span. Nested starter calls therefore record correct parentage without ambient state.
Technical Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Propagation model | Explicit parent contexts | Explicit propagation avoids ambient state and stays portable across runtimes. |
| Contract shape | Vendor-neutral callback API | A neutral API lets adapters target tracing or logging backends without changing call sites. |
| Schema role | Schema-first compile-time vocabularies | Compile-time checks keep emitted names consistent without runtime validation costs. |
| Settlement ownership | startSpan owns settlement | Single ownership keeps spans open across async work and makes post-settlement calls inert. |
| Failure handling | Passive non-throwing recording | Passive recording keeps diagnostics from changing business outcomes. |
Risks and Unknowns
- Unbounded in-memory storage can grow without limit in long-lived recording scopes.
- Sensitive attributes rely on schema authors and data policies rather than runtime redaction.
- Backend translation quality depends on adapter authors applying the conformance suite correctly.
Out of Scope
- Exporters and sampling and flushing remain adapter responsibilities.
- No OpenAPI contract exists because the surface is a TypeScript module rather than an HTTP service.
- Durable persistence of contexts or spans is excluded from this package.
Test Plan: Telemetry Contracts
Scope
This plan covers unit behavior in telemetry.test.ts and adapter conformance in conformance.test.ts. Package-level behavior is verified with vitest from the telemetry package directory. External backend exporters remain out of scope for this plan.
Unit Tests
| ID | Description | Input | Expected Output |
|---|---|---|---|
| TC-1 | Schema definition preserves identity and infers exact attributes | Serializable definition with closed string sets | Same object reference with JSON round-trip and exact kind union |
| TC-2 | Combined vocabularies bind child starters and preserve parentage | Operation and request schemas with nested typed starter calls | Child span records parent identifier and result value passes through |
| TC-3 | Noop context admits callbacks synchronously with one frozen span | Named span with nested child callback | Synchronous admission with identical child span and frozen parent span |
| TC-4 | Noop and typed starters preserve rejection identity | Callbacks that throw synchronously or reject asynchronously | Promises rejected with the identical thrown or rejected value |
| TC-5 | Recording ignores unreadable payloads without throwing | Proxy payloads that throw on property reads | Business callback still runs exactly once without exceptions |
Integration Tests
| ID | Description | Preconditions | Expected Outcome |
|---|---|---|---|
| TC-6 | Conformance callback lifecycle preserves results and errors | Fresh in-memory context per case | Single synchronous admission with ok status and identical rejection values |
| TC-7 | Conformance status keeps last explicit write | Spans with explicit ok or error before return or throw | Final status matches the last explicit write without automatic overwrite |
| TC-8 | Conformance recording merges attributes and orders events | Start attributes plus repeated setAttributes and two events | Merged bag with last-defined wins and ordered event list |
| TC-9 | Conformance atomicity ignores failed recording calls | Attributes containing unreadable values | Prior attributes retained with no partial merge and no throw |
| TC-10 | Conformance settlement makes late calls inert | Captured span used after its callback returned | Late mutations ignored while late child span still executes normally |
| TC-11 | Conformance parentage tracks nested and concurrent children | Overlapping first and second child spans under one parent | Correct parent identifiers with ordered end sequences |
| TC-12 | Conformance passivity suppresses backend payload failures | Unreadable span options and recording payloads | Business callbacks execute once with empty or retained snapshots |
| TC-13 | Snapshot isolation detaches recorded state | Open span observed mid-callback plus later external mutation | Open snapshot unsettled without end sequence and later reads unaffected by mutation |
End-to-End Tests
| ID | Description | Steps | Expected Outcome |
|---|---|---|---|
| TC-14 | Package-level span flow exercises start through settlement | Start span then add event then set attributes then set status then resolve | Settled snapshot contains merged attributes and ordered events and final status |
Edge Cases and Failure Scenarios
| ID | Scenario | Expected Behavior |
|---|---|---|
| TC-15 | Synchronous throw inside callback | Span settles as error and promise rejects with identical value |
| TC-16 | Asynchronous rejection inside callback | Span settles as error and rejection value is preserved |
| TC-17 | Explicit ok status before a throw | Explicit status survives without automatic overwrite |
| TC-18 | Child started from settled parent | Call degrades to noop semantics while still executing the callback |
| TC-19 | Undefined attribute values in merges | Undefined entries are ignored without removing prior values |
Test Infrastructure
- Vitest runs the telemetry package suite from the package directory.
- In-memory contexts isolate each test without shared global state.
- Proxy-based unreadable payloads verify passive non-throwing behavior.
Coverage Matrix
| Requirement | Test Cases |
|---|---|
| FRFunctional Requirement.-1MustThe system shall provide TelemetryContext with startSpan that admits the callback synchronously exactly once. | TC-3, TC-4, TC-6 |
| FRFunctional Requirement.-2MustThe system shall provide TelemetrySpan with setAttributes that merges attribute bags with last-defined-wins semantics. | TC-8, TC-9, TC-19 |
| FRFunctional Requirement.-3MustThe system shall provide TelemetrySpan with addEvent that records ordered named events with attributes. | TC-8, TC-13 |
| FRFunctional Requirement.-4MustThe system shall provide TelemetrySpan with setStatus that records explicit ok or error outcomes with last-write-wins semantics. | TC-7, TC-14, TC-17 |
| FRFunctional Requirement.-5MustThe system shall treat normal completion as ok and throws or rejections as errors unless an explicit status was set. | TC-4, TC-6, TC-15, TC-16 |
| FRFunctional Requirement.-6MustThe system shall provide a shared noop context that invokes callbacks without inspecting or retaining payloads. | TC-3, TC-4, TC-5 |
| FRFunctional Requirement.-7MustThe system shall provide an in-memory context that records detached span snapshots in span-start order. | TC-11, TC-12, TC-13, TC-18 |
| FRFunctional Requirement.-8MustThe system shall provide serializable schema helpers that infer exact start and end and event attribute types. | TC-1, TC-2 |
| FRFunctional Requirement.-9MustThe system shall provide a typed span starter that binds a parent context to one or more schema vocabularies. | TC-2, TC-11 |
| NFRNon-Functional Requirement.-1MustThe system shall keep recording methods synchronous and passive and non-throwing. | TC-5, TC-9, TC-12 |
| NFRNon-Functional Requirement.-2MustThe system shall remain runtime-neutral without ambient context APIs. | TC-2, TC-6 |
| NFRNon-Functional Requirement.-3ShouldThe system shall keep noop overhead negligible by sharing one frozen inert span. | TC-3 |
| NFRNon-Functional Requirement.-4MustThe system shall restrict attribute values to primitive scalars and arrays and avoid sensitive payloads by default. | TC-5, TC-8 |
requirements
No open questions remain because the contracts are implemented and documented.
Vocabulary
Domain Terms
| Term | Definition |
|---|---|
| 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. |
| 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. |
| ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. | The facet/service composition runtime (packages/chord) where facets declare, provide, and consume singleton or keyed services with replicated state. |
| 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. |
| 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. |
| Durable runtimeThe Pico storage contract separating transcript scope (`conversation`), executable work (`task`), and persisted JSON (`document`) with monotonic commit sequences. | The PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. storage contract separating transcript scope (conversation), executable work (task), and persisted JSON (document) with monotonic commit sequences. |
| 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. |
| 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. |
| FacetA setup unit with an `id` and `setup(env)` that declares service dependencies, provides implementations, and owns lifecycle callbacks in a Chord host. | A setup unit with an id and setup(env) that declares service dependencies, provides implementations, and owns lifecycle callbacks in a ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. host. |
| 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. |
| Follow-upA queued message delivered after the agent fully stops. | A queued message delivered after the agent fully stops. |
| FTSFull-Text Search (optional SQLite projection in pi-storage-sqlite-node). | Full-Text Search (optional SQLite projection in pi-storage-sqlite-node). |
| 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. |
| Materialized view (SQLite) | Precomputed session projections (e.g. branch tips, session summaries) maintained by pi-storage-sqlite-node. |
| MemoryStorageThe detached in-memory `Storage` implementation used for tests and reference Pico behavior with no persistence. | The detached in-memory Storage implementation used for tests and reference PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. behavior with no persistence. |
| 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. |
| 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. |
| PicoThe durable agent harness (`packages/durable`, spec `pico-v5.md`) persisting conversations, tasks, and documents as atomic storage commits. | The durable agent harness (packages/durable, spec pico-v5.md) persisting conversations, tasks, and documents as atomic storage commits. |
| PiServerThe experimental token-authenticated session server in pi-server (`createUnixServer` preset, `LiveSessionManager`). | The experimental token-authenticated session server in pi-server (createUnixServer preset, LiveSessionManager). |
| PluginA versioned bundle identity (`FacetBundlePlugin` with `id`/`version`) packaging one or more Chord facets for distribution and loading. | A versioned bundle identity (FacetBundlePlugin with id/version) packaging one or more ChordThe facet/service composition runtime (`packages/chord`) where facets declare, provide, and consume singleton or keyed services with replicated state. facets for distribution and loading. |
| 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. |
| Prompt templateA Markdown file with `{{variable}}` expansion invoked as `/templatename`. | A Markdown file with {{variable}} expansion invoked as /templatename. |
| 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. |
| Replicated stateAn immutable-value shared state handle (`ReplicatedState`/`MutableReplicatedState`) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. | An immutable-value shared state handle (ReplicatedState/MutableReplicatedState) mutated via a tracked proxy and published to subscribers with hydrate/update delivery. |
| Scope (model) | A scoped model set selected with --models pat1,pat2 for Ctrl+P cycling. |
| Service (singleton/keyed) | A stable typed contract identity (Service<T> with string id) provided once (singleton) or per-key (keyed) and consumed via use/observe. |
| 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). |
| 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. |
| 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. |
| Snapshot (protocol) | An authoritative server or session state payload in the pi protocol; progress events are transient and never mutated optimistically. |
| 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. |
Technical Terms
| Term | Definition |
|---|---|
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. |
| 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). |
AssistantMessageEventStream |
pi-ai's async-iterable event queue (push queue + result promise) carrying start/*_delta/done/error events. |
ByteTransportFactory |
A factory creating a fresh connected, authenticated ByteTransport from ByteTransportHandlers, with exactly one terminal handler expected. |
| CBORConcise Binary Object Representation, the payload format for pi protocol messages. | Concise Binary Object Representation, the payload format for pi protocol messages. |
| 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. |
| 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. |
| 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. |
Models collection |
pi-ai's provider registry that routes model lookups and streams by owning provider. |
| 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). |
| 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. |
| Span/Event taxonomyThe versioned telemetry schema (`defineTelemetrySchema`) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. | The versioned telemetry schema (defineTelemetrySchema) declaring allowed span names, parents, start/end attributes, and events for typed telemetry. |
streamFn / StreamFn |
The injectable function the agent calls to reach the LLMLarge Language Model.; streamSimple is the default. |
| 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. |
TelemetryContext / TelemetrySpan |
The span-creation scope (TelemetryContext.startSpan) and its active unit (TelemetrySpan, itself a context) carrying attributes, events, and status. |
| 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. |
| TypeBoxThe schema library used for tool parameter definitions (serializable JSON, self-validating). | The schema library used for tool parameter definitions (serializable JSON, self-validating). |
Acronyms and Abbreviations
| Abbreviation | Expansion |
|---|---|
| ACAcceptance Criterion / Acceptance Criteria. | Acceptance Criterion / Acceptance Criteria. |
| ADRArchitecture Decision Record. | Architecture Decision Record. |
| 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. |
| API keyApplication Programming Interface key (ambient provider authentication). | Application Programming Interface key (ambient provider authentication). |
| CJKChinese, Japanese, Korean (terminal width handling for wide characters). | Chinese, Japanese, Korean (terminal width handling for wide characters). |
| CLICommand-Line Interface (the `pi` binary). | Command-Line Interface (the pi binary). |
| CVECommon Vulnerabilities and Exposures. | Common Vulnerabilities and Exposures. |
| FRFunctional Requirement. | Functional Requirement. |
| 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). |
| 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). |
| NFRNon-Functional Requirement. | Non-Functional Requirement. |
| 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). |
| OIDCOpenID Connect (used for npm trusted publishing identity). | OpenID Connect (used for npm trusted publishing identity). |
| 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). |
| RICEReach, Impact, Confidence, Effort (issue prioritization scoring). | Reach, Impact, Confidence, Effort (issue prioritization scoring). |
| RPCRemote Procedure Call (the JSONL stdin/stdout protocol mode). | Remote Procedure Call (the JSONL stdin/stdout protocol mode). |
| SDKSoftware Development Kit (the embeddable programmatic API). | Software Development Kit (the embeddable programmatic API). |
| SLO / SLIService Level Objective / Service Level Indicator. | Service Level Objective / Service Level Indicator. |
| TUITerminal User Interface (the interactive mode, and the `pi-tui` library). | Terminal User Interface (the interactive mode, and the pi-tui library). |
| WASMWebAssembly (photon-node used for image resizing). | WebAssembly (photon-node used for image resizing). |