@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.
- package/build/cjs/durableobject.js +114 -36
- package/build/cjs/durableobject.js.map +1 -1
- package/build/cjs/flush.js.map +1 -1
- package/build/cjs/instrumentations/agents/index.js +2 -0
- package/build/cjs/instrumentations/agents/index.js.map +1 -1
- package/build/cjs/instrumentations/agents/instrumentAgentCallableRpc.js +2 -2
- package/build/cjs/instrumentations/agents/instrumentAgentCallableRpc.js.map +1 -1
- package/build/cjs/instrumentations/agents/instrumentAgentRequestConversation.js +19 -0
- package/build/cjs/instrumentations/agents/instrumentAgentRequestConversation.js.map +1 -0
- package/build/cjs/instrumentations/agents/instrumentChatAgentConversation.js +3 -3
- package/build/cjs/instrumentations/agents/instrumentChatAgentConversation.js.map +1 -1
- package/build/cjs/instrumentations/agents/types.js +58 -13
- package/build/cjs/instrumentations/agents/types.js.map +1 -1
- package/build/cjs/request.js +3 -2
- package/build/cjs/request.js.map +1 -1
- package/build/cjs/utils/invocationScope.js +12 -0
- package/build/cjs/utils/invocationScope.js.map +1 -0
- package/build/cjs/utils/rpcMeta.js +4 -0
- package/build/cjs/utils/rpcMeta.js.map +1 -1
- package/build/cjs/workflows.js +1 -1
- package/build/cjs/workflows.js.map +1 -1
- package/build/cjs/wrapMethodWithSentry.js +2 -3
- package/build/cjs/wrapMethodWithSentry.js.map +1 -1
- package/build/esm/durableobject.js +117 -39
- package/build/esm/durableobject.js.map +1 -1
- package/build/esm/flush.js.map +1 -1
- package/build/esm/instrumentations/agents/index.js +2 -0
- package/build/esm/instrumentations/agents/index.js.map +1 -1
- package/build/esm/instrumentations/agents/instrumentAgentCallableRpc.js +2 -2
- package/build/esm/instrumentations/agents/instrumentAgentCallableRpc.js.map +1 -1
- package/build/esm/instrumentations/agents/instrumentAgentRequestConversation.js +17 -0
- package/build/esm/instrumentations/agents/instrumentAgentRequestConversation.js.map +1 -0
- package/build/esm/instrumentations/agents/instrumentChatAgentConversation.js +4 -4
- package/build/esm/instrumentations/agents/instrumentChatAgentConversation.js.map +1 -1
- package/build/esm/instrumentations/agents/types.js +56 -13
- package/build/esm/instrumentations/agents/types.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/request.js +4 -3
- package/build/esm/request.js.map +1 -1
- package/build/esm/utils/invocationScope.js +10 -0
- package/build/esm/utils/invocationScope.js.map +1 -0
- package/build/esm/utils/rpcMeta.js +4 -1
- package/build/esm/utils/rpcMeta.js.map +1 -1
- package/build/esm/workflows.js +2 -2
- package/build/esm/workflows.js.map +1 -1
- package/build/esm/wrapMethodWithSentry.js +3 -4
- package/build/esm/wrapMethodWithSentry.js.map +1 -1
- package/build/types/durableobject.d.ts +17 -8
- package/build/types/durableobject.d.ts.map +1 -1
- package/build/types/flush.d.ts +1 -1
- package/build/types/flush.d.ts.map +1 -1
- package/build/types/instrumentations/agents/index.d.ts +4 -3
- package/build/types/instrumentations/agents/index.d.ts.map +1 -1
- package/build/types/instrumentations/agents/instrumentAgentCallableRpc.d.ts +4 -1
- package/build/types/instrumentations/agents/instrumentAgentCallableRpc.d.ts.map +1 -1
- package/build/types/instrumentations/agents/instrumentAgentRequestConversation.d.ts +16 -0
- package/build/types/instrumentations/agents/instrumentAgentRequestConversation.d.ts.map +1 -0
- package/build/types/instrumentations/agents/instrumentChatAgentConversation.d.ts +10 -9
- package/build/types/instrumentations/agents/instrumentChatAgentConversation.d.ts.map +1 -1
- package/build/types/instrumentations/agents/types.d.ts +63 -18
- package/build/types/instrumentations/agents/types.d.ts.map +1 -1
- package/build/types/request.d.ts.map +1 -1
- package/build/types/utils/invocationScope.d.ts +24 -0
- package/build/types/utils/invocationScope.d.ts.map +1 -0
- package/build/types/utils/rpcMeta.d.ts +8 -0
- package/build/types/utils/rpcMeta.d.ts.map +1 -1
- package/build/types/wrapMethodWithSentry.d.ts +1 -1
- package/build/types/wrapMethodWithSentry.d.ts.map +1 -1
- package/build/types-ts3.8/durableobject.d.ts +17 -8
- package/build/types-ts3.8/flush.d.ts +1 -1
- package/build/types-ts3.8/instrumentations/agents/index.d.ts +4 -3
- package/build/types-ts3.8/instrumentations/agents/instrumentAgentCallableRpc.d.ts +4 -1
- package/build/types-ts3.8/instrumentations/agents/instrumentAgentRequestConversation.d.ts +16 -0
- package/build/types-ts3.8/instrumentations/agents/instrumentChatAgentConversation.d.ts +10 -9
- package/build/types-ts3.8/instrumentations/agents/types.d.ts +63 -18
- package/build/types-ts3.8/utils/invocationScope.d.ts +24 -0
- package/build/types-ts3.8/utils/rpcMeta.d.ts +8 -0
- package/build/types-ts3.8/wrapMethodWithSentry.d.ts +1 -1
- package/package.json +3 -3
|
@@ -17,9 +17,49 @@ function constructInstrumentedDurableObject(target, ctx, env, newTarget, options
|
|
|
17
17
|
const options$1 = options.getFinalOptions(optionsCallback(env), env);
|
|
18
18
|
const context = instrumentContext.instrumentContext(ctx);
|
|
19
19
|
const instrumentedEnv = instrumentEnv.instrumentEnv(env, options$1);
|
|
20
|
+
const prototype = newTarget.prototype ?? target.prototype;
|
|
21
|
+
const cachedFrameworkManagedMethods = frameworkManagedMethodsCache.get(prototype);
|
|
22
|
+
const methodsBeforeConstruction = cachedFrameworkManagedMethods ? void 0 : resolvePrototypeMethods(prototype);
|
|
20
23
|
const obj = Reflect.construct(target, [context, instrumentedEnv], newTarget);
|
|
24
|
+
const frameworkManagedMethods = resolveFrameworkManagedMethods(
|
|
25
|
+
prototype,
|
|
26
|
+
obj,
|
|
27
|
+
methodsBeforeConstruction,
|
|
28
|
+
cachedFrameworkManagedMethods
|
|
29
|
+
);
|
|
21
30
|
instrumentDurableObjectHandlers(obj, options$1, context);
|
|
22
|
-
return { obj, options: options$1, context };
|
|
31
|
+
return { obj, options: options$1, context, frameworkManagedMethods };
|
|
32
|
+
}
|
|
33
|
+
const frameworkManagedMethodsCache = /* @__PURE__ */ new WeakMap();
|
|
34
|
+
function resolvePrototypeMethods(prototype) {
|
|
35
|
+
const methods = /* @__PURE__ */ new Map();
|
|
36
|
+
for (let current = prototype; current && current !== Object.prototype; current = Object.getPrototypeOf(current)) {
|
|
37
|
+
for (const name of Object.getOwnPropertyNames(current)) {
|
|
38
|
+
if (name === "constructor" || methods.has(name)) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, name);
|
|
42
|
+
if (descriptor && typeof descriptor.value === "function") {
|
|
43
|
+
methods.set(name, descriptor.value);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return methods;
|
|
48
|
+
}
|
|
49
|
+
function resolveFrameworkManagedMethods(prototype, obj, methodsBeforeConstruction, cached) {
|
|
50
|
+
if (cached) {
|
|
51
|
+
return cached;
|
|
52
|
+
}
|
|
53
|
+
const methodsAfterConstruction = resolvePrototypeMethods(Object.getPrototypeOf(obj));
|
|
54
|
+
const managed = /* @__PURE__ */ new Set();
|
|
55
|
+
for (const [name, method] of methodsAfterConstruction) {
|
|
56
|
+
const before = methodsBeforeConstruction?.get(name);
|
|
57
|
+
if (before && before !== method) {
|
|
58
|
+
managed.add(name);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
frameworkManagedMethodsCache.set(prototype, managed);
|
|
62
|
+
return managed;
|
|
23
63
|
}
|
|
24
64
|
function instrumentDurableObjectHandlers(obj, options, context) {
|
|
25
65
|
if (obj.fetch && typeof obj.fetch === "function") {
|
|
@@ -72,70 +112,108 @@ function instrumentDurableObjectHandlers(obj, options, context) {
|
|
|
72
112
|
);
|
|
73
113
|
}
|
|
74
114
|
}
|
|
75
|
-
|
|
115
|
+
const RESERVED_RPC_METHOD_NAMES = /* @__PURE__ */ new Set([
|
|
116
|
+
"constructor",
|
|
117
|
+
"fetch",
|
|
118
|
+
"connect",
|
|
119
|
+
"alarm",
|
|
120
|
+
"webSocketMessage",
|
|
121
|
+
"webSocketClose",
|
|
122
|
+
"webSocketError",
|
|
123
|
+
"dup"
|
|
124
|
+
]);
|
|
125
|
+
const rpcInstanceStates = /* @__PURE__ */ new WeakMap();
|
|
126
|
+
function finalizeWithRpcInstrumentation(obj, options, context, excludedMethods) {
|
|
76
127
|
const rpcPropagation = rpcOptions.getEffectiveRpcPropagation(options);
|
|
77
128
|
if (!rpcPropagation) {
|
|
78
129
|
return obj;
|
|
79
130
|
}
|
|
80
|
-
const
|
|
131
|
+
const allowedMethods = Array.isArray(options.instrumentPrototypeMethods) ? (
|
|
81
132
|
// eslint-disable-next-line typescript/no-deprecated
|
|
82
|
-
options.instrumentPrototypeMethods
|
|
133
|
+
new Set(options.instrumentPrototypeMethods)
|
|
83
134
|
) : void 0;
|
|
84
|
-
const allowSet = instrumentPrototypeMethods ? new Set(instrumentPrototypeMethods) : null;
|
|
85
135
|
const alwaysTrace = options.enableRpcTracePropagation === void 0;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
136
|
+
rpcInstanceStates.set(obj, { options, context, alwaysTrace });
|
|
137
|
+
instrumentPrototypeRpcMethods(obj, excludedMethods, allowedMethods);
|
|
138
|
+
return obj;
|
|
139
|
+
}
|
|
140
|
+
function getRpcMethodDescriptor(obj, prototype, methodName, excludedMethods, allowedMethods) {
|
|
141
|
+
if (RESERVED_RPC_METHOD_NAMES.has(methodName) || Object.prototype.hasOwnProperty.call(obj, methodName) || excludedMethods?.has(methodName) || allowedMethods && !allowedMethods.has(methodName)) {
|
|
142
|
+
return void 0;
|
|
143
|
+
}
|
|
144
|
+
const descriptor = Object.getOwnPropertyDescriptor(prototype, methodName);
|
|
145
|
+
if (!descriptor || typeof descriptor.value !== "function" || instrument.getInstrumented(descriptor.value)) {
|
|
146
|
+
return void 0;
|
|
147
|
+
}
|
|
148
|
+
return descriptor;
|
|
149
|
+
}
|
|
150
|
+
function instrumentPrototypeRpcMethods(obj, excludedMethods, allowedMethods) {
|
|
151
|
+
let prototype = Object.getPrototypeOf(obj);
|
|
152
|
+
while (prototype && prototype !== Object.prototype) {
|
|
153
|
+
for (const methodName of Object.getOwnPropertyNames(prototype)) {
|
|
154
|
+
const descriptor = getRpcMethodDescriptor(obj, prototype, methodName, excludedMethods, allowedMethods);
|
|
155
|
+
if (!descriptor) {
|
|
156
|
+
continue;
|
|
96
157
|
}
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
158
|
+
const wrapped = createRpcPrototypeWrapper(methodName, descriptor.value);
|
|
159
|
+
try {
|
|
160
|
+
Object.defineProperty(prototype, methodName, { ...descriptor, value: wrapped });
|
|
161
|
+
} catch {
|
|
101
162
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
163
|
+
instrument.markAsInstrumented(wrapped);
|
|
164
|
+
}
|
|
165
|
+
prototype = Object.getPrototypeOf(prototype);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function createRpcPrototypeWrapper(methodName, originalMethod) {
|
|
169
|
+
const wrapper = function(...args) {
|
|
170
|
+
const state = core.isObjectLike(this) ? rpcInstanceStates.get(this) : void 0;
|
|
171
|
+
if (!state || !state.alwaysTrace && !rpcMeta.hasRpcMeta(args)) {
|
|
172
|
+
return Reflect.apply(originalMethod, this, args);
|
|
173
|
+
}
|
|
174
|
+
const tracedMethods = state.tracedMethods ?? (state.tracedMethods = /* @__PURE__ */ new Map());
|
|
175
|
+
let traced = tracedMethods.get(methodName);
|
|
176
|
+
if (!traced) {
|
|
177
|
+
traced = wrapMethodWithSentry.wrapMethodWithSentry(
|
|
178
|
+
{
|
|
179
|
+
options: state.options,
|
|
180
|
+
context: state.context,
|
|
181
|
+
spanName: methodName,
|
|
182
|
+
spanOp: "rpc",
|
|
183
|
+
origin: "auto.faas.cloudflare.durable_object"
|
|
184
|
+
},
|
|
185
|
+
originalMethod,
|
|
105
186
|
void 0,
|
|
106
187
|
true
|
|
107
188
|
);
|
|
108
|
-
|
|
109
|
-
methodCache.set(prop, tracedMethod);
|
|
110
|
-
return tracedMethod;
|
|
111
|
-
}
|
|
112
|
-
const wrappedMethod = ((...args) => {
|
|
113
|
-
const { rpcMeta: rpcMeta$1 } = rpcMeta.extractRpcMeta(args);
|
|
114
|
-
return rpcMeta$1 ? tracedMethod(...args) : boundMethod(...args);
|
|
115
|
-
});
|
|
116
|
-
methodCache.set(prop, wrappedMethod);
|
|
117
|
-
return wrappedMethod;
|
|
189
|
+
tracedMethods.set(methodName, traced);
|
|
118
190
|
}
|
|
191
|
+
return Reflect.apply(traced, this, args);
|
|
192
|
+
};
|
|
193
|
+
Object.defineProperties(wrapper, {
|
|
194
|
+
name: { value: originalMethod.name, configurable: true },
|
|
195
|
+
length: { value: originalMethod.length, configurable: true }
|
|
119
196
|
});
|
|
197
|
+
return wrapper;
|
|
120
198
|
}
|
|
121
199
|
function instrumentDurableObjectWithSentry(optionsCallback, DurableObjectClass) {
|
|
122
200
|
return new Proxy(DurableObjectClass, {
|
|
123
201
|
construct(target, [ctx, env], newTarget) {
|
|
124
|
-
const { obj, options, context } = constructInstrumentedDurableObject(
|
|
202
|
+
const { obj, options, context, frameworkManagedMethods } = constructInstrumentedDurableObject(
|
|
125
203
|
target,
|
|
126
204
|
ctx,
|
|
127
205
|
env,
|
|
128
206
|
newTarget,
|
|
129
207
|
optionsCallback
|
|
130
208
|
);
|
|
131
|
-
return finalizeWithRpcInstrumentation(obj, options, context);
|
|
209
|
+
return finalizeWithRpcInstrumentation(obj, options, context, frameworkManagedMethods);
|
|
132
210
|
}
|
|
133
211
|
});
|
|
134
212
|
}
|
|
135
213
|
function instrumentAgentWithSentry(optionsCallback, AgentClass) {
|
|
136
214
|
return new Proxy(AgentClass, {
|
|
137
215
|
construct(target, [ctx, env], newTarget) {
|
|
138
|
-
const { obj, options, context } = constructInstrumentedDurableObject(
|
|
216
|
+
const { obj, options, context, frameworkManagedMethods } = constructInstrumentedDurableObject(
|
|
139
217
|
target,
|
|
140
218
|
ctx,
|
|
141
219
|
env,
|
|
@@ -143,7 +221,7 @@ function instrumentAgentWithSentry(optionsCallback, AgentClass) {
|
|
|
143
221
|
optionsCallback
|
|
144
222
|
);
|
|
145
223
|
index.instrumentCloudflareAgent(obj);
|
|
146
|
-
return finalizeWithRpcInstrumentation(obj, options, context);
|
|
224
|
+
return finalizeWithRpcInstrumentation(obj, options, context, frameworkManagedMethods);
|
|
147
225
|
}
|
|
148
226
|
});
|
|
149
227
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"durableobject.js","sources":["../../src/durableobject.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/unbound-method */\nimport { captureException } from '@sentry/core';\nimport type { DurableObject } from 'cloudflare:workers';\nimport { setAsyncLocalStorageAsyncContextStrategy } from './async';\nimport type { CloudflareOptions } from './client';\nimport { ensureInstrumented } from './instrument';\nimport { instrumentEnv } from './instrumentations/worker/instrumentEnv';\nimport { getFinalOptions } from './options';\nimport { wrapRequestHandler } from './request';\nimport { instrumentContext } from './utils/instrumentContext';\nimport { extractRpcMeta } from './utils/rpcMeta';\nimport { getEffectiveRpcPropagation } from './utils/rpcOptions';\nimport { instrumentCloudflareAgent } from './instrumentations/agents';\nimport { type UncheckedMethod, wrapMethodWithSentry } from './wrapMethodWithSentry';\n\n/**\n * The instrumented context passed between the shared construction helpers.\n *\n * This is intentionally `any` rather than `ReturnType<typeof instrumentContext<DurableObjectState>>`.\n * A concrete `DurableObjectState` here forces `tsc` to structurally relate its `storage: SqlStorage`\n * graph against the `ExecutionContext | InstrumentedDurableObjectState` parameter of\n * `wrapMethodWithSentry`, while the RPC-branded `DurableObject` from `cloudflare:workers` is also in\n * scope. That union comparison explodes (1296×1296) and hangs the type build. The original inline\n * implementation avoided this only incidentally, because `ctx` reached it as `any` through the Proxy\n * `construct` trap. Keeping the shared context `any` preserves that behavior.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InstrumentedDurableObjectContext = any;\n\n/**\n * Constructs a Durable Object instance and instruments its built-in handler methods\n * (`fetch`, `alarm`, `webSocketMessage`, `webSocketClose`, `webSocketError`).\n *\n * This is the shared construction path used by both {@link instrumentDurableObjectWithSentry}\n * and {@link instrumentAgentWithSentry}. It intentionally does NOT apply the RPC prototype-method\n * proxy — callers apply that last via {@link finalizeWithRpcInstrumentation}, after any additional\n * per-instance instrumentation has been layered onto the returned object.\n *\n * @internal\n */\nexport function constructInstrumentedDurableObject<E, T extends DurableObject<E>>(\n target: new (state: DurableObjectState, env: E) => T,\n ctx: DurableObjectState,\n env: E,\n newTarget: NewableFunction,\n optionsCallback: (env: E) => CloudflareOptions,\n): { obj: T; options: CloudflareOptions; context: InstrumentedDurableObjectContext } {\n setAsyncLocalStorageAsyncContextStrategy();\n const options = getFinalOptions(optionsCallback(env), env);\n // See InstrumentedDurableObjectContext — `ctx` is widened to `any` so the concrete\n // `DurableObjectState` type never enters the checker's relation graph in this module.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const context = instrumentContext(ctx as any);\n const instrumentedEnv = instrumentEnv(env as Record<string, unknown>, options);\n\n // Pass `newTarget` so that subclasses of the instrumented class (e.g. the wrapper classes\n // created by wrangler's local dev tooling or `@cloudflare/vitest-pool-workers`) keep their\n // own prototype — otherwise subclass methods disappear and `instanceof` checks break.\n const obj = Reflect.construct(target, [context, instrumentedEnv], newTarget) as T;\n\n instrumentDurableObjectHandlers(obj, options, context);\n\n return { obj, options, context };\n}\n\n/**\n * Instruments the built-in Durable Object handler methods on a constructed instance.\n *\n * These are the methods that are available on a Durable Object\n * ref: https://developers.cloudflare.com/durable-objects/api/base/\n * - obj.alarm\n * - obj.fetch\n * - obj.webSocketError\n * - obj.webSocketClose\n * - obj.webSocketMessage\n *\n * Any other public methods on the Durable Object instance are RPC calls.\n */\nfunction instrumentDurableObjectHandlers<E, T extends DurableObject<E>>(\n obj: T,\n options: CloudflareOptions,\n context: InstrumentedDurableObjectContext,\n): void {\n // Bind each built-in handler to this instance before wrapping.\n // See https://github.com/getsentry/sentry-javascript/issues/22328\n if (obj.fetch && typeof obj.fetch === 'function') {\n obj.fetch = ensureInstrumented(\n obj.fetch.bind(obj),\n original =>\n new Proxy(original, {\n apply(target, thisArg, args) {\n return wrapRequestHandler({ options, request: args[0], context }, () => {\n return Reflect.apply(target, thisArg, args);\n });\n },\n }),\n );\n }\n\n if (obj.alarm && typeof obj.alarm === 'function') {\n // Alarms are independent invocations, so we start a new trace and link to the previous alarm\n obj.alarm = wrapMethodWithSentry(\n {\n options,\n context,\n spanName: 'alarm',\n spanOp: 'function',\n startNewTrace: true,\n origin: 'auto.faas.cloudflare.durable_object',\n },\n obj.alarm.bind(obj),\n );\n }\n\n if (obj.webSocketMessage && typeof obj.webSocketMessage === 'function') {\n obj.webSocketMessage = wrapMethodWithSentry(\n { options, context, spanName: 'webSocketMessage', origin: 'auto.faas.cloudflare.durable_object' },\n obj.webSocketMessage.bind(obj),\n );\n }\n\n if (obj.webSocketClose && typeof obj.webSocketClose === 'function') {\n obj.webSocketClose = wrapMethodWithSentry(\n { options, context, spanName: 'webSocketClose', origin: 'auto.faas.cloudflare.durable_object' },\n obj.webSocketClose.bind(obj),\n );\n }\n\n if (obj.webSocketError && typeof obj.webSocketError === 'function') {\n obj.webSocketError = wrapMethodWithSentry(\n { options, context, spanName: 'webSocketError', origin: 'auto.faas.cloudflare.durable_object' },\n obj.webSocketError.bind(obj),\n (_, error) =>\n captureException(error, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object_websocket',\n handled: false,\n },\n }),\n );\n }\n}\n\n/**\n * Wraps a constructed (and already handler-instrumented) Durable Object instance with the RPC\n * prototype-method proxy, when RPC trace propagation is enabled. Returns the object unchanged when\n * RPC instrumentation is disabled.\n *\n * This must be applied last, so that any per-instance instrumentation (own properties such as\n * `fetch`, `alarm`, or Agent-specific handlers) is excluded from RPC method tracing.\n *\n * @internal\n */\nexport function finalizeWithRpcInstrumentation<T extends object>(\n obj: T,\n options: CloudflareOptions,\n context: InstrumentedDurableObjectContext,\n): T {\n // Get effective RPC propagation setting (handles deprecation of instrumentPrototypeMethods)\n const rpcPropagation = getEffectiveRpcPropagation(options);\n\n // Skip RPC instrumentation if not enabled\n if (!rpcPropagation) {\n return obj;\n }\n\n // If `instrumentPrototypeMethods` was passed as an array (deprecated),\n // only the listed method names should be instrumented.\n // eslint-disable-next-line typescript/no-deprecated\n const instrumentPrototypeMethods = Array.isArray(options.instrumentPrototypeMethods)\n ? // eslint-disable-next-line typescript/no-deprecated\n options.instrumentPrototypeMethods\n : undefined;\n const allowSet = instrumentPrototypeMethods ? new Set(instrumentPrototypeMethods) : null;\n\n // When using the deprecated `instrumentPrototypeMethods` option, always create spans.\n // When using the new `enableRpcTracePropagation`, only create spans when RPC metadata is present.\n const alwaysTrace = options.enableRpcTracePropagation === undefined;\n\n // Return a Proxy that binds all methods to the original object and creates spans\n // for RPC calls that have Sentry trace context propagated.\n // Binding is required because frameworks may use private fields (babel WeakMap pattern),\n // which fail if `this` is the Proxy instead of the original object.\n const methodCache = new Map<string, UncheckedMethod>();\n\n return new Proxy(obj, {\n get(proxyTarget, prop, receiver) {\n const value = Reflect.get(proxyTarget, prop, receiver);\n\n if (typeof prop !== 'string' || typeof value !== 'function' || prop === 'constructor') {\n return value;\n }\n\n const cached = methodCache.get(prop);\n\n if (cached) {\n return cached;\n }\n\n const boundMethod = (value as UncheckedMethod).bind(proxyTarget);\n\n if (\n prop in Object.prototype ||\n Object.prototype.hasOwnProperty.call(proxyTarget, prop) ||\n (allowSet && !allowSet.has(prop))\n ) {\n methodCache.set(prop, boundMethod);\n\n return boundMethod;\n }\n\n // Pre-create the traced version\n const tracedMethod = wrapMethodWithSentry(\n { options, context, spanName: prop, spanOp: 'rpc', origin: 'auto.faas.cloudflare.durable_object' },\n boundMethod,\n undefined,\n true,\n );\n\n // For deprecated `instrumentPrototypeMethods`, always trace.\n // For new `enableRpcTracePropagation`, only trace when RPC metadata is present.\n if (alwaysTrace) {\n methodCache.set(prop, tracedMethod);\n\n return tracedMethod;\n }\n\n // Wrapper that checks for Sentry RPC metadata at call time\n const wrappedMethod = ((...args: unknown[]) => {\n const { rpcMeta } = extractRpcMeta(args);\n\n // If Sentry RPC metadata is present, use the traced version (creates span)\n // Otherwise, call the bound method directly (no span)\n return rpcMeta ? tracedMethod(...args) : boundMethod(...args);\n }) as UncheckedMethod;\n\n methodCache.set(prop, wrappedMethod);\n\n return wrappedMethod;\n },\n });\n}\n\n/**\n * Instruments a Durable Object class to capture errors and performance data.\n *\n * Instruments the following methods by default:\n * - fetch\n * - alarm\n * - webSocketMessage\n * - webSocketClose\n * - webSocketError\n *\n * To instrument RPC methods (prototype methods), enable the `enableRpcTracePropagation` option.\n *\n * @param optionsCallback Function that returns the options for the SDK initialization.\n * @param DurableObjectClass The Durable Object class to instrument.\n * @returns The instrumented Durable Object class.\n *\n * @example\n * ```ts\n * class MyDurableObjectBase extends DurableObject {\n * constructor(ctx: DurableObjectState, env: Env) {\n * super(ctx, env);\n * }\n * }\n *\n * export const MyDurableObject = instrumentDurableObjectWithSentry(\n * env => ({\n * dsn: env.SENTRY_DSN,\n * tracesSampleRate: 1.0,\n * }),\n * MyDurableObjectBase,\n * );\n * ```\n */\nexport function instrumentDurableObjectWithSentry<\n E,\n T extends DurableObject<E>,\n C extends new (state: DurableObjectState, env: E) => T,\n>(optionsCallback: (env: E) => CloudflareOptions, DurableObjectClass: C): C {\n return new Proxy(DurableObjectClass, {\n construct(target, [ctx, env], newTarget) {\n const { obj, options, context } = constructInstrumentedDurableObject(\n target,\n ctx,\n env,\n newTarget,\n optionsCallback,\n );\n\n return finalizeWithRpcInstrumentation(obj, options, context);\n },\n });\n}\n\n/**\n * Instruments a Cloudflare [`agents`](https://www.npmjs.com/package/agents) Agent class with Sentry.\n *\n * An `Agent` is a Durable Object under the hood, so this applies the same instrumentation as\n * {@link instrumentDurableObjectWithSentry} (request transactions, `alarm`, WebSocket handlers, RPC\n * trace propagation, SQL spans) and additionally captures Agent-specific telemetry via\n * `instrumentCloudflareAgent`:\n *\n * - **Callable RPC spans** — a span (op `rpc`) for each `@callable()` method invoked over WebSocket.\n * - **Breadcrumbs** — for every Agent observability event (`rpc`, `state:update`, `connect`,\n * `disconnect`, `schedule:*`, `queue:*`, `workflow:*`, `email:*`, `mcp:*`, ...).\n *\n * Cloudflare Workers cannot auto-instrument, so the Agent class must be wrapped manually.\n *\n * @param optionsCallback Function that returns the options for the SDK initialization.\n * @param AgentClass The Agent class to instrument.\n * @returns The instrumented Agent class.\n *\n * @example\n * ```ts\n * import { Agent, callable, routeAgentRequest } from 'agents';\n * import * as Sentry from '@sentry/cloudflare';\n *\n * class MyAgentBase extends Agent<Env> {\n * @callable()\n * async greet(name: string): Promise<string> {\n * return `Hello, ${name}!`;\n * }\n * }\n *\n * export const MyAgent = Sentry.instrumentAgentWithSentry(\n * env => ({\n * dsn: env.SENTRY_DSN,\n * tracesSampleRate: 1.0,\n * enableRpcTracePropagation: true,\n * }),\n * MyAgentBase,\n * );\n * ```\n */\nexport function instrumentAgentWithSentry<\n E,\n T extends DurableObject<E>,\n C extends new (state: DurableObjectState, env: E) => T,\n>(optionsCallback: (env: E) => CloudflareOptions, AgentClass: C): C {\n return new Proxy(AgentClass, {\n construct(target, [ctx, env], newTarget) {\n const { obj, options, context } = constructInstrumentedDurableObject(\n target,\n ctx,\n env,\n newTarget,\n optionsCallback,\n );\n\n instrumentCloudflareAgent(obj);\n\n // Apply RPC prototype-method instrumentation last, so the Agent-specific own-property\n // handlers we just installed are excluded from RPC method tracing.\n return finalizeWithRpcInstrumentation(obj, options, context);\n },\n });\n}\n"],"names":["setAsyncLocalStorageAsyncContextStrategy","options","getFinalOptions","instrumentContext","instrumentEnv","ensureInstrumented","wrapRequestHandler","wrapMethodWithSentry","captureException","getEffectiveRpcPropagation","rpcMeta","extractRpcMeta","instrumentCloudflareAgent"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"durableobject.js","sources":["../../src/durableobject.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/unbound-method */\nimport { captureException, isObjectLike } from '@sentry/core';\nimport type { DurableObject } from 'cloudflare:workers';\nimport { setAsyncLocalStorageAsyncContextStrategy } from './async';\nimport type { CloudflareOptions } from './client';\nimport { ensureInstrumented, getInstrumented, markAsInstrumented } from './instrument';\nimport { instrumentEnv } from './instrumentations/worker/instrumentEnv';\nimport { getFinalOptions } from './options';\nimport { wrapRequestHandler } from './request';\nimport { instrumentContext } from './utils/instrumentContext';\nimport { hasRpcMeta } from './utils/rpcMeta';\nimport { getEffectiveRpcPropagation } from './utils/rpcOptions';\nimport { instrumentCloudflareAgent } from './instrumentations/agents';\nimport { type UncheckedMethod, wrapMethodWithSentry } from './wrapMethodWithSentry';\n\n/**\n * The instrumented context passed between the shared construction helpers.\n *\n * This is intentionally `any` rather than `ReturnType<typeof instrumentContext<DurableObjectState>>`.\n * A concrete `DurableObjectState` here forces `tsc` to structurally relate its `storage: SqlStorage`\n * graph against the `ExecutionContext | InstrumentedDurableObjectState` parameter of\n * `wrapMethodWithSentry`, while the RPC-branded `DurableObject` from `cloudflare:workers` is also in\n * scope. That union comparison explodes (1296×1296) and hangs the type build. The original inline\n * implementation avoided this only incidentally, because `ctx` reached it as `any` through the Proxy\n * `construct` trap. Keeping the shared context `any` preserves that behavior.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype InstrumentedDurableObjectContext = any;\n\n/**\n * Constructs a Durable Object instance and instruments its built-in handler methods\n * (`fetch`, `alarm`, `webSocketMessage`, `webSocketClose`, `webSocketError`).\n *\n * This is the shared construction path used by both {@link instrumentDurableObjectWithSentry}\n * and {@link instrumentAgentWithSentry}. It intentionally does NOT apply the RPC prototype-method\n * instrumentation — callers apply that last via {@link finalizeWithRpcInstrumentation}, after any\n * additional per-instance instrumentation has been layered onto the returned object.\n *\n * @internal\n */\nexport function constructInstrumentedDurableObject<E, T extends DurableObject<E>>(\n target: new (state: DurableObjectState, env: E) => T,\n ctx: DurableObjectState,\n env: E,\n newTarget: NewableFunction,\n optionsCallback: (env: E) => CloudflareOptions,\n): {\n obj: T;\n options: CloudflareOptions;\n context: InstrumentedDurableObjectContext;\n frameworkManagedMethods: ReadonlySet<string>;\n} {\n setAsyncLocalStorageAsyncContextStrategy();\n const options = getFinalOptions(optionsCallback(env), env);\n // See InstrumentedDurableObjectContext — `ctx` is widened to `any` so the concrete\n // `DurableObjectState` type never enters the checker's relation graph in this module.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const context = instrumentContext(ctx as any);\n const instrumentedEnv = instrumentEnv(env as Record<string, unknown>, options);\n\n const prototype = (newTarget as unknown as { prototype?: object }).prototype ?? target.prototype;\n const cachedFrameworkManagedMethods = frameworkManagedMethodsCache.get(prototype);\n const methodsBeforeConstruction = cachedFrameworkManagedMethods ? undefined : resolvePrototypeMethods(prototype);\n\n // Pass `newTarget` so that subclasses of the instrumented class (e.g. the wrapper classes\n // created by wrangler's local dev tooling or `@cloudflare/vitest-pool-workers`) keep their\n // own prototype — otherwise subclass methods disappear and `instanceof` checks break.\n const obj = Reflect.construct(target, [context, instrumentedEnv], newTarget) as T;\n\n const frameworkManagedMethods = resolveFrameworkManagedMethods(\n prototype,\n obj,\n methodsBeforeConstruction,\n cachedFrameworkManagedMethods,\n );\n\n instrumentDurableObjectHandlers(obj, options, context);\n\n return { obj, options, context, frameworkManagedMethods };\n}\n\nconst frameworkManagedMethodsCache = new WeakMap<object, ReadonlySet<string>>();\n\n/**\n * Collects the methods visible from a prototype, using normal property lookup precedence.\n * Methods inherited from `Object.prototype` are excluded because they cannot be Durable Object RPC methods.\n */\nfunction resolvePrototypeMethods(prototype: object | null): Map<string, unknown> {\n const methods = new Map<string, unknown>();\n\n for (let current = prototype; current && current !== Object.prototype; current = Object.getPrototypeOf(current)) {\n for (const name of Object.getOwnPropertyNames(current)) {\n // The first occurrence wins, mirroring what a property lookup on the instance would find\n if (name === 'constructor' || methods.has(name)) {\n continue;\n }\n\n const descriptor = Object.getOwnPropertyDescriptor(current, name);\n\n if (descriptor && typeof descriptor.value === 'function') {\n methods.set(name, descriptor.value);\n }\n }\n }\n\n return methods;\n}\n\n/**\n * Finds methods that a framework replaced while constructing the first instance.\n *\n * Some frameworks register methods by function identity, so replacing one of their wrappers would\n * break dispatch. The result is cached because frameworks commonly install their wrappers only\n * once; later constructions would no longer reveal which methods they manage.\n */\nfunction resolveFrameworkManagedMethods(\n prototype: object,\n obj: object,\n methodsBeforeConstruction: Map<string, unknown> | undefined,\n cached: ReadonlySet<string> | undefined,\n): ReadonlySet<string> {\n if (cached) {\n return cached;\n }\n\n const methodsAfterConstruction = resolvePrototypeMethods(Object.getPrototypeOf(obj) as object);\n const managed = new Set<string>();\n\n for (const [name, method] of methodsAfterConstruction) {\n const before = methodsBeforeConstruction?.get(name);\n\n if (before && before !== method) {\n managed.add(name);\n }\n }\n\n frameworkManagedMethodsCache.set(prototype, managed);\n\n return managed;\n}\n\n/**\n * Instruments the built-in Durable Object handler methods on a constructed instance.\n *\n * These are the methods that are available on a Durable Object\n * ref: https://developers.cloudflare.com/durable-objects/api/base/\n * - obj.alarm\n * - obj.fetch\n * - obj.webSocketError\n * - obj.webSocketClose\n * - obj.webSocketMessage\n *\n * Any other public methods on the Durable Object instance are RPC calls.\n */\nfunction instrumentDurableObjectHandlers<E, T extends DurableObject<E>>(\n obj: T,\n options: CloudflareOptions,\n context: InstrumentedDurableObjectContext,\n): void {\n // Bind each built-in handler to this instance before wrapping.\n // See https://github.com/getsentry/sentry-javascript/issues/22328\n if (obj.fetch && typeof obj.fetch === 'function') {\n obj.fetch = ensureInstrumented(\n obj.fetch.bind(obj),\n original =>\n new Proxy(original, {\n apply(target, thisArg, args) {\n return wrapRequestHandler({ options, request: args[0], context }, () => {\n return Reflect.apply(target, thisArg, args);\n });\n },\n }),\n );\n }\n\n if (obj.alarm && typeof obj.alarm === 'function') {\n // Alarms are independent invocations, so we start a new trace and link to the previous alarm\n obj.alarm = wrapMethodWithSentry(\n {\n options,\n context,\n spanName: 'alarm',\n spanOp: 'function',\n startNewTrace: true,\n origin: 'auto.faas.cloudflare.durable_object',\n },\n obj.alarm.bind(obj),\n );\n }\n\n if (obj.webSocketMessage && typeof obj.webSocketMessage === 'function') {\n obj.webSocketMessage = wrapMethodWithSentry(\n { options, context, spanName: 'webSocketMessage', origin: 'auto.faas.cloudflare.durable_object' },\n obj.webSocketMessage.bind(obj),\n );\n }\n\n if (obj.webSocketClose && typeof obj.webSocketClose === 'function') {\n obj.webSocketClose = wrapMethodWithSentry(\n { options, context, spanName: 'webSocketClose', origin: 'auto.faas.cloudflare.durable_object' },\n obj.webSocketClose.bind(obj),\n );\n }\n\n if (obj.webSocketError && typeof obj.webSocketError === 'function') {\n obj.webSocketError = wrapMethodWithSentry(\n { options, context, spanName: 'webSocketError', origin: 'auto.faas.cloudflare.durable_object' },\n obj.webSocketError.bind(obj),\n (_, error) =>\n captureException(error, {\n mechanism: {\n type: 'auto.faas.cloudflare.durable_object_websocket',\n handled: false,\n },\n }),\n );\n }\n}\n\ntype RpcInstanceState = {\n options: CloudflareOptions;\n context: InstrumentedDurableObjectContext;\n /**\n * Whether to trace calls that carry no RPC metadata. Only the deprecated\n * `instrumentPrototypeMethods` option does, because it predates metadata propagation.\n */\n alwaysTrace: boolean;\n /** Per-instance cache of the traced method wrappers, keyed by method name. Created on first use. */\n tracedMethods?: Map<string, UncheckedMethod>;\n};\n\n/**\n * Method names the runtime never dispatches over RPC, so wrapping them buys no tracing.\n *\n * Mirrors `isReservedName` in workerd (`src/workerd/api/worker-rpc.c++`): the runtime rejects these\n * before any property lookup happens. `fetch`, `alarm` and the `webSocket*` handlers are also\n * instrumented per-instance as own properties, and `constructor` is on every prototype.\n */\nconst RESERVED_RPC_METHOD_NAMES: ReadonlySet<string> = new Set([\n 'constructor',\n 'fetch',\n 'connect',\n 'alarm',\n 'webSocketMessage',\n 'webSocketClose',\n 'webSocketError',\n 'dup',\n]);\n\n// Prototype wrappers are shared by all instances, while SDK options and traced method caches are not.\nconst rpcInstanceStates = new WeakMap<object, RpcInstanceState>();\n\n/**\n * Adds trace propagation to a constructed Durable Object's RPC methods.\n *\n * RPC methods are wrapped on the prototype because Cloudflare dispatches them with the Durable\n * Object instance as the receiver. This preserves native private-field access and keeps the methods\n * visible to Cloudflare's RPC dispatcher. Built-in handlers, Agent handlers, and methods managed by\n * another framework are left untouched.\n *\n * Call this after all per-instance instrumentation has been applied. If RPC trace propagation is\n * disabled, the object is returned unchanged.\n *\n * @param obj The constructed Durable Object instance.\n * @param options The resolved SDK options for this instance.\n * @param context The instrumented execution context for this instance.\n * @param excludedMethods Method names owned by another framework and therefore not safe to wrap.\n * @returns The same Durable Object instance, with eligible prototype methods instrumented.\n * @internal\n */\nexport function finalizeWithRpcInstrumentation<T extends object>(\n obj: T,\n options: CloudflareOptions,\n context: InstrumentedDurableObjectContext,\n excludedMethods?: ReadonlySet<string>,\n): T {\n // Get effective RPC propagation setting (handles deprecation of instrumentPrototypeMethods)\n const rpcPropagation = getEffectiveRpcPropagation(options);\n\n // Skip RPC instrumentation if not enabled\n if (!rpcPropagation) {\n return obj;\n }\n\n // If `instrumentPrototypeMethods` was passed as an array (deprecated),\n // only the listed method names should be instrumented.\n // eslint-disable-next-line typescript/no-deprecated\n const allowedMethods = Array.isArray(options.instrumentPrototypeMethods)\n ? // eslint-disable-next-line typescript/no-deprecated\n new Set(options.instrumentPrototypeMethods)\n : undefined;\n\n // When using the deprecated `instrumentPrototypeMethods` option, always create spans.\n // When using the new `enableRpcTracePropagation`, only create spans when RPC metadata is present.\n const alwaysTrace = options.enableRpcTracePropagation === undefined;\n\n rpcInstanceStates.set(obj, { options, context, alwaysTrace });\n\n instrumentPrototypeRpcMethods(obj, excludedMethods, allowedMethods);\n\n return obj;\n}\n\n/**\n * Returns a prototype method when it is eligible for RPC instrumentation.\n */\nfunction getRpcMethodDescriptor(\n obj: object,\n prototype: object,\n methodName: string,\n excludedMethods?: ReadonlySet<string>,\n allowedMethods?: ReadonlySet<string>,\n): PropertyDescriptor | undefined {\n if (\n RESERVED_RPC_METHOD_NAMES.has(methodName) ||\n Object.prototype.hasOwnProperty.call(obj, methodName) ||\n excludedMethods?.has(methodName) ||\n (allowedMethods && !allowedMethods.has(methodName))\n ) {\n return undefined;\n }\n\n const descriptor = Object.getOwnPropertyDescriptor(prototype, methodName);\n\n if (!descriptor || typeof descriptor.value !== 'function' || getInstrumented(descriptor.value)) {\n return undefined;\n }\n\n return descriptor;\n}\n\n/**\n * Wraps eligible methods on the instance's prototype chain once per class.\n *\n * Because the wrappers live on the prototype, the allow-list of the first constructed instance\n * decides which methods carry a wrapper for every later instance of that class. This only affects\n * the deprecated array form of `instrumentPrototypeMethods`, where differing options per instance\n * of the same class were never a supported configuration.\n */\nfunction instrumentPrototypeRpcMethods(\n obj: object,\n excludedMethods?: ReadonlySet<string>,\n allowedMethods?: ReadonlySet<string>,\n): void {\n let prototype: object | null = Object.getPrototypeOf(obj);\n\n while (prototype && prototype !== Object.prototype) {\n for (const methodName of Object.getOwnPropertyNames(prototype)) {\n const descriptor = getRpcMethodDescriptor(obj, prototype, methodName, excludedMethods, allowedMethods);\n\n if (!descriptor) {\n continue;\n }\n\n const wrapped = createRpcPrototypeWrapper(methodName, descriptor.value as UncheckedMethod);\n\n try {\n Object.defineProperty(prototype, methodName, { ...descriptor, value: wrapped });\n } catch {}\n\n // Only the wrapper is marked, not the original method: `wrapMethodWithSentry` resolves\n // through the same global map and must not resolve the original to this wrapper,\n // which would recurse.\n markAsInstrumented(wrapped);\n }\n\n prototype = Object.getPrototypeOf(prototype);\n }\n}\n\n/**\n * Creates a prototype wrapper that traces RPC calls carrying Sentry metadata.\n *\n * The wrapper looks up SDK state from its receiver, allowing one prototype function to serve every\n * instance. Calls without instance state use the original method directly, as do calls without RPC\n * metadata unless the instance opted into unconditional tracing. The original function name and\n * arity are preserved because frameworks may inspect them for dispatch.\n */\nfunction createRpcPrototypeWrapper(methodName: string, originalMethod: UncheckedMethod): UncheckedMethod {\n const wrapper = function (this: unknown, ...args: unknown[]): unknown {\n const state = isObjectLike(this) ? rpcInstanceStates.get(this) : undefined;\n\n // Untraced calls are the common case — every internal call the instance makes to one of its\n // own methods lands here too, and those never carry RPC metadata.\n if (!state || (!state.alwaysTrace && !hasRpcMeta(args))) {\n return Reflect.apply(originalMethod, this, args);\n }\n\n const tracedMethods = (state.tracedMethods ??= new Map());\n let traced = tracedMethods.get(methodName);\n\n if (!traced) {\n traced = wrapMethodWithSentry(\n {\n options: state.options,\n context: state.context,\n spanName: methodName,\n spanOp: 'rpc',\n origin: 'auto.faas.cloudflare.durable_object',\n },\n originalMethod,\n undefined,\n true,\n );\n tracedMethods.set(methodName, traced);\n }\n\n return Reflect.apply(traced, this, args);\n };\n\n Object.defineProperties(wrapper, {\n name: { value: originalMethod.name, configurable: true },\n length: { value: originalMethod.length, configurable: true },\n });\n\n return wrapper as UncheckedMethod;\n}\n\n/**\n * Instruments a Durable Object class to capture errors and performance data.\n *\n * Instruments the following methods by default:\n * - fetch\n * - alarm\n * - webSocketMessage\n * - webSocketClose\n * - webSocketError\n *\n * To instrument RPC methods (prototype methods), enable the `enableRpcTracePropagation` option.\n *\n * @param optionsCallback Function that returns the options for the SDK initialization.\n * @param DurableObjectClass The Durable Object class to instrument.\n * @returns The instrumented Durable Object class.\n *\n * @example\n * ```ts\n * class MyDurableObjectBase extends DurableObject {\n * constructor(ctx: DurableObjectState, env: Env) {\n * super(ctx, env);\n * }\n * }\n *\n * export const MyDurableObject = instrumentDurableObjectWithSentry(\n * env => ({\n * dsn: env.SENTRY_DSN,\n * tracesSampleRate: 1.0,\n * }),\n * MyDurableObjectBase,\n * );\n * ```\n */\nexport function instrumentDurableObjectWithSentry<\n E,\n T extends DurableObject<E>,\n C extends new (state: DurableObjectState, env: E) => T,\n>(optionsCallback: (env: E) => CloudflareOptions, DurableObjectClass: C): C {\n return new Proxy(DurableObjectClass, {\n construct(target, [ctx, env], newTarget) {\n const { obj, options, context, frameworkManagedMethods } = constructInstrumentedDurableObject(\n target,\n ctx,\n env,\n newTarget,\n optionsCallback,\n );\n\n return finalizeWithRpcInstrumentation(obj, options, context, frameworkManagedMethods);\n },\n });\n}\n\n/**\n * Instruments a Cloudflare [`agents`](https://www.npmjs.com/package/agents) Agent class with Sentry.\n *\n * An `Agent` is a Durable Object under the hood, so this applies the same instrumentation as\n * {@link instrumentDurableObjectWithSentry} (request transactions, `alarm`, WebSocket handlers, RPC\n * trace propagation, SQL spans) and additionally captures Agent-specific telemetry via\n * `instrumentCloudflareAgent`:\n *\n * - **Callable RPC spans** — a span (op `rpc`) for each `@callable()` method invoked over WebSocket.\n * - **Breadcrumbs** — for every Agent observability event (`rpc`, `state:update`, `connect`,\n * `disconnect`, `schedule:*`, `queue:*`, `workflow:*`, `email:*`, `mcp:*`, ...).\n *\n * Cloudflare Workers cannot auto-instrument, so the Agent class must be wrapped manually.\n *\n * @param optionsCallback Function that returns the options for the SDK initialization.\n * @param AgentClass The Agent class to instrument.\n * @returns The instrumented Agent class.\n *\n * @example\n * ```ts\n * import { Agent, callable, routeAgentRequest } from 'agents';\n * import * as Sentry from '@sentry/cloudflare';\n *\n * class MyAgentBase extends Agent<Env> {\n * @callable()\n * async greet(name: string): Promise<string> {\n * return `Hello, ${name}!`;\n * }\n * }\n *\n * export const MyAgent = Sentry.instrumentAgentWithSentry(\n * env => ({\n * dsn: env.SENTRY_DSN,\n * tracesSampleRate: 1.0,\n * enableRpcTracePropagation: true,\n * }),\n * MyAgentBase,\n * );\n * ```\n */\nexport function instrumentAgentWithSentry<\n E,\n T extends DurableObject<E>,\n C extends new (state: DurableObjectState, env: E) => T,\n>(optionsCallback: (env: E) => CloudflareOptions, AgentClass: C): C {\n return new Proxy(AgentClass, {\n construct(target, [ctx, env], newTarget) {\n const { obj, options, context, frameworkManagedMethods } = constructInstrumentedDurableObject(\n target,\n ctx,\n env,\n newTarget,\n optionsCallback,\n );\n\n instrumentCloudflareAgent(obj);\n\n // Apply RPC prototype-method instrumentation last, so the Agent-specific own-property\n // handlers we just installed are excluded from RPC method tracing. Methods the Agent\n // framework installed itself are excluded too — `instrumentCloudflareAgent` traces those by\n // wrapping the dispatch instead of the method.\n return finalizeWithRpcInstrumentation(obj, options, context, frameworkManagedMethods);\n },\n });\n}\n"],"names":["setAsyncLocalStorageAsyncContextStrategy","options","getFinalOptions","instrumentContext","instrumentEnv","ensureInstrumented","wrapRequestHandler","wrapMethodWithSentry","captureException","getEffectiveRpcPropagation","getInstrumented","markAsInstrumented","isObjectLike","hasRpcMeta","instrumentCloudflareAgent"],"mappings":"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"}
|
package/build/cjs/flush.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flush.js","sources":["../../src/flush.ts"],"sourcesContent":["import type { ExecutionContext } from '@cloudflare/workers-types';\nimport type { Client } from '@sentry/core';\nimport { debug, flush } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport type { ExecutionContextCompat } from './executionContext';\n\ntype FlushLock = {\n readonly ready: Promise<void>;\n readonly finalize: () => Promise<void>;\n};\n\ntype FlushLockRegistry = {\n readonly locks: Set<FlushLockInternal>;\n readonly originalWaitUntil: ExecutionContext['waitUntil'];\n};\n\ntype FlushLockInternal = FlushLock & {\n readonly acquire: () => void;\n readonly release: () => void;\n};\n\nconst flushLockRegistries = new WeakMap<ExecutionContext['waitUntil'], FlushLockRegistry>();\n\n/**\n * Returns the original (un-instrumented) waitUntil function for a context.\n * This should be used when calling waitUntil with flushAndDispose to avoid deadlock.\n *\n * The flush lock mechanism wraps context.waitUntil to track pending tasks.\n * If we call waitUntil(flushAndDispose(client)) through the instrumented version,\n * it creates a deadlock because:\n * 1. The instrumented waitUntil acquires the flush lock\n * 2. flushAndDispose calls client.flush() which waits for the lock to be released\n * 3. The lock won't be released until the waitUntil promise completes\n * 4. The waitUntil promise won't complete until flush() returns\n *\n * By using the original waitUntil for flush operations, we bypass this issue.\n */\nexport function getOriginalWaitUntil(context: ExecutionContextCompat): ExecutionContext['waitUntil']
|
|
1
|
+
{"version":3,"file":"flush.js","sources":["../../src/flush.ts"],"sourcesContent":["import type { ExecutionContext } from '@cloudflare/workers-types';\nimport type { Client } from '@sentry/core';\nimport { debug, flush } from '@sentry/core';\nimport { DEBUG_BUILD } from './debug-build';\nimport type { ExecutionContextCompat } from './executionContext';\n\ntype FlushLock = {\n readonly ready: Promise<void>;\n readonly finalize: () => Promise<void>;\n};\n\ntype FlushLockRegistry = {\n readonly locks: Set<FlushLockInternal>;\n readonly originalWaitUntil: ExecutionContext['waitUntil'];\n};\n\ntype FlushLockInternal = FlushLock & {\n readonly acquire: () => void;\n readonly release: () => void;\n};\n\nconst flushLockRegistries = new WeakMap<ExecutionContext['waitUntil'], FlushLockRegistry>();\n\n/**\n * Returns the original (un-instrumented) waitUntil function for a context.\n * This should be used when calling waitUntil with flushAndDispose to avoid deadlock.\n *\n * The flush lock mechanism wraps context.waitUntil to track pending tasks.\n * If we call waitUntil(flushAndDispose(client)) through the instrumented version,\n * it creates a deadlock because:\n * 1. The instrumented waitUntil acquires the flush lock\n * 2. flushAndDispose calls client.flush() which waits for the lock to be released\n * 3. The lock won't be released until the waitUntil promise completes\n * 4. The waitUntil promise won't complete until flush() returns\n *\n * By using the original waitUntil for flush operations, we bypass this issue.\n */\nexport function getOriginalWaitUntil(context: ExecutionContextCompat): ExecutionContext['waitUntil'] {\n // eslint-disable-next-line @typescript-eslint/unbound-method\n const currentWaitUntil = context.waitUntil;\n const original = flushLockRegistries.get(currentWaitUntil)?.originalWaitUntil;\n return original ?? currentWaitUntil;\n}\n\n/**\n * Enhances the given execution context by wrapping its `waitUntil` method with a proxy\n * to monitor pending tasks, and provides a flusher function to ensure all tasks\n * have been completed before executing any subsequent logic.\n *\n * @param {ExecutionContext} context - The execution context to be enhanced. If no context is provided, the function returns undefined.\n * @return {FlushLock} Returns a flusher function if a valid context is provided, otherwise undefined.\n */\nexport function makeFlushLock(context: ExecutionContextCompat): FlushLock {\n const registry = getOrCreateFlushLockRegistry(context);\n let resolveAllDone: () => void = () => undefined;\n const allDone = new Promise<void>(res => {\n resolveAllDone = res;\n });\n let pending = 0;\n\n const lock: FlushLockInternal = {\n ready: allDone,\n acquire: () => {\n pending++;\n },\n release: () => {\n if (--pending === 0) {\n registry.locks.delete(lock);\n resolveAllDone();\n }\n },\n finalize: () => {\n if (pending === 0) {\n registry.locks.delete(lock);\n resolveAllDone();\n }\n return allDone;\n },\n };\n\n registry.locks.add(lock);\n return Object.freeze(lock);\n}\n\nfunction getOrCreateFlushLockRegistry(context: ExecutionContextCompat): FlushLockRegistry {\n // eslint-disable-next-line @typescript-eslint/unbound-method\n const waitUntil = context.waitUntil;\n const existingRegistry = flushLockRegistries.get(waitUntil);\n\n if (existingRegistry) {\n return existingRegistry;\n }\n\n const originalWaitUntil = context.waitUntil.bind(context) as typeof context.waitUntil;\n const registry: FlushLockRegistry = { locks: new Set(), originalWaitUntil };\n const instrumentedWaitUntil: typeof context.waitUntil = promise => {\n // Snapshot active locks so locks created after this call do not wait for earlier waitUntil work.\n const locks = [...registry.locks];\n\n for (const lock of locks) {\n lock.acquire();\n }\n\n return originalWaitUntil(\n promise.finally(() => {\n for (const lock of locks) {\n lock.release();\n }\n }),\n );\n };\n\n flushLockRegistries.set(instrumentedWaitUntil, registry);\n context.waitUntil = instrumentedWaitUntil;\n\n return registry;\n}\n\n/**\n * Flushes the client and then disposes of it to allow garbage collection.\n * This should be called at the end of each request to prevent memory leaks.\n *\n * This function never rejects. On Workers, a rejected promise passed to\n * `ctx.waitUntil` marks the whole invocation as `outcome: exception` even when\n * the handler itself completed successfully. Since flush/dispose is internal\n * SDK housekeeping, a failure here must not fail the user's invocation.\n *\n * @param client - The CloudflareClient instance to flush and dispose\n * @param timeout - Timeout in milliseconds for the flush operation\n * @returns A promise that resolves when flush and dispose are complete\n */\nexport async function flushAndDispose(client: Client | undefined, timeout = 2000): Promise<void> {\n try {\n if (!client) {\n await flush(timeout);\n return;\n }\n await client.flush(timeout);\n } catch (e) {\n DEBUG_BUILD && debug.warn('Failed to flush client', e);\n } finally {\n try {\n client?.dispose();\n } catch (e) {\n DEBUG_BUILD && debug.warn('Failed to dispose client', e);\n }\n }\n}\n"],"names":["flush","DEBUG_BUILD","debug"],"mappings":"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"}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
3
|
const instrumentAgentCallableRpc = require('./instrumentAgentCallableRpc.js');
|
|
4
|
+
const instrumentAgentRequestConversation = require('./instrumentAgentRequestConversation.js');
|
|
4
5
|
const instrumentChatAgentConversation = require('./instrumentChatAgentConversation.js');
|
|
5
6
|
|
|
6
7
|
function instrumentCloudflareAgent(agent) {
|
|
7
8
|
const internals = agent;
|
|
8
9
|
instrumentAgentCallableRpc.instrumentAgentCallableRpc(internals);
|
|
9
10
|
instrumentChatAgentConversation.instrumentChatAgentConversation(internals);
|
|
11
|
+
instrumentAgentRequestConversation.instrumentAgentRequestConversation(internals);
|
|
10
12
|
return agent;
|
|
11
13
|
}
|
|
12
14
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../src/instrumentations/agents/index.ts"],"sourcesContent":["import { instrumentAgentCallableRpc } from './instrumentAgentCallableRpc';\nimport { instrumentChatAgentConversation } from './instrumentChatAgentConversation';\nimport type { AgentInternals } from './types';\n\n/**\n * Instruments an instance of a Cloudflare [`agents`](https://www.npmjs.com/package/agents) `Agent`\n * with Sentry, adding telemetry that is specific to the Agent's runtime behavior:\n *\n * - **Callable RPC spans** — a span (op `rpc`) for each `@callable()` method invoked over WebSocket.\n * - **Conversation correlation** — sets the conversation id on the scope for each unit of agent\n * work — chat turn
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../src/instrumentations/agents/index.ts"],"sourcesContent":["import { instrumentAgentCallableRpc } from './instrumentAgentCallableRpc';\nimport { instrumentAgentRequestConversation } from './instrumentAgentRequestConversation';\nimport { instrumentChatAgentConversation } from './instrumentChatAgentConversation';\nimport type { AgentInternals } from './types';\n\n/**\n * Instruments an instance of a Cloudflare [`agents`](https://www.npmjs.com/package/agents) `Agent`\n * with Sentry, adding telemetry that is specific to the Agent's runtime behavior:\n *\n * - **Callable RPC spans** — a span (op `rpc`) for each `@callable()` method invoked over WebSocket.\n * - **Conversation correlation** — sets the conversation id on the scope for each unit of agent\n * work — chat turn, callable RPC call, or HTTP request — so `gen_ai` spans created within it are\n * correlated, for chat and plain agents alike. The id is minted once per agent instance and\n * rotated when the chat is cleared (the `message:clear` observability event); it is persisted to\n * Durable Object storage so it survives hibernation.\n *\n * It only hooks the `agents` package internals and uses Sentry's tracing primitives. On Cloudflare\n * Workers, prefer `instrumentAgentWithSentry`, which additionally instruments the Durable Object\n * handlers and initializes the SDK; that function calls this one internally.\n *\n * The hooks replace own-properties on the instance and defer to the original implementation, so\n * they compose with any other instrumentation applied to the same instance.\n *\n * @param agent The `agents` `Agent` instance to instrument.\n * @returns The same instance, instrumented.\n *\n * @internal Use `instrumentAgentWithSentry` instead — this is called internally by it.\n */\nexport function instrumentCloudflareAgent<T extends object>(agent: T): T {\n const internals = agent as T & AgentInternals;\n\n instrumentAgentCallableRpc(internals);\n instrumentChatAgentConversation(internals);\n instrumentAgentRequestConversation(internals);\n\n return agent;\n}\n"],"names":["instrumentAgentCallableRpc","instrumentChatAgentConversation","instrumentAgentRequestConversation"],"mappings":";;;;;;AA4BO,SAAS,0BAA4C,KAAA,EAAa;AACvE,EAAA,MAAM,SAAA,GAAY,KAAA;AAElB,EAAAA,qDAAA,CAA2B,SAAS,CAAA;AACpC,EAAAC,+DAAA,CAAgC,SAAS,CAAA;AACzC,EAAAC,qEAAA,CAAmC,SAAS,CAAA;AAE5C,EAAA,OAAO,KAAA;AACT;;;;"}
|
|
@@ -25,8 +25,8 @@ function instrumentAgentCallableRpc(obj) {
|
|
|
25
25
|
...types.getAgentAttributes(thisArg)
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
|
-
() => {
|
|
29
|
-
types.setAgentConversationId(thisArg);
|
|
28
|
+
async () => {
|
|
29
|
+
await types.setAgentConversationId(thisArg);
|
|
30
30
|
return Reflect.apply(target, thisArg, args);
|
|
31
31
|
}
|
|
32
32
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentAgentCallableRpc.js","sources":["../../../../src/instrumentations/agents/instrumentAgentCallableRpc.ts"],"sourcesContent":["import { debug, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { AGENT_SPAN_ORIGIN, type AgentInternals, getAgentAttributes, setAgentConversationId } from './types';\n\n/**\n * Wraps the Agent's `onMessage` handler to create a span for each `@callable()` RPC invocation.\n * RPC requests arrive as WebSocket messages, so this span nests under the active transaction for\n * the WebSocket message (on Cloudflare, the instrumented Durable Object `webSocketMessage` hook).\n *\n * Also sets the conversation id on the scope for the duration of the call: callable methods are the\n * unit of work for plain (non-chat) agents, which run LLM calls just like chat turns do.\n */\nexport function instrumentAgentCallableRpc(obj: AgentInternals): void {\n const original = obj.onMessage;\n if (typeof original !== 'function') {\n DEBUG_BUILD && debug.log('[Sentry] Agent `onMessage` not found — callable RPC span instrumentation skipped.');\n return;\n }\n\n obj.onMessage = new Proxy(original, {\n apply(target, thisArg: AgentInternals, args: unknown[]): unknown {\n const method = extractCallableMethod(args[1]);\n\n if (!method) {\n return Reflect.apply(target, thisArg, args);\n }\n\n return startSpan(\n {\n name: method,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'rpc',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: AGENT_SPAN_ORIGIN,\n ...getAgentAttributes(thisArg),\n },\n },\n () => {\n setAgentConversationId(thisArg);\n return Reflect.apply(target, thisArg, args);\n },\n );\n },\n });\n}\n\n/** Extracts the RPC method name from a WebSocket message, mirroring the SDK's `isRPCRequest`. */\nfunction extractCallableMethod(message: unknown): string | undefined {\n const text =\n typeof message === 'string'\n ? message\n : message instanceof ArrayBuffer\n ? new TextDecoder().decode(message)\n : undefined;\n\n if (!text) {\n return undefined;\n }\n\n try {\n const parsed = JSON.parse(text) as { type?: unknown; method?: unknown; args?: unknown };\n if (parsed.type === 'rpc' && typeof parsed.method === 'string' && Array.isArray(parsed.args)) {\n return parsed.method;\n }\n } catch {\n // Not JSON, or not an RPC request — no span.\n }\n\n return undefined;\n}\n"],"names":["DEBUG_BUILD","debug","startSpan","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","AGENT_SPAN_ORIGIN","getAgentAttributes","setAgentConversationId"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"instrumentAgentCallableRpc.js","sources":["../../../../src/instrumentations/agents/instrumentAgentCallableRpc.ts"],"sourcesContent":["import { debug, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core';\nimport { DEBUG_BUILD } from '../../debug-build';\nimport { AGENT_SPAN_ORIGIN, type AgentInternals, getAgentAttributes, setAgentConversationId } from './types';\n\n/**\n * Wraps the Agent's `onMessage` handler to create a span for each `@callable()` RPC invocation.\n * RPC requests arrive as WebSocket messages, so this span nests under the active transaction for\n * the WebSocket message (on Cloudflare, the instrumented Durable Object `webSocketMessage` hook).\n *\n * Also sets the conversation id on the scope for the duration of the call: callable methods are the\n * unit of work for plain (non-chat) agents, which run LLM calls just like chat turns do. It is\n * awaited so the id is on the scope before the method body creates any span; the `agents`\n * `onMessage` own property this wraps is already `async`, so the promise return is nothing new to\n * the WebSocket dispatch upstream.\n */\nexport function instrumentAgentCallableRpc(obj: AgentInternals): void {\n const original = obj.onMessage;\n if (typeof original !== 'function') {\n DEBUG_BUILD && debug.log('[Sentry] Agent `onMessage` not found — callable RPC span instrumentation skipped.');\n return;\n }\n\n obj.onMessage = new Proxy(original, {\n apply(target, thisArg: AgentInternals, args: unknown[]): unknown {\n const method = extractCallableMethod(args[1]);\n\n if (!method) {\n return Reflect.apply(target, thisArg, args);\n }\n\n return startSpan(\n {\n name: method,\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'rpc',\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: AGENT_SPAN_ORIGIN,\n ...getAgentAttributes(thisArg),\n },\n },\n async () => {\n await setAgentConversationId(thisArg);\n return Reflect.apply(target, thisArg, args);\n },\n );\n },\n });\n}\n\n/** Extracts the RPC method name from a WebSocket message, mirroring the SDK's `isRPCRequest`. */\nfunction extractCallableMethod(message: unknown): string | undefined {\n const text =\n typeof message === 'string'\n ? message\n : message instanceof ArrayBuffer\n ? new TextDecoder().decode(message)\n : undefined;\n\n if (!text) {\n return undefined;\n }\n\n try {\n const parsed = JSON.parse(text) as { type?: unknown; method?: unknown; args?: unknown };\n if (parsed.type === 'rpc' && typeof parsed.method === 'string' && Array.isArray(parsed.args)) {\n return parsed.method;\n }\n } catch {\n // Not JSON, or not an RPC request — no span.\n }\n\n return undefined;\n}\n"],"names":["DEBUG_BUILD","debug","startSpan","SEMANTIC_ATTRIBUTE_SENTRY_OP","SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN","AGENT_SPAN_ORIGIN","getAgentAttributes","setAgentConversationId"],"mappings":";;;;;;AAeO,SAAS,2BAA2B,GAAA,EAA2B;AACpE,EAAA,MAAM,WAAW,GAAA,CAAI,SAAA;AACrB,EAAA,IAAI,OAAO,aAAa,UAAA,EAAY;AAClC,IAAAA,sBAAA,IAAeC,UAAA,CAAM,IAAI,wFAAmF,CAAA;AAC5G,IAAA;AAAA,EACF;AAEA,EAAA,GAAA,CAAI,SAAA,GAAY,IAAI,KAAA,CAAM,QAAA,EAAU;AAAA,IAClC,KAAA,CAAM,MAAA,EAAQ,OAAA,EAAyB,IAAA,EAA0B;AAC/D,MAAA,MAAM,MAAA,GAAS,qBAAA,CAAsB,IAAA,CAAK,CAAC,CAAC,CAAA;AAE5C,MAAA,IAAI,CAAC,MAAA,EAAQ;AACX,QAAA,OAAO,OAAA,CAAQ,KAAA,CAAM,MAAA,EAAQ,OAAA,EAAS,IAAI,CAAA;AAAA,MAC5C;AAEA,MAAA,OAAOC,cAAA;AAAA,QACL;AAAA,UACE,IAAA,EAAM,MAAA;AAAA,UACN,UAAA,EAAY;AAAA,YACV,CAACC,iCAA4B,GAAG,KAAA;AAAA,YAChC,CAACC,qCAAgC,GAAGC,uBAAA;AAAA,YACpC,GAAGC,yBAAmB,OAAO;AAAA;AAC/B,SACF;AAAA,QACA,YAAY;AACV,UAAA,MAAMC,6BAAuB,OAAO,CAAA;AACpC,UAAA,OAAO,OAAA,CAAQ,KAAA,CAAM,MAAA,EAAQ,OAAA,EAAS,IAAI,CAAA;AAAA,QAC5C;AAAA,OACF;AAAA,IACF;AAAA,GACD,CAAA;AACH;AAGA,SAAS,sBAAsB,OAAA,EAAsC;AACnE,EAAA,MAAM,IAAA,GACJ,OAAO,OAAA,KAAY,QAAA,GACf,OAAA,GACA,OAAA,YAAmB,WAAA,GACjB,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,OAAO,CAAA,GAChC,MAAA;AAER,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA;AAC9B,IAAA,IAAI,MAAA,CAAO,IAAA,KAAS,KAAA,IAAS,OAAO,MAAA,CAAO,MAAA,KAAW,QAAA,IAAY,KAAA,CAAM,OAAA,CAAQ,MAAA,CAAO,IAAI,CAAA,EAAG;AAC5F,MAAA,OAAO,MAAA,CAAO,MAAA;AAAA,IAChB;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AAEA,EAAA,OAAO,MAAA;AACT;;;;"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
const types = require('./types.js');
|
|
4
|
+
|
|
5
|
+
function instrumentAgentRequestConversation(obj) {
|
|
6
|
+
const original = obj.onRequest;
|
|
7
|
+
if (typeof original !== "function") {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
obj.onRequest = new Proxy(original, {
|
|
11
|
+
async apply(target, thisArg, args) {
|
|
12
|
+
await types.setAgentConversationId(thisArg);
|
|
13
|
+
return Reflect.apply(target, thisArg, args);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
exports.instrumentAgentRequestConversation = instrumentAgentRequestConversation;
|
|
19
|
+
//# sourceMappingURL=instrumentAgentRequestConversation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrumentAgentRequestConversation.js","sources":["../../../../src/instrumentations/agents/instrumentAgentRequestConversation.ts"],"sourcesContent":["import { type AgentInternals, setAgentConversationId } from './types';\n\n/**\n * Correlates the AI spans of an HTTP-driven agent turn with a conversation id on the active scope.\n *\n * `onRequest` is the third unit of agent work, alongside chat turns and `@callable()` RPC: the\n * `agents` router sends every non-WebSocket request to it, which is how REST endpoints and webhooks\n * reach an agent.\n *\n * `agents` installs `onRequest` as an own property in the `Agent` constructor (as it does\n * `onMessage`), and we instrument after construction, so wrapping the own property is what the\n * router ends up calling. That own property is already `async`, and partyserver's `fetch` awaits it,\n * so returning a promise from this wrapper — to get the conversation id onto the scope before the\n * request handler creates any span — keeps the existing contract.\n */\nexport function instrumentAgentRequestConversation(obj: AgentInternals): void {\n const original = obj.onRequest;\n\n if (typeof original !== 'function') {\n return;\n }\n\n obj.onRequest = new Proxy(original, {\n async apply(target, thisArg: AgentInternals, args: unknown[]): Promise<unknown> {\n await setAgentConversationId(thisArg);\n\n return Reflect.apply(target, thisArg, args);\n },\n });\n}\n"],"names":["setAgentConversationId"],"mappings":";;;;AAeO,SAAS,mCAAmC,GAAA,EAA2B;AAC5E,EAAA,MAAM,WAAW,GAAA,CAAI,SAAA;AAErB,EAAA,IAAI,OAAO,aAAa,UAAA,EAAY;AAClC,IAAA;AAAA,EACF;AAEA,EAAA,GAAA,CAAI,SAAA,GAAY,IAAI,KAAA,CAAM,QAAA,EAAU;AAAA,IAClC,MAAM,KAAA,CAAM,MAAA,EAAQ,OAAA,EAAyB,IAAA,EAAmC;AAC9E,MAAA,MAAMA,6BAAuB,OAAO,CAAA;AAEpC,MAAA,OAAO,OAAA,CAAQ,KAAA,CAAM,MAAA,EAAQ,OAAA,EAAS,IAAI,CAAA;AAAA,IAC5C;AAAA,GACD,CAAA;AACH;;;;"}
|
|
@@ -9,7 +9,7 @@ function instrumentChatAgentConversation(obj) {
|
|
|
9
9
|
obj._emit = new Proxy(originalEmit, {
|
|
10
10
|
apply(target, thisArg, args) {
|
|
11
11
|
if (args[0] === "message:clear") {
|
|
12
|
-
thisArg
|
|
12
|
+
types.storeAgentConversationId(thisArg, core.uuid4());
|
|
13
13
|
}
|
|
14
14
|
return Reflect.apply(target, thisArg, args);
|
|
15
15
|
}
|
|
@@ -20,8 +20,8 @@ function instrumentChatAgentConversation(obj) {
|
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
22
|
obj.onChatMessage = new Proxy(original, {
|
|
23
|
-
apply(target, thisArg, args) {
|
|
24
|
-
types.setAgentConversationId(thisArg);
|
|
23
|
+
async apply(target, thisArg, args) {
|
|
24
|
+
await types.setAgentConversationId(thisArg);
|
|
25
25
|
return Reflect.apply(target, thisArg, args);
|
|
26
26
|
}
|
|
27
27
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instrumentChatAgentConversation.js","sources":["../../../../src/instrumentations/agents/instrumentChatAgentConversation.ts"],"sourcesContent":["import { uuid4 } from '@sentry/core';\nimport { type AgentInternals, setAgentConversationId } from './types';\n\n/**\n * For chat agents (`AIChatAgent` from `@cloudflare/ai-chat`), correlates each chat turn's AI spans\n * with a conversation id on the active scope.\n *\n * In the Agents model one agent instance is one long-lived conversation, so the instance
|
|
1
|
+
{"version":3,"file":"instrumentChatAgentConversation.js","sources":["../../../../src/instrumentations/agents/instrumentChatAgentConversation.ts"],"sourcesContent":["import { uuid4 } from '@sentry/core';\nimport { type AgentInternals, setAgentConversationId, storeAgentConversationId } from './types';\n\n/**\n * For chat agents (`AIChatAgent` from `@cloudflare/ai-chat`), correlates each chat turn's AI spans\n * with a conversation id on the active scope.\n *\n * In the Agents model one agent instance is one long-lived conversation, so the conversation id is\n * minted once per instance and persisted to Durable Object storage, which is what carries it across\n * hibernation (that destroys the in-memory instance). When the user clears the chat, they expect a\n * fresh conversation — but recreating the Durable Object for that would also drop the MCP/OAuth\n * state stored per instance (GitHub/Sentry sign-in). To get a fresh conversation id *without*\n * losing that state, we rotate the persisted id when the SDK reports a cleared chat.\n *\n * The id itself is not attached to spans here — `setAgentConversationId` resolves it, and the SDK's\n * `conversationIdIntegration` picks it off the scope at `spanStart` to stamp\n * `gen_ai.conversation.id` onto the AI spans created inside the turn (e.g. by the Workers AI\n * instrumentation), which correlates a turn's model and tool calls.\n *\n * Plain (non-chat) `Agent`s do not define `onChatMessage`, so they are skipped here — their unit\n * of work is the callable RPC method, where `instrumentAgentCallableRpc` sets the conversation id\n * instead.\n */\nexport function instrumentChatAgentConversation(obj: AgentInternals): void {\n // Rotate the conversation id when the chat is cleared. `_emit` is the central choke-point through\n // which all `agents:*` observability events are published, and it already exists on the base\n // `Agent` class — so this hook composes with the RPC instrumentation, which keys off the same\n // surface. We shadow it with an own property so the original stays reachable on the prototype.\n const originalEmit = obj._emit;\n\n if (typeof originalEmit === 'function') {\n obj._emit = new Proxy(originalEmit, {\n apply(target, thisArg: AgentInternals, args: [string, Record<string, unknown>?]) {\n if (args[0] === 'message:clear') {\n storeAgentConversationId(thisArg, uuid4());\n }\n\n return Reflect.apply(target, thisArg, args);\n },\n });\n }\n\n const original = obj.onChatMessage;\n\n if (typeof original !== 'function') {\n return;\n }\n\n obj.onChatMessage = new Proxy(original, {\n async apply(target, thisArg: AgentInternals, args: unknown[]): Promise<unknown> {\n await setAgentConversationId(thisArg);\n\n return Reflect.apply(target, thisArg, args);\n },\n });\n}\n"],"names":["storeAgentConversationId","uuid4","setAgentConversationId"],"mappings":";;;;;AAuBO,SAAS,gCAAgC,GAAA,EAA2B;AAKzE,EAAA,MAAM,eAAe,GAAA,CAAI,KAAA;AAEzB,EAAA,IAAI,OAAO,iBAAiB,UAAA,EAAY;AACtC,IAAA,GAAA,CAAI,KAAA,GAAQ,IAAI,KAAA,CAAM,YAAA,EAAc;AAAA,MAClC,KAAA,CAAM,MAAA,EAAQ,OAAA,EAAyB,IAAA,EAA0C;AAC/E,QAAA,IAAI,IAAA,CAAK,CAAC,CAAA,KAAM,eAAA,EAAiB;AAC/B,UAAAA,8BAAA,CAAyB,OAAA,EAASC,YAAO,CAAA;AAAA,QAC3C;AAEA,QAAA,OAAO,OAAA,CAAQ,KAAA,CAAM,MAAA,EAAQ,OAAA,EAAS,IAAI,CAAA;AAAA,MAC5C;AAAA,KACD,CAAA;AAAA,EACH;AAEA,EAAA,MAAM,WAAW,GAAA,CAAI,aAAA;AAErB,EAAA,IAAI,OAAO,aAAa,UAAA,EAAY;AAClC,IAAA;AAAA,EACF;AAEA,EAAA,GAAA,CAAI,aAAA,GAAgB,IAAI,KAAA,CAAM,QAAA,EAAU;AAAA,IACtC,MAAM,KAAA,CAAM,MAAA,EAAQ,OAAA,EAAyB,IAAA,EAAmC;AAC9E,MAAA,MAAMC,6BAAuB,OAAO,CAAA;AAEpC,MAAA,OAAO,OAAA,CAAQ,KAAA,CAAM,MAAA,EAAQ,OAAA,EAAS,IAAI,CAAA;AAAA,IAC5C;AAAA,GACD,CAAA;AACH;;;;"}
|
|
@@ -1,32 +1,77 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
3
|
const core = require('@sentry/core');
|
|
4
|
+
const debugBuild = require('../../debug-build.js');
|
|
4
5
|
|
|
6
|
+
const GEN_AI_AGENT_NAME_ATTRIBUTE = "gen_ai.agent.name";
|
|
5
7
|
const AGENT_SPAN_ORIGIN = "auto.faas.cloudflare.agents";
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
+
const AGENT_CONVERSATION_ID_STORAGE_KEY = "__SENTRY_AGENT_CONVERSATION_ID__";
|
|
9
|
+
const AGENT_CONVERSATION_ID_SYMBOL = /* @__PURE__ */ Symbol("sentryAgentConversationId");
|
|
10
|
+
const AGENT_APPLIED_CONVERSATION_ID_SYMBOL = /* @__PURE__ */ Symbol("sentryAgentAppliedConversationId");
|
|
8
11
|
function getAgentAttributes(instance) {
|
|
9
12
|
const attributes = {};
|
|
10
13
|
const agentClass = instance._ParentClass?.name;
|
|
11
14
|
if (typeof agentClass === "string" && agentClass) {
|
|
12
|
-
attributes[
|
|
13
|
-
}
|
|
14
|
-
const agentName = instance.name;
|
|
15
|
-
if (typeof agentName === "string" && agentName) {
|
|
16
|
-
attributes[AGENT_NAME_ATTRIBUTE] = agentName;
|
|
15
|
+
attributes[GEN_AI_AGENT_NAME_ATTRIBUTE] = agentClass;
|
|
17
16
|
}
|
|
18
17
|
return attributes;
|
|
19
18
|
}
|
|
20
|
-
function
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
function storeAgentConversationId(instance, conversationId) {
|
|
20
|
+
instance[AGENT_CONVERSATION_ID_SYMBOL] = conversationId;
|
|
21
|
+
const storage = resolveStorage(instance);
|
|
22
|
+
if (!storage) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
storage.put(AGENT_CONVERSATION_ID_STORAGE_KEY, conversationId).catch((error) => debugBuild.DEBUG_BUILD && core.debug.log("[Sentry] Failed to persist agent conversation id", error));
|
|
27
|
+
} catch (error) {
|
|
28
|
+
debugBuild.DEBUG_BUILD && core.debug.log("[Sentry] Failed to persist agent conversation id", error);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function setAgentConversationId(instance) {
|
|
32
|
+
const isolationScope = core.getIsolationScope();
|
|
33
|
+
const existing = isolationScope.getScopeData().conversationId ?? core.getCurrentScope().getScopeData().conversationId;
|
|
34
|
+
if (existing && existing !== instance[AGENT_APPLIED_CONVERSATION_ID_SYMBOL]) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const conversationId = instance[AGENT_CONVERSATION_ID_SYMBOL] ?? await loadAgentConversationId(instance);
|
|
38
|
+
instance[AGENT_APPLIED_CONVERSATION_ID_SYMBOL] = conversationId;
|
|
39
|
+
isolationScope.setConversationId(conversationId);
|
|
40
|
+
}
|
|
41
|
+
async function loadAgentConversationId(instance) {
|
|
42
|
+
let stored;
|
|
43
|
+
let readFailed = false;
|
|
44
|
+
try {
|
|
45
|
+
stored = await resolveStorage(instance)?.get(AGENT_CONVERSATION_ID_STORAGE_KEY);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
readFailed = true;
|
|
48
|
+
debugBuild.DEBUG_BUILD && core.debug.log("[Sentry] Failed to read agent conversation id from storage", error);
|
|
49
|
+
}
|
|
50
|
+
const cached = instance[AGENT_CONVERSATION_ID_SYMBOL];
|
|
51
|
+
if (cached !== void 0) {
|
|
52
|
+
return cached;
|
|
53
|
+
}
|
|
54
|
+
if (typeof stored === "string" && stored) {
|
|
55
|
+
instance[AGENT_CONVERSATION_ID_SYMBOL] = stored;
|
|
56
|
+
return stored;
|
|
24
57
|
}
|
|
58
|
+
const conversationId = core.uuid4();
|
|
59
|
+
if (readFailed) {
|
|
60
|
+
instance[AGENT_CONVERSATION_ID_SYMBOL] = conversationId;
|
|
61
|
+
} else {
|
|
62
|
+
storeAgentConversationId(instance, conversationId);
|
|
63
|
+
}
|
|
64
|
+
return conversationId;
|
|
65
|
+
}
|
|
66
|
+
function resolveStorage(instance) {
|
|
67
|
+
return instance.ctx?.originalStorage ?? instance.ctx?.storage;
|
|
25
68
|
}
|
|
26
69
|
|
|
27
|
-
exports.
|
|
28
|
-
exports.
|
|
70
|
+
exports.AGENT_APPLIED_CONVERSATION_ID_SYMBOL = AGENT_APPLIED_CONVERSATION_ID_SYMBOL;
|
|
71
|
+
exports.AGENT_CONVERSATION_ID_STORAGE_KEY = AGENT_CONVERSATION_ID_STORAGE_KEY;
|
|
72
|
+
exports.AGENT_CONVERSATION_ID_SYMBOL = AGENT_CONVERSATION_ID_SYMBOL;
|
|
29
73
|
exports.AGENT_SPAN_ORIGIN = AGENT_SPAN_ORIGIN;
|
|
30
74
|
exports.getAgentAttributes = getAgentAttributes;
|
|
31
75
|
exports.setAgentConversationId = setAgentConversationId;
|
|
76
|
+
exports.storeAgentConversationId = storeAgentConversationId;
|
|
32
77
|
//# sourceMappingURL=types.js.map
|