@sentry/cloudflare 10.69.0 → 10.70.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/build/cjs/durableobject.js +114 -36
  2. package/build/cjs/durableobject.js.map +1 -1
  3. package/build/cjs/flush.js.map +1 -1
  4. package/build/cjs/instrumentations/agents/index.js +2 -0
  5. package/build/cjs/instrumentations/agents/index.js.map +1 -1
  6. package/build/cjs/instrumentations/agents/instrumentAgentCallableRpc.js +2 -2
  7. package/build/cjs/instrumentations/agents/instrumentAgentCallableRpc.js.map +1 -1
  8. package/build/cjs/instrumentations/agents/instrumentAgentRequestConversation.js +19 -0
  9. package/build/cjs/instrumentations/agents/instrumentAgentRequestConversation.js.map +1 -0
  10. package/build/cjs/instrumentations/agents/instrumentChatAgentConversation.js +3 -3
  11. package/build/cjs/instrumentations/agents/instrumentChatAgentConversation.js.map +1 -1
  12. package/build/cjs/instrumentations/agents/types.js +58 -13
  13. package/build/cjs/instrumentations/agents/types.js.map +1 -1
  14. package/build/cjs/request.js +3 -2
  15. package/build/cjs/request.js.map +1 -1
  16. package/build/cjs/utils/invocationScope.js +12 -0
  17. package/build/cjs/utils/invocationScope.js.map +1 -0
  18. package/build/cjs/utils/rpcMeta.js +4 -0
  19. package/build/cjs/utils/rpcMeta.js.map +1 -1
  20. package/build/cjs/workflows.js +1 -1
  21. package/build/cjs/workflows.js.map +1 -1
  22. package/build/cjs/wrapMethodWithSentry.js +2 -3
  23. package/build/cjs/wrapMethodWithSentry.js.map +1 -1
  24. package/build/esm/durableobject.js +117 -39
  25. package/build/esm/durableobject.js.map +1 -1
  26. package/build/esm/flush.js.map +1 -1
  27. package/build/esm/instrumentations/agents/index.js +2 -0
  28. package/build/esm/instrumentations/agents/index.js.map +1 -1
  29. package/build/esm/instrumentations/agents/instrumentAgentCallableRpc.js +2 -2
  30. package/build/esm/instrumentations/agents/instrumentAgentCallableRpc.js.map +1 -1
  31. package/build/esm/instrumentations/agents/instrumentAgentRequestConversation.js +17 -0
  32. package/build/esm/instrumentations/agents/instrumentAgentRequestConversation.js.map +1 -0
  33. package/build/esm/instrumentations/agents/instrumentChatAgentConversation.js +4 -4
  34. package/build/esm/instrumentations/agents/instrumentChatAgentConversation.js.map +1 -1
  35. package/build/esm/instrumentations/agents/types.js +56 -13
  36. package/build/esm/instrumentations/agents/types.js.map +1 -1
  37. package/build/esm/package.json +1 -1
  38. package/build/esm/request.js +4 -3
  39. package/build/esm/request.js.map +1 -1
  40. package/build/esm/utils/invocationScope.js +10 -0
  41. package/build/esm/utils/invocationScope.js.map +1 -0
  42. package/build/esm/utils/rpcMeta.js +4 -1
  43. package/build/esm/utils/rpcMeta.js.map +1 -1
  44. package/build/esm/workflows.js +2 -2
  45. package/build/esm/workflows.js.map +1 -1
  46. package/build/esm/wrapMethodWithSentry.js +3 -4
  47. package/build/esm/wrapMethodWithSentry.js.map +1 -1
  48. package/build/types/durableobject.d.ts +17 -8
  49. package/build/types/durableobject.d.ts.map +1 -1
  50. package/build/types/flush.d.ts +1 -1
  51. package/build/types/flush.d.ts.map +1 -1
  52. package/build/types/instrumentations/agents/index.d.ts +4 -3
  53. package/build/types/instrumentations/agents/index.d.ts.map +1 -1
  54. package/build/types/instrumentations/agents/instrumentAgentCallableRpc.d.ts +4 -1
  55. package/build/types/instrumentations/agents/instrumentAgentCallableRpc.d.ts.map +1 -1
  56. package/build/types/instrumentations/agents/instrumentAgentRequestConversation.d.ts +16 -0
  57. package/build/types/instrumentations/agents/instrumentAgentRequestConversation.d.ts.map +1 -0
  58. package/build/types/instrumentations/agents/instrumentChatAgentConversation.d.ts +10 -9
  59. package/build/types/instrumentations/agents/instrumentChatAgentConversation.d.ts.map +1 -1
  60. package/build/types/instrumentations/agents/types.d.ts +63 -18
  61. package/build/types/instrumentations/agents/types.d.ts.map +1 -1
  62. package/build/types/request.d.ts.map +1 -1
  63. package/build/types/utils/invocationScope.d.ts +24 -0
  64. package/build/types/utils/invocationScope.d.ts.map +1 -0
  65. package/build/types/utils/rpcMeta.d.ts +8 -0
  66. package/build/types/utils/rpcMeta.d.ts.map +1 -1
  67. package/build/types/wrapMethodWithSentry.d.ts +1 -1
  68. package/build/types/wrapMethodWithSentry.d.ts.map +1 -1
  69. package/build/types-ts3.8/durableobject.d.ts +17 -8
  70. package/build/types-ts3.8/flush.d.ts +1 -1
  71. package/build/types-ts3.8/instrumentations/agents/index.d.ts +4 -3
  72. package/build/types-ts3.8/instrumentations/agents/instrumentAgentCallableRpc.d.ts +4 -1
  73. package/build/types-ts3.8/instrumentations/agents/instrumentAgentRequestConversation.d.ts +16 -0
  74. package/build/types-ts3.8/instrumentations/agents/instrumentChatAgentConversation.d.ts +10 -9
  75. package/build/types-ts3.8/instrumentations/agents/types.d.ts +63 -18
  76. package/build/types-ts3.8/utils/invocationScope.d.ts +24 -0
  77. package/build/types-ts3.8/utils/rpcMeta.d.ts +8 -0
  78. package/build/types-ts3.8/wrapMethodWithSentry.d.ts +1 -1
  79. package/package.json +3 -3
@@ -4,9 +4,10 @@
4
4
  *
5
5
  * - **Callable RPC spans** — a span (op `rpc`) for each `@callable()` method invoked over WebSocket.
6
6
  * - **Conversation correlation** — sets the conversation id on the scope for each unit of agent
7
- * work — chat turn or callable RPC call — so `gen_ai` spans created within it are correlated, for
8
- * chat and plain agents alike. Defaults to the instance `name` and is rotated when the chat is
9
- * cleared (the `message:clear` observability event).
7
+ * work — chat turn, callable RPC call, or HTTP request — so `gen_ai` spans created within it are
8
+ * correlated, for chat and plain agents alike. The id is minted once per agent instance and
9
+ * rotated when the chat is cleared (the `message:clear` observability event); it is persisted to
10
+ * Durable Object storage so it survives hibernation.
10
11
  *
11
12
  * It only hooks the `agents` package internals and uses Sentry's tracing primitives. On Cloudflare
12
13
  * Workers, prefer `instrumentAgentWithSentry`, which additionally instruments the Durable Object
@@ -5,7 +5,10 @@ import { AgentInternals } from './types';
5
5
  * the WebSocket message (on Cloudflare, the instrumented Durable Object `webSocketMessage` hook).
6
6
  *
7
7
  * Also sets the conversation id on the scope for the duration of the call: callable methods are the
8
- * unit of work for plain (non-chat) agents, which run LLM calls just like chat turns do.
8
+ * unit of work for plain (non-chat) agents, which run LLM calls just like chat turns do. It is
9
+ * awaited so the id is on the scope before the method body creates any span; the `agents`
10
+ * `onMessage` own property this wraps is already `async`, so the promise return is nothing new to
11
+ * the WebSocket dispatch upstream.
9
12
  */
10
13
  export declare function instrumentAgentCallableRpc(obj: AgentInternals): void;
11
14
  //# sourceMappingURL=instrumentAgentCallableRpc.d.ts.map
@@ -0,0 +1,16 @@
1
+ import { AgentInternals } from './types';
2
+ /**
3
+ * Correlates the AI spans of an HTTP-driven agent turn with a conversation id on the active scope.
4
+ *
5
+ * `onRequest` is the third unit of agent work, alongside chat turns and `@callable()` RPC: the
6
+ * `agents` router sends every non-WebSocket request to it, which is how REST endpoints and webhooks
7
+ * reach an agent.
8
+ *
9
+ * `agents` installs `onRequest` as an own property in the `Agent` constructor (as it does
10
+ * `onMessage`), and we instrument after construction, so wrapping the own property is what the
11
+ * router ends up calling. That own property is already `async`, and partyserver's `fetch` awaits it,
12
+ * so returning a promise from this wrapper — to get the conversation id onto the scope before the
13
+ * request handler creates any span — keeps the existing contract.
14
+ */
15
+ export declare function instrumentAgentRequestConversation(obj: AgentInternals): void;
16
+ //# sourceMappingURL=instrumentAgentRequestConversation.d.ts.map
@@ -3,16 +3,17 @@ import { AgentInternals } from './types';
3
3
  * For chat agents (`AIChatAgent` from `@cloudflare/ai-chat`), correlates each chat turn's AI spans
4
4
  * with a conversation id on the active scope.
5
5
  *
6
- * In the Agents model one agent instance is one long-lived conversation, so the instance `name` is
7
- * the base conversation id. When the user clears the chat, they expect a fresh conversation — but
8
- * recreating the Durable Object for that would also drop the MCP/OAuth state stored per instance
9
- * (GitHub/Sentry sign-in). To get a fresh conversation id *without* losing that state, we rotate an
10
- * in-memory id on the instance when the SDK reports a cleared chat, and stamp that (falling back to
11
- * the instance `name` before the first clear).
6
+ * In the Agents model one agent instance is one long-lived conversation, so the conversation id is
7
+ * minted once per instance and persisted to Durable Object storage, which is what carries it across
8
+ * hibernation (that destroys the in-memory instance). When the user clears the chat, they expect a
9
+ * fresh conversation — but recreating the Durable Object for that would also drop the MCP/OAuth
10
+ * state stored per instance (GitHub/Sentry sign-in). To get a fresh conversation id *without*
11
+ * losing that state, we rotate the persisted id when the SDK reports a cleared chat.
12
12
  *
13
- * The id itself is not attached to spans here — the SDK's `conversationIdIntegration` reads it off
14
- * the scope at `spanStart` and stamps `gen_ai.conversation.id` onto the AI spans created inside the
15
- * turn (e.g. by the Workers AI instrumentation), which correlates a turn's model and tool calls.
13
+ * The id itself is not attached to spans here — `setAgentConversationId` resolves it, and the SDK's
14
+ * `conversationIdIntegration` picks it off the scope at `spanStart` to stamp
15
+ * `gen_ai.conversation.id` onto the AI spans created inside the turn (e.g. by the Workers AI
16
+ * instrumentation), which correlates a turn's model and tool calls.
16
17
  *
17
18
  * Plain (non-chat) `Agent`s do not define `onChatMessage`, so they are skipped here — their unit
18
19
  * of work is the callable RPC method, where `instrumentAgentCallableRpc` sets the conversation id
@@ -1,6 +1,14 @@
1
+ import { InstrumentedDurableObjectState } from '../../wrapMethodWithSentry';
1
2
  export declare const AGENT_SPAN_ORIGIN = "auto.faas.cloudflare.agents";
2
- export declare const AGENT_CLASS_ATTRIBUTE = "cloudflare.agent.class";
3
- export declare const AGENT_NAME_ATTRIBUTE = "cloudflare.agent.name";
3
+ /** DO storage key under which the conversation id is persisted so it survives hibernation. */
4
+ export declare const AGENT_CONVERSATION_ID_STORAGE_KEY = "__SENTRY_AGENT_CONVERSATION_ID__";
5
+ /**
6
+ * Instance keys for our conversation-id bookkeeping, keyed by symbol so the state stays invisible
7
+ * to anything enumerating the user-owned agent instance (`Object.keys`, `JSON.stringify`, spread).
8
+ * Exported because the exported `AgentInternals` interface references them.
9
+ */
10
+ export declare const AGENT_CONVERSATION_ID_SYMBOL: unique symbol;
11
+ export declare const AGENT_APPLIED_CONVERSATION_ID_SYMBOL: unique symbol;
4
12
  /**
5
13
  * The subset of the `agents` `Agent` instance internals that we instrument. These are runtime
6
14
  * implementation details of the `agents` package (v0.13.x) rather than part of its public type
@@ -16,34 +24,71 @@ export interface AgentInternals {
16
24
  * does not, so its presence discriminates a chat agent.
17
25
  */
18
26
  onChatMessage?: (...args: unknown[]) => unknown;
27
+ /** HTTP request handler; the router sends every non-WebSocket request here. */
28
+ onRequest?: (...args: unknown[]) => unknown;
19
29
  /** The user's Agent class (used by the SDK for the observability event `agent` field). */
20
30
  _ParentClass?: {
21
31
  name?: string;
22
32
  };
23
- /** The Agent instance name, which in the Agents model identifies the conversation/thread. */
33
+ /** The Agent instance name, reported as the `cloudflare.agent.name` span attribute. */
24
34
  name?: string;
25
35
  /**
26
- * Internal: the active conversation id, rotated when the chat is cleared (the `message:clear`
27
- * observability event) so a reset chat groups as a fresh conversation while the instance (and its
28
- * MCP/OAuth state) stays put. Set by `instrumentChatAgentConversation`; falls back to `name`
29
- * before the first clear.
36
+ * The Durable Object state of the instance. Present on every real Agent; guarded like the other
37
+ * internals so partially-mocked instances keep working.
38
+ */
39
+ ctx?: InstrumentedDurableObjectState;
40
+ /**
41
+ * The current conversation id for this agent instance. It is cached in memory after being loaded
42
+ * from Durable Object storage and updated when the conversation rotates. `undefined` means storage
43
+ * has not been read yet.
44
+ */
45
+ [AGENT_CONVERSATION_ID_SYMBOL]?: string;
46
+ /**
47
+ * The conversation id this instrumentation most recently applied to the isolation scope. It lets
48
+ * subsequent units of work distinguish an SDK-applied id, which may be replaced after rotation,
49
+ * from an id explicitly set by the user, which must be preserved.
30
50
  */
31
- __sentryConversationId?: string;
51
+ [AGENT_APPLIED_CONVERSATION_ID_SYMBOL]?: string;
32
52
  }
33
53
  /** Reads best-effort agent identity attributes from the instance, tolerating missing internals. */
34
54
  export declare function getAgentAttributes(instance: AgentInternals): Record<string, string>;
35
55
  /**
36
- * Sets the agent instance's conversation id on the current scope for the duration of the
37
- * surrounding unit of work (chat turn, callable RPC call). In the Agents model one instance is one
38
- * conversation, so the instance `name` is the natural conversation id — for chat and plain agents
39
- * alike, since plain agents run LLM calls too (e.g. inside `@callable()` methods). Once the chat
40
- * has been cleared, the rotated `__sentryConversationId` takes precedence so LLM calls from any
41
- * unit of work group under the fresh conversation.
56
+ * Persists the conversation id to Durable Object storage, so it survives hibernation, and updates
57
+ * the in-memory cache synchronously so the current wake is immediately consistent even if the
58
+ * write fails. Uses the async storage API, which exists on KV- and SQLite-backed DOs alike.
59
+ * The write is fire-and-forget: DO storage writes are tracked by the runtime and land without
60
+ * awaiting (`waitUntil` is a no-op in Durable Objects), and the catch keeps a rejection from
61
+ * surfacing as unhandled — a failed write just means the next wake starts a new conversation,
62
+ * which must never throw into user code. Uses the original uninstrumented storage so internal
63
+ * bookkeeping doesn't create spans.
64
+ */
65
+ export declare function storeAgentConversationId(instance: AgentInternals, conversationId: string): void;
66
+ /**
67
+ * Sets the agent instance's conversation id on the active scope for the duration of the
68
+ * surrounding unit of work (chat turn, callable RPC call, HTTP request). In the Agents model one
69
+ * instance is one long-lived conversation, so the id is minted once per instance and persisted to
70
+ * DO storage — for chat and plain agents alike, since plain agents run LLM calls too (e.g. inside
71
+ * `@callable()` methods). The instance `name` is deliberately not used: it is caller-chosen and can
72
+ * be a stable, guessable, or shared value, whereas a conversation id should identify exactly one
73
+ * conversation. Clearing the chat rotates it (see `storeAgentConversationId`) so subsequent LLM
74
+ * calls group under the fresh conversation.
75
+ *
76
+ * Every handler wrapper awaits this before invoking the original, so the id is on the scope before
77
+ * the unit of work — and any `gen_ai` span it creates — starts. Only the first unit of work per wake
78
+ * pays the storage read; the rest resolve from the in-memory cache. Awaiting is safe on all three
79
+ * paths because the `agents` `Agent` constructor already replaces `onMessage` and `onRequest` with
80
+ * `async` wrappers of its own, and `onChatMessage` is async by contract, so every caller upstream
81
+ * already handles a promise.
82
+ *
83
+ * An id the user set explicitly outranks this inferred one, in either order: a `setConversationId()`
84
+ * call that already happened is detected here and left alone, and one made inside the handler lands
85
+ * on the same scope afterwards and therefore wins. That is why the write targets the isolation scope
86
+ * — it is the scope the public `Sentry.setConversationId()` writes to, and `conversationIdIntegration`
87
+ * prefers the current scope over it, so writing there would make a user's call unoverridable.
42
88
  *
43
89
  * `conversationIdIntegration` reads the id off the scope at `spanStart` and stamps
44
- * `gen_ai.conversation.id` onto AI spans created within the unit of work, correlating its model
45
- * and tool calls. Callers run inside a per-event forked scope (`wrapMethodWithSentry`), so the id
46
- * does not leak into unrelated events.
90
+ * `gen_ai.conversation.id` onto AI spans created within the unit of work, correlating its model and
91
+ * tool calls.
47
92
  */
48
- export declare function setAgentConversationId(instance: AgentInternals): void;
93
+ export declare function setAgentConversationId(instance: AgentInternals): Promise<void>;
49
94
  //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,24 @@
1
+ import { Scope } from '@sentry/core';
2
+ /**
3
+ * Runs `callback` on the isolation scope for the current invocation.
4
+ *
5
+ * An instrumented handler is either the entry point of an invocation or reentrant — reached from
6
+ * another instrumented handler already serving the same invocation (a Durable Object method calling
7
+ * its own `fetch`, an RPC method reaching a sibling method). Only the entry point may fork:
8
+ *
9
+ * - Forking at the entry point is mandatory. `setUser`/`setTag` write to the isolation scope, and a
10
+ * Durable Object's isolation scope outlives the invocation that touched it, so without a fork one
11
+ * invocation's user and tags reappear on the next invocation's events in the same isolate.
12
+ * Forking clones, so request data set by an enclosing wrapper is still inherited.
13
+ * - Forking again when reentrant would be wrong. Everything below the entry point is one logical
14
+ * unit of work: a nested call must see what the caller set and be able to add to it, the way it
15
+ * would if the SDK were not wrapping it at all.
16
+ *
17
+ * The AsyncLocalStorage strategy hands the default isolation scope back whenever no invocation is in
18
+ * flight, and a forked one while inside `withIsolationScope`. Reference-comparing against the default
19
+ * is therefore enough to tell the two cases apart. The stack fallback does not fork, so it reports the
20
+ * default scope even inside an invocation; there the fork degrades to a no-op, which the stack strategy
21
+ * tolerates. This matches the approach used by `patchEventHandler` in Nuxt.
22
+ */
23
+ export declare function withInvocationIsolationScope<T>(callback: (scope: Scope) => T): T;
24
+ //# sourceMappingURL=invocationScope.d.ts.map
@@ -4,6 +4,14 @@ import { SerializedTraceData } from '@sentry/core';
4
4
  * If no active trace exists, returns the original args unchanged.
5
5
  */
6
6
  export declare function appendRpcMeta(args: unknown[]): unknown[];
7
+ /**
8
+ * Whether the trailing argument carries Sentry RPC metadata.
9
+ *
10
+ * Separate from {@link extractRpcMeta} because the RPC method wrappers run this check on every
11
+ * call — including the instance's own internal method calls, which never carry metadata — and
12
+ * must not allocate on that path.
13
+ */
14
+ export declare function hasRpcMeta(args: unknown[]): boolean;
7
15
  /**
8
16
  * Extracts Sentry RPC metadata from the trailing argument of an args array.
9
17
  * Returns cleaned args (without meta) and the extracted trace data if found.
@@ -1,7 +1,7 @@
1
1
  import { DurableObjectStorage } from '@cloudflare/workers-types';
2
2
  import { CloudflareOptions } from './client';
3
3
  /** Extended DurableObjectState with originalStorage exposed by instrumentContext */
4
- interface InstrumentedDurableObjectState extends DurableObjectState {
4
+ export interface InstrumentedDurableObjectState extends DurableObjectState {
5
5
  originalStorage?: DurableObjectStorage;
6
6
  }
7
7
  type MethodWrapperOptions = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/cloudflare",
3
- "version": "10.69.0",
3
+ "version": "10.70.0",
4
4
  "description": "Official Sentry SDK for Cloudflare Workers and Pages",
5
5
  "repository": "git://github.com/getsentry/sentry-javascript.git",
6
6
  "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/cloudflare",
@@ -70,8 +70,8 @@
70
70
  },
71
71
  "dependencies": {
72
72
  "@opentelemetry/api": "^1.9.1",
73
- "@sentry/core": "10.69.0",
74
- "@sentry/server-utils": "10.69.0",
73
+ "@sentry/core": "10.70.0",
74
+ "@sentry/server-utils": "10.70.0",
75
75
  "magic-string": "~0.30.21"
76
76
  },
77
77
  "peerDependencies": {