Agent to instrument and Bitfab records every run as a trace. Each prompt becomes a replayable root span. Each model request and each tool call becomes a span beneath it. You write no withSpan wrappers.
Canonical signatures: TypeScript reference
Experimental.
getPiIntegration may change before it is stable.Client capture off applies to the integration. With
captureEnabled: false, the instrumented agent runs exactly as before and sends nothing. Keep it instrumented: replay items and seedTrace still record when explicitly invoked.Supported Languages
The integration works with the current
@earendil-works/pi-agent-core 1.x packages. It also works with the earlier @mariozechner/pi-agent-core 0.x packages. It imports neither, so it adds no dependency.
Recent pnpm versions may refuse to add
@bitfab/sdk to a Pi project. @earendil-works/pi-ai depends on @google/genai, protobufjs, and esbuild, which have build scripts. pnpm stops any pnpm add with ERR_PNPM_IGNORED_BUILDS until each is approved or denied. Run pnpm approve-builds, or list them under allowBuilds in pnpm-workspace.yaml, then add the SDK again.Quick Start
instrument returns the same agent. Call it exactly as before. Runs through the returned agent are traced under the admin-agent key. Runs through the original, uninstrumented agent are not.
What Gets Captured
Eachagent.prompt() or agent.continue() records one trace.
Each
llm span also records the model, its input, output, and cached token counts, and the cost Pi computed. Bitfab shows these in its cost and token views. Bitfab’s input token count is the whole prompt, so it includes Pi’s cache reads and cache writes.
A model request that ends with Pi’s error or aborted stop reason marks its llm span and the run’s root span with Pi’s error message. A tool that throws marks its function span with the error.
The integration never records API keys, request headers, or callbacks from Pi’s request options.
Tools added after instrument are traced too, whether you assign agent.state.tools or push onto it. Each prompt’s trace is grouped under the agent’s sessionId when it has one, so a conversation reads as one session in Bitfab.
Calls made outside a run through the instrumented agent are not recorded. A model request or tool call that reaches the integration with no run open logs one warning naming the fix.
Coding-agent sessions
AnAgentSession from createAgentSession in @earendil-works/pi-coding-agent instruments the same way.
session.prompt() records one root span. The session’s own retries and follow-up runs nest inside it rather than starting new traces. Its model requests and tool calls are recorded beneath that root, including the custom tools you pass to createAgentSession. Each trace is grouped under the session’s id. traced.agent is the instrumented agent the session runs.
Replay
The root span carries the prompt, so a recorded run replays by key with no hand-written root. Passreplay a function that builds a fresh agent, prompts it, and returns the reply.
mockToolsOnReplay.
true(the default) answers every tool call from the recording.falseruns every tool live.- A list of tool names answers only those tools from the recording. The rest run live.
Replaying a later turn of a conversation
A Pi agent keeps its conversation inagent.state.messages. When a prompt runs on an agent that already has a conversation, the root span also records that conversation. Replay restores it into the fresh agent before the prompt runs, so a later turn replays with the turns before it.
- The fresh agent keeps its own system prompt and tools. Only the user, assistant, and tool result messages are restored, so a replay tests your current prompt.
- A recorded
continue()run replays ascontinue(). Your replay function can callagent.prompt(request)for every item. When the recorded run was acontinue(),requestisundefinedand the integration callscontinue()instead. - A conversation larger than one span can carry (about 2 MB, usually from large tool results or images) is recorded from its most recent turns, as many as fit, always starting at a user message. The SDK logs a warning when it records such a turn, and again when a replay starts from only those turns. Pruning
agent.state.messageskeeps long conversations replayable in full. - For an
AgentSession, build it in your replay function withSessionManager.inMemory(). The conversation is restored into that in-memory session. A session saved to disk is never written to by replay. Bitfab logs one warning and replays the turn without its history instead.
Grading
Pi traces work with every Bitfab grader, dataset, and experiment, with nothing specific to Pi. A grader can read the root’s prompt and reply, every tool call with its arguments and result, and every model request. For example, a grader for an admin agent can check that a destructive tool only runs when the user asked for it. See Datasets and Graders.Nesting with core tracing
Inside awithSpan or withTrace root of the same key, the integration records no second root. Its model and tool spans nest under your root.
withTrace root, the calls your tool code makes are recorded beneath that tool’s span. The tool records once, not once as the tool and again as its own function.
TypeScript
Method Signature
traceFunctionKey(string, required). Groups every trace from this integration under one key in Bitfab.options.mockToolsOnReplay(optional). Which tool calls replay answers from the recording. Defaults totrue.
instrument returns the agent or session it was given, traced. Calling it again with the same agent returns the same traced agent. Passing an agent that any Pi integration already instrumented returns it unchanged, so each call is recorded once.
Throws: instrument throws a BitfabError when the value is neither a Pi Agent nor an AgentSession.
bitfab.getFunction(key).getPiIntegration(options) is the same integration, bound to that key.