raindrop-ai 0.2.4 → 0.3.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.
@@ -4,7 +4,7 @@ if (!globalThis.RAINDROP_ASYNC_LOCAL_STORAGE) {
4
4
  globalThis.RAINDROP_ASYNC_LOCAL_STORAGE = AsyncLocalStorage;
5
5
  }
6
6
 
7
- // ../core/dist/chunk-QTUH3BHZ.js
7
+ // ../core/dist/chunk-YUIXRQWN.js
8
8
  function normalizeFeatureFlags(input) {
9
9
  if (Array.isArray(input)) {
10
10
  return Object.fromEntries(input.map((name) => [name, "true"]));
@@ -13,6 +13,293 @@ function normalizeFeatureFlags(input) {
13
13
  Object.entries(input).map(([k, v]) => [k, String(v)])
14
14
  );
15
15
  }
16
+ function getCrypto() {
17
+ const c = globalThis.crypto;
18
+ return c;
19
+ }
20
+ function randomBytes(length) {
21
+ const cryptoObj = getCrypto();
22
+ const out = new Uint8Array(length);
23
+ if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
24
+ cryptoObj.getRandomValues(out);
25
+ return out;
26
+ }
27
+ for (let i = 0; i < out.length; i++) out[i] = Math.floor(Math.random() * 256);
28
+ return out;
29
+ }
30
+ function randomUUID() {
31
+ const cryptoObj = getCrypto();
32
+ if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
33
+ return cryptoObj.randomUUID();
34
+ }
35
+ const b = randomBytes(16);
36
+ b[6] = b[6] & 15 | 64;
37
+ b[8] = b[8] & 63 | 128;
38
+ const hex = [...b].map((x) => x.toString(16).padStart(2, "0")).join("");
39
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
40
+ }
41
+ var HANDOFF_ATTRIBUTE_SUFFIXES = {
42
+ mode: "raindrop.handoff.mode",
43
+ childEventId: "raindrop.handoff.childEventId",
44
+ parentEventId: "raindrop.handoff.parentEventId",
45
+ parentSpanId: "raindrop.handoff.parentSpanId",
46
+ name: "raindrop.handoff.name",
47
+ terminal: "raindrop.handoff.terminal",
48
+ agentRole: "raindrop.agent.role"
49
+ };
50
+ var AI_SDK_METADATA_PREFIX = "ai.telemetry.metadata.";
51
+ var DETACHED_MODE = "detached";
52
+ var SUBAGENT_AGENT_ROLE = "subagent";
53
+ var HANDOFF_TERMINAL_CANCELLED = "cancelled";
54
+ var TOOL_EVENTS_SUFFIX = "raindrop.toolEvents";
55
+ var TOOL_EVENTS_ALLOW = "allow";
56
+ var OPERATION_ID_ATTRIBUTE = "ai.operationId";
57
+ var SUBAGENT_ABORT_OPERATION_ID = "ai.subagent.failed";
58
+ var SUBAGENT_ABORT_SPAN_NAME = SUBAGENT_ABORT_OPERATION_ID;
59
+ var DEFAULT_DISPATCH_SPAN_NAME = "launch_subagent";
60
+ var HANDOFF_TERMINAL_PROPERTY_KEY = HANDOFF_ATTRIBUTE_SUFFIXES.terminal;
61
+ function defined(entries) {
62
+ const out = {};
63
+ for (const [key, value] of Object.entries(entries)) {
64
+ if (typeof value === "string" && value.trim().length > 0) out[key] = value;
65
+ }
66
+ return out;
67
+ }
68
+ function detachedDispatchMetadata(dispatch) {
69
+ return defined({
70
+ [HANDOFF_ATTRIBUTE_SUFFIXES.mode]: DETACHED_MODE,
71
+ [HANDOFF_ATTRIBUTE_SUFFIXES.childEventId]: dispatch.childEventId,
72
+ [HANDOFF_ATTRIBUTE_SUFFIXES.name]: dispatch.name
73
+ });
74
+ }
75
+ function detachedChildMetadata(link) {
76
+ return defined({
77
+ [HANDOFF_ATTRIBUTE_SUFFIXES.parentEventId]: link.parentEventId,
78
+ [HANDOFF_ATTRIBUTE_SUFFIXES.parentSpanId]: link.parentSpanId,
79
+ [HANDOFF_ATTRIBUTE_SUFFIXES.name]: link.name,
80
+ [HANDOFF_ATTRIBUTE_SUFFIXES.agentRole]: SUBAGENT_AGENT_ROLE
81
+ });
82
+ }
83
+ function cancelledTerminalMetadata() {
84
+ return { [HANDOFF_ATTRIBUTE_SUFFIXES.terminal]: HANDOFF_TERMINAL_CANCELLED };
85
+ }
86
+ function cancelledTerminalAttributes() {
87
+ const bare = cancelledTerminalMetadata();
88
+ return { ...bare, ...withMetadataPrefix(bare) };
89
+ }
90
+ function toolEventsAllowMetadata() {
91
+ return { [TOOL_EVENTS_SUFFIX]: TOOL_EVENTS_ALLOW };
92
+ }
93
+ function withMetadataPrefix(metadata) {
94
+ const out = {};
95
+ for (const [key, value] of Object.entries(metadata)) {
96
+ out[key.startsWith(AI_SDK_METADATA_PREFIX) ? key : `${AI_SDK_METADATA_PREFIX}${key}`] = value;
97
+ }
98
+ return out;
99
+ }
100
+ var BAGGAGE_KEYS = {
101
+ eventId: "raindrop-event-id",
102
+ childEventId: "raindrop-child-event-id",
103
+ name: "raindrop-handoff-name",
104
+ convoId: "raindrop-convo-id",
105
+ userId: "raindrop-user-id",
106
+ // The dispatch span and its trace, carried as fields as well as in
107
+ // `traceparent`. Not redundant: OTel's HTTP instrumentations rewrite
108
+ // `traceparent` from whatever span is current as the request goes out, so by
109
+ // the time the child reads it the ids name the HTTP client span and its trace
110
+ // instead of the dispatch. The child would then point its reverse reference at
111
+ // a span the launcher's UI does not know as a dispatch. A field states which
112
+ // span it means rather than meaning "whoever wrote this header last".
113
+ dispatchSpanId: "raindrop-dispatch-span-id",
114
+ traceId: "raindrop-trace-id"
115
+ };
116
+ var TRACEPARENT_HEADER = "traceparent";
117
+ var BAGGAGE_HEADER = "baggage";
118
+ var W3C_TRACE_ID = /^[0-9a-f]{32}$/;
119
+ var W3C_SPAN_ID = /^[0-9a-f]{16}$/;
120
+ function isW3CTraceId(value) {
121
+ return value !== void 0 && W3C_TRACE_ID.test(value);
122
+ }
123
+ function isW3CSpanId(value) {
124
+ return value !== void 0 && W3C_SPAN_ID.test(value);
125
+ }
126
+ var warnedNonConformantIds = false;
127
+ function warnNonConformantIdsOnce(traceId, spanId) {
128
+ if (warnedNonConformantIds) return;
129
+ warnedNonConformantIds = true;
130
+ console.warn(
131
+ `[raindrop] hand-off carrier ids are not W3C-shaped (traceId="${traceId}", spanId="${spanId}"). The child still reports as its own event, but its reverse reference names a span id the backend does not store, so the link back to the launcher will not resolve. A LangSmith carrier reads this way \u2014 its run ids are UUIDs; propagate Raindrop's own carrier (toHeaders) to link.`
132
+ );
133
+ }
134
+ var HANDOFF_HEADER = "x-raindrop-handoff";
135
+ var LANGSMITH_TRACE_HEADER = "langsmith-trace";
136
+ var LANGSMITH_METADATA_BAGGAGE_KEY = "langsmith-metadata";
137
+ function encodeBaggage(entries) {
138
+ return Object.entries(entries).filter((entry) => Boolean(entry[1])).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join(",");
139
+ }
140
+ function decodeBaggage(header) {
141
+ if (!header) return {};
142
+ const parsed = {};
143
+ for (const part of header.split(",")) {
144
+ const [rawKey, ...rest] = part.split("=");
145
+ if (!rawKey || rest.length === 0) continue;
146
+ const key = rawKey.trim();
147
+ let value;
148
+ try {
149
+ value = decodeURIComponent(rest.join("=").trim());
150
+ } catch (e) {
151
+ value = rest.join("=").trim();
152
+ }
153
+ if (key in parsed && parsed[key] !== value) throw new ConflictingCarrierError(key);
154
+ parsed[key] = value;
155
+ }
156
+ return parsed;
157
+ }
158
+ function carrierBaggage(carrier) {
159
+ return {
160
+ [BAGGAGE_KEYS.eventId]: carrier.eventId,
161
+ [BAGGAGE_KEYS.childEventId]: carrier.childEventId,
162
+ [BAGGAGE_KEYS.name]: carrier.name,
163
+ [BAGGAGE_KEYS.convoId]: carrier.convoId,
164
+ [BAGGAGE_KEYS.userId]: carrier.userId,
165
+ [BAGGAGE_KEYS.dispatchSpanId]: carrier.spanId,
166
+ [BAGGAGE_KEYS.traceId]: carrier.traceId
167
+ };
168
+ }
169
+ function toHeaders(carrier) {
170
+ const fields = encodeBaggage(carrierBaggage(carrier));
171
+ const headers = {
172
+ [BAGGAGE_HEADER]: fields,
173
+ [HANDOFF_HEADER]: fields
174
+ };
175
+ if (isW3CTraceId(carrier.traceId) && isW3CSpanId(carrier.spanId)) {
176
+ headers[TRACEPARENT_HEADER] = `00-${carrier.traceId}-${carrier.spanId}-01`;
177
+ }
178
+ return headers;
179
+ }
180
+ function langsmithStamp() {
181
+ return (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "000000");
182
+ }
183
+ function toLangSmithHeaders(carrier) {
184
+ const stamp = langsmithStamp();
185
+ const dottedOrder = `${stamp}Z${carrier.traceId}.${stamp}Z${carrier.spanId}`;
186
+ return {
187
+ [LANGSMITH_TRACE_HEADER]: dottedOrder,
188
+ [BAGGAGE_HEADER]: encodeBaggage({
189
+ [LANGSMITH_METADATA_BAGGAGE_KEY]: JSON.stringify(carrierBaggage(carrier))
190
+ }),
191
+ // Carried here too: a LangSmith-shaped carrier is rewritten in transit
192
+ // exactly like the native one, and an injected `traceparent` is preferred
193
+ // over the dotted order on read.
194
+ [HANDOFF_HEADER]: encodeBaggage(carrierBaggage(carrier))
195
+ };
196
+ }
197
+ function isHeaderLookup(headers) {
198
+ return "get" in headers && typeof headers.get === "function";
199
+ }
200
+ var ConflictingCarrierError = class extends Error {
201
+ };
202
+ function readHeader(headers, name) {
203
+ var _a, _b, _c;
204
+ if (isHeaderLookup(headers)) return (_a = headers.get(name)) != null ? _a : void 0;
205
+ const value = (_c = headers[name]) != null ? _c : (_b = Object.entries(headers).find(([key]) => key.toLowerCase() === name)) == null ? void 0 : _b[1];
206
+ if (!Array.isArray(value)) return value;
207
+ const distinct = new Set(value);
208
+ if (distinct.size > 1) throw new ConflictingCarrierError(name);
209
+ return value[0];
210
+ }
211
+ function readTraceHeader(headers, name) {
212
+ const value = readHeader(headers, name);
213
+ if (!(value == null ? void 0 : value.includes(","))) return value;
214
+ const copies = value.split(",").map((copy) => copy.trim());
215
+ if (new Set(copies).size > 1) throw new ConflictingCarrierError(name);
216
+ return copies[0];
217
+ }
218
+ function fromHeaders(headers) {
219
+ var _a, _b;
220
+ let baggage;
221
+ let handoff;
222
+ let traceparent;
223
+ let langsmithTrace;
224
+ try {
225
+ baggage = decodeBaggage(readHeader(headers, BAGGAGE_HEADER));
226
+ handoff = decodeBaggage(readHeader(headers, HANDOFF_HEADER));
227
+ traceparent = readTraceHeader(headers, TRACEPARENT_HEADER);
228
+ langsmithTrace = readTraceHeader(headers, LANGSMITH_TRACE_HEADER);
229
+ } catch (error) {
230
+ if (error instanceof ConflictingCarrierError) return null;
231
+ throw error;
232
+ }
233
+ const langsmithMetadata = baggage[LANGSMITH_METADATA_BAGGAGE_KEY];
234
+ const nested = langsmithMetadata ? safeParseStringRecord(langsmithMetadata) : {};
235
+ for (const [key, value] of Object.entries(nested)) {
236
+ if (key in baggage && baggage[key] !== value) return null;
237
+ }
238
+ const fields = {
239
+ ...baggage,
240
+ ...nested,
241
+ // Last, so it wins: the standard headers may have been rewritten in transit
242
+ // by a propagator, this one is only ever written by us.
243
+ ...handoff
244
+ };
245
+ let traceId;
246
+ let spanId;
247
+ if (traceparent) {
248
+ const [, parsedTraceId, parsedSpanId] = traceparent.split("-");
249
+ if (isW3CTraceId(parsedTraceId) && isW3CSpanId(parsedSpanId)) {
250
+ traceId = parsedTraceId;
251
+ spanId = parsedSpanId;
252
+ }
253
+ }
254
+ if (!traceId && !spanId && langsmithTrace) {
255
+ const segments = langsmithTrace.split(".");
256
+ traceId = (_a = segments[0]) == null ? void 0 : _a.split("Z").pop();
257
+ spanId = (_b = segments[segments.length - 1]) == null ? void 0 : _b.split("Z").pop();
258
+ }
259
+ traceId = fields[BAGGAGE_KEYS.traceId] || traceId;
260
+ spanId = fields[BAGGAGE_KEYS.dispatchSpanId] || spanId;
261
+ const eventId = fields[BAGGAGE_KEYS.eventId];
262
+ if (!traceId || !spanId || !eventId) return null;
263
+ if (!isW3CTraceId(traceId) || !isW3CSpanId(spanId)) {
264
+ warnNonConformantIdsOnce(traceId, spanId);
265
+ }
266
+ const carrier = { traceId, spanId, eventId };
267
+ const childEventId = fields[BAGGAGE_KEYS.childEventId];
268
+ if (childEventId) carrier.childEventId = childEventId;
269
+ const name = fields[BAGGAGE_KEYS.name];
270
+ if (name) carrier.name = name;
271
+ const convoId = fields[BAGGAGE_KEYS.convoId];
272
+ if (convoId) carrier.convoId = convoId;
273
+ const userId = fields[BAGGAGE_KEYS.userId];
274
+ if (userId) carrier.userId = userId;
275
+ return carrier;
276
+ }
277
+ var UNRESOLVED_CARRIER_WARNING = `[raindrop] resume: headers were supplied but no hand-off carrier was found on them (searched ${HANDOFF_HEADER}, ${TRACEPARENT_HEADER}, ${BAGGAGE_HEADER}). This run reports as an UNLINKED event, and the launcher that dispatched it will stay on "queued" forever because nothing ever references its child. Passing headers means a parent handed off, so no carrier on them is almost certainly a defect: a gateway or proxy stripping ${HANDOFF_HEADER}, a middleware overwriting ${BAGGAGE_HEADER}, a request forwarded without its headers, or a hand-built carrier whose field names do not match. Send every header the launcher's dispatch returned. Logged once per process; pass no headers for a directly-invoked sub-agent to silence it.`;
278
+ var UNRESOLVED_CARRIER_WARNED_KEY = /* @__PURE__ */ Symbol.for("raindrop.handoff.warnedUnresolvedCarrier");
279
+ function warnedHolder() {
280
+ return globalThis;
281
+ }
282
+ function warnUnresolvedCarrier(headers, carrier) {
283
+ if (carrier) return;
284
+ if (!headers) return;
285
+ const holder = warnedHolder();
286
+ if (holder[UNRESOLVED_CARRIER_WARNED_KEY]) return;
287
+ holder[UNRESOLVED_CARRIER_WARNED_KEY] = true;
288
+ console.warn(UNRESOLVED_CARRIER_WARNING);
289
+ }
290
+ function safeParseStringRecord(raw) {
291
+ try {
292
+ const parsed = JSON.parse(raw);
293
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
294
+ const result = {};
295
+ for (const [key, value] of Object.entries(parsed)) {
296
+ if (typeof value === "string") result[key] = value;
297
+ }
298
+ return result;
299
+ } catch (e) {
300
+ return {};
301
+ }
302
+ }
16
303
  function runWithTracingSuppressed(fn) {
17
304
  const hook = globalThis.RAINDROP_SUPPRESS_TRACING;
18
305
  if (typeof hook !== "function") return fn();
@@ -394,6 +681,62 @@ function projectIdHeaders(projectId) {
394
681
  }
395
682
  var SHUTDOWN_DEADLINE_MS = 1e4;
396
683
  var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
684
+ var MODEL_USAGE_ATTRIBUTES = {
685
+ providerName: "gen_ai.provider.name",
686
+ requestModel: "gen_ai.request.model",
687
+ responseModel: "gen_ai.response.model",
688
+ inputTokens: "gen_ai.usage.input_tokens",
689
+ outputTokens: "gen_ai.usage.output_tokens",
690
+ reasoningTokens: "gen_ai.usage.reasoning_tokens",
691
+ cacheReadInputTokens: "gen_ai.usage.cache_read_input_tokens",
692
+ cacheWriteInputTokens: "gen_ai.usage.cache_write_input_tokens",
693
+ nonCachedInputTokens: "raindrop.usage.input_tokens.non_cached",
694
+ nonReasoningOutputTokens: "raindrop.usage.output_tokens.non_reasoning",
695
+ totalOutputTokens: "raindrop.usage.output_tokens.total",
696
+ reasoningOutputTokens: "raindrop.usage.output_tokens.reasoning",
697
+ cacheReadTokens: "raindrop.usage.input_tokens.cache_read",
698
+ cacheWriteTokens: "raindrop.usage.input_tokens.cache_write"
699
+ };
700
+ var STRING_ALIASES = {
701
+ [MODEL_USAGE_ATTRIBUTES.requestModel]: ["ai.model.id", "ai.model"]
702
+ };
703
+ var NUMBER_ALIASES = {
704
+ [MODEL_USAGE_ATTRIBUTES.inputTokens]: [
705
+ "gen_ai.usage.prompt_tokens",
706
+ "ai.usage.prompt_tokens",
707
+ "ai.usage.promptTokens",
708
+ "ai.usage.input_tokens",
709
+ "ai.usage.inputTokens"
710
+ ],
711
+ [MODEL_USAGE_ATTRIBUTES.outputTokens]: [
712
+ "gen_ai.usage.completion_tokens",
713
+ "ai.usage.completion_tokens",
714
+ "ai.usage.completionTokens",
715
+ "ai.usage.output_tokens",
716
+ "ai.usage.outputTokens"
717
+ ],
718
+ [MODEL_USAGE_ATTRIBUTES.reasoningTokens]: [
719
+ "gen_ai.usage.reasoning.output_tokens",
720
+ "ai.usage.reasoningTokens",
721
+ "ai.usage.thoughts_tokens"
722
+ ],
723
+ [MODEL_USAGE_ATTRIBUTES.cacheReadInputTokens]: [
724
+ "gen_ai.usage.cache_read_tokens",
725
+ "gen_ai.usage.cache_read.input_tokens",
726
+ "ai.usage.cachedInputTokens",
727
+ "ai.usage.cached_tokens",
728
+ "ai.usage.cache_read_tokens",
729
+ "ai.usage.cache_read_input_tokens",
730
+ "ai.usage.cacheReadInputTokens"
731
+ ],
732
+ [MODEL_USAGE_ATTRIBUTES.cacheWriteInputTokens]: [
733
+ "gen_ai.usage.cache_creation_input_tokens",
734
+ "gen_ai.usage.cache_creation.input_tokens",
735
+ "ai.usage.cacheWriteInputTokens",
736
+ "ai.usage.cache_creation_input_tokens",
737
+ "ai.usage.cacheCreationInputTokens"
738
+ ]
739
+ };
397
740
  var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
398
741
  "ai.request.providerOptions",
399
742
  "ai.response.providerMetadata"
@@ -408,8 +751,10 @@ function getWeakRefCtor() {
408
751
  }
409
752
  var MAX_BINDING_STACK = 128;
410
753
  var EMPTY_STACK = [];
411
- var RoutingContextStorage = class {
412
- constructor() {
754
+ var ContextStackStorage = class {
755
+ /** Emitted once when this runtime cannot support an ambient push. */
756
+ constructor(noAmbientBindWarning) {
757
+ this.noAmbientBindWarning = noAmbientBindWarning;
413
758
  this._als = null;
414
759
  this._enterWithUsable = false;
415
760
  this._syncStack = EMPTY_STACK;
@@ -506,17 +851,17 @@ var RoutingContextStorage = class {
506
851
  warnNoAmbientBindOnce() {
507
852
  if (this._warnedNoAmbientBind) return;
508
853
  this._warnedNoAmbientBind = true;
509
- console.warn(
510
- "[raindrop] This runtime's AsyncLocalStorage does not implement enterWith (e.g. Cloudflare Workers), so begin()/finish() cannot scope auto-instrumented spans to a project without cross-contaminating concurrent requests. Manual events and explicit span routing are unaffected; use asCurrent()/withSpan (run-based) to route auto-instrumented spans on this runtime."
511
- );
854
+ console.warn(this.noAmbientBindWarning);
512
855
  }
513
856
  };
857
+ var NO_AMBIENT_ROUTING_BIND_WARNING = "[raindrop] This runtime's AsyncLocalStorage does not implement enterWith (e.g. Cloudflare Workers), so begin()/finish() cannot scope auto-instrumented spans to a project without cross-contaminating concurrent requests. Manual events and explicit span routing are unaffected; use asCurrent()/withSpan (run-based) to route auto-instrumented spans on this runtime.";
858
+ var NO_AMBIENT_ATTRIBUTE_BIND_WARNING = "[raindrop] This runtime's AsyncLocalStorage does not implement enterWith (e.g. Cloudflare Workers), so contextual span attributes (the detached sub-agent hand-off reference) cannot be bound ambiently without cross-contaminating concurrent requests. The child's own event still reports; only the per-span reverse reference is skipped.";
514
859
  var ROUTING_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.routingContextStorage");
515
860
  function routingStorage() {
516
861
  const holder = globalThis;
517
862
  let storage = holder[ROUTING_STORAGE_KEY];
518
863
  if (!storage) {
519
- storage = new RoutingContextStorage();
864
+ storage = new ContextStackStorage(NO_AMBIENT_ROUTING_BIND_WARNING);
520
865
  holder[ROUTING_STORAGE_KEY] = storage;
521
866
  }
522
867
  return storage;
@@ -564,6 +909,104 @@ function currentRoutingBinding() {
564
909
  function isBoundContext(value) {
565
910
  return typeof value.isLive === "function";
566
911
  }
912
+ var ATTRIBUTE_STORAGE_KEY = /* @__PURE__ */ Symbol.for("raindrop.tracing.spanAttributeStorage");
913
+ function attributeStorage() {
914
+ const holder = globalThis;
915
+ let storage = holder[ATTRIBUTE_STORAGE_KEY];
916
+ if (!storage) {
917
+ storage = new ContextStackStorage(NO_AMBIENT_ATTRIBUTE_BIND_WARNING);
918
+ holder[ATTRIBUTE_STORAGE_KEY] = storage;
919
+ }
920
+ return storage;
921
+ }
922
+ function makeAttributeFrame(attributes, owner) {
923
+ const snapshot = { ...attributes };
924
+ const WeakRefCtor = owner !== void 0 ? getWeakRefCtor() : void 0;
925
+ const ref = WeakRefCtor && owner !== void 0 ? new WeakRefCtor(owner) : void 0;
926
+ const owned = /* @__PURE__ */ new WeakSet();
927
+ let dead = false;
928
+ let sawError = false;
929
+ return {
930
+ attributes: snapshot,
931
+ sawErrorSpan: () => sawError,
932
+ ownsSpan: (span) => owned.has(span),
933
+ claimSpan(span) {
934
+ owned.add(span);
935
+ },
936
+ markErrorSpanSeen() {
937
+ sawError = true;
938
+ },
939
+ // A frame whose owner has been collected is dead even though nothing
940
+ // unbound it: a run that never settles must not keep handing its reverse
941
+ // reference to whatever runs next on a reused context.
942
+ isLive() {
943
+ if (dead) return false;
944
+ return ref === void 0 || ref.deref() !== void 0;
945
+ },
946
+ markDead() {
947
+ dead = true;
948
+ }
949
+ };
950
+ }
951
+ function bindSpanAttributes(attributes, owner) {
952
+ if (Object.keys(attributes).length === 0) return void 0;
953
+ const frame = makeAttributeFrame(attributes, owner);
954
+ try {
955
+ attributeStorage().push(frame);
956
+ } catch (e) {
957
+ return void 0;
958
+ }
959
+ return frame;
960
+ }
961
+ function unbindSpanAttributes(frame) {
962
+ if (!frame || !isAttributeFrame(frame)) return;
963
+ try {
964
+ attributeStorage().remove(frame);
965
+ } catch (e) {
966
+ }
967
+ }
968
+ function applySpanAttributeFrames(span, setAttribute) {
969
+ try {
970
+ for (const frame of attributeStorage().getStack()) {
971
+ if (!frame.isLive()) continue;
972
+ frame.claimSpan(span);
973
+ for (const [key, value] of Object.entries(frame.attributes)) {
974
+ setAttribute(key, value);
975
+ }
976
+ }
977
+ } catch (e) {
978
+ }
979
+ }
980
+ function claimSpanForFrames(span) {
981
+ try {
982
+ for (const frame of attributeStorage().getStack()) {
983
+ if (frame.isLive()) frame.claimSpan(span);
984
+ }
985
+ } catch (e) {
986
+ }
987
+ }
988
+ function currentSpanAttributes() {
989
+ const merged = {};
990
+ try {
991
+ for (const frame of attributeStorage().getStack()) {
992
+ if (!frame.isLive()) continue;
993
+ Object.assign(merged, frame.attributes);
994
+ }
995
+ } catch (e) {
996
+ }
997
+ return merged;
998
+ }
999
+ function markErrorSpanSeen(span) {
1000
+ try {
1001
+ for (const frame of attributeStorage().getStack()) {
1002
+ if (frame.ownsSpan(span)) frame.markErrorSpanSeen();
1003
+ }
1004
+ } catch (e) {
1005
+ }
1006
+ }
1007
+ function isAttributeFrame(value) {
1008
+ return typeof value.isLive === "function";
1009
+ }
567
1010
 
568
1011
  // ../core/dist/index.node.js
569
1012
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
@@ -597,7 +1040,7 @@ installTracingSuppressionHook();
597
1040
  // package.json
598
1041
  var package_default = {
599
1042
  name: "raindrop-ai",
600
- version: "0.2.4",
1043
+ version: "0.3.0",
601
1044
  main: "dist/index.js",
602
1045
  module: "dist/index.mjs",
603
1046
  types: "dist/index.d.ts",
@@ -717,6 +1160,7 @@ var PROJECT_ID_SPAN_ATTRIBUTE = "raindrop.project_id";
717
1160
  var AUTH_HINT_SPAN_ATTRIBUTE = "raindrop.auth_hint";
718
1161
  var EXPORT_SUCCESS = 0;
719
1162
  var EXPORT_FAILED = 1;
1163
+ var SPAN_STATUS_ERROR = 2;
720
1164
  function authHintForKey(writeKey) {
721
1165
  if (!writeKey) return void 0;
722
1166
  return createHash("sha256").update(writeKey, "utf8").digest("hex").slice(0, 8);
@@ -779,16 +1223,29 @@ function stampSpan(span, projectId, authHint) {
779
1223
  } catch (e) {
780
1224
  }
781
1225
  }
1226
+ function stampContextAttributes(span) {
1227
+ try {
1228
+ applySpanAttributeFrames(span, (key, value) => span.setAttribute(key, value));
1229
+ } catch (e) {
1230
+ }
1231
+ }
782
1232
  var RaindropContextSpanProcessor = class {
783
1233
  onStart(span) {
784
1234
  try {
1235
+ stampContextAttributes(span);
785
1236
  const bound = currentRoutingBinding();
786
1237
  if (!bound) return;
787
1238
  stampSpan(span, bound.projectId, bound.authHint);
788
1239
  } catch (e) {
789
1240
  }
790
1241
  }
791
- onEnd() {
1242
+ onEnd(span) {
1243
+ var _a;
1244
+ try {
1245
+ if (((_a = span == null ? void 0 : span.status) == null ? void 0 : _a.code) !== SPAN_STATUS_ERROR) return;
1246
+ markErrorSpanSeen(span);
1247
+ } catch (e) {
1248
+ }
792
1249
  }
793
1250
  async forceFlush() {
794
1251
  }
@@ -895,7 +1352,7 @@ function traceloopConfiguredWithoutClaim() {
895
1352
  }
896
1353
 
897
1354
  // src/tracing/tracer-core.ts
898
- import { context as context4, SpanStatusCode as SpanStatusCode4, trace as trace4 } from "@opentelemetry/api";
1355
+ import { context as context5, SpanStatusCode as SpanStatusCode5, trace as trace5 } from "@opentelemetry/api";
899
1356
  import { OTLPTraceExporter as OTLPHttpTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
900
1357
  import { AnthropicInstrumentation } from "@traceloop/instrumentation-anthropic";
901
1358
  import { BedrockInstrumentation } from "@traceloop/instrumentation-bedrock";
@@ -1005,12 +1462,12 @@ async function postJson2(url, body, headers, opts) {
1005
1462
  }
1006
1463
 
1007
1464
  // src/tracing/direct/ids.ts
1008
- function getCrypto() {
1465
+ function getCrypto2() {
1009
1466
  const c = globalThis.crypto;
1010
1467
  return c;
1011
1468
  }
1012
1469
  function randomBytes2(length) {
1013
- const cryptoObj = getCrypto();
1470
+ const cryptoObj = getCrypto2();
1014
1471
  const out = new Uint8Array(length);
1015
1472
  if (cryptoObj && typeof cryptoObj.getRandomValues === "function") {
1016
1473
  cryptoObj.getRandomValues(out);
@@ -1048,7 +1505,7 @@ function base64Encode2(bytes) {
1048
1505
  }
1049
1506
  return out;
1050
1507
  }
1051
- function base64ToHex(base64) {
1508
+ function base64ToHex2(base64) {
1052
1509
  const maybeBuffer = globalThis.Buffer;
1053
1510
  if (maybeBuffer) {
1054
1511
  return maybeBuffer.from(base64, "base64").toString("hex");
@@ -1215,9 +1672,18 @@ var DirectTraceShipper = class {
1215
1672
  attrString2(`traceloop.association.properties.${key}`, serializeAssociationValue(value))
1216
1673
  );
1217
1674
  }
1675
+ const spanIdentity = { spanIdB64 };
1676
+ claimSpanForFrames(spanIdentity);
1677
+ for (const [key, value] of Object.entries({
1678
+ ...currentSpanAttributes(),
1679
+ ...params.attributes
1680
+ })) {
1681
+ attributes.push(attrString2(key, value));
1682
+ }
1218
1683
  let status;
1219
1684
  if (params.error) {
1220
1685
  status = { code: 2, message: params.error };
1686
+ markErrorSpanSeen(spanIdentity);
1221
1687
  } else {
1222
1688
  status = {
1223
1689
  code: 1
@@ -1367,23 +1833,311 @@ var DirectTraceShipper = class {
1367
1833
 
1368
1834
  // src/tracing/liveInteraction.ts
1369
1835
  import {
1370
- context as context2,
1836
+ context as context3,
1371
1837
  isValidSpanId as isValidSpanId2,
1372
- SpanStatusCode as SpanStatusCode2,
1838
+ isValidTraceId as isValidTraceId2,
1839
+ SpanStatusCode as SpanStatusCode3,
1373
1840
  TraceFlags,
1374
- trace as trace2
1841
+ trace as trace3
1375
1842
  } from "@opentelemetry/api";
1376
1843
  import * as traceloop from "@traceloop/node-server-sdk";
1377
- function getPropertiesFromContext(context5) {
1844
+
1845
+ // src/tracing/subagents.ts
1846
+ import { SpanStatusCode as SpanStatusCode2, context as context2, trace as trace2 } from "@opentelemetry/api";
1847
+ var DEFAULT_SUBAGENT_NAME = "subagent";
1848
+ var DISPATCH_ACCEPTED = "accepted";
1849
+ function reasonText(reason) {
1850
+ var _a;
1851
+ if (reason instanceof Error) return `${reason.name}: ${reason.message}`;
1852
+ if (typeof reason === "string") return reason;
1853
+ if (reason === void 0 || reason === null) return "";
1854
+ try {
1855
+ return (_a = JSON.stringify(reason)) != null ? _a : String(reason);
1856
+ } catch (e) {
1857
+ return String(reason);
1858
+ }
1859
+ }
1860
+ function stampCurrentSpan(attributes) {
1861
+ try {
1862
+ const span = trace2.getSpan(context2.active());
1863
+ if (!(span == null ? void 0 : span.isRecording())) return;
1864
+ for (const [key, value] of Object.entries(attributes)) {
1865
+ span.setAttribute(key, value);
1866
+ }
1867
+ } catch (e) {
1868
+ }
1869
+ }
1870
+ function stampOwnSpan(attributes, frame) {
1871
+ if (!frame) return;
1872
+ try {
1873
+ const span = trace2.getSpan(context2.active());
1874
+ if (!(span == null ? void 0 : span.isRecording()) || !frame.ownsSpan(span)) return;
1875
+ for (const [key, value] of Object.entries(attributes)) {
1876
+ span.setAttribute(key, value);
1877
+ }
1878
+ } catch (e) {
1879
+ }
1880
+ }
1881
+ function failOwnSpan(reason, frame) {
1882
+ try {
1883
+ const span = trace2.getSpan(context2.active());
1884
+ if (!(span == null ? void 0 : span.isRecording())) return false;
1885
+ if (!(frame == null ? void 0 : frame.ownsSpan(span))) return false;
1886
+ const message = reasonText(reason);
1887
+ span.setStatus({ code: SpanStatusCode2.ERROR, message });
1888
+ span.recordException(reason instanceof Error ? reason : new Error(message));
1889
+ return true;
1890
+ } catch (e) {
1891
+ return false;
1892
+ }
1893
+ }
1894
+ function allowToolEvents(interaction) {
1895
+ const attributes = withMetadataPrefix(toolEventsAllowMetadata());
1896
+ interaction.bindToolEventsOptIn(attributes);
1897
+ stampCurrentSpan(attributes);
1898
+ }
1899
+ function recordSubagentDispatch(interaction, options) {
1900
+ var _a;
1901
+ const childEventId = options.childEventId || randomUUID();
1902
+ const name = options.name || DEFAULT_SUBAGENT_NAME;
1903
+ const parentEventId = (_a = interaction.getEventId()) != null ? _a : "";
1904
+ let traceIdHex;
1905
+ let dispatchSpanId;
1906
+ try {
1907
+ allowToolEvents(interaction);
1908
+ const recorded = interaction.recordDispatchSpan({
1909
+ name: options.toolName || DEFAULT_DISPATCH_SPAN_NAME,
1910
+ input: options.input,
1911
+ // A launch's "result" is the job handle; the answer belongs to the
1912
+ // child's event and never appears here.
1913
+ output: { jobId: childEventId, status: DISPATCH_ACCEPTED },
1914
+ properties: options.properties,
1915
+ attributes: detachedDispatchMetadata({ childEventId, name })
1916
+ });
1917
+ traceIdHex = recorded.traceIdHex;
1918
+ dispatchSpanId = recorded.spanIdHex;
1919
+ if (!dispatchSpanId) {
1920
+ console.warn(
1921
+ `[raindrop] subagent(${name}): no dispatch span was recorded, so event ${parentEventId} will not show the launch and NO carrier is returned \u2014 there is no span for the child to reference. Pass childEventId (${childEventId}) to the worker yourself if it must still report under that id. Tracing must be enabled for dispatches to be recorded.`
1922
+ );
1923
+ }
1924
+ } catch (error) {
1925
+ console.warn(
1926
+ `[raindrop] subagent(${name}) failed to record the dispatch; returning a carrier so the child still links back.`,
1927
+ error instanceof Error ? error.message : error
1928
+ );
1929
+ }
1930
+ if (!traceIdHex || !dispatchSpanId) {
1931
+ return {
1932
+ childEventId,
1933
+ name,
1934
+ parentEventId,
1935
+ dispatchSpanId: void 0,
1936
+ carrier: null,
1937
+ headers: {},
1938
+ langsmithHeaders: {}
1939
+ };
1940
+ }
1941
+ const carrier = {
1942
+ traceId: traceIdHex,
1943
+ spanId: dispatchSpanId,
1944
+ eventId: parentEventId,
1945
+ childEventId,
1946
+ name,
1947
+ ...interaction.getConvoId() ? { convoId: interaction.getConvoId() } : {},
1948
+ ...interaction.getUserId() ? { userId: interaction.getUserId() } : {}
1949
+ };
1950
+ return {
1951
+ childEventId,
1952
+ name,
1953
+ parentEventId,
1954
+ dispatchSpanId,
1955
+ carrier,
1956
+ headers: toHeaders(carrier),
1957
+ langsmithHeaders: toLangSmithHeaders(carrier)
1958
+ };
1959
+ }
1960
+ function carrierFirst(local, carried, field) {
1961
+ if (carried && local && local !== carried) {
1962
+ console.warn(
1963
+ `[raindrop] resumeSubagent: ignoring ${field}="${local}" in favor of the launcher's "${carried}" from the carrier; the launcher allocated it, and a child that substitutes its own breaks the link back to it.`
1964
+ );
1965
+ }
1966
+ return carried || local;
1967
+ }
1968
+ function isTraceCarrier(value) {
1969
+ const candidate = value;
1970
+ return typeof candidate.traceId === "string" && typeof candidate.spanId === "string" && typeof candidate.eventId === "string";
1971
+ }
1972
+ function readCarrier(carrierOrHeaders) {
1973
+ if (!carrierOrHeaders) return null;
1974
+ try {
1975
+ if (isTraceCarrier(carrierOrHeaders)) return carrierOrHeaders;
1976
+ return fromHeaders(carrierOrHeaders);
1977
+ } catch (error) {
1978
+ console.warn(
1979
+ `[raindrop] resumeSubagent: ignoring an unreadable hand-off carrier; the run reports as an unlinked event.`,
1980
+ error instanceof Error ? error.message : error
1981
+ );
1982
+ return null;
1983
+ }
1984
+ }
1985
+ function resolveSubagentResume(carrierOrHeaders, options = {}) {
1986
+ const parentStated = options.parent !== void 0;
1987
+ const parent = parentStated ? options.parent : readCarrier(carrierOrHeaders);
1988
+ if (!parentStated) warnUnresolvedCarrier(carrierOrHeaders, parent);
1989
+ const name = carrierFirst(options.name, parent == null ? void 0 : parent.name, "name") || DEFAULT_SUBAGENT_NAME;
1990
+ const eventId = carrierFirst(options.eventId, parent == null ? void 0 : parent.childEventId, "eventId") || randomUUID();
1991
+ const userId = carrierFirst(options.userId, parent == null ? void 0 : parent.userId, "userId") || "";
1992
+ const convoId = carrierFirst(options.convoId, parent == null ? void 0 : parent.convoId, "convoId");
1993
+ const link = parent ? {
1994
+ parentEventId: parent.eventId,
1995
+ ...parent.spanId ? { parentSpanId: parent.spanId } : {},
1996
+ name
1997
+ } : null;
1998
+ return {
1999
+ parent,
2000
+ link,
2001
+ name,
2002
+ eventId,
2003
+ event: options.event || `subagent.${name}`,
2004
+ userId,
2005
+ ...convoId ? { convoId } : {}
2006
+ };
2007
+ }
2008
+ function bindSubagentLink(link, owner) {
2009
+ if (!link) return void 0;
2010
+ return bindSpanAttributes(detachedChildMetadata(link), owner);
2011
+ }
2012
+ var LiveSubagentRun = class {
2013
+ constructor(params) {
2014
+ this.settled = false;
2015
+ this.interaction = params.interaction;
2016
+ this.eventId = params.eventId;
2017
+ this.name = params.name;
2018
+ this.convoId = params.convoId;
2019
+ this.parent = params.parent;
2020
+ this.frame = params.frame;
2021
+ this.frameOwner = params.frameOwner;
2022
+ }
2023
+ get linked() {
2024
+ return this.parent !== null;
2025
+ }
2026
+ async finish(result) {
2027
+ await this.close(result);
2028
+ }
2029
+ async fail(reason) {
2030
+ await this.abort(reason);
2031
+ }
2032
+ async cancel(reason) {
2033
+ const text = reason === void 0 ? "cancelled by caller" : reasonText(reason);
2034
+ const cancelled = cancelledTerminalAttributes();
2035
+ stampOwnSpan(cancelled, this.frame);
2036
+ await this.close({
2037
+ output: `Cancelled: ${text}`,
2038
+ // The caller's view of the child is its EVENT, not its spans, so the
2039
+ // marker has to be a property of the event as well.
2040
+ properties: { [HANDOFF_TERMINAL_PROPERTY_KEY]: HANDOFF_TERMINAL_CANCELLED }
2041
+ });
2042
+ }
2043
+ /**
2044
+ * Close the run with an abort reason, having first made sure the failure is
2045
+ * visible on a span.
2046
+ *
2047
+ * A failure has to leave an errored span behind or it is derived as finished.
2048
+ * Mark whatever is open; if nothing is and the child has not errored a span
2049
+ * already, record one — the killed-before-it-reached-the-model case, which is
2050
+ * much of what aborting is.
2051
+ */
2052
+ async abort(reason) {
2053
+ var _a, _b, _c;
2054
+ const text = reasonText(reason);
2055
+ if (!this.settled && !failOwnSpan(reason, this.frame)) {
2056
+ if (!((_a = this.frame) == null ? void 0 : _a.sawErrorSpan())) {
2057
+ this.interaction.recordSubagentAbortSpan({
2058
+ reason,
2059
+ message: text,
2060
+ attributes: {
2061
+ [HANDOFF_ATTRIBUTE_SUFFIXES.name]: this.name,
2062
+ // The name explicitly, not only via the frame: an UNLINKED run has
2063
+ // no frame, and since the span is named for the operation
2064
+ // (`ai.subagent.failed`) rather than the sub-agent, this attribute
2065
+ // is the only thing left that says which sub-agent failed.
2066
+ ...(_c = (_b = this.frame) == null ? void 0 : _b.attributes) != null ? _c : {}
2067
+ }
2068
+ });
2069
+ }
2070
+ }
2071
+ await this.close({ output: text ? `Aborted: ${text}` : "Aborted before producing a result." });
2072
+ }
2073
+ async close(result) {
2074
+ if (this.settled) return;
2075
+ this.settled = true;
2076
+ try {
2077
+ await this.interaction.finish({ ...result, eventId: this.eventId });
2078
+ } finally {
2079
+ unbindSpanAttributes(this.frame);
2080
+ this.frame = void 0;
2081
+ this.frameOwner = void 0;
2082
+ }
2083
+ }
2084
+ /**
2085
+ * Run `fn` and guarantee the run reports an outcome, mirroring the Python
2086
+ * SDK's `with rd.resume_subagent(...) as run:`.
2087
+ *
2088
+ * A run that leaves without reporting output is closed with its abort reason.
2089
+ * That is not cosmetic: an event is created only from a run with non-empty
2090
+ * output, so a child that dies silently produces no event at all and the
2091
+ * caller's launch stays `queued` forever — indistinguishable from a job that
2092
+ * never started.
2093
+ */
2094
+ async use(fn) {
2095
+ try {
2096
+ const result = await fn(this);
2097
+ if (!this.settled) {
2098
+ console.warn(
2099
+ `[raindrop] sub-agent run ${this.eventId} (${this.name}) ended without reporting output; closing it with an abort reason. An event needs non-empty output to exist, so a silent child leaves its launcher showing "queued" forever \u2014 call finish({ output }), fail(reason) or cancel() before returning.`
2100
+ );
2101
+ await this.abortQuietly("sub-agent ended without reporting output");
2102
+ }
2103
+ return result;
2104
+ } catch (error) {
2105
+ await this.abortQuietly(error);
2106
+ throw error;
2107
+ }
2108
+ }
2109
+ /**
2110
+ * {@link abort} for the outcomes this class reports on the caller's behalf.
2111
+ *
2112
+ * `abort()` ends in `interaction.finish()`, which awaits the terminal event
2113
+ * POST and rejects when it fails. An explicit `fail()` may surface that — the
2114
+ * caller awaited it — but an implicit one must not: rejecting on the error
2115
+ * path replaces the caller's exception with a telemetry one, and rejecting on
2116
+ * the success path loses a result `fn` already produced.
2117
+ */
2118
+ async abortQuietly(reason) {
2119
+ try {
2120
+ await this.abort(reason);
2121
+ } catch (error) {
2122
+ console.warn(
2123
+ `[raindrop] sub-agent run ${this.eventId} (${this.name}) could not report its outcome; the run itself is unaffected.`,
2124
+ error instanceof Error ? error.message : error
2125
+ );
2126
+ }
2127
+ }
2128
+ };
2129
+
2130
+ // src/tracing/liveInteraction.ts
2131
+ function getPropertiesFromContext(context6) {
1378
2132
  const properties = {
1379
- ...context5.userId && { user_id: context5.userId },
1380
- ...context5.convoId && { convo_id: context5.convoId },
1381
- ...context5.eventId && { event_id: context5.eventId },
1382
- ...context5.event && { event_name: context5.event },
1383
- ...context5.properties || {}
2133
+ ...context6.userId && { user_id: context6.userId },
2134
+ ...context6.convoId && { convo_id: context6.convoId },
2135
+ ...context6.eventId && { event_id: context6.eventId },
2136
+ ...context6.event && { event_name: context6.event },
2137
+ ...context6.properties || {}
1384
2138
  };
1385
- if (context5.attachments && context5.attachments.length > 0) {
1386
- properties.attachments = stringifyBounded(context5.attachments);
2139
+ if (context6.attachments && context6.attachments.length > 0) {
2140
+ properties.attachments = stringifyBounded(context6.attachments);
1387
2141
  }
1388
2142
  return properties;
1389
2143
  }
@@ -1403,12 +2157,12 @@ function serializeAssociationProperties(properties) {
1403
2157
  function serializeSpanValue(value) {
1404
2158
  return stringifyBounded(value);
1405
2159
  }
1406
- function getLocalDebuggerMetadata(context5) {
2160
+ function getLocalDebuggerMetadata(context6) {
1407
2161
  return {
1408
- ...context5.eventId ? { eventId: context5.eventId } : {},
1409
- ...context5.event ? { eventName: context5.event } : {},
1410
- ...context5.userId ? { userId: context5.userId } : {},
1411
- ...context5.convoId ? { convoId: context5.convoId } : {}
2162
+ ...context6.eventId ? { eventId: context6.eventId } : {},
2163
+ ...context6.event ? { eventName: context6.event } : {},
2164
+ ...context6.userId ? { userId: context6.userId } : {},
2165
+ ...context6.convoId ? { convoId: context6.convoId } : {}
1412
2166
  };
1413
2167
  }
1414
2168
  var LiveToolSpan = class {
@@ -1432,13 +2186,13 @@ var LiveToolSpan = class {
1432
2186
  setError(error) {
1433
2187
  this.hasError = true;
1434
2188
  this.errorMessage = error instanceof Error ? error.message : String(error);
1435
- this.span.setStatus({ code: SpanStatusCode2.ERROR, message: this.errorMessage });
2189
+ this.span.setStatus({ code: SpanStatusCode3.ERROR, message: this.errorMessage });
1436
2190
  this.span.recordException(error instanceof Error ? error : new Error(this.errorMessage));
1437
2191
  }
1438
2192
  end() {
1439
2193
  var _a;
1440
2194
  if (!this.hasError) {
1441
- this.span.setStatus({ code: SpanStatusCode2.OK });
2195
+ this.span.setStatus({ code: SpanStatusCode3.OK });
1442
2196
  }
1443
2197
  this.span.end();
1444
2198
  (_a = this.emitLiveEvent) == null ? void 0 : _a.call(this, {
@@ -1509,10 +2263,10 @@ var DirectToolSpan = class {
1509
2263
  }
1510
2264
  };
1511
2265
  var LiveInteraction = class {
1512
- constructor(context5, traceId, analytics, directShipper, routing) {
1513
- this.context = context5;
2266
+ constructor(context6, traceId, analytics, directShipper, routing) {
2267
+ this.context = context6;
1514
2268
  this.analytics = analytics;
1515
- this.tracer = trace2.getTracer("traceloop.tracer");
2269
+ this.tracer = trace3.getTracer("traceloop.tracer");
1516
2270
  this.traceId = traceId;
1517
2271
  this.directShipper = directShipper;
1518
2272
  this.routing = routing;
@@ -1547,7 +2301,7 @@ var LiveInteraction = class {
1547
2301
  this.traceId = explicitTraceId;
1548
2302
  return explicitTraceId;
1549
2303
  }
1550
- const activeSpan = trace2.getSpan(context2.active());
2304
+ const activeSpan = trace3.getSpan(context3.active());
1551
2305
  const activeTraceId = activeSpan == null ? void 0 : activeSpan.spanContext().traceId;
1552
2306
  if (activeTraceId) {
1553
2307
  this.traceId = activeTraceId;
@@ -1590,17 +2344,17 @@ var LiveInteraction = class {
1590
2344
  const spanIdB64 = base64Encode2(randomBytes2(8));
1591
2345
  const startTimeMs = Date.now();
1592
2346
  const spanContext = {
1593
- traceId: base64ToHex(traceIdB64),
1594
- spanId: base64ToHex(spanIdB64),
2347
+ traceId: base64ToHex2(traceIdB64),
2348
+ spanId: base64ToHex2(spanIdB64),
1595
2349
  traceFlags: TraceFlags.SAMPLED
1596
2350
  };
1597
- const wrappedContext = trace2.setSpan(
1598
- context2.active(),
1599
- trace2.wrapSpanContext(spanContext)
2351
+ const wrappedContext = trace3.setSpan(
2352
+ context3.active(),
2353
+ trace3.wrapSpanContext(spanContext)
1600
2354
  );
1601
2355
  const invoke = () => this.routing ? withRoutingContext(this.routing, () => fn.apply(thisArg, args)) : fn.apply(thisArg, args);
1602
2356
  try {
1603
- const result = await context2.with(wrappedContext, invoke);
2357
+ const result = await context3.with(wrappedContext, invoke);
1604
2358
  const endTimeMs = Date.now();
1605
2359
  this.directShipper.sendSpan({
1606
2360
  name: taskName,
@@ -1635,7 +2389,7 @@ var LiveInteraction = class {
1635
2389
  }
1636
2390
  }
1637
2391
  const wrappedFn = (...fnArgs) => {
1638
- const activeSpan = trace2.getSpan(context2.active());
2392
+ const activeSpan = trace3.getSpan(context3.active());
1639
2393
  const activeTraceId = activeSpan == null ? void 0 : activeSpan.spanContext().traceId;
1640
2394
  if (activeTraceId) {
1641
2395
  this.traceId = activeTraceId;
@@ -1685,19 +2439,19 @@ var LiveInteraction = class {
1685
2439
  metadata: params.inputParameters ? { args: params.inputParameters } : void 0
1686
2440
  });
1687
2441
  const spanContext = {
1688
- traceId: base64ToHex(traceIdB64),
1689
- spanId: base64ToHex(spanIdB64),
2442
+ traceId: base64ToHex2(traceIdB64),
2443
+ spanId: base64ToHex2(spanIdB64),
1690
2444
  traceFlags: TraceFlags.SAMPLED
1691
2445
  };
1692
- const wrappedContext = trace2.setSpan(
1693
- context2.active(),
1694
- trace2.wrapSpanContext(spanContext)
2446
+ const wrappedContext = trace3.setSpan(
2447
+ context3.active(),
2448
+ trace3.wrapSpanContext(spanContext)
1695
2449
  );
1696
2450
  const invoke = () => this.routing ? withRoutingContext(this.routing, () => fn.apply(thisArg, args)) : fn.apply(thisArg, args);
1697
2451
  let result;
1698
2452
  let error;
1699
2453
  try {
1700
- result = await context2.with(wrappedContext, invoke);
2454
+ result = await context3.with(wrappedContext, invoke);
1701
2455
  } catch (err) {
1702
2456
  error = err instanceof Error ? err.message : String(err);
1703
2457
  const endTimeMs2 = Date.now();
@@ -1762,7 +2516,7 @@ var LiveInteraction = class {
1762
2516
  const inputParams = params.inputParameters ? [params.inputParameters] : void 0;
1763
2517
  const emitLive = (event) => this.emitLiveEvent(event);
1764
2518
  const wrapped = async (...wrappedArgs) => {
1765
- const activeSpan = trace2.getSpan(context2.active());
2519
+ const activeSpan = trace3.getSpan(context3.active());
1766
2520
  const ctx = activeSpan == null ? void 0 : activeSpan.spanContext();
1767
2521
  const liveSpanId = ctx && isValidSpanId2(ctx.spanId) ? ctx.spanId : void 0;
1768
2522
  if (activeSpan) {
@@ -1965,10 +2719,151 @@ var LiveInteraction = class {
1965
2719
  getEventId() {
1966
2720
  return this.context.eventId;
1967
2721
  }
2722
+ getUserId() {
2723
+ return this.context.userId;
2724
+ }
2725
+ getConvoId() {
2726
+ return this.context.convoId;
2727
+ }
2728
+ /**
2729
+ * Bind this turn's tool-call event opt-in, once.
2730
+ *
2731
+ * Owned by the interaction rather than by the execution context: the frame
2732
+ * lives exactly as long as the turn does (removed by `finish()`, and dead once
2733
+ * this interaction is collected), instead of outliving it on a context the
2734
+ * host may reuse for unrelated work.
2735
+ */
2736
+ bindToolEventsOptIn(attributes) {
2737
+ if (this.toolEventsFrame) return;
2738
+ this.toolEventsFrame = bindSpanAttributes(attributes, this);
2739
+ }
2740
+ subagent(options) {
2741
+ return recordSubagentDispatch(this, options);
2742
+ }
2743
+ /**
2744
+ * Record the tool span a detached dispatch is written on.
2745
+ *
2746
+ * Distinct from {@link startToolSpan} in the two ways the hand-off needs:
2747
+ * the contract's keys are set as RAW span attributes (under the
2748
+ * `traceloop.association.properties.` prefix the backend's reader cannot see
2749
+ * them), and the caller needs the span's ids to build the carrier.
2750
+ *
2751
+ * @internal
2752
+ */
2753
+ recordDispatchSpan(params) {
2754
+ var _a;
2755
+ const properties = {
2756
+ ...getPropertiesFromContext(this.context),
2757
+ ...(_a = params.properties) != null ? _a : {}
2758
+ };
2759
+ const input = params.input !== void 0 ? serializeSpanValue(params.input) : void 0;
2760
+ const output = serializeSpanValue(params.output);
2761
+ if (this.directShipper) {
2762
+ const activeContext = getActiveTraceContext();
2763
+ const traceIdB64 = this.ensureTraceId(activeContext.traceIdB64);
2764
+ const spanIdB64 = base64Encode2(randomBytes2(8));
2765
+ const timeMs = Date.now();
2766
+ this.directShipper.sendToolSpan({
2767
+ name: params.name,
2768
+ spanId: spanIdB64,
2769
+ input,
2770
+ output,
2771
+ durationMs: 0,
2772
+ startTimeMs: timeMs,
2773
+ endTimeMs: timeMs,
2774
+ properties,
2775
+ traceId: traceIdB64,
2776
+ parentSpanId: activeContext.parentSpanIdB64,
2777
+ attributes: params.attributes
2778
+ });
2779
+ this.emitLiveEvent({
2780
+ traceId: traceIdB64,
2781
+ spanId: spanIdB64,
2782
+ type: "tool_result",
2783
+ content: params.name,
2784
+ timestamp: timeMs,
2785
+ metadata: { result: params.output }
2786
+ });
2787
+ return { traceIdHex: base64ToHex2(traceIdB64), spanIdHex: base64ToHex2(spanIdB64) };
2788
+ }
2789
+ const span = this.tracer.startSpan(params.name);
2790
+ setAssociationProperties(span, properties);
2791
+ span.setAttribute("traceloop.span.kind", "tool");
2792
+ this.stampRouting(span);
2793
+ for (const [key, value] of Object.entries(params.attributes)) {
2794
+ span.setAttribute(key, value);
2795
+ }
2796
+ if (input !== void 0) {
2797
+ span.setAttribute("traceloop.entity.input", input);
2798
+ }
2799
+ span.setAttribute("traceloop.entity.output", output);
2800
+ span.setStatus({ code: SpanStatusCode3.OK });
2801
+ const spanContext = span.spanContext();
2802
+ span.end();
2803
+ this.emitLiveEvent({
2804
+ spanId: isValidSpanId2(spanContext.spanId) ? spanContext.spanId : void 0,
2805
+ type: "tool_result",
2806
+ content: params.name,
2807
+ timestamp: Date.now(),
2808
+ metadata: { result: params.output }
2809
+ });
2810
+ return {
2811
+ traceIdHex: isValidTraceId2(spanContext.traceId) ? spanContext.traceId : void 0,
2812
+ spanIdHex: isValidSpanId2(spanContext.spanId) ? spanContext.spanId : void 0
2813
+ };
2814
+ }
2815
+ /**
2816
+ * Record the errored span a detached sub-agent's failure needs when nothing
2817
+ * else is open.
2818
+ *
2819
+ * Frequently the child's ONLY span, so it carries the full reverse reference:
2820
+ * anything reading it alone still knows which launch it belongs to. Those
2821
+ * attributes come from the run's own frame rather than the ambient stack,
2822
+ * because `fail()` is often called from a different task than the one that
2823
+ * opened the run and this is the span that can least afford to lose the link.
2824
+ *
2825
+ * @internal
2826
+ */
2827
+ recordSubagentAbortSpan(params) {
2828
+ const { reason, message, attributes } = params;
2829
+ const properties = getPropertiesFromContext(this.context);
2830
+ if (this.directShipper) {
2831
+ const activeContext = getActiveTraceContext();
2832
+ const timeMs = Date.now();
2833
+ this.directShipper.sendSpan({
2834
+ name: SUBAGENT_ABORT_SPAN_NAME,
2835
+ // Not "tool": the run died, it did not call anything. `ai.operationId`
2836
+ // below is what keeps the span (see the attribute's docs in core).
2837
+ spanKind: "task",
2838
+ spanId: base64Encode2(randomBytes2(8)),
2839
+ durationMs: 0,
2840
+ startTimeMs: timeMs,
2841
+ endTimeMs: timeMs,
2842
+ error: message,
2843
+ properties,
2844
+ traceId: this.ensureTraceId(activeContext.traceIdB64),
2845
+ parentSpanId: activeContext.parentSpanIdB64,
2846
+ attributes: { ...attributes, [OPERATION_ID_ATTRIBUTE]: SUBAGENT_ABORT_OPERATION_ID }
2847
+ });
2848
+ return;
2849
+ }
2850
+ const span = this.tracer.startSpan(SUBAGENT_ABORT_SPAN_NAME);
2851
+ span.setAttribute(OPERATION_ID_ATTRIBUTE, SUBAGENT_ABORT_OPERATION_ID);
2852
+ setAssociationProperties(span, properties);
2853
+ this.stampRouting(span);
2854
+ for (const [key, value] of Object.entries(attributes)) {
2855
+ span.setAttribute(key, value);
2856
+ }
2857
+ span.setStatus({ code: SpanStatusCode3.ERROR, message });
2858
+ span.recordException(reason instanceof Error ? reason : new Error(message));
2859
+ span.end();
2860
+ }
1968
2861
  async finish(resultEvent) {
1969
2862
  var _a, _b, _c, _d;
1970
2863
  unbindRoutingContext(this.boundContext);
1971
2864
  this.boundContext = void 0;
2865
+ unbindSpanAttributes(this.toolEventsFrame);
2866
+ this.toolEventsFrame = void 0;
1972
2867
  if (!this.traceId) {
1973
2868
  this.resolveLiveTraceId();
1974
2869
  }
@@ -2078,10 +2973,10 @@ var LiveInteraction = class {
2078
2973
  }
2079
2974
  if (error) {
2080
2975
  const errorMessage = error instanceof Error ? error.message : String(error);
2081
- span.setStatus({ code: SpanStatusCode2.ERROR, message: errorMessage });
2976
+ span.setStatus({ code: SpanStatusCode3.ERROR, message: errorMessage });
2082
2977
  span.recordException(error instanceof Error ? error : new Error(errorMessage));
2083
2978
  } else {
2084
- span.setStatus({ code: SpanStatusCode2.OK });
2979
+ span.setStatus({ code: SpanStatusCode3.OK });
2085
2980
  }
2086
2981
  span.end(endTimeMs);
2087
2982
  if ((_c = this.analytics) == null ? void 0 : _c.debugLogs) {
@@ -2109,11 +3004,11 @@ var LiveInteraction = class {
2109
3004
  };
2110
3005
 
2111
3006
  // src/tracing/liveTracer.ts
2112
- import { context as context3, isValidSpanId as isValidSpanId3, SpanStatusCode as SpanStatusCode3, trace as trace3 } from "@opentelemetry/api";
3007
+ import { context as context4, isValidSpanId as isValidSpanId3, SpanStatusCode as SpanStatusCode4, trace as trace4 } from "@opentelemetry/api";
2113
3008
  import * as traceloop2 from "@traceloop/node-server-sdk";
2114
3009
  var LiveTracer = class {
2115
3010
  constructor(globalProperties, directShipper, routing) {
2116
- this.tracer = trace3.getTracer("traceloop.tracer");
3011
+ this.tracer = trace4.getTracer("traceloop.tracer");
2117
3012
  this.globalProperties = globalProperties || {};
2118
3013
  this.directShipper = directShipper;
2119
3014
  this.routing = routing;
@@ -2125,7 +3020,7 @@ var LiveTracer = class {
2125
3020
  }
2126
3021
  emitLiveEvent(event) {
2127
3022
  var _a, _b;
2128
- const activeSpan = trace3.getSpan(context3.active());
3023
+ const activeSpan = trace4.getSpan(context4.active());
2129
3024
  const activeTraceId = activeSpan == null ? void 0 : activeSpan.spanContext().traceId;
2130
3025
  const active = getActiveTraceContext();
2131
3026
  const traceId = (_b = (_a = event.traceId) != null ? _a : activeTraceId) != null ? _b : active.traceIdB64;
@@ -2145,7 +3040,7 @@ var LiveTracer = class {
2145
3040
  };
2146
3041
  const inputParameters = typeof params === "string" ? void 0 : params.inputParameters;
2147
3042
  const wrappedFn = (...fnArgs) => {
2148
- const activeSpan = trace3.getSpan(context3.active());
3043
+ const activeSpan = trace4.getSpan(context4.active());
2149
3044
  if (activeSpan) {
2150
3045
  this.stampRouting(activeSpan);
2151
3046
  }
@@ -2240,14 +3135,14 @@ var LiveTracer = class {
2240
3135
  }
2241
3136
  if (error) {
2242
3137
  const errorMessage = error instanceof Error ? error.message : String(error);
2243
- span.setStatus({ code: SpanStatusCode3.ERROR, message: errorMessage });
3138
+ span.setStatus({ code: SpanStatusCode4.ERROR, message: errorMessage });
2244
3139
  span.recordException(error instanceof Error ? error : new Error(errorMessage));
2245
3140
  } else {
2246
- span.setStatus({ code: SpanStatusCode3.OK });
3141
+ span.setStatus({ code: SpanStatusCode4.OK });
2247
3142
  }
2248
3143
  span.end(endTimeMs);
2249
3144
  const ctx = span.spanContext();
2250
- const traceId = ctx.traceId || ((_b = trace3.getSpan(context3.active())) == null ? void 0 : _b.spanContext().traceId);
3145
+ const traceId = ctx.traceId || ((_b = trace4.getSpan(context4.active())) == null ? void 0 : _b.spanContext().traceId);
2251
3146
  const spanIdHex = isValidSpanId3(ctx.spanId) ? ctx.spanId : void 0;
2252
3147
  if (traceId) {
2253
3148
  this.emitLiveEvent({
@@ -2540,7 +3435,7 @@ try {
2540
3435
  e instanceof Error ? e.message : e
2541
3436
  );
2542
3437
  span.setStatus({
2543
- code: SpanStatusCode4.ERROR,
3438
+ code: SpanStatusCode5.ERROR,
2544
3439
  message: e instanceof Error ? e.message : String(e)
2545
3440
  });
2546
3441
  span.end();
@@ -2572,7 +3467,7 @@ try {
2572
3467
  );
2573
3468
  if (!spanEnded) {
2574
3469
  span.setStatus({
2575
- code: SpanStatusCode4.ERROR,
3470
+ code: SpanStatusCode5.ERROR,
2576
3471
  message: e instanceof Error ? e.message : String(e)
2577
3472
  });
2578
3473
  span.end();
@@ -2586,7 +3481,7 @@ try {
2586
3481
  } catch (error) {
2587
3482
  if (!spanEnded) {
2588
3483
  const msg = error instanceof Error ? error.message : String(error);
2589
- span.setStatus({ code: SpanStatusCode4.ERROR, message: msg });
3484
+ span.setStatus({ code: SpanStatusCode5.ERROR, message: msg });
2590
3485
  span.recordException(error);
2591
3486
  span.end();
2592
3487
  spanEnded = true;
@@ -2609,7 +3504,7 @@ try {
2609
3504
  return result;
2610
3505
  }).catch((error) => {
2611
3506
  span.setStatus({
2612
- code: SpanStatusCode4.ERROR,
3507
+ code: SpanStatusCode5.ERROR,
2613
3508
  message: error.message
2614
3509
  });
2615
3510
  span.recordException(error);
@@ -2672,7 +3567,7 @@ function patchBedrockStreamingBug(inst) {
2672
3567
  } catch (error) {
2673
3568
  if (!spanEnded) {
2674
3569
  const msg = error instanceof Error ? error.message : String(error);
2675
- span.setStatus({ code: SpanStatusCode4.ERROR, message: msg });
3570
+ span.setStatus({ code: SpanStatusCode5.ERROR, message: msg });
2676
3571
  span.recordException(error);
2677
3572
  span.end();
2678
3573
  spanEnded = true;
@@ -2695,7 +3590,7 @@ function patchBedrockStreamingBug(inst) {
2695
3590
  return result;
2696
3591
  }).catch((error) => {
2697
3592
  span.setStatus({
2698
- code: SpanStatusCode4.ERROR,
3593
+ code: SpanStatusCode5.ERROR,
2699
3594
  message: error.message
2700
3595
  });
2701
3596
  span.recordException(error);
@@ -2777,7 +3672,7 @@ function createManualInstrumentations(instrumentModules) {
2777
3672
  }
2778
3673
  function getCurrentTraceId() {
2779
3674
  var _a;
2780
- return (_a = trace4.getSpan(context4.active())) == null ? void 0 : _a.spanContext().traceId;
3675
+ return (_a = trace5.getSpan(context5.active())) == null ? void 0 : _a.spanContext().traceId;
2781
3676
  }
2782
3677
  var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
2783
3678
  var _a;
@@ -2970,6 +3865,72 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
2970
3865
  } catch (e) {
2971
3866
  }
2972
3867
  }) : void 0;
3868
+ const beginInteraction = (traceContext) => {
3869
+ var _a2;
3870
+ if (!traceContext.eventId) {
3871
+ traceContext.eventId = crypto.randomUUID();
3872
+ }
3873
+ const traceId = getCurrentTraceId();
3874
+ const interaction = new LiveInteraction(
3875
+ traceContext,
3876
+ traceId,
3877
+ analytics,
3878
+ directShipper,
3879
+ { projectId, authHint }
3880
+ );
3881
+ const bound = bindRoutingContext({ projectId, authHint }, interaction);
3882
+ interaction.setBoundContext(bound);
3883
+ try {
3884
+ analytics._trackAiPartial({
3885
+ ...traceContext,
3886
+ properties: {
3887
+ ...(_a2 = traceContext.properties) != null ? _a2 : {},
3888
+ ...interaction.getTraceId() ? { trace_id: interaction.getTraceId() } : {}
3889
+ }
3890
+ });
3891
+ if (interaction.getTraceId()) {
3892
+ activeInteractions.set(interaction.getTraceId(), interaction);
3893
+ }
3894
+ activeInteractionsByEventId.set(traceContext.eventId, interaction);
3895
+ } catch (error) {
3896
+ unbindRoutingContext(bound);
3897
+ throw error;
3898
+ }
3899
+ return interaction;
3900
+ };
3901
+ const openSubagentRun = (carrierOrHeaders, options2) => {
3902
+ const resolved = resolveSubagentResume(carrierOrHeaders, options2);
3903
+ if (!resolved.parent && isDebug) {
3904
+ console.log(
3905
+ `[raindrop] resumeSubagent(${resolved.name}): no hand-off carrier on the request; reporting as an unlinked event ${resolved.eventId}.`
3906
+ );
3907
+ }
3908
+ const frameOwner = { subagentEventId: resolved.eventId };
3909
+ const frame = bindSubagentLink(resolved.link, frameOwner);
3910
+ let interaction;
3911
+ try {
3912
+ interaction = beginInteraction({
3913
+ eventId: resolved.eventId,
3914
+ event: resolved.event,
3915
+ userId: resolved.userId,
3916
+ ...resolved.convoId ? { convoId: resolved.convoId } : {},
3917
+ ...options2.input !== void 0 ? { input: options2.input } : {},
3918
+ ...options2.model !== void 0 ? { model: options2.model } : {},
3919
+ ...options2.properties ? { properties: options2.properties } : {}
3920
+ });
3921
+ } catch (error) {
3922
+ unbindSpanAttributes(frame);
3923
+ throw error;
3924
+ }
3925
+ return new LiveSubagentRun({
3926
+ interaction,
3927
+ eventId: resolved.eventId,
3928
+ name: resolved.name,
3929
+ ...resolved.convoId ? { convoId: resolved.convoId } : {},
3930
+ parent: resolved.link,
3931
+ ...frame ? { frame, frameOwner } : {}
3932
+ });
3933
+ };
2973
3934
  return {
2974
3935
  /**
2975
3936
  * Returns instrumentation instances for your NodeSDK.
@@ -3079,37 +4040,13 @@ var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
3079
4040
  );
3080
4041
  },
3081
4042
  begin(traceContext) {
3082
- var _a2;
3083
- if (!traceContext.eventId) {
3084
- traceContext.eventId = crypto.randomUUID();
3085
- }
3086
- const traceId = getCurrentTraceId();
3087
- const interaction = new LiveInteraction(
3088
- traceContext,
3089
- traceId,
3090
- analytics,
3091
- directShipper,
3092
- { projectId, authHint }
3093
- );
3094
- const bound = bindRoutingContext({ projectId, authHint }, interaction);
3095
- interaction.setBoundContext(bound);
3096
- try {
3097
- analytics._trackAiPartial({
3098
- ...traceContext,
3099
- properties: {
3100
- ...(_a2 = traceContext.properties) != null ? _a2 : {},
3101
- ...interaction.getTraceId() ? { trace_id: interaction.getTraceId() } : {}
3102
- }
3103
- });
3104
- if (interaction.getTraceId()) {
3105
- activeInteractions.set(interaction.getTraceId(), interaction);
3106
- }
3107
- activeInteractionsByEventId.set(traceContext.eventId, interaction);
3108
- } catch (error) {
3109
- unbindRoutingContext(bound);
3110
- throw error;
3111
- }
3112
- return interaction;
4043
+ return beginInteraction(traceContext);
4044
+ },
4045
+ resumeSubagent(carrierOrHeaders, options2 = {}) {
4046
+ return openSubagentRun(carrierOrHeaders, options2);
4047
+ },
4048
+ withSubagentRun(carrierOrHeaders, fn, options2 = {}) {
4049
+ return openSubagentRun(carrierOrHeaders, options2).use(fn);
3113
4050
  },
3114
4051
  getActiveInteraction(eventId) {
3115
4052
  const interactionByEventId = activeInteractionsByEventId.get(eventId);