Created
June 5, 2026 12:39
-
-
Save rigtorp/51f4efc78a19c8f2e697309bfb5b625d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Pi.dev Clone Specification | |
| This document serves as a comprehensive specification for reimplementing a clone of the **Pi.dev** terminal coding agent in any modern programming language (e.g., Rust, Go, Python, or C#). | |
| ## 1. Overview & Philosophy | |
| Pi.dev is a minimal, terminal-first, local coding assistant. Unlike complex autonomous agents that rely on heavy sub-agent architectures or intricate planning modes, Pi.dev focuses on a tight, developer-centric loop. | |
| **Core Principles:** | |
| * **Minimalism & Speed:** Focus on a core set of highly reliable tools without the bloat of complex planning systems. | |
| * **Local & Private:** Run purely locally in the terminal. No proprietary SaaS backends. User data stays on the machine. | |
| * **Model Agnosticism:** Support multiple LLM providers (OpenAI, Anthropic, Google, and local models via Ollama/vLLM). Users should be able to switch models mid-session. | |
| * **Highly Extensible:** Allow developers to mold the agent to their workflow via custom tools, prompts, skills, and themes. | |
| --- | |
| ## 2. Architecture Layers | |
| To maintain maintainability and extensibility, the architecture should be divided into four distinct, decoupled modules (or packages/crates). | |
| ### 2.1 API Abstraction Layer (`ai-provider`) | |
| **Responsibility:** Standardize communication with various LLM providers. | |
| * **Unified Interface:** A single interface for chat completions and tool calling across different APIs. | |
| * **Streaming:** Handle Server-Sent Events (SSE) or streaming responses seamlessly. | |
| * **Token Management:** Built-in token counting and context window management to prevent context overflow. | |
| * **Error Handling:** Graceful fallbacks, retries, and rate-limit handling. | |
| ### 2.2 Agent Core Logic (`agent-core`) | |
| **Responsibility:** Manage the fundamental ReAct (Reasoning and Acting) loop. | |
| * **State Machine:** Manage agent states (e.g., `Idle`, `Thinking`, `Executing Tool`, `Streaming Response`). | |
| * **Tool Engine:** An execution engine that safely routes tool calls to implementations and returns formatted results to the LLM. | |
| * **Context Management:** Maintain session history, manage message arrays, and implement trimming strategies when the context limit is reached. | |
| ### 2.3 Terminal UI (`tui-engine`) | |
| **Responsibility:** Render a beautiful, responsive, and non-blocking terminal interface. | |
| * **Markdown Rendering:** Syntax highlighting for code blocks and rich text formatting. | |
| * **Streaming Updates:** Smoothly render text as it streams from the LLM. | |
| * **Interactive Prompts:** Handle keyboard input, multi-line editing, and command history. | |
| * **Status Indicators:** Visual feedback (spinners, progress bars) for thinking and tool execution states. | |
| ### 2.4 Runtime & CLI (`cli-runtime`) | |
| **Responsibility:** The main entry point that glues the components together. | |
| * **CLI Parsing:** Handle flags (e.g., `--model`, `--prompt`, `--workspace`). | |
| * **Session Persistence:** Save and load chat history from a local database (SQLite) or JSON files. | |
| * **Configuration:** Load API keys and preferences from `.env` files or a central config file (e.g., `~/.config/pi-clone/config.toml`). | |
| --- | |
| ## 3. Core Toolset | |
| Pi.dev intentionally avoids bloat by shipping with a small but extremely powerful set of foundational tools. These four tools are sufficient for an LLM to navigate a filesystem, understand code, write new code, and test it. | |
| > [!IMPORTANT] | |
| > Tool execution must be robust. Command failures or file read errors must return clear, raw error messages (like stderr output) back to the LLM so it can autonomously correct its behavior, rather than crashing the agent. | |
| ### 3.1 `read` (File & Directory Inspection) | |
| * **Purpose:** Allows the agent to gather context about the project structure and file contents. | |
| * **How it Works:** | |
| * If given a directory path, it returns a tree-like list of files and subdirectories. | |
| * If given a file path, it reads and returns the text content of the file. | |
| * *Implementation detail:* It should handle large files gracefully, either by refusing to read files over a certain token limit or by supporting line-range arguments (e.g., `start_line` and `end_line`). | |
| ### 3.2 `write` (File Creation) | |
| * **Purpose:** Allows the agent to create entirely new files or completely overwrite existing ones. | |
| * **How it Works:** | |
| * Accepts a `file_path` and `content`. | |
| * Automatically creates any missing parent directories before writing the file. | |
| * Overwrites the file entirely. It should *not* be used for small modifications to large files due to token inefficiency. | |
| ### 3.3 `edit` (File Modification) | |
| * **Purpose:** Allows the agent to make targeted modifications to existing files without rewriting the entire file. | |
| * **How it Works:** | |
| * There are a few ways to implement this (e.g., regex, AST, line numbers). A robust approach is the **search-and-replace block** method. | |
| * The tool accepts a `file_path`, a `target_content` (the exact text to find in the file), and a `replacement_content`. | |
| * The engine reads the file, finds the exact match of `target_content`, replaces it, and saves. If the match is not exact or multiple matches are found, it errors out and prompts the LLM to provide more context (e.g., include surrounding unchanged lines). | |
| ### 3.4 `bash` (Command Execution) | |
| * **Purpose:** Allows the agent to run terminal commands to compile code, run tests, manage git, or use system utilities. | |
| * **How it Works:** | |
| * Executes the command in the agent's current working directory. | |
| * Must capture and stream `stdout` and `stderr` back to the LLM. | |
| * *Security & Safety:* Requires a strict timeout mechanism to prevent hanging on commands that wait for user input. Interactive commands should generally be disallowed or handled by a pseudo-terminal (PTY) wrapper if advanced functionality is needed. | |
| --- | |
| ## 4. Exact System Prompt Construction Workflow | |
| Unlike many agents that rely on massive, opaque 10k+ token system prompts, Pi.dev uses a highly deterministic, modular prompt builder. The clone must construct the final system prompt in the exact following order: | |
| ### 4.1 Base Prompt Initialization | |
| The agent first checks for a user-provided override: | |
| 1. **Custom Override (`SYSTEM.md`):** If a `.pi/SYSTEM.md` file exists in the project root (or a global config), its text completely overrides the default base prompt. | |
| 2. **Default Base:** If no override exists, the agent generates a tiny base prompt ("You are an expert coding assistant..."). | |
| 3. **Dynamic Tool & Guideline Injection:** If using the default base, the agent dynamically appends: | |
| * A list of currently loaded tools, formatting their one-line descriptions. | |
| * Dynamic guidelines based on available tools (e.g., if `bash` is loaded but `ls`/`grep` are not, it explicitly instructs the LLM: "Use bash for file operations like ls, rg, find"). It always appends standard rules: "Be concise" and "Show file paths clearly". | |
| ### 4.2 Custom Appends (`APPEND_SYSTEM.md`) | |
| If `.pi/APPEND_SYSTEM.md` exists, its contents are directly concatenated to the end of the base prompt. | |
| ### 4.3 Project Context (`AGENTS.md`) | |
| The agent walks up the directory tree looking for `AGENTS.md` or `CLAUDE.md` files. If found, it appends them wrapped in explicit XML tags to delineate them from core instructions: | |
| ```xml | |
| <project_context> | |
| Project-specific instructions and guidelines: | |
| <project_instructions path="/absolute/path/to/AGENTS.md"> | |
| ...contents of AGENTS.md... | |
| </project_instructions> | |
| </project_context> | |
| ``` | |
| ### 4.4 Skills Injection | |
| If the `read` tool is currently loaded in the session, the agent appends the `<available_skills>` XML block (detailed in Section 5) containing the frontmatter metadata of discovered skills. | |
| ### 4.5 Environmental Variables | |
| Finally, at the very end of the system prompt, the agent appends the absolute path of the current working directory and the current date (e.g., `YYYY-MM-DD`). This prevents the LLM from hallucinating its location in the filesystem. | |
| ### 4.6 Exportability | |
| The agent must provide a command (e.g., `/export` or a CLI flag) to dump this fully constructed string to the terminal for developer debugging. | |
| --- | |
| ## 5. Skills and Extension System | |
| The true power of Pi.dev lies in its extensibility. Rather than baking every possible feature into the core, it uses a "Skills" system. | |
| ### 5.1 What are Skills? | |
| Skills are on-demand "capability packages." They are bundles that contain: | |
| 1. **Custom Prompts/Instructions:** Specific context or system prompt overrides for a task. | |
| 2. **Custom Tools:** New functions the LLM can call (e.g., an API connector to Jira, or a specific AST parser). | |
| ### 5.2 How Skills are Discovered and Loaded | |
| The Pi agent discovers skills by traversing the file system, treating them as local "capability packages." The clone should implement a robust skill-loading mechanism: | |
| * **Directory Scanning:** The agent scans standard locations for skills, such as a global `~/.pi/skills/` directory and a project-specific `.pi/skills/` directory. | |
| * **The `SKILL.md` File:** A directory is recognized as a valid skill if it contains a `SKILL.md` file (or if the file itself is a standalone `.md` file in the skills directory). | |
| * **Frontmatter Parsing:** The clone reads the YAML frontmatter of the markdown file to extract the skill's metadata, strictly validating properties like `name` (alphanumeric with hyphens), `description`, and `disable-model-invocation`. If the name is missing, it falls back to the directory name. | |
| * **Ignored Files:** The scanning engine must respect `.gitignore` and `.piignore` patterns, avoiding deep traversal into dependencies like `node_modules` or hidden folders. | |
| ### 5.3 Context Injection & On-Demand Invocation | |
| * **Token Efficiency & Awareness:** While the full content and rules of a skill are *not* automatically dumped into the LLM's context to prevent bloat, the agent must inject the **frontmatter metadata** (name, description, and file path) of all discovered skills into the base system prompt. | |
| * **The `<available_skills>` Block:** The clone should format this metadata into an XML block (e.g., `<available_skills>`) in the system prompt. This acts as an index, making the LLM aware of its capabilities. | |
| * **Invocation Methods:** | |
| 1. **Dynamic Loading (LLM-Driven):** If a user requests a task that matches a skill's description in the `<available_skills>` block, the LLM will autonomously use the `read` tool on the provided `location` path to load the full skill instructions into its context mid-session. | |
| 2. **Explicit Loading (User-Driven):** The user can explicitly invoke them via the TUI (e.g., using a `/skill:name` slash command or a `--skill` CLI flag). | |
| --- | |
| ## 6. Interactive Slash Commands | |
| In the Interactive TUI mode, the clone must support slash commands (`/`) to allow the developer to orchestrate the session, manage context, and control the agent. | |
| ### 6.1 Built-in Commands | |
| The core should provide built-in commands covering essential workflows: | |
| * **Session Management:** `/new` (start fresh), `/resume` (load previous), `/session` (stats), `/compact` (trim context window manually), `/name` (rename session). | |
| * **Branching & History:** `/fork` (branch off a previous message), `/clone` (duplicate session), `/tree` (navigate branches), `/export` (save to HTML/JSONL), `/import`, `/share` (to GitHub gist). | |
| * **Configuration & Auth:** `/settings` (open menu), `/model` (switch LLM provider mid-session), `/scoped-models`, `/login`, `/logout`. | |
| * **System:** `/reload` (hot-reload extensions/skills without restarting), `/hotkeys`, `/changelog`, `/copy` (copy last output), `/quit`. | |
| ### 6.2 Extensible Commands | |
| Because the harness is highly extensible, external modules (like skills, prompt templates, and TypeScript extensions) should be able to dynamically register their own slash commands at runtime (e.g., loading a testing skill registers a `/skill:test` command). | |
| --- | |
| ## 7. Session Management & Tree Architecture | |
| Unlike basic chatbots that store history as a linear array, Pi.dev stores session history as a **Directed Acyclic Graph (DAG) / Tree**. This allows developers to fork conversations and try different approaches without losing past context. | |
| ### 7.1 Data Storage (JSONL) | |
| Sessions are stored locally (usually in `~/.pi/sessions/`) using the **JSONL (JSON Lines)** format. This ensures that even if the agent crashes mid-response, the previous lines are safely committed to disk. | |
| * **Header:** The first line of the file is a `SessionHeader` containing the session ID, timestamp, working directory, and the `parentSession` ID (if it's a fork). | |
| * **Entries:** Subsequent lines represent discrete events (`SessionEntry` objects). Each entry has an `id`, a `parentId`, and a `timestamp`. | |
| ### 7.2 Types of Session Entries | |
| The clone must record more than just "user" and "assistant" messages. It should record state changes as nodes in the tree: | |
| * `SessionMessageEntry`: Standard user or agent messages. | |
| * `ModelChangeEntry`: Records if the user switched LLM providers mid-session. | |
| * `CompactionEntry`: Records when the context window was trimmed/summarized to save tokens. | |
| * `CustomMessageEntry` / `CustomEntry`: Extension-specific metadata or messages injected directly into the LLM context (e.g., from a skill). | |
| ### 7.3 Resuming and Branching | |
| * **Resuming:** When a user resumes a session, the clone parses the JSONL file and reconstructs the tree by mapping `parentId` to `id`. It then traverses from the root node to the specified leaf node (the last interaction) to build the linear context array passed to the LLM. | |
| * **Branching (`/fork`):** Because of the tree structure, if the LLM goes down a bad path, the user can `/fork` from an earlier message `id`. The clone simply creates a new node pointing to the older `parentId`, ignoring the bad path moving forward. | |
| --- | |
| ## 8. Execution Modes | |
| The agent should support multiple modes of operation depending on how the developer wants to invoke it: | |
| 1. **Interactive Mode:** The standard chat UI in the terminal (TUI). | |
| 2. **Headless / CLI Mode:** Run a single prompt, execute the required tools, output the result to stdout, and exit. | |
| * *Example:* `cat error.log | agent "Fix the issue causing this error"` | |
| 3. **RPC / Server Mode:** Run as a background daemon exposing a local API (HTTP or gRPC) allowing IDE extensions or other tools to leverage the agent's capabilities. | |
| --- | |
| ## 9. Implementation Roadmap | |
| For the agent building this clone, follow this phased approach: | |
| 1. **Phase 1: Foundation:** Setup the project structure, CLI argument parser, and configuration loader. | |
| 2. **Phase 2: LLM Integration:** Implement the `ai-provider` abstraction. Start with one major provider (e.g., Anthropic or OpenAI) supporting streaming and tool schemas. | |
| 3. **Phase 3: Core Tools:** Build the `read`, `write`, `edit`, and `bash` tools. Ensure output capturing for bash commands works flawlessly. | |
| 4. **Phase 4: Agent Loop:** Implement the core event loop. Tie the LLM provider and tools together so the LLM can autonomously call tools until a task is complete. | |
| 5. **Phase 5: User Interface:** Build the TUI. Ensure streaming markdown is readable and tool execution is transparent to the user. | |
| 6. **Phase 6: Polish:** Add JSONL session tree persistence, context trimming, and the extension registry. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Pi Agent Reimplementation Specification | |
| This document specifies the runtime behavior of pi's coding agent closely enough to reimplement it in another language. It describes the observable contracts between the CLI/runtime, session store, model provider, tools, extensions, and UI modes. | |
| For the exact JSONL entry schema, see [Session format](session-format.md). This document focuses on how those entries are produced and consumed. | |
| ## Scope | |
| A compatible implementation must reproduce these behaviors: | |
| - Build a system prompt from settings, resources, available tools, current date, and working directory. | |
| - Maintain a persistent session tree in JSONL. | |
| - Convert persisted session entries into model context. | |
| - Run an agent loop that streams model output, executes tool calls, appends tool results, and continues until complete. | |
| - Support user steering and follow-up queues while a run is active. | |
| - Emit lifecycle events in the same order. | |
| - Provide the built-in coding tools and their schemas. | |
| - Compact long sessions into summaries and recover from context overflow. | |
| - Expose the same high-level modes: interactive, print, JSON event stream, RPC, and SDK-style embedding. | |
| The implementation language does not matter. TypeScript-specific details such as TypeBox may be replaced by equivalent JSON Schema validation, but runtime semantics should match. | |
| ## Core Terms | |
| - Agent: stateful object that owns the current transcript, model, tools, queues, active abort controller, and event subscribers. | |
| - Agent session: higher-level runtime wrapper that connects an Agent to settings, sessions, tools, extensions, compaction, retries, model selection, and bash execution. | |
| - Agent message: internal transcript item. It includes normal LLM messages plus pi-specific custom message roles. | |
| - Model context: `{ systemPrompt, messages, tools }` sent to the provider. | |
| - Turn: one assistant response plus any tool calls requested by that response and their tool-result messages. | |
| - Run: one prompt or continuation request, including all turns caused by tool calls, steering, and follow-ups. | |
| - Steering message: user input injected after the current turn completes, before the agent would naturally stop. | |
| - Follow-up message: user input held until the agent would otherwise stop. | |
| - Tool definition: pi-facing metadata plus executable tool handler. | |
| - Agent tool: provider-facing tool name, description, parameters, and execute function. | |
| ## Runtime Layers | |
| Pi is structured as four separable layers: | |
| 1. Provider layer: converts a model context into a stream of assistant-message events. | |
| 2. Agent loop: consumes the provider stream, executes tool calls, and emits lifecycle events. | |
| 3. Agent session: owns persistence, settings, prompts, tool registry, extensions, retries, compaction, and tree navigation. | |
| 4. Mode layer: interactive TUI, print mode, JSON stream mode, RPC mode, or SDK caller. | |
| A reimplementation can merge layers internally, but external behavior should preserve the same contracts. | |
| ## Data Model | |
| ### Content Blocks | |
| Text content: | |
| ```json | |
| { "type": "text", "text": "..." } | |
| ``` | |
| Image content: | |
| ```json | |
| { "type": "image", "data": "<base64>", "mimeType": "image/png" } | |
| ``` | |
| Thinking content: | |
| ```json | |
| { "type": "thinking", "thinking": "..." } | |
| ``` | |
| Tool call content: | |
| ```json | |
| { "type": "toolCall", "id": "call_...", "name": "read", "arguments": { "path": "README.md" } } | |
| ``` | |
| ### Message Roles | |
| User message: | |
| - `role`: `"user"` | |
| - `content`: string or array of text/image blocks | |
| - `timestamp`: Unix milliseconds | |
| Assistant message: | |
| - `role`: `"assistant"` | |
| - `content`: array of text/thinking/toolCall blocks | |
| - `api`, `provider`, `model`: provider identifiers | |
| - `usage`: token and cost accounting | |
| - `stopReason`: `"stop"`, `"length"`, `"toolUse"`, `"error"`, or `"aborted"` | |
| - `errorMessage`: optional string | |
| - `timestamp`: Unix milliseconds | |
| Tool result message: | |
| - `role`: `"toolResult"` | |
| - `toolCallId`: original tool call id | |
| - `toolName`: original tool name | |
| - `content`: array of text/image blocks | |
| - `details`: optional tool-specific metadata | |
| - `isError`: boolean | |
| - `timestamp`: Unix milliseconds | |
| Pi-specific message roles: | |
| - `bashExecution`: user-triggered shell command outside provider tool calls. | |
| - `custom`: extension-injected context message. | |
| - `branchSummary`: summary of an abandoned branch. | |
| - `compactionSummary`: summary replacing compacted earlier context. | |
| ### LLM Message Conversion | |
| Before every provider request, convert internal messages to provider-compatible messages: | |
| - `user`, `assistant`, and `toolResult` pass through. | |
| - `bashExecution` becomes a `user` message unless `excludeFromContext` is true. | |
| - `custom` becomes a `user` message. String content is wrapped in one text block. | |
| - `branchSummary` becomes a `user` message containing: | |
| ```text | |
| The following is a summary of a branch that this conversation came back from: | |
| <summary> | |
| {summary}</summary> | |
| ``` | |
| - `compactionSummary` becomes a `user` message containing: | |
| ```text | |
| The conversation history before this point was compacted into the following summary: | |
| <summary> | |
| {summary} | |
| </summary> | |
| ``` | |
| The converter must filter only messages that intentionally do not enter context. It should not throw; on conversion failure, return a safe fallback such as the original filtered list. | |
| ## Session Store | |
| Sessions are JSONL files. The first line is a session header; following lines are tree entries with `id` and `parentId`. | |
| Default path: | |
| ```text | |
| ~/.pi/agent/sessions/--<cwd-with-slashes-replaced-by-dashes>--/<timestamp>_<uuid>.jsonl | |
| ``` | |
| Current session version is `3`. | |
| Entry IDs are short random ids, normally 8 hex characters, collision-checked within the file. Session ids are UUIDv7 unless explicitly supplied. | |
| ### Loading | |
| To load a session: | |
| 1. Read JSONL entries. | |
| 2. Ignore blank and malformed lines. | |
| 3. Require the first valid entry to be a `session` header with string `id`; otherwise treat the file as invalid. | |
| 4. Migrate older versions: | |
| - v1 to v2: add `id` and `parentId` linearly; convert compaction `firstKeptEntryIndex` to `firstKeptEntryId`. | |
| - v2 to v3: rename old `hookMessage` message role to `custom`. | |
| 5. Build an in-memory map by entry id and remember the current leaf. | |
| ### Context Building | |
| To build model context from a session: | |
| 1. If the selected leaf is explicitly null, return no messages, thinking level `"off"`, and no model. | |
| 2. Walk from leaf to root via `parentId`, then reverse into chronological path order. | |
| 3. Track the latest thinking-level change on the path. | |
| 4. Track the latest model change on the path, or infer model/provider from assistant messages. | |
| 5. Find the latest compaction entry on the path, if any. | |
| 6. If no compaction exists, append all context-producing entries on the path: | |
| - `message` entries append their `message`. | |
| - `custom_message` entries append a `custom` message. | |
| - `branch_summary` entries append a `branchSummary` message. | |
| 7. If compaction exists: | |
| - Append one `compactionSummary` message first. | |
| - Append entries before the compaction starting at `firstKeptEntryId`. | |
| - Append context-producing entries after the compaction. | |
| Non-context entries such as labels, custom extension state, and session info do not enter model context. | |
| ## Agent State | |
| An Agent owns: | |
| - `systemPrompt` | |
| - `model` | |
| - `thinkingLevel` | |
| - `tools` | |
| - `messages` | |
| - `isStreaming` | |
| - `streamingMessage` | |
| - `pendingToolCalls` | |
| - `errorMessage` | |
| - steering queue | |
| - follow-up queue | |
| - active run with abort controller | |
| Array assignments to `messages` and `tools` should copy the top-level array so callers cannot mutate state accidentally. | |
| Default queue drain mode is `"one-at-a-time"` for both steering and follow-up. The alternate mode is `"all"`. | |
| ## Provider Stream Contract | |
| The provider stream function receives: | |
| - model | |
| - context: `{ systemPrompt, messages, tools }` | |
| - options including API key, reasoning level, session id, transport, request hooks, abort signal, and retry settings | |
| It returns an async stream of assistant-message events plus a final `result()` promise. | |
| The stream must not throw for model/provider/runtime failures. It must encode failures as stream events ending in an assistant message whose `stopReason` is `"error"` or `"aborted"` and whose `errorMessage` describes the problem. | |
| Provider events include: | |
| - `start` | |
| - `text_start`, `text_delta`, `text_end` | |
| - `thinking_start`, `thinking_delta`, `thinking_end` | |
| - `toolcall_start`, `toolcall_delta`, `toolcall_end` | |
| - `done` | |
| - `error` | |
| Each streamed event carries a partial assistant message. The agent replaces the current partial in context as deltas arrive. | |
| ## Agent Loop Algorithm | |
| ### Starting a Prompt | |
| When `prompt(input, images?)` is called: | |
| 1. Reject if a run is active. | |
| 2. Convert string input to a `user` message with one text block and optional image blocks. | |
| 3. Start a run with a fresh abort controller. | |
| 4. Mark `isStreaming` true and clear runtime error state. | |
| 5. Create a context snapshot from current state. | |
| 6. Emit `agent_start`. | |
| 7. Emit `turn_start`. | |
| 8. For each initial prompt message, emit `message_start`, then `message_end`, and append it to context and new-message list. | |
| 9. Enter the shared run loop. | |
| ### Continuing | |
| When `continue()` is called: | |
| 1. Reject if a run is active. | |
| 2. Reject if the transcript is empty. | |
| 3. If the last message is an assistant: | |
| - If steering queue has messages, run them as a new prompt and skip the initial steering poll. | |
| - Else if follow-up queue has messages, run them as a new prompt. | |
| - Else reject because provider continuation cannot start from an assistant. | |
| 4. If the last message is user or toolResult, enter a continuation run without appending a new prompt. | |
| ### Shared Loop | |
| The loop maintains `currentContext`, `config`, `newMessages`, `pendingMessages`, and `firstTurn`. | |
| At run start, poll steering messages unless skipped. Then: | |
| 1. Enter an outer loop. It only exits when there are no tool calls, no steering messages, and no follow-up messages. | |
| 2. Enter an inner loop while there are tool calls to process or pending steering/follow-up messages to inject. | |
| 3. Emit `turn_start` for every inner-loop iteration after the first. | |
| 4. If `pendingMessages` is non-empty: | |
| - For each pending message, emit `message_start`, `message_end`, append to context, and append to `newMessages`. | |
| - Clear `pendingMessages`. | |
| 5. Stream one assistant response. | |
| 6. Append the final assistant message to `newMessages`. | |
| 7. If assistant `stopReason` is `"error"` or `"aborted"`: | |
| - Emit `turn_end` with no tool results. | |
| - Emit `agent_end`. | |
| - Return. | |
| 8. Extract tool calls from assistant content. | |
| 9. If tool calls exist, execute them and append resulting toolResult messages to context and `newMessages`. | |
| 10. Emit `turn_end` with the assistant message and tool results. | |
| 11. Call `prepareNextTurn`, if configured. It may replace context, model, and thinking level for the next provider request. | |
| 12. Call `shouldStopAfterTurn`, if configured. If true, emit `agent_end` and return before queue polling. | |
| 13. Poll steering messages. If any exist, repeat the inner loop. | |
| 14. If inner loop stops, poll follow-up messages. If any exist, assign them to `pendingMessages` and continue the outer loop. | |
| 15. If no follow-ups exist, emit `agent_end` and return. | |
| When a run finishes, set `isStreaming` false, clear `streamingMessage`, clear `pendingToolCalls`, resolve waiters, and clear the active run. | |
| ## Event Semantics | |
| Events must be emitted and awaited in subscription order. The run is not idle until all `agent_end` listeners have settled. | |
| Core events: | |
| - `agent_start` | |
| - `turn_start` | |
| - `message_start` | |
| - `message_update` | |
| - `message_end` | |
| - `tool_execution_start` | |
| - `tool_execution_update` | |
| - `tool_execution_end` | |
| - `turn_end` | |
| - `agent_end` | |
| State reductions: | |
| - `message_start`: set `streamingMessage`. | |
| - `message_update`: replace `streamingMessage`. | |
| - `message_end`: clear `streamingMessage` and append the message to state. | |
| - `tool_execution_start`: add tool call id to `pendingToolCalls`. | |
| - `tool_execution_end`: remove tool call id from `pendingToolCalls`. | |
| - `turn_end`: if assistant has `errorMessage`, set `errorMessage`. | |
| - `agent_end`: clear `streamingMessage`. | |
| AgentSession adds: | |
| - `queue_update` | |
| - `compaction_start` | |
| - `compaction_end` | |
| - `session_info_changed` | |
| - `thinking_level_changed` | |
| - `auto_retry_start` | |
| - `auto_retry_end` | |
| ## Tool Execution | |
| Tool calls are content blocks in assistant messages. Each block has `id`, `name`, and `arguments`. | |
| For each tool call: | |
| 1. Emit `tool_execution_start`. | |
| 2. Find a registered tool by name. If missing, produce an error tool result: `Tool {name} not found`. | |
| 3. If the tool has `prepareArguments`, apply it before validation. | |
| 4. Validate arguments against the tool schema. | |
| 5. Run `beforeToolCall`, if configured. If it returns `{ block: true }`, produce an error tool result using the provided reason or `Tool execution was blocked`. | |
| 6. If aborted before execution, produce an error tool result `Operation aborted`. | |
| 7. Execute the tool with `(toolCallId, validatedArgs, signal, onUpdate, context)`. | |
| 8. Forward partial results from `onUpdate` as `tool_execution_update`. | |
| 9. Convert thrown errors into error tool results. Error text is the error message. | |
| 10. Run `afterToolCall`, if configured. It may replace `content`, `details`, `isError`, or `terminate` field-by-field. | |
| 11. Emit `tool_execution_end`. | |
| 12. Emit the created toolResult message as `message_start`, then `message_end`. | |
| Execution mode: | |
| - `"parallel"` is the default. Preflight steps run in assistant source order. Allowed tools then execute concurrently. `tool_execution_end` emits in completion order. Tool-result messages are emitted afterward in original assistant source order. | |
| - `"sequential"` executes, finalizes, and emits each tool result before starting the next tool call. | |
| - If any requested tool declares `executionMode: "sequential"`, the entire batch is sequential. | |
| Early termination: | |
| - Tool results may set `terminate: true`. | |
| - The tool batch terminates only if every finalized tool result in the batch has `terminate: true`. | |
| - Termination prevents the next tool-driven provider request after that batch. | |
| ## Tool Result Shape | |
| Successful tool result: | |
| ```json | |
| { | |
| "role": "toolResult", | |
| "toolCallId": "call_...", | |
| "toolName": "read", | |
| "content": [{ "type": "text", "text": "..." }], | |
| "details": {}, | |
| "isError": false, | |
| "timestamp": 1710000000000 | |
| } | |
| ``` | |
| Error tool result: | |
| ```json | |
| { | |
| "role": "toolResult", | |
| "toolCallId": "call_...", | |
| "toolName": "read", | |
| "content": [{ "type": "text", "text": "error text" }], | |
| "isError": true, | |
| "timestamp": 1710000000000 | |
| } | |
| ``` | |
| ## Built-In Tools | |
| The default coding tool set is `read`, `bash`, `edit`, and `write`. Read-only mode uses `read`, `grep`, `find`, and `ls`. The complete built-in set is `read`, `bash`, `edit`, `write`, `grep`, `find`, and `ls`. | |
| Tool availability is filtered by: | |
| 1. Initial active tool names. | |
| 2. Allowed tool names, if provided. | |
| 3. Excluded tool names, if provided. | |
| 4. Extension-provided tool registration and overrides. | |
| ### Path Resolution | |
| Relative paths resolve against the session working directory. Absolute paths are accepted. Implementations may add sandbox policies around this, but the core tool semantics resolve paths this way. | |
| ### Truncation | |
| Text-heavy tools use shared truncation limits: | |
| - Default maximum lines: 2000. | |
| - Default maximum bytes: 128 KiB. | |
| - Grep long-line maximum: 2000 characters. | |
| When output is truncated, tools include an actionable notice in the text and structured `details.truncation` metadata. | |
| ### `read` | |
| Schema: | |
| ```json | |
| { | |
| "path": "string", | |
| "offset": "optional number, 1-indexed line number", | |
| "limit": "optional number, max lines" | |
| } | |
| ``` | |
| Behavior: | |
| - Checks readability. | |
| - Detects supported image files: jpg, png, gif, webp. | |
| - For images, returns a text note plus an image block. By default, resize to fit provider inline-image limits. | |
| - If the current model lacks image input support, include a note saying the image will be omitted from the request. | |
| - For text, read as UTF-8 and split by `\n`. | |
| - `offset` is 1-indexed. If offset is beyond EOF, error with `Offset {offset} is beyond end of file ({lineCount} lines total)`. | |
| - `limit` slices lines before truncation. | |
| - If first selected line exceeds byte limit, return a message recommending a bash `sed` and `head -c` fallback. | |
| - If truncated, include line range and next `offset`. | |
| - If user `limit` stopped early, include remaining line count and next `offset`. | |
| ### `bash` | |
| Schema: | |
| ```json | |
| { | |
| "command": "string", | |
| "timeout": "optional number, seconds" | |
| } | |
| ``` | |
| Behavior: | |
| - Execute in the session working directory using the configured shell. | |
| - Prepend configured `shellCommandPrefix`, if any. | |
| - Merge stdout and stderr in arrival order. | |
| - Stream partial output updates, throttled to roughly 100 ms. | |
| - On abort, kill the process tree. | |
| - On timeout, kill the process tree and error with `Command timed out after {timeout} seconds`, preserving captured output before the status. | |
| - If exit code is non-zero, error with captured output plus `Command exited with code {code}`. | |
| - Empty successful output returns `(no output)`. | |
| - Truncated output is persisted to a temp file and reported as `Full output: {path}`. | |
| ### `edit` | |
| Schema: | |
| ```json | |
| { | |
| "path": "string", | |
| "edits": [ | |
| { "oldText": "string", "newText": "string" } | |
| ] | |
| } | |
| ``` | |
| Behavior: | |
| - Legacy arguments `{ oldText, newText }` are converted into one `edits` entry. | |
| - If `edits` is a JSON string, try to parse it as an array. | |
| - Require at least one edit. | |
| - Check file readability and writability. | |
| - Read as UTF-8. | |
| - Strip BOM before matching; preserve it when writing. | |
| - Normalize content to LF for matching. | |
| - Each `oldText` must match exactly one non-overlapping region of the original normalized file. | |
| - All replacements are computed against the original file, not incrementally. | |
| - Preserve original line ending style when writing. | |
| - Serialize file mutations per absolute path so concurrent edits/writes cannot interleave. | |
| - Return `Successfully replaced {n} block(s) in {path}.` | |
| - Details include display diff, unified patch, and first changed line. | |
| ### `write` | |
| Schema: | |
| ```json | |
| { | |
| "path": "string", | |
| "content": "string" | |
| } | |
| ``` | |
| Behavior: | |
| - Resolve path against cwd. | |
| - Create parent directories recursively. | |
| - Write UTF-8 content, overwriting existing files. | |
| - Serialize file mutations per absolute path. | |
| - Return `Successfully wrote {byteLength} bytes to {path}` where `byteLength` is string length in the current implementation. | |
| ### `grep` | |
| Schema: | |
| ```json | |
| { | |
| "pattern": "string", | |
| "path": "optional string, default current directory", | |
| "glob": "optional string", | |
| "ignoreCase": "optional boolean, default false", | |
| "literal": "optional boolean, default false", | |
| "context": "optional number, default 0", | |
| "limit": "optional number, default 100" | |
| } | |
| ``` | |
| Behavior: | |
| - Use ripgrep with JSON output when using local filesystem operations. | |
| - Search hidden files and respect `.gitignore`. | |
| - Use `--ignore-case` when requested. | |
| - Use `--fixed-strings` when `literal` is true. | |
| - Use `--glob` when provided. | |
| - Limit matches to at least 1. | |
| - Return `No matches found` when ripgrep exits with no matches. | |
| - Format each match as `path:line: text`. | |
| - With context, include surrounding lines as `path-line- text`. | |
| - Long lines are truncated and reported in details. | |
| - If match limit is reached, kill ripgrep and append a notice suggesting a higher limit or refined pattern. | |
| - Byte truncation is applied after formatting. | |
| ### `find` | |
| Schema: | |
| ```json | |
| { | |
| "pattern": "string glob", | |
| "path": "optional string, default current directory", | |
| "limit": "optional number, default 1000" | |
| } | |
| ``` | |
| Behavior: | |
| - Use `fd` for local filesystem search. | |
| - Search hidden files and respect `.gitignore`. | |
| - Use `--glob`, `--color=never`, `--hidden`, `--no-require-git`, and `--max-results`. | |
| - If the pattern contains `/`, use `--full-path`; for relative path-containing patterns not starting with `/`, `**/`, or equal to `**`, prefix `**/`. | |
| - Return paths relative to the search root, normalized with `/`. | |
| - Append `/` for directory results when the backend emits it. | |
| - Return `No files found matching pattern` when no results. | |
| - If limit is reached, append a notice suggesting a higher limit or refined pattern. | |
| - Byte truncation is applied after formatting. | |
| ### `ls` | |
| Schema: | |
| ```json | |
| { | |
| "path": "optional string, default current directory", | |
| "limit": "optional number, default 500" | |
| } | |
| ``` | |
| Behavior: | |
| - Require the path to exist and be a directory. | |
| - Include dotfiles. | |
| - Sort entries alphabetically case-insensitively. | |
| - Append `/` to directories. | |
| - Skip entries that cannot be statted. | |
| - Return `(empty directory)` for an empty directory. | |
| - If limit is reached, append a notice suggesting a doubled limit. | |
| - Byte truncation is applied after formatting. | |
| ## System Prompt | |
| Default prompt construction inputs: | |
| - custom prompt, if configured | |
| - selected tool names | |
| - one-line tool snippets by name | |
| - prompt guideline bullets from tools and extensions | |
| - append-only system prompt text | |
| - cwd | |
| - context files | |
| - skills | |
| - current local date in `YYYY-MM-DD` | |
| If a custom prompt exists: | |
| 1. Start with custom prompt. | |
| 2. Append configured extra system prompt text. | |
| 3. Append project context files in `<project_context>` if present. | |
| 4. Append skills only if the read tool is selected. | |
| 5. Append current date and cwd. | |
| If no custom prompt exists: | |
| 1. Start with pi's default coding-assistant prompt. | |
| 2. Include an "Available tools" list only for selected tools that have prompt snippets. | |
| 3. Include guidelines: | |
| - If bash is selected and grep/find/ls are not, add `Use bash for file operations like ls, rg, find`. | |
| - Append normalized tool/extension guidelines without duplicates. | |
| - Always include `Be concise in your responses`. | |
| - Always include `Show file paths clearly when working with files`. | |
| 4. Include read-only pi documentation path hints. | |
| 5. Append configured extra system prompt text. | |
| 6. Append project context files. | |
| 7. Append skills only if the read tool is selected. | |
| 8. Append current date and cwd. | |
| The current working directory in the prompt uses `/` separators on all platforms. | |
| ## Skills | |
| Skills are resources with name, description, path, and body in `SKILL.md`. | |
| The runtime discovers skills from configured sources and extension-provided resource paths. The system prompt lists available skills when the read tool is available. Skill commands can be registered as `/skill:<name>` when enabled. | |
| Skill invocation expands into a user message shaped like: | |
| ```xml | |
| <skill name="{name}" location="{location}"> | |
| {content} | |
| </skill> | |
| {optional user message} | |
| ``` | |
| The parser recognizes exactly this XML-like block at the start of a message and returns skill name, location, body content, and optional trailing user message. | |
| ## User Input Handling | |
| High-level prompt handling in AgentSession: | |
| 1. If text is an extension command, execute it and do not call the model. | |
| 2. Expand prompt templates unless disabled. | |
| 3. Expand skill commands when enabled. | |
| 4. Apply extension input handlers. | |
| 5. Convert text and images into a user message. | |
| 6. If the agent is active: | |
| - Queue as steering or follow-up depending on requested streaming behavior. | |
| - Emit `queue_update`. | |
| 7. If the agent is idle: | |
| - Flush pending bash messages into context first. | |
| - Run the agent prompt. | |
| - Persist generated messages. | |
| - Run post-agent tasks: retries, auto-compaction, pending next-turn custom messages. | |
| Extension slash commands are not allowed in steering/follow-up queues. They must execute immediately or be rejected. | |
| ## Bash Outside Tool Calls | |
| Interactive mode supports user shell commands outside provider tool calls. | |
| Result messages use `bashExecution`: | |
| - `command` | |
| - `output` | |
| - `exitCode` | |
| - `cancelled` | |
| - `truncated` | |
| - `fullOutputPath` | |
| - `excludeFromContext` | |
| - `timestamp` | |
| Single-bang commands enter model context. Double-bang commands set `excludeFromContext` and are omitted from provider requests. | |
| Conversion to LLM text: | |
| ```text | |
| Ran `{command}` | |
| ``` | |
| Then either a fenced output block or `(no output)`, followed by status lines for cancellation, non-zero exit code, and truncation. | |
| ## Compaction | |
| Default settings: | |
| - enabled: true | |
| - reserveTokens: 16384 | |
| - keepRecentTokens: 20000 | |
| ### Token Estimation | |
| Use the last non-error, non-aborted assistant usage when available: | |
| ```text | |
| contextTokens = usage.totalTokens || usage.input + usage.output + usage.cacheRead + usage.cacheWrite | |
| ``` | |
| For messages after that assistant usage, add heuristic estimates: | |
| - text length divided by 4, rounded up. | |
| - images count as 4800 characters. | |
| - assistant tool calls count tool name length plus JSON arguments length. | |
| - bashExecution counts command plus output length. | |
| - summaries count summary length. | |
| If no assistant usage exists, estimate all messages heuristically. | |
| Trigger compaction when: | |
| ```text | |
| contextTokens > model.contextWindow - reserveTokens | |
| ``` | |
| ### Cut Point | |
| To keep recent context: | |
| 1. Consider entries between the previous compaction boundary and current end. | |
| 2. Valid cut points are user, assistant, custom, bashExecution, branch_summary, and custom_message entries. | |
| 3. Never cut at toolResult because it must follow its assistant tool call. | |
| 4. Walk backward from newest entries, accumulating estimated tokens. | |
| 5. Stop after accumulating at least `keepRecentTokens`. | |
| 6. Choose the closest valid cut point at or after that entry. | |
| 7. Scan backward to include adjacent non-message entries until a message or compaction boundary. | |
| 8. If the cut point is not a user message, find the user or bashExecution message that started that turn and mark the cut as a split turn. | |
| ### Summary Generation | |
| Generate a structured summary with the current model or configured compaction model: | |
| - Max summary tokens are `min(0.8 * reserveTokens, model.maxTokens if positive)`. | |
| - Convert current messages to LLM messages. | |
| - Serialize the conversation into plain text. | |
| - Use a summarization system prompt. | |
| - If a previous summary exists, use the update-summary prompt and include the previous summary in tags. | |
| - If custom instructions are provided, append `Additional focus: {instructions}`. | |
| - On provider stopReason `"error"`, fail compaction. | |
| Summary format must contain: | |
| - Goal | |
| - Constraints & Preferences | |
| - Progress: Done, In Progress, Blocked | |
| - Key Decisions | |
| - Next Steps | |
| - Critical Context | |
| After success, append a `compaction` entry with summary, `firstKeptEntryId`, `tokensBefore`, and optional file-operation details. Rebuild session context from the updated session. | |
| ## Context Overflow Recovery | |
| When a provider returns a context overflow error: | |
| 1. Detect the overflow by provider-specific error classification. | |
| 2. Attempt automatic compaction once per run. | |
| 3. Emit `compaction_start` with reason `"overflow"`. | |
| 4. If compaction succeeds, emit `compaction_end` with reason `"overflow"` and retry the provider request. | |
| 5. If compaction fails or was already attempted, surface the provider error. | |
| Threshold compaction is similar but reason is `"threshold"` and it runs after a turn when estimated usage crosses the configured threshold. | |
| Manual compaction uses reason `"manual"` and may include custom user instructions. | |
| ## Retry Semantics | |
| The session layer may retry failed assistant messages when retry is enabled. | |
| Default retry settings: | |
| - enabled: true | |
| - maxRetries: 3 | |
| - baseDelayMs: 2000 | |
| Retryable failures are transient provider/network failures. Non-retryable provider limit errors are not retried. | |
| On retry: | |
| 1. Emit `auto_retry_start` with attempt, max attempts, delay, and error message. | |
| 2. Wait with exponential backoff. | |
| 3. Remove or replace the failed assistant message as appropriate. | |
| 4. Continue from the last user or toolResult message. | |
| 5. Emit `auto_retry_end` with success or final error. | |
| Provider-layer retries are separate and controlled by provider retry settings. | |
| ## Model and Thinking Selection | |
| The active model comes from, in order: | |
| 1. Explicit CLI/SDK selection. | |
| 2. Session model change or latest assistant message when resuming. | |
| 3. Settings default provider/model. | |
| 4. Model registry default. | |
| Thinking levels are: | |
| ```text | |
| off, minimal, low, medium, high, xhigh | |
| ``` | |
| Only levels supported by the selected model are usable. When switching models: | |
| - Preserve an explicit scoped-model thinking level if provided. | |
| - Otherwise preserve the current thinking level if supported. | |
| - Otherwise clamp to the nearest supported level. | |
| - Default thinking level is `medium` at the coding-agent session layer, while the low-level Agent default is `off`. | |
| When sending provider options, omit reasoning entirely for `"off"`; otherwise pass the thinking level. | |
| Model changes append `model_change` entries. Thinking changes append `thinking_level_change` entries and emit `thinking_level_changed`. | |
| ## Extension Runtime | |
| Extensions can contribute: | |
| - tools | |
| - slash commands | |
| - prompt snippets and prompt guidelines | |
| - system prompt customizers | |
| - input transforms | |
| - event listeners | |
| - UI components | |
| - compaction hooks | |
| - branch summary hooks | |
| - resource paths for skills, prompts, themes, context files | |
| - shutdown handlers | |
| The extension runner is recreated on reload and rebound to the current AgentSession. Extension errors are reported through the configured error listener and should not crash the core loop unless the hook is part of a required preflight path. | |
| Tool registration rules: | |
| - Base tools are registered first. | |
| - SDK custom tools and extension tools are added afterward. | |
| - Tool names are unique in the active registry; later registrations may override when explicitly designed to do so. | |
| - Active tool filtering applies after all registrations. | |
| - The system prompt is rebuilt whenever the active tool set or resource set changes. | |
| ## Runtime Modes | |
| ### Interactive Mode | |
| Interactive mode owns terminal UI, keybindings, editor state, slash command menus, tree navigation, model selector, settings selector, login dialog, and rendering of streamed events. It uses AgentSession for all agent behavior. | |
| ### Print Mode | |
| Print mode sends one prompt, streams assistant output to stdout, optionally emits JSON events, then exits with success or failure depending on final agent state. | |
| ### JSON Event Stream Mode | |
| JSON mode serializes AgentSession events as JSONL. It should preserve event ordering and include enough message/tool fields for external clients to reconstruct the run. | |
| ### RPC Mode | |
| RPC mode communicates over stdin/stdout JSONL. It exposes session operations, prompt calls, cancellation, model/tool updates, and event subscription semantics without terminal rendering. | |
| ### SDK Mode | |
| SDK users can construct sessions programmatically, pass custom tools, settings, models, resource loaders, and event listeners, then call the same AgentSession operations. | |
| ## Session Tree Navigation | |
| The session tree allows branching inside a single file. | |
| - `branch(entryId)` moves the current leaf to an earlier entry. | |
| - New messages append as children of the current leaf, creating a branch. | |
| - `resetLeaf()` moves to before the first entry. | |
| - `branchWithSummary(entryId, summary, details, fromHook)` appends a branch summary at the branch point so the new branch retains context about the abandoned path. | |
| - `createBranchedSession(leafId)` can extract a branch into a new session file. | |
| When navigating away from a branch, pi may generate a branch summary. This summary is represented as `branch_summary` entry and converted to a user context message. | |
| ## Settings | |
| Settings are deep-merged with project settings overriding global settings. Nested objects merge one level; arrays and primitives replace. | |
| Important settings: | |
| - provider/model defaults | |
| - default thinking level | |
| - transport | |
| - steering and follow-up queue modes | |
| - compaction | |
| - branch summary | |
| - retry | |
| - shell path and shell command prefix | |
| - package/resource sources | |
| - extensions, skills, prompts, themes | |
| - terminal image behavior | |
| - image auto-resize and image blocking | |
| - enabled model patterns | |
| - session directory | |
| - HTTP/WebSocket timeouts | |
| File settings are guarded by lock files with short retry loops. Project settings live in `.pi/settings.json`; global settings live under the pi agent directory. | |
| ## Authentication | |
| Before provider requests, resolve request auth dynamically for the active model provider. Dynamic lookup is required because OAuth tokens may expire during long tool phases. | |
| If no usable model or API key exists, the session should reject prompt preflight with a clear auth guidance message and should not append a user message. | |
| The provider request may include: | |
| - API key or OAuth token | |
| - provider-specific headers | |
| - session id for cache-aware backends | |
| - transport preference | |
| ## Abort and Shutdown | |
| Abort behavior: | |
| - `abort()` aborts the active agent run if one exists. | |
| - Active tools receive the same abort signal. | |
| - Bash kills process trees. | |
| - File mutation tools do not release their per-file queue until in-flight filesystem work settles. | |
| - Compaction, branch summary, retry delay, and user bash each have separate abort controllers at the AgentSession layer. | |
| Shutdown behavior: | |
| - Emit extension shutdown events. | |
| - Run registered shutdown handlers. | |
| - Clean up session/provider resources. | |
| - Do not leave active command sessions running. | |
| ## Compatibility Checklist | |
| A reimplementation is behaviorally close when it passes these checks: | |
| 1. A simple prompt emits `agent_start`, `turn_start`, user `message_start/end`, assistant `message_start/update/end`, `turn_end`, `agent_end`. | |
| 2. An assistant tool call executes the tool, emits tool execution events, appends a toolResult, then makes another provider request. | |
| 3. Parallel tool calls return toolResult messages in assistant source order even if execution finishes out of order. | |
| 4. Steering input during a run is injected after the current turn but before natural stop. | |
| 5. Follow-up input during a run waits until no tool calls and no steering messages remain. | |
| 6. `continue()` rejects from an assistant message unless queued input exists. | |
| 7. Session context built from a branch includes only the selected path plus branch summaries. | |
| 8. Session context after compaction begins with a compaction summary and then kept recent messages. | |
| 9. `bashExecution` with `excludeFromContext` is omitted from provider context. | |
| 10. Read/write/edit/bash/grep/find/ls match the schemas and output notices above. | |
| 11. Context threshold compaction triggers based on the last assistant usage plus trailing estimates. | |
| 12. Context overflow causes one auto-compaction recovery attempt before surfacing failure. | |
| 13. Model and thinking changes persist as session entries and affect later provider requests. | |
| 14. Dynamic auth lookup runs before every provider request. | |
| 15. Event listeners are awaited, including `agent_end`, before the run becomes idle. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment