4.2 KiB
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.pyand the translation logic is insrc/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 <prompt>
Notes:
--printis required for non-interactive runs.--mode jsonoutputs all agent events (no TUI banners).- Pi does not support
-- <prompt>; 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 <id>`
The token is the short session id, derived from the first JSON object in the session file. If the id cannot be read, Takopi falls back to the session file path.
Why not --resume?
--resume/-ropens an interactive session picker; it does not accept a session token. Takopi must use--session <path>instead.
3. Session lifecycle + serialization
Takopi requires serialization per session token:
- For new runs (
resume=None), do not acquire a lock until astartedevent is emitted (Takopi emits this as soon as the first JSON event arrives). - Once the session is known, acquire a lock for
pi:<session_path>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:
{"type":"tool_execution_start","toolCallId":"tool_1","toolName":"bash","args":{"command":"ls"}}
Mapping:
- Emit
actionwithphase="started". action.id = toolCallId.action.kindfrom tool name (see section 5).action.titlederived from tool + args.
4.2 tool_execution_end
Example:
{"type":"tool_execution_end","toolCallId":"tool_1","toolName":"bash","result":{...},"isError":false}
Mapping:
- Emit
actionwithphase="completed". ok = !isError.- Carry
resultandisErrorindetailfor 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
stopReasoniserrororaborted, storeerrorMessage. - Capture
usageforcompleted.usage.
4.4 agent_end
Example:
{"type":"agent_end","messages":[...]}
Mapping:
- Emit a single
completedevent:ok = trueunless the last assistant message hasstopReasonerrororaborted.answer = last assistant text(frommessage_endoragent_end.messages).error = errorMessageif 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: <path> |
grep |
tool |
grep: <pattern> |
find |
tool |
find: <pattern> |
ls |
tool |
ls: <path> |
| (default) | tool |
tool name |
For file_change, include detail.changes = [{"path": <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:
[pi]
model = "..."
provider = "..."
extra_args = []
Use extra_args for any Pi CLI flags not explicitly mapped.