# Pi -> Takopi event mapping (spec) This document describes how the Pi runner translates Pi CLI `--mode json` JSONL events into Takopi events. > **Authoritative source:** The schema definitions are in `src/takopi/schemas/pi.py` and the translation logic is in `src/takopi/runners/pi.py`. When in doubt, refer to the code. The goal is to make Pi feel identical to the Codex/Claude runners from the bridge/renderer point of view while preserving Takopi invariants (stable action ids, per-session serialization, single completed event). --- ## 1. Input stream contract (Pi CLI) Pi CLI emits **one JSON object per line** (JSONL) when invoked with: ``` pi --print --mode json ``` Notes: - `--print` is required for non-interactive runs. - `--mode json` outputs all agent events (no TUI banners). - Pi does not support `-- `; prompts starting with `-` must be prefixed (Takopi does this automatically). --- ## 2. Resume tokens and resume lines - Engine id: `pi` - Canonical resume line (embedded in chat): ``` `pi --session ` ``` The token is the **session JSONL file path**. Why not `--resume`? - `--resume/-r` opens an interactive session picker; it does not accept a session token. Takopi must use `--session ` instead. --- ## 3. Session lifecycle + serialization Takopi requires **serialization per session token**: - For new runs (`resume=None`), do **not** acquire a lock until a `started` event is emitted (Takopi emits this as soon as the first JSON event arrives). - Once the session is known, acquire a lock for `pi:` and hold it until the run completes. - For resumed runs, acquire the lock immediately on entry. --- ## 4. Event translation (Pi JSONL -> Takopi) Pi emits `AgentSessionEvent` objects. Only a subset is required for Takopi. ### 4.1 `tool_execution_start` Example: ```json {"type":"tool_execution_start","toolCallId":"tool_1","toolName":"bash","args":{"command":"ls"}} ``` Mapping: - Emit `action` with `phase="started"`. - `action.id = toolCallId`. - `action.kind` from tool name (see section 5). - `action.title` derived from tool + args. ### 4.2 `tool_execution_end` Example: ```json {"type":"tool_execution_end","toolCallId":"tool_1","toolName":"bash","result":{...},"isError":false} ``` Mapping: - Emit `action` with `phase="completed"`. - `ok = !isError`. - Carry `result` and `isError` in `detail` for debugging. ### 4.3 `message_end` (assistant) Pi emits message lifecycle events. For `message_end` where `message.role == "assistant"`: - Store the latest assistant text as the **final answer fallback**. - If `stopReason` is `error` or `aborted`, store `errorMessage`. - Capture `usage` for `completed.usage`. ### 4.4 `agent_end` Example: ```json {"type":"agent_end","messages":[...]} ``` Mapping: - Emit a single `completed` event: - `ok = true` unless the last assistant message has `stopReason` `error` or `aborted`. - `answer = last assistant text` (from `message_end` or `agent_end.messages`). - `error = errorMessage` if present. - `resume = ResumeToken(engine="pi", value=session_path)`. - `usage = last assistant usage`. ### 4.5 Other events Ignore unknown events. If a JSONL line is malformed, emit a warning action and continue (default `JsonlSubprocessRunner` behavior). --- ## 5. Tool name -> ActionKind mapping heuristics Pi tool names are lower-case by default. Suggested mapping: | Tool name | ActionKind | Title logic | | --- | --- | --- | | `bash` | `command` | `args.command` | | `edit`, `write` | `file_change` | `args.path` | | `read` | `tool` | `read: ` | | `grep` | `tool` | `grep: ` | | `find` | `tool` | `find: ` | | `ls` | `tool` | `ls: ` | | (default) | `tool` | tool name | For `file_change`, include `detail.changes = [{"path": , "kind": "update"}]`. --- ## 6. Usage mapping Takopi `completed.usage` should mirror Pi's assistant `usage` object without transformation. --- ## 7. Suggested Takopi config keys A minimal TOML config for Pi: ```toml [pi] model = "..." provider = "..." extra_args = [] ``` Use `extra_args` for any Pi CLI flags not explicitly mapped.