From 0881fc6259dafffd3f3f580e12d6f1576f6d8a05 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 08:20:47 +0000 Subject: [PATCH 1/3] docs(javascript): Add Pi Durable guide Co-Authored-By: Andrei Borza --- .../javascript/common/agent-tracing/index.mdx | 6 + .../common/configuration/event-loop-block.mdx | 1 + .../configuration/integrations/anthropic.mdx | 1 + .../configuration/integrations/firebase.mdx | 1 + .../integrations/google-genai.mdx | 1 + .../configuration/integrations/groq.mdx | 1 + .../configuration/integrations/langchain.mdx | 1 + .../configuration/integrations/langgraph.mdx | 1 + .../configuration/integrations/mistral.mdx | 1 + .../configuration/integrations/nest.mdx | 1 + .../configuration/integrations/openai.mdx | 1 + .../integrations/together-ai.mdx | 1 + .../configuration/integrations/typesafe.mdx | 1 + .../configuration/integrations/vercelai.mdx | 1 + .../javascript/common/feature-flags/index.mdx | 1 + .../migration/v7-to-v8/v8-opentelemetry.mdx | 1 + .../tracing/instrumentation/caches-module.mdx | 2 +- .../user-feedback/configuration/index.mdx | 1 + .../configuration/index__v7.x.mdx | 3 +- .../guides/pi-durable/cloudflare.mdx | 168 ++++++++++++++++ .../javascript/guides/pi-durable/index.mdx | 188 ++++++++++++++++++ docs/product/agents/getting-started.mdx | 3 +- includes/pi-durable/captured-summary.mdx | 3 + includes/pi-durable/experimental-alert.mdx | 5 + includes/pi-durable/link-conversations.mdx | 7 + includes/pi-durable/next-steps.mdx | 5 + includes/pi-durable/verify.mdx | 9 + includes/pi-durable/what-gets-captured.mdx | 23 +++ src/components/platformIcon.tsx | 7 + src/icons/pi-durable.svg | 5 + 30 files changed, 446 insertions(+), 4 deletions(-) create mode 100644 docs/platforms/javascript/guides/pi-durable/cloudflare.mdx create mode 100644 docs/platforms/javascript/guides/pi-durable/index.mdx create mode 100644 includes/pi-durable/captured-summary.mdx create mode 100644 includes/pi-durable/experimental-alert.mdx create mode 100644 includes/pi-durable/link-conversations.mdx create mode 100644 includes/pi-durable/next-steps.mdx create mode 100644 includes/pi-durable/verify.mdx create mode 100644 includes/pi-durable/what-gets-captured.mdx create mode 100644 src/icons/pi-durable.svg diff --git a/docs/platforms/javascript/common/agent-tracing/index.mdx b/docs/platforms/javascript/common/agent-tracing/index.mdx index 4d49c088f020ef..cbe02f841c9340 100644 --- a/docs/platforms/javascript/common/agent-tracing/index.mdx +++ b/docs/platforms/javascript/common/agent-tracing/index.mdx @@ -99,6 +99,12 @@ As of SDK version `11.0.0`, Sentry auto-instruments the most popular AI SDKs and title: "Flue", icon: "flue", }, + { + to: "/agent-tracing/pi-durable/", + href: "/platforms/javascript/guides/pi-durable/", + title: "Pi Durable", + icon: "pi-durable", + }, ]} /> diff --git a/docs/platforms/javascript/common/configuration/event-loop-block.mdx b/docs/platforms/javascript/common/configuration/event-loop-block.mdx index 927f02eb4f1163..5ad553ade7eaf5 100644 --- a/docs/platforms/javascript/common/configuration/event-loop-block.mdx +++ b/docs/platforms/javascript/common/configuration/event-loop-block.mdx @@ -7,6 +7,7 @@ supported: - javascript.eve - javascript.flue - javascript.mastra + - javascript.pi-durable - javascript.aws-lambda - javascript.azure-functions - javascript.express diff --git a/docs/platforms/javascript/common/configuration/integrations/anthropic.mdx b/docs/platforms/javascript/common/configuration/integrations/anthropic.mdx index 07f72854ebe78c..08c1dc5e9b6d85 100644 --- a/docs/platforms/javascript/common/configuration/integrations/anthropic.mdx +++ b/docs/platforms/javascript/common/configuration/integrations/anthropic.mdx @@ -6,6 +6,7 @@ supportedCategories: - serverless notSupported: - javascript.mastra + - javascript.pi-durable --- diff --git a/docs/platforms/javascript/common/configuration/integrations/firebase.mdx b/docs/platforms/javascript/common/configuration/integrations/firebase.mdx index 8e2ca3518b892b..bdad7114dbb8f4 100644 --- a/docs/platforms/javascript/common/configuration/integrations/firebase.mdx +++ b/docs/platforms/javascript/common/configuration/integrations/firebase.mdx @@ -6,6 +6,7 @@ supported: - javascript.eve - javascript.flue - javascript.mastra + - javascript.pi-durable - javascript.firebase - javascript.gcp-functions - javascript.astro diff --git a/docs/platforms/javascript/common/configuration/integrations/google-genai.mdx b/docs/platforms/javascript/common/configuration/integrations/google-genai.mdx index 12c687fe90609b..1512a6a9872a92 100644 --- a/docs/platforms/javascript/common/configuration/integrations/google-genai.mdx +++ b/docs/platforms/javascript/common/configuration/integrations/google-genai.mdx @@ -6,6 +6,7 @@ supportedCategories: - serverless notSupported: - javascript.mastra + - javascript.pi-durable --- diff --git a/docs/platforms/javascript/common/configuration/integrations/groq.mdx b/docs/platforms/javascript/common/configuration/integrations/groq.mdx index 9f8d9dd72b3bb1..dddece51893247 100644 --- a/docs/platforms/javascript/common/configuration/integrations/groq.mdx +++ b/docs/platforms/javascript/common/configuration/integrations/groq.mdx @@ -6,6 +6,7 @@ supportedCategories: - serverless notSupported: - javascript.mastra + - javascript.pi-durable --- diff --git a/docs/platforms/javascript/common/configuration/integrations/langchain.mdx b/docs/platforms/javascript/common/configuration/integrations/langchain.mdx index 2affbeabd9428f..858aac058c1268 100644 --- a/docs/platforms/javascript/common/configuration/integrations/langchain.mdx +++ b/docs/platforms/javascript/common/configuration/integrations/langchain.mdx @@ -6,6 +6,7 @@ supportedCategories: - serverless notSupported: - javascript.mastra + - javascript.pi-durable --- diff --git a/docs/platforms/javascript/common/configuration/integrations/langgraph.mdx b/docs/platforms/javascript/common/configuration/integrations/langgraph.mdx index 63331cfbbdafe0..51e7061e5c428b 100644 --- a/docs/platforms/javascript/common/configuration/integrations/langgraph.mdx +++ b/docs/platforms/javascript/common/configuration/integrations/langgraph.mdx @@ -6,6 +6,7 @@ supportedCategories: - serverless notSupported: - javascript.mastra + - javascript.pi-durable --- diff --git a/docs/platforms/javascript/common/configuration/integrations/mistral.mdx b/docs/platforms/javascript/common/configuration/integrations/mistral.mdx index a7c4abfc77a2d0..d09e263dba2b1a 100644 --- a/docs/platforms/javascript/common/configuration/integrations/mistral.mdx +++ b/docs/platforms/javascript/common/configuration/integrations/mistral.mdx @@ -6,6 +6,7 @@ supportedCategories: - serverless notSupported: - javascript.mastra + - javascript.pi-durable --- diff --git a/docs/platforms/javascript/common/configuration/integrations/nest.mdx b/docs/platforms/javascript/common/configuration/integrations/nest.mdx index 1af22a579c87e3..66c3533ae7823d 100644 --- a/docs/platforms/javascript/common/configuration/integrations/nest.mdx +++ b/docs/platforms/javascript/common/configuration/integrations/nest.mdx @@ -6,6 +6,7 @@ supported: - javascript.eve - javascript.flue - javascript.mastra + - javascript.pi-durable - javascript.nestjs --- diff --git a/docs/platforms/javascript/common/configuration/integrations/openai.mdx b/docs/platforms/javascript/common/configuration/integrations/openai.mdx index 97b2c93ecabcc2..f1811b9b590ec5 100644 --- a/docs/platforms/javascript/common/configuration/integrations/openai.mdx +++ b/docs/platforms/javascript/common/configuration/integrations/openai.mdx @@ -6,6 +6,7 @@ supportedCategories: - serverless notSupported: - javascript.mastra + - javascript.pi-durable --- diff --git a/docs/platforms/javascript/common/configuration/integrations/together-ai.mdx b/docs/platforms/javascript/common/configuration/integrations/together-ai.mdx index 9ea04af0450126..94c80c7f8cbb04 100644 --- a/docs/platforms/javascript/common/configuration/integrations/together-ai.mdx +++ b/docs/platforms/javascript/common/configuration/integrations/together-ai.mdx @@ -6,6 +6,7 @@ supportedCategories: - serverless notSupported: - javascript.mastra + - javascript.pi-durable --- diff --git a/docs/platforms/javascript/common/configuration/integrations/typesafe.mdx b/docs/platforms/javascript/common/configuration/integrations/typesafe.mdx index e496bc386773a6..f2881f3e32250f 100644 --- a/docs/platforms/javascript/common/configuration/integrations/typesafe.mdx +++ b/docs/platforms/javascript/common/configuration/integrations/typesafe.mdx @@ -7,6 +7,7 @@ supportedCategories: notSupported: - javascript.cloudflare - javascript.mastra + - javascript.pi-durable --- diff --git a/docs/platforms/javascript/common/configuration/integrations/vercelai.mdx b/docs/platforms/javascript/common/configuration/integrations/vercelai.mdx index a7fd32fe3c6938..38e21098010172 100644 --- a/docs/platforms/javascript/common/configuration/integrations/vercelai.mdx +++ b/docs/platforms/javascript/common/configuration/integrations/vercelai.mdx @@ -6,6 +6,7 @@ supportedCategories: - serverless notSupported: - javascript.mastra + - javascript.pi-durable --- diff --git a/docs/platforms/javascript/common/feature-flags/index.mdx b/docs/platforms/javascript/common/feature-flags/index.mdx index 382ef6708bd978..9e19bfbbfc0f98 100644 --- a/docs/platforms/javascript/common/feature-flags/index.mdx +++ b/docs/platforms/javascript/common/feature-flags/index.mdx @@ -8,6 +8,7 @@ notSupported: - javascript.eve - javascript.flue - javascript.mastra + - javascript.pi-durable --- ## Prerequisites diff --git a/docs/platforms/javascript/common/migration/v7-to-v8/v8-opentelemetry.mdx b/docs/platforms/javascript/common/migration/v7-to-v8/v8-opentelemetry.mdx index eadc108e734239..3ab28f8c08c7b4 100644 --- a/docs/platforms/javascript/common/migration/v7-to-v8/v8-opentelemetry.mdx +++ b/docs/platforms/javascript/common/migration/v7-to-v8/v8-opentelemetry.mdx @@ -8,6 +8,7 @@ supported: - javascript.eve - javascript.flue - javascript.mastra + - javascript.pi-durable - javascript.aws-lambda - javascript.azure-functions - javascript.express diff --git a/docs/platforms/javascript/common/tracing/instrumentation/caches-module.mdx b/docs/platforms/javascript/common/tracing/instrumentation/caches-module.mdx index b8b073bb175542..114f7cb371d564 100644 --- a/docs/platforms/javascript/common/tracing/instrumentation/caches-module.mdx +++ b/docs/platforms/javascript/common/tracing/instrumentation/caches-module.mdx @@ -30,7 +30,7 @@ If you're using anything other than Sentry's Redis integration, you'll need to m You'll need to create two spans - one indicating that something is being put into the cache, and a second one indicating that something is being fetched from the cache. - + Make sure that there's an active span before you create your cache spans. If you're using a web framework like Express, a span will be created for you automatically. See Tracing for more information. diff --git a/docs/platforms/javascript/common/user-feedback/configuration/index.mdx b/docs/platforms/javascript/common/user-feedback/configuration/index.mdx index 591b9cc391ad31..19100ef64938ef 100644 --- a/docs/platforms/javascript/common/user-feedback/configuration/index.mdx +++ b/docs/platforms/javascript/common/user-feedback/configuration/index.mdx @@ -7,6 +7,7 @@ notSupported: - javascript.eve - javascript.flue - javascript.mastra + - javascript.pi-durable --- diff --git a/docs/platforms/javascript/common/user-feedback/configuration/index__v7.x.mdx b/docs/platforms/javascript/common/user-feedback/configuration/index__v7.x.mdx index 94b579bd60acf9..bc11ab87076dee 100644 --- a/docs/platforms/javascript/common/user-feedback/configuration/index__v7.x.mdx +++ b/docs/platforms/javascript/common/user-feedback/configuration/index__v7.x.mdx @@ -10,6 +10,7 @@ notSupported: - javascript.eve - javascript.flue - javascript.mastra + - javascript.pi-durable --- @@ -18,7 +19,7 @@ In version 7 of our JavaScript SDK, User Feedback was released as a Beta integra - + ## User Feedback Widget diff --git a/docs/platforms/javascript/guides/pi-durable/cloudflare.mdx b/docs/platforms/javascript/guides/pi-durable/cloudflare.mdx new file mode 100644 index 00000000000000..cfde28a4f313ce --- /dev/null +++ b/docs/platforms/javascript/guides/pi-durable/cloudflare.mdx @@ -0,0 +1,168 @@ +--- +title: Cloudflare Quick Start +sidebar_order: 0.5 +description: Learn how to send Pi Durable agent runs, model requests, tool calls, and errors to Sentry from Cloudflare Durable Objects. +--- + +[Pi Durable](https://earendil.com/posts/pi-durable/) is Earendil's durable agent harness, built on the same foundations as the Pi coding agent. This guide sets up the Sentry SDK in a Worker that runs Pi Durable in a Durable Object, for example through the Agents SDK `PiHarness`, so agent runs, model requests, tool calls, and errors flow into [Sentry Agent Tracing](/product/agents/). + + + +Running Pi Durable on Node.js? Follow the [Pi Durable Quick Start](../) instead. + +## Prerequisites + +Before you begin, you need: + +- A Sentry [account](https://sentry.io/signup/) and [project](/product/projects/). The project's DSN tells the SDK where to send data. +- A Worker using `@earendil-works/pi-durable` version `1.0.0` or later, built with Vite and `@cloudflare/vite-plugin`. To host Pi Durable with `PiHarness`, you need the Agents SDK (`agents`) version `0.26.0` or later. +- `@sentry/cloudflare` version `11.6.0` or later. + + + +## Install + +Choose the features you want to configure, and this guide will show you how: + + + +Install the Sentry Cloudflare SDK: + +```bash {tabTitle:npm} +npm install @sentry/cloudflare@^11.6.0 +``` + +```bash {tabTitle:yarn} +yarn add @sentry/cloudflare@^11.6.0 +``` + +```bash {tabTitle:pnpm} +pnpm add @sentry/cloudflare@^11.6.0 +``` + +## Configure + +Add the Sentry Vite plugin to your Vite config: + +```typescript {filename:vite.config.mts} +import { cloudflare } from "@cloudflare/vite-plugin"; +import { sentryCloudflareVitePlugin } from "@sentry/cloudflare/vite"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [cloudflare(), sentryCloudflareVitePlugin()], +}); +``` + +The plugin instruments `Harness.open()` at build time, so every Harness your Worker opens is traced. It also wraps your Worker entry, your Durable Object classes, and your Agents SDK classes with Sentry, so `Sentry.init()` runs in the Durable Object that hosts Pi Durable without any changes to your code. + +Create an `instrument.server.ts` file next to your Worker entry (the file named in Wrangler's `main`). The plugin picks it up by convention and uses its default export as the Sentry options: + +```typescript {filename:src/instrument.server.ts} +import { defineCloudflareOptions } from "@sentry/cloudflare"; + +export default defineCloudflareOptions((env: Env) => ({ + dsn: "___PUBLIC_DSN___", + // ___PRODUCT_OPTION_START___ performance + + // Set tracesSampleRate to 1.0 to capture 100% + // of spans for tracing. + // We recommend adjusting this value in production. + tracesSampleRate: 1.0, + // ___PRODUCT_OPTION_END___ performance +})); +``` + +Enable Node.js compatibility in your Wrangler config so the SDK can run on Workers: + +```toml {filename:wrangler.toml} +main = "src/index.ts" +compatibility_flags = ["nodejs_compat"] +``` + +Your Pi Durable code doesn't change. For example, an Agents SDK class that hosts Pi Durable with `PiHarness` is traced as is: + +```typescript {filename:src/assistant.ts} +import { createModels } from "@earendil-works/pi-ai/models"; +import { openaiProvider } from "@earendil-works/pi-ai/providers/openai"; +import { createRegistry, Harness } from "@earendil-works/pi-durable"; +import { Agent } from "agents"; +import { PiHarness } from "agents/harness/pi"; + +export class Assistant extends Agent { + registry = createRegistry(); + + harness = new PiHarness({ + harness: ({ storage, context }) => { + // pi-ai reads provider keys from `process.env` by default, so hand over the Worker's secret. + const models = createModels({ + authContext: { + env: async (name) => + name === "OPENAI_API_KEY" ? this.env.OPENAI_API_KEY : undefined, + fileExists: async () => false, + }, + }); + models.setProvider(openaiProvider()); + return Harness.open( + storage, + { models, registry: this.registry }, + context + ); + }, + defaults: { model: { provider: "openai", id: "gpt-6.1-sol" } }, + }); + + constructor(ctx: DurableObjectState, env: Env) { + super(ctx, env); + this.lifecycle.use(this.harness); + } +} +``` + + + +### Control the Data You Send to Sentry (Optional) + +By default, the SDK sends the inputs and outputs of your model requests and tool calls, such as prompts, responses, and tool arguments. +This will give you rich debugging context. Review the data your agent handles before you deploy to production. + +The SDK always filters sensitive values whose keys match a built-in denylist, such as `auth` or `password`, and sends `[Filtered]` instead. + +To send less data, turn off the categories you don't need in the `dataCollection` option: + +```typescript {filename:src/instrument.server.ts} {5-7} +import { defineCloudflareOptions } from "@sentry/cloudflare"; + +export default defineCloudflareOptions((env: Env) => ({ + dsn: "___PUBLIC_DSN___", + dataCollection: { + genAI: { inputs: false, outputs: false }, + }, +})); +``` + +For the full list of categories and their defaults, see the [`dataCollection` options](../configuration/options/#dataCollection). + + + + + + + +## Troubleshooting + +- **No AI spans at all.** Confirm `sentryCloudflareVitePlugin()` is in your Vite config and that you build and run the Worker through Vite. A Worker you deploy with `wrangler` directly, without the plugin, doesn't get the build-time instrumentation of `Harness.open()`. +- **Every restart of the Durable Object starts a new conversation in Sentry.** This is expected for now. See [Link Conversations](#link-conversations). + + + + + +## Supported Versions + +- `@sentry/cloudflare`: `>=11.6.0` +- `@earendil-works/pi-durable`: `>=1.0.0 <2.0.0` +- `agents`: `>=0.26.0`, when you host Pi Durable with `PiHarness` diff --git a/docs/platforms/javascript/guides/pi-durable/index.mdx b/docs/platforms/javascript/guides/pi-durable/index.mdx new file mode 100644 index 00000000000000..45cc4aefb98866 --- /dev/null +++ b/docs/platforms/javascript/guides/pi-durable/index.mdx @@ -0,0 +1,188 @@ +--- +title: Pi Durable +description: Learn how to send Pi Durable agent runs, model requests, tool calls, and errors to Sentry with the Sentry SDK. +sdk: sentry.javascript.node +fallbackGuide: javascript.node +icon: pi-durable +categories: + - server + - server-only +keywords: + - pi + - pi-durable + - earendil + - ai + - agents + - agent tracing +--- + +[Pi Durable](https://earendil.com/posts/pi-durable/) is Earendil's durable agent harness, built on the same foundations as the Pi coding agent. It runs many conversations at once and survives crashes, picking each run up from its last checkpoint. This guide sets up the Sentry SDK in a Pi Durable app so agent runs, model requests, tool calls, and errors flow into [Sentry Agent Tracing](/product/agents/). + + + +Running Pi Durable in a Cloudflare Durable Object? Follow the [Cloudflare Quick Start](./cloudflare/) instead. + +## Prerequisites + +Before you begin, you need: + +- A Sentry [account](https://sentry.io/signup/) and [project](/product/projects/). The project's DSN tells the SDK where to send data. +- A Node.js application using `@earendil-works/pi-durable` version `1.0.0` or later. +- `@sentry/node` version `11.6.0` or later, or [`@sentry/cloudflare@^11.6.0` when running on Cloudflare](./cloudflare/). Browser runtimes are not supported. + + + +## Install + +Choose the features you want to configure, and this guide will show you how: + + + +Install the Sentry Node SDK: + + + +```bash {tabTitle:npm} +npm install @sentry/node@^11.6.0 +``` + +```bash {tabTitle:yarn} +yarn add @sentry/node@^11.6.0 +``` + +```bash {tabTitle:pnpm} +pnpm add @sentry/node@^11.6.0 +``` + + + + + +```bash {tabTitle:npm} +npm install @sentry/node@^11.6.0 @sentry/profiling-node@^11.6.0 +``` + +```bash {tabTitle:yarn} +yarn add @sentry/node@^11.6.0 @sentry/profiling-node@^11.6.0 +``` + +```bash {tabTitle:pnpm} +pnpm add @sentry/node@^11.6.0 @sentry/profiling-node@^11.6.0 +``` + + + +## Configure + +Create an instrument file that calls `Sentry.init()`. The Pi Durable integration is enabled by default, so you don't add it to `integrations`: + +```javascript {filename:instrument.mjs} +import * as Sentry from "@sentry/node"; +// ___PRODUCT_OPTION_START___ profiling +import { nodeProfilingIntegration } from "@sentry/profiling-node"; +// ___PRODUCT_OPTION_END___ profiling + +Sentry.init({ + dsn: "___PUBLIC_DSN___", + // ___PRODUCT_OPTION_START___ profiling + + integrations: [ + // Add our Profiling integration + nodeProfilingIntegration(), + ], + // ___PRODUCT_OPTION_END___ profiling + // ___PRODUCT_OPTION_START___ performance + + // Set tracesSampleRate to 1.0 to capture 100% + // of spans for tracing. + // We recommend adjusting this value in production. + // Learn more at + // https://docs.sentry.io/platforms/javascript/guides/node/configuration/options/#tracesSampleRate + tracesSampleRate: 1.0, + // ___PRODUCT_OPTION_END___ performance + // ___PRODUCT_OPTION_START___ profiling + + // Set profileSessionSampleRate to 1.0 to profile every session. + // Learn more at + // https://docs.sentry.io/platforms/javascript/configuration/options/#profileSessionSampleRate + profileSessionSampleRate: 1.0, + // ___PRODUCT_OPTION_END___ profiling +}); +``` + +Preload the instrument file with the `--import` flag when you start your app: + +```json {filename:package.json} +{ + "scripts": { + "start": "node --import ./instrument.mjs server.mjs" + } +} +``` + +The SDK instruments `Harness.open()` while Node.js loads `@earendil-works/pi-durable`, so Sentry has to start before your app imports it. If you import the instrument file from your app instead of preloading it, the SDK can't instrument Pi Durable. If you bundle your server and can't pass `--import`, use the bundler plugin instead. + +Your Pi Durable code doesn't change. Every Harness your app opens is traced, whichever storage and execution environment it uses: + +```javascript {filename:server.mjs} +import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context"; +import { createModels } from "@earendil-works/pi-ai/models"; +import { openaiProvider } from "@earendil-works/pi-ai/providers/openai"; +import { createRegistry, Harness } from "@earendil-works/pi-durable"; +import { openNodeSqliteStorage } from "@earendil-works/pi-durable/storage/sqlite/node"; + +const context = BACKGROUND_CONTEXT; +const models = createModels(); +models.setProvider(openaiProvider()); + +const harness = await Harness.open( + await openNodeSqliteStorage("./agent.sqlite"), + { models, registry: createRegistry() }, + context +); +harness.resume(); +``` + + + +### Control the Data You Send to Sentry (Optional) + + + +To change recording for Pi Durable only, pass `recordInputs` and `recordOutputs` to the integration. They take precedence over `dataCollection`: + +```javascript {filename:instrument.mjs} +Sentry.init({ + dsn: "___PUBLIC_DSN___", + integrations: [ + Sentry.piDurableIntegration({ + recordInputs: false, + recordOutputs: false, + }), + ], +}); +``` + + + + + + + +## Troubleshooting + +- **No AI spans at all.** Confirm the instrument file is preloaded with `--import`, or that your bundle is built with the Sentry bundler plugin. Importing it at the top of your entry file runs too late, because `@earendil-works/pi-durable` is already loaded by then. +- **OpenAI, Anthropic, or Google Gen AI calls outside Pi Durable stopped showing up.** Once a Pi Durable run starts, the SDK turns off these provider integrations for the whole process, so Pi Durable's model requests aren't counted twice. See _What gets captured?_ below. + + + + + +## Supported Versions + +- `@sentry/node`: `>=11.6.0` +- `@sentry/cloudflare`: `>=11.6.0` +- `@earendil-works/pi-durable`: `>=1.0.0 <2.0.0` diff --git a/docs/product/agents/getting-started.mdx b/docs/product/agents/getting-started.mdx index b7d705ac33cdff..8234431b984710 100644 --- a/docs/product/agents/getting-started.mdx +++ b/docs/product/agents/getting-started.mdx @@ -31,10 +31,9 @@ To start sending agent data to Sentry, make sure you've created a Sentry project - [Flue](/platforms/javascript/guides/flue/) - [Laravel](/platforms/php/guides/laravel/agent-tracing/) - [Mastra](/platforms/javascript/guides/mastra/) -- [Flue](/platforms/javascript/guides/flue/) -- [Eve](/platforms/javascript/guides/eve/) - [Next.js](/platforms/javascript/guides/nextjs/agent-tracing/) - [Node.js](/platforms/javascript/guides/node/agent-tracing/) +- [Pi Durable](/platforms/javascript/guides/pi-durable/) - [Python](/platforms/python/agent-tracing/) - [Ruby](/platforms/ruby/tracing/instrumentation/custom-instrumentation/ai-agents-module/) - [.NET](/platforms/dotnet/tracing/instrumentation/ai-agents-module/) diff --git a/includes/pi-durable/captured-summary.mdx b/includes/pi-durable/captured-summary.mdx new file mode 100644 index 00000000000000..193e24c25c1dcd --- /dev/null +++ b/includes/pi-durable/captured-summary.mdx @@ -0,0 +1,3 @@ +With this setup, Sentry captures errors thrown by your tools and extensions, and AI spans for every run (agent runs, model requests, tool calls, token usage, and latency). Manual spans you start inside a tool nest under that tool's span. Prompts and model outputs are recorded on your AI spans by default. + +To review what's captured and turn recording of prompts and responses off, see [Control the Data You Send to Sentry](#control-the-data-you-send-to-sentry-optional) below. diff --git a/includes/pi-durable/experimental-alert.mdx b/includes/pi-durable/experimental-alert.mdx new file mode 100644 index 00000000000000..2cac57d63caa6f --- /dev/null +++ b/includes/pi-durable/experimental-alert.mdx @@ -0,0 +1,5 @@ + + +Pi Durable is experimental, and its API can change between releases. The Sentry integration supports `@earendil-works/pi-durable` `>=1.0.0 <2.0.0` and requires JavaScript SDK version `11.6.0` or later. + + diff --git a/includes/pi-durable/link-conversations.mdx b/includes/pi-durable/link-conversations.mdx new file mode 100644 index 00000000000000..5c91486d946eae --- /dev/null +++ b/includes/pi-durable/link-conversations.mdx @@ -0,0 +1,7 @@ +### Link Conversations + +Sentry groups the runs of a Pi Durable conversation into a single [Conversation](/product/agents/conversations/) automatically — there's no Sentry-specific setup. The SDK sets `gen_ai.conversation.id` on the agent, model, and tool spans of every run to `:`. + +Pi Durable numbers conversations per storage, starting from 1, so the SDK prefixes a random id for each opened Harness to keep root conversations from different storages apart. This id changes when the Harness reopens, for example after a process restart or after a Durable Object restarts. Runs from before and after a restart then show up as separate conversations in Sentry. + +A subagent runs in a conversation of its own. Its first run nests under the tool call that created it, so the subagent shows up inside the parent's trace. diff --git a/includes/pi-durable/next-steps.mdx b/includes/pi-durable/next-steps.mdx new file mode 100644 index 00000000000000..f897729c73e3de --- /dev/null +++ b/includes/pi-durable/next-steps.mdx @@ -0,0 +1,5 @@ +## Next Steps + +- [Track AI agent spend with dashboards and alerts](https://sentry.io/cookbook/monitor-ai-agent-spend-with-dashboards-and-alerts/): build a dashboard for cost, tokens, models, and conversations. +- [Pi Durable's README](https://github.com/earendil-works/pi/tree/main/packages/durable): conversations, tasks, extensions, and storage, from the Pi side. +- Use the appropriate [JavaScript framework guide](/platforms/javascript/) for application monitoring in a separate frontend or service. diff --git a/includes/pi-durable/verify.mdx b/includes/pi-durable/verify.mdx new file mode 100644 index 00000000000000..2972c11612785e --- /dev/null +++ b/includes/pi-durable/verify.mdx @@ -0,0 +1,9 @@ +## Verify + +Submit a message to one of your conversations, ideally one that calls a tool. Then open [**Explore → Agents**](https://sentry.io/orgredirect/organizations/:orgslug/explore/agents/) in Sentry. Select the conversation to open its detail. The timeline shows the agent run, model requests, tool calls, token usage, latency, and any errors your tools threw. + +If no data appears, confirm that: + +- The `dsn` belongs to the Sentry project you're viewing. +- `tracesSampleRate` is greater than `0`. +- Your app completed at least one run after you added the setup. diff --git a/includes/pi-durable/what-gets-captured.mdx b/includes/pi-durable/what-gets-captured.mdx new file mode 100644 index 00000000000000..e12ea8842ee3bb --- /dev/null +++ b/includes/pi-durable/what-gets-captured.mdx @@ -0,0 +1,23 @@ + + +Sentry's Pi Durable integration maps Pi Durable's runtime operations to Sentry operations for the Agents dashboards: + +| Pi Durable Operation | Sentry Operation | +| ------------------------------- | --------------------- | +| Run of a conversation | `gen_ai.invoke_agent` | +| Model request (`pi.generation`) | `gen_ai.chat` | +| Tool call (`pi.tool`) | `gen_ai.execute_tool` | + +Spans are tagged with `sentry.origin: auto.ai.pi_durable` and carry the model, token usage, and finish reasons for each request. Each run starts a trace of its own, separate from the request that submitted it, so concurrent runs never end up in each other's traces. + +**Crash recovery.** When the process dies during a run, the spans that were open in that process are lost. Once a new process resumes the run, its spans arrive in a new trace that starts with the resumed step, such as the rerun of a tool call. + +**Tool results.** A tool span records the result the model receives, including output streamed with `api.output()` and results an `afterTool` hook rewrote. + +**Errors.** A tool that throws is captured as an error, and its span is marked failed. Pi Durable hands the throw back to the model as a tool result, so your app never sees it. The built-in coding tools (`bash`, `read`, `edit`, `write`) are the exception: they throw to report expected failures, such as a command that exits with a non-zero code, so their span is marked failed but no error is captured. Failures Pi Durable only passes to `onReport`, such as a hook or system prompt section that throws, are captured too. A failed model request isn't captured as an error; its `chat` span carries the error status. + +**Provider SDKs.** Pi Durable sends model requests through `@earendil-works/pi-ai`, which calls the `openai`, `@anthropic-ai/sdk`, and `@google/genai` clients, or the Workers AI binding. From the first run on, the SDK turns off its OpenAI, Anthropic, Google Gen AI, and Workers AI integrations for the whole process, so these requests aren't counted twice. Calls your app makes with those clients outside Pi Durable aren't traced either. Amazon Bedrock requests are still reported a second time by the AWS integration. + +**Not captured yet.** Tool calls that Pi Durable answers without running the tool, custom tasks you define with `defineTask`, agent names (Pi Durable has none), a link between a run and the request that submitted it, and a marker for compaction requests. + + diff --git a/src/components/platformIcon.tsx b/src/components/platformIcon.tsx index bfd682a912a3eb..1fb05e55e23d3d 100644 --- a/src/components/platformIcon.tsx +++ b/src/components/platformIcon.tsx @@ -323,6 +323,7 @@ import XboxSVGLarge from 'platformicons/svg_80x80/xbox.svg'; import EveSVG from 'sentry-docs/icons/eve.svg'; import ExpoSVG from 'sentry-docs/icons/expo.svg'; import FlueSVG from 'sentry-docs/icons/flue.svg'; +import PiDurableSVG from 'sentry-docs/icons/pi-durable.svg'; const formatToSVG = { HTML5: { @@ -766,6 +767,10 @@ const formatToSVG = { sm: PerlSVG, lg: PerlSVGLarge, }, + 'pi-durable': { + sm: PiDurableSVG, + lg: PiDurableSVG, + }, php: { sm: PhpSVG, lg: PhpSVGLarge, @@ -1079,6 +1084,7 @@ export const PLATFORM_TO_ICON = { 'javascript-nuxt': 'nuxt', 'javascript-nitro': 'nitro', 'javascript-node': 'nodejs', + 'javascript-pi-durable': 'pi-durable', 'javascript-react': 'react', 'javascript-react-router': 'react-router', 'javascript-remix': 'remix', @@ -1105,6 +1111,7 @@ export const PLATFORM_TO_ICON = { 'nintendo-switch': 'nintendo-switch', openai: 'openai', perl: 'perl', + 'pi-durable': 'pi-durable', php: 'php', 'php-laravel': 'laravel', 'php-monolog': 'php', diff --git a/src/icons/pi-durable.svg b/src/icons/pi-durable.svg new file mode 100644 index 00000000000000..58e2e3a052ac13 --- /dev/null +++ b/src/icons/pi-durable.svg @@ -0,0 +1,5 @@ + + + + + From f4895e79a4e9ee9b4a97407daf1a45100df1f536 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 08:25:08 +0000 Subject: [PATCH 2/3] docs(javascript): Split Pi Durable error notes Co-Authored-By: Andrei Borza --- includes/pi-durable/what-gets-captured.mdx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/includes/pi-durable/what-gets-captured.mdx b/includes/pi-durable/what-gets-captured.mdx index e12ea8842ee3bb..e7b5506b8a9058 100644 --- a/includes/pi-durable/what-gets-captured.mdx +++ b/includes/pi-durable/what-gets-captured.mdx @@ -14,7 +14,11 @@ Spans are tagged with `sentry.origin: auto.ai.pi_durable` and carry the model, t **Tool results.** A tool span records the result the model receives, including output streamed with `api.output()` and results an `afterTool` hook rewrote. -**Errors.** A tool that throws is captured as an error, and its span is marked failed. Pi Durable hands the throw back to the model as a tool result, so your app never sees it. The built-in coding tools (`bash`, `read`, `edit`, `write`) are the exception: they throw to report expected failures, such as a command that exits with a non-zero code, so their span is marked failed but no error is captured. Failures Pi Durable only passes to `onReport`, such as a hook or system prompt section that throws, are captured too. A failed model request isn't captured as an error; its `chat` span carries the error status. +**Errors.** A tool that throws is captured as an error, and its span is marked failed. Pi Durable hands the throw back to the model as a tool result, so your app never sees it. + +The built-in coding tools (`bash`, `read`, `edit`, `write`) are the exception: they throw to report expected failures, such as a command that exits with a non-zero code, so their span is marked failed but no error is captured. + +Failures Pi Durable only passes to `onReport`, such as a hook or system prompt section that throws, are captured too. A failed model request isn't captured as an error; its `chat` span carries the error status. **Provider SDKs.** Pi Durable sends model requests through `@earendil-works/pi-ai`, which calls the `openai`, `@anthropic-ai/sdk`, and `@google/genai` clients, or the Workers AI binding. From the first run on, the SDK turns off its OpenAI, Anthropic, Google Gen AI, and Workers AI integrations for the whole process, so these requests aren't counted twice. Calls your app makes with those clients outside Pi Durable aren't traced either. Amazon Bedrock requests are still reported a second time by the AWS integration. From 2e53c5dad77150f678ee1839a8c47ee5c7c4def0 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 08:37:23 +0000 Subject: [PATCH 3/3] docs(agents): Keep getting-started list scoped to Pi Durable Co-Authored-By: Andrei Borza --- docs/product/agents/getting-started.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/product/agents/getting-started.mdx b/docs/product/agents/getting-started.mdx index 8234431b984710..d7eb1d4e149bf6 100644 --- a/docs/product/agents/getting-started.mdx +++ b/docs/product/agents/getting-started.mdx @@ -31,6 +31,8 @@ To start sending agent data to Sentry, make sure you've created a Sentry project - [Flue](/platforms/javascript/guides/flue/) - [Laravel](/platforms/php/guides/laravel/agent-tracing/) - [Mastra](/platforms/javascript/guides/mastra/) +- [Flue](/platforms/javascript/guides/flue/) +- [Eve](/platforms/javascript/guides/eve/) - [Next.js](/platforms/javascript/guides/nextjs/agent-tracing/) - [Node.js](/platforms/javascript/guides/node/agent-tracing/) - [Pi Durable](/platforms/javascript/guides/pi-durable/)