> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://respan.ai/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://respan.ai/docs/_mcp/server.

# pi (tracing)

> Trace interactive pi coding sessions with Respan using the @respan/instrumentation-pi package: turns, model calls, tool executions, and compactions.

[pi](https://pi.dev) is a minimal terminal coding agent. The `@respan/instrumentation-pi` package is a pi extension that traces your pi sessions to Respan: every turn, model call, tool execution, and compaction. Nothing is written to disk on your machine; spans are batched in memory and sent straight to Respan.

> **Note**
>
> This page covers interactive `pi` sessions in the terminal. To trace the `@earendil-works/pi-coding-agent` SDK inside your own application, see [Pi Coding Agent SDK](/docs/integrations/pi-sdk).

#### Set up Respan

Create an account at [platform.respan.ai](https://platform.respan.ai) and grab an [API key](https://platform.respan.ai/platform/gateway/api-keys).

For application setup, follow [Agent setup](/docs/documentation/agent-setup).

#### Use Respan Gateway

See [pi gateway setup](/docs/gateway/pi) to route pi's model calls through the Respan gateway. Every LLM call is then logged by the gateway with no SDK; the extension on this page adds the agent-level structure (turns, tool calls, threads) on top.

#### Package README

* [`@respan/instrumentation-pi` README](https://github.com/RespanAI/respan/tree/main/javascript-sdks/instrumentations/respan-instrumentation-pi) — full option, configuration, and troubleshooting reference

## How it works

The extension traces pi by observing it, not by proxying it:

* pi talks to its model provider directly.
* pi's native extension events notify the extension when sessions start, turns run, models respond, and tools execute.
* The extension turns those events into OpenTelemetry spans and sends them to Respan from inside the pi process. There is no daemon, no local journal, and no log directory: if Respan is unreachable, the batch is retried and then dropped, and pi keeps working. For guaranteed delivery through outages, see [Delivery guarantees](#delivery-guarantees).
* Credentials come from `RESPAN_API_KEY` or from `~/.respan/credentials.json` written by `respan auth login`; the extension never stores them in its own config.

Each prompt you send appears in Respan as one trace with turn, model call, and tool spans; all traces of a pi session share the session id as thread id. See [What gets traced](#what-gets-traced).

## Set up tracing

The extension requires pi v0.65.0 or later.

#### Install pi

If you haven't already, [install pi](https://pi.dev).

#### Install the extension

Paste this into your coding agent:

```text
Help me set up Respan tracing for pi: fetch and follow https://www.respan.ai/docs/documentation/agent-setup.md
```

For manual installation:

```bash
pi install npm:@respan/instrumentation-pi
export RESPAN_API_KEY="YOUR_RESPAN_API_KEY"
```

#### Restart pi

Restart `pi` so it loads the extension. The footer shows `Respan: tracing` while active, and after each prompt a widget below the editor shows a link to its trace.

#### View your trace

Open the [Traces page](https://platform.respan.ai/platform/traces). Each pi session is one trace and every prompt adds a turn to it, even after a restart or a week-long pause; the [Threads view](/docs/documentation/features/observe/view-debug-traces#follow-a-session-with-threads) shows the whole pi session as a conversation.

## What gets traced

Each agent run (one prompt → `agent_end`) is one root agent span, `pi.turn-<n>.agent`, displayed as `agent.turn-<n>` — n is the prompt's number within the pi session, so a session resumed with `pi -c` continues at the next turn.

```text
pi.turn-1.agent (agent)                         one per prompt, shown as agent.turn-1
├── pi.chat (chat)                              one per assistant message
│     prompts, completion, tool_calls, usage, TTFT, cost
├── bash.tool (tool)                            one per tool execution
├── read.tool (tool)                            skill usage detected from SKILL.md
├── pi.turn-2.steer (task)                      a message steered into the running turn, shown as steer.turn-2
├── pi.chat (chat)
└── pi.compaction (task)                        when compaction happens mid-run
```

| Data                            | Description                                                                                                                                                                            |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Turns                           | One root agent span per prompt, with the prompt as input, the final assistant text as output, turn / tool-call counts, thinking level, pi version, session file and working directory. |
| Model calls                     | One chat span per assistant message: provider, model, the conversation sent to the model, the response, this turn's tool calls, available tools.                                       |
| Usage                           | Input, output, total, cache-read and cache-creation tokens, reasoning tokens, estimated cost, time to first token.                                                                     |
| Tool executions                 | One tool span per execution with arguments and result; failures carry the error. When pi reads a `SKILL.md` or runs the skill tool, the span is tagged with the skill name.            |
| Compaction and branch summaries | Task spans with the trigger and the resulting summary.                                                                                                                                 |
| Steered messages                | A message delivered into a turn that is already working gets its own `steer.turn-<n>` span at the point pi injected it. See [Steering](#steering).                                     |
| Git metadata                    | Repository URL (credentials stripped), branch and commit of the working directory on each turn span.                                                                                   |
| Errors                          | Assistant errors and aborts, tool errors, and runs interrupted by quitting pi.                                                                                                         |

Chat and tool spans are sent the moment they complete, so a long-running turn streams into the dashboard while it is still running; the turn span arrives when the run ends.

## Steering

When you send a message while pi is still working, pi does not start a new run. It queues the message and delivers it into the running turn at the next tool boundary, and the agent carries on with the new information. Respan records each delivered message as its own row under the turn, named `steer.turn-<n>`, at the exact point pi injected it:

```text
agent.turn-1                steer_count: 2
├── llm.gpt-5
├── tool.read
├── steer.turn-2            input = the steered message
├── llm.gpt-5               after_steer = true
├── tool.bash
├── steer.turn-3
├── llm.gpt-5               after_steer = true
└── tool.write
agent.turn-4
```

| Where                        | What is recorded                                                                                                                                                                               |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `steer.turn-<n>` span        | Input is the steered message. Metadata: `turn_number` (the steer's own number), `steered_into_turn`, `steer_index`, and `delivered_after` (`tool_results`, `assistant_reply`, or `run_start`). |
| Turn span                    | Input lists the original prompt followed by every steered message, so one click shows everything the run was given. Metadata: `steer_count`.                                                   |
| First LLM call after a steer | Metadata `after_steer = true` and `steer_turn_numbers`. The steered message is the last item of that call's input.                                                                             |

**Turn numbers.** Turn numbers count the session's user messages, which keeps them stable when a session is resumed in another process. A steered message takes the next number, so a `turn-1` run that received three steers (`steer.turn-2`, `steer.turn-3`, `steer.turn-4`) is followed by `agent.turn-5`. Nothing was skipped: the numbers in between are the steer rows inside the turn.

**Find steers across traces.** On the [Spans page](/docs/documentation/features/observe/explore-spans#filter-by-custom-properties), filter by custom property:

* `after_steer` is `true`: exactly the model calls that reacted to a steered message.
* `steer_count` is `1` (or `2`, `3`, ...): the turn spans that received that many steers.
* `steered_into_turn` is `1`: the steer spans delivered into turn 1, each with its message as input.

A steer is an event inside the run, not a new run: the steps after it stay under the same turn, because the agent usually keeps working on its original task while taking the new message into account.

Steer spans require `@respan/instrumentation-pi` 0.1.5 or later.

Upgrade with `pi update npm:@respan/instrumentation-pi`.

## Settings

Save non-secret configuration to `~/.pi/agent/respan.json`. A project-level `.pi/respan.json` overrides it, and environment variables override both. The API key is never written to these files.

| Setting             | Saved key       | Environment variable                      | How to change it                                                                                       |
| ------------------- | --------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| Enable tracing      | `enabled`       | `RESPAN_PI_TRACING` (`true`/`false`)      | Ask your coding agent to update `enabled`, or edit the file.                                           |
| API endpoint        | `base_url`      | `RESPAN_BASE_URL`                         | Point it at a local [collector](#delivery-guarantees) or a self-hosted Respan.                         |
| Trace scope         | `trace_scope`   | `RESPAN_PI_TRACE_SCOPE` (`session`/`run`) | `session` (default) = one trace per pi session with a root per prompt; `run` = one trace per prompt.   |
| Customer            | `customer_id`   | `RESPAN_CUSTOMER_ID`                      | Sets `customer_identifier` on every span.                                                              |
| Workflow name       | `workflow_name` | —                                         | Set `workflow_name` in the configuration file; `traceloop.workflow.name` on turn spans (default `pi`). |
| Project             | `project_id`    | `RESPAN_PROJECT_ID`                       | Set `project_id` in the configuration file; routes traces to a Respan project.                         |
| Extra span metadata | `metadata`      | —                                         | Edit `metadata` (string key-value pairs) in the file.                                                  |
| Debug output        | —               | `RESPAN_PI_DEBUG=1`                       | Prints the extension's diagnostics to stderr.                                                          |

Trace scope decides what one trace is:

* `session` (default): every prompt of the session joins one trace whose id is derived from the pi session id, so a session resumed with `pi -c` in another process, or after a week-long pause, keeps adding turns to the same trace. Respan shows the turns as multiple roots under one synthetic root, ordered by time; the trace's duration spans from the first to the latest turn. No span is ever held open across idle time.
* `run`: every prompt is its own trace with its own cost and latency. All traces of the session still share `thread_identifier` = the pi session id, so the Threads view shows the whole session in order.

To switch:

```text
Set my pi Respan tracing to one trace per prompt by setting trace_scope to run in its tracing configuration.
```

## Common workflows

### Install for one project only

```bash
pi install -l npm:@respan/instrumentation-pi
```

This records the extension in the project's `.pi/settings.json` instead of `~/.pi/agent/settings.json`.

### Turn tracing off temporarily

```bash
RESPAN_PI_TRACING=false pi
```

For a persistent change, ask your coding agent to set `enabled: false` in the tracing configuration. The package remains installed.

### Non-interactive runs

`pi -p "..."` runs are traced the same way; the extension flushes before pi exits.

### Upgrade

```bash
pi update npm:@respan/instrumentation-pi
```

### Uninstall

```bash
pi remove npm:@respan/instrumentation-pi   # add -l if it was installed with --local
rm ~/.pi/agent/respan.json                  # optional: drop the saved settings
```

## Delivery guarantees

Inside the pi process, spans are batched in memory and posted to Respan with retries. Nothing is written to your disk, so nothing piles up on the machine, and a session that sits idle for days costs nothing. The trade-off is that if Respan is unreachable for longer than the retry window while pi is running, that batch is lost.

If you need at-least-once delivery through outages and restarts, run the Respan Collector (see the [CLI](/docs/documentation/cli)) next to pi and point the extension at it:

```text
Set up a local Respan Collector for pi tracing and point the extension's base_url at it. Follow https://www.respan.ai/docs/documentation/cli.md for collector commands.
```

The collector keeps a bounded on-disk queue that drains as soon as Respan acknowledges the data; it never grows without limit.

## Troubleshooting

**Traces do not appear.** Check the following, in order:

1. Verify the extension is installed: `pi list` should show `npm:@respan/instrumentation-pi`.
2. Verify the key is available: `RESPAN_API_KEY` is set, or `respan auth status` shows you are logged in. If credentials are missing, use the setup prompt above.
3. Verify tracing is enabled: `enabled` is not `false` in `~/.pi/agent/respan.json` or `.pi/respan.json`, and `RESPAN_PI_TRACING` is not set to `false`.
4. Restart pi after any configuration change, then run at least one prompt; the turn span is sent when the run ends.
5. Run `RESPAN_PI_DEBUG=1 pi` to print the extension's diagnostics to stderr (initialization, export errors).

Tracing failures never interrupt pi: the session keeps working even when traces do not appear, and the footer shows `Respan: tracing unavailable: <reason>`.

**Turn numbers skip, for example `agent.turn-1` is followed by `agent.turn-5`.** The turn in between received steered messages, and each one took a number. Expand the turn to see the `steer.turn-<n>` rows. See [Steering](#steering).

**The trace link widget does not appear.** The widget is only shown in interactive mode, after the first completed prompt, and only for the Respan cloud endpoint (self-hosted or collector-routed setups show the footer status only).

## Resources

* [`@respan/instrumentation-pi` on npm](https://www.npmjs.com/package/@respan/instrumentation-pi)
* [Package README](https://github.com/RespanAI/respan/tree/main/javascript-sdks/instrumentations/respan-instrumentation-pi) — full configuration reference
* [Pi Coding Agent SDK](/docs/integrations/pi-sdk) — trace pi inside your own application
* [CLI: collector commands](/docs/documentation/cli)
* [pi documentation](https://pi.dev)