@raindrop-ai/pi-agent
Raindrop observability for Pi Agent — automatic tracing via subscriber or pi-coding-agent extension
Package details
Install @raindrop-ai/pi-agent from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@raindrop-ai/pi-agent- Package
@raindrop-ai/pi-agent- Version
0.2.7- Published
- Nov 11, 2026
- Downloads
- 21.1K/mo · 4,334/wk
- Author
- rsd-raindrop
- License
- MIT
- Types
- extension
- Size
- 628.3 KB
- Dependencies
- 0 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./dist/extension.js"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@raindrop-ai/pi-agent
Automatic observability for Pi Agent with Raindrop. Captures agent runs, LLM generations, tool calls, and token usage.
Two entry points:
@raindrop-ai/pi-agent— programmatic subscriber forpi-agent-coreusers@raindrop-ai/pi-agent/extension— pi-coding-agent CLI extension (auto-discovered viapi install)
Requires Pi 0.84.4 or later and Node.js 22.19.0 or later, matching Pi's runtime requirement. This requirement applies to the Pi integration, not other Raindrop SDKs.
Quick Start — Programmatic
import { Agent } from "@earendil-works/pi-agent-core";
import { createModels } from "@earendil-works/pi-ai";
import { openaiProvider } from "@earendil-works/pi-ai/providers/openai";
import { createRaindropPiAgent } from "@raindrop-ai/pi-agent";
const raindrop = createRaindropPiAgent({
writeKey: "your-write-key",
userId: "user-123",
});
const models = createModels();
models.setProvider(openaiProvider());
const model = models.getModel("openai", "gpt-4o-mini");
if (!model) throw new Error("Model unavailable");
const agent = new Agent({
streamFn: models.streamSimple,
initialState: {
systemPrompt: "You are a helpful assistant.",
model,
},
});
raindrop.subscribe(agent);
await agent.prompt("Hello!");
await raindrop.shutdown();
Quick Start — Pi Coding Agent CLI
pi install npm:@raindrop-ai/pi-agent
Set RAINDROP_WRITE_KEY in your environment. Traces appear automatically.
Shared client options
createRaindropPiAgent(...) also takes the options every Raindrop JS integration shares (the Pi Coding Agent CLI extension reads its settings from config files and env vars, so these apply to the programmatic client only):
| Option | Type | Default | Description |
|---|---|---|---|
redactPii |
boolean |
false |
Redact PII (emails, phone numbers, card numbers, addresses, secrets) from event input/output before it leaves the process |
redactPiiFields |
PiiFieldMap |
- | More wire field paths to redact when redactPii is on, e.g. { event: ["properties.customer.email"] }; add properties["error.message"] to also redact error messages |
flushTimeoutMs |
number |
10000 |
Deadline for flush() / shutdown(). Both resolve to a result (delivered, dropped, queued, ...) and never reject |
flush() and shutdown() resolve to { delivered, timedOut, dropped, queued, inFlight } and never reject; ignoring the result is fine.
Projects
If your org has multiple projects, route events to a specific one by passing its slug as projectId programmatically:
const raindrop = createRaindropPiAgent({
writeKey: "your-write-key",
projectId: "support-prod",
});
For the CLI extension, set the slug via the RAINDROP_PROJECT_ID env var (or the project_id config-file key). Either way this sets the X-Raindrop-Project-Id header on every event. Omit it (or use "default") to use your org's default Production project — the existing behavior. Single-project orgs need nothing new.
Model context and tool results
Programmatic subscriptions capture the system prompt, messages, and tools after
Pi applies transformContext. Model spans start before the provider call and
include reasoning and tool calls. Tool spans preserve the full result envelope,
including model-visible content and any details, for both successful and failed tools.
Tool catalog capture
Each model span also records the agent's context.tools as ai.prompt.tools: one
JSON document per tool with name, description and the full TypeBox parameters
schema as inputSchema. Unlike the tools preview inside ai.prompt, this list is not
subject to the text budget, so schemas survive intact. Absent when the agent has no
tool list; set tools to record a catalog explicitly.
| Option | Type | Default | Description |
|---|---|---|---|
tools |
ToolDeclaration[] |
- | Overrides the tool catalog recorded as ai.prompt.tools on every model span. Use when the integration cannot see the tools the model was given. Accepted on createRaindropPiAgent and per subscription via subscribe(agent, { tools }); the subscription value wins. tools: [] records an empty catalog. |
For agents that finish by committing a result through a tool, provide an output reader. It runs at completion and cannot interrupt the agent if it throws:
raindrop.subscribe(agent, {
runName: "Simulate lookup",
getOutput: () => JSON.stringify(committedResult),
});
Payload size limits
Programmatic event and span text fields default to 1,000,000 characters.
Set maxTextFieldChars on createRaindropPiAgent to use a different positive
limit across the subscriber and both shippers. For full capture of large replay
payloads, use Number.MAX_SAFE_INTEGER; transport and ingest limits still apply.
The CLI extension retains its existing event and span limits.
Oversized JSON tool payloads are pruned to fit the configured limit while staying
valid JSON. A stricter OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT can still truncate
serialized JSON afterward in the shared trace shipper.
Per-call context
For one call, wrap
it in raindrop.withContext(...): userId, convoId, eventName,
properties and attachments apply to every event captured inside the
callback, sync or async (nested calls merge; the inner value wins). Your
properties win over keys the integration sets itself (ai.usage.*,
error.*, ...).
const result = await raindrop.withContext(
{
userId: "user-123",
properties: { tenant: "acme" },
attachments: [{ type: "text", role: "input", name: "policy.txt", value: policyText }],
},
() => agent.prompt("Summarize the policy"),
);
Attachments need type (text, image or iframe) and role (input or
output); entries without them are dropped. Images go in as data URIs or URLs.
Your properties win
Properties you pass (as defaults, per call, or through events.setProperties / events.patch / events.finish) are merged into the event. If one of your keys has the same name as a key the integration sets itself, such as ai.usage.*, ai.provider, error.type / error.message, or the integration's own prefixed keys, your value is kept, whatever order the updates are sent in. With debug on, the integration logs each key you override.
Event ids
Pass your own id for a turn with raindrop.withContext({ eventId }, () => ...) around the call; the event and every span of the turn carry it. Use a fresh id per turn. After a turn finishes, raindrop.lastEventId holds its id, for attaching a signal or feedback:
await raindrop.signals.track({ eventId: raindrop.lastEventId!, name: "thumbs_up", type: "feedback", sentiment: "POSITIVE" });
Finishes you make through raindrop.events.* don't change lastEventId. It holds the last turn finished anywhere in the process, so on a client shared by concurrent turns, use the id you passed instead.
Documentation
See the full Pi Agent docs for configuration, per-subscribe overrides, and extension settings.
License
MIT
Application Git identity
Set appGit on createRaindropPiAgent to override commitSha, commitDirty, and optional ordinary branch, or pass false to opt out. For the CLI extension, use app_git in ~/.pi/agent/raindrop.json or .pi/raindrop.json, with commit_sha, commit_dirty, branch, source_directory, detect_branch, and auto_detect; app_git: false opts out. Pi may observe a coding workspace remotely, so the revision is unknown by default: ambient deploy/CI and observer working-directory metadata are ignored. Local Git discovery requires an explicit sourceDirectory / source_directory (or RAINDROP_GIT_SOURCE_DIRECTORY); explicit RAINDROP_COMMIT_SHA values are honored. Automatic branch discovery requires detectBranch: true / detect_branch: true.