@raindrop-ai/ai-sdk 0.0.17 → 0.0.18

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.
@@ -1,4 +1,4 @@
1
- // ../core/dist/chunk-FTBZHS25.js
1
+ // ../core/dist/chunk-FOHDGBT5.js
2
2
  function getCrypto() {
3
3
  const c = globalThis.crypto;
4
4
  return c;
@@ -269,6 +269,7 @@ var EventShipper = class {
269
269
  console.log(`${this.prefix} queue patch`, {
270
270
  eventId,
271
271
  userId: patch.userId,
272
+ convoId: patch.convoId,
272
273
  eventName: patch.eventName,
273
274
  hasInput: typeof patch.input === "string" && patch.input.length > 0,
274
275
  hasOutput: typeof patch.output === "string" && patch.output.length > 0,
@@ -397,16 +398,18 @@ var EventShipper = class {
397
398
  return;
398
399
  }
399
400
  const { wizardSession, ...restProperties } = (_e = accumulated.properties) != null ? _e : {};
401
+ const convoId = (_f = accumulated.convoId) != null ? _f : sticky.convoId;
402
+ const isPending = (_h = (_g = accumulated.isPending) != null ? _g : sticky.isPending) != null ? _h : true;
400
403
  const payload = {
401
404
  event_id: eventId,
402
405
  user_id: userId,
403
406
  event: eventName,
404
- timestamp: (_f = accumulated.timestamp) != null ? _f : (/* @__PURE__ */ new Date()).toISOString(),
407
+ timestamp: (_i = accumulated.timestamp) != null ? _i : (/* @__PURE__ */ new Date()).toISOString(),
405
408
  ai_data: {
406
409
  input: accumulated.input,
407
410
  output: accumulated.output,
408
411
  model: accumulated.model,
409
- convo_id: (_g = accumulated.convoId) != null ? _g : sticky.convoId
412
+ convo_id: convoId
410
413
  },
411
414
  properties: {
412
415
  ...restProperties,
@@ -414,7 +417,7 @@ var EventShipper = class {
414
417
  $context: this.context
415
418
  },
416
419
  attachments: accumulated.attachments,
417
- is_pending: ((_i = (_h = accumulated.isPending) != null ? _h : sticky.isPending) != null ? _i : true) !== false
420
+ is_pending: isPending
418
421
  };
419
422
  const url = `${this.baseUrl}events/track_partial`;
420
423
  if (this.debug) {
@@ -422,7 +425,8 @@ var EventShipper = class {
422
425
  eventId,
423
426
  eventName,
424
427
  userId,
425
- isPending: payload.is_pending,
428
+ convoId,
429
+ isPending,
426
430
  inputPreview: typeof accumulated.input === "string" ? accumulated.input.slice(0, 120) : void 0,
427
431
  outputPreview: typeof accumulated.output === "string" ? accumulated.output.slice(0, 120) : void 0,
428
432
  attachments: (_k = (_j = accumulated.attachments) == null ? void 0 : _j.length) != null ? _k : 0,
@@ -454,7 +458,6 @@ var EventShipper = class {
454
458
  } finally {
455
459
  this.inFlight.delete(p);
456
460
  }
457
- const isPending = payload.is_pending;
458
461
  if (!isPending) {
459
462
  this.sticky.delete(eventId);
460
463
  }
@@ -782,7 +785,7 @@ async function* asyncGeneratorWithCurrent(span, gen) {
782
785
  // package.json
783
786
  var package_default = {
784
787
  name: "@raindrop-ai/ai-sdk",
785
- version: "0.0.17"};
788
+ version: "0.0.18"};
786
789
 
787
790
  // src/internal/version.ts
788
791
  var libraryName = package_default.name;
@@ -1168,6 +1171,145 @@ function extractResponseMessages(result) {
1168
1171
  }
1169
1172
  return [];
1170
1173
  }
1174
+ function buildToolCallMatchKey(info) {
1175
+ if (typeof info.toolCallId === "string" && info.toolCallId.length > 0) {
1176
+ return `id:${info.toolCallId}`;
1177
+ }
1178
+ if (typeof info.toolName === "string" && info.toolName.length > 0) {
1179
+ const inputJson = safeJsonWithUint8(info.input);
1180
+ return inputJson !== void 0 ? `name:${info.toolName}|input:${inputJson}` : `name:${info.toolName}`;
1181
+ }
1182
+ return void 0;
1183
+ }
1184
+ function isTranscriptToolResultError(part, result) {
1185
+ if (part["isError"] === true || part["type"] === "tool-error") {
1186
+ return true;
1187
+ }
1188
+ if (isRecord(result) && typeof result["type"] === "string") {
1189
+ return result["type"].startsWith("error") || result["type"] === "execution-denied";
1190
+ }
1191
+ return false;
1192
+ }
1193
+ function getTranscriptToolErrorMessage(result) {
1194
+ if (typeof result === "string" && result.length > 0) {
1195
+ return result;
1196
+ }
1197
+ if (isRecord(result)) {
1198
+ if (typeof result["value"] === "string" && result["value"].length > 0) {
1199
+ return result["value"];
1200
+ }
1201
+ if (typeof result["reason"] === "string" && result["reason"].length > 0) {
1202
+ return result["reason"];
1203
+ }
1204
+ if (typeof result["message"] === "string" && result["message"].length > 0) {
1205
+ return result["message"];
1206
+ }
1207
+ }
1208
+ return safeJsonWithUint8(result);
1209
+ }
1210
+ function getTranscriptMessageParts(message) {
1211
+ if (!isRecord(message)) return [];
1212
+ const content = message["content"];
1213
+ if (Array.isArray(content)) {
1214
+ return content.filter(isRecord);
1215
+ }
1216
+ return isRecord(content) ? [content] : [];
1217
+ }
1218
+ function getTranscriptToolCallInput(part) {
1219
+ return "input" in part ? part["input"] : "args" in part ? part["args"] : void 0;
1220
+ }
1221
+ function getTranscriptToolCallId(part) {
1222
+ const toolCallId = part["toolCallId"];
1223
+ return typeof toolCallId === "string" && toolCallId.length > 0 ? toolCallId : void 0;
1224
+ }
1225
+ function getTranscriptToolResultValue(part) {
1226
+ if ("output" in part) return part["output"];
1227
+ if ("result" in part) return part["result"];
1228
+ return part["error"];
1229
+ }
1230
+ function rememberPendingTranscriptToolCallKey(pendingKeysByToolName, toolName, key) {
1231
+ var _a;
1232
+ if (!toolName) return;
1233
+ const pendingKeys = (_a = pendingKeysByToolName.get(toolName)) != null ? _a : [];
1234
+ if (pendingKeys.includes(key)) return;
1235
+ pendingKeys.push(key);
1236
+ pendingKeysByToolName.set(toolName, pendingKeys);
1237
+ }
1238
+ function takePendingTranscriptToolCallKey(pendingKeysByToolName, toolName) {
1239
+ if (!toolName) return void 0;
1240
+ const pendingKeys = pendingKeysByToolName.get(toolName);
1241
+ if (!pendingKeys || pendingKeys.length === 0) return void 0;
1242
+ const key = pendingKeys.shift();
1243
+ if (key === void 0) return void 0;
1244
+ if (pendingKeys.length === 0) {
1245
+ pendingKeysByToolName.delete(toolName);
1246
+ }
1247
+ return key;
1248
+ }
1249
+ function mergeTranscriptToolCallPart(params) {
1250
+ var _a, _b, _c, _d;
1251
+ const { spans, pendingKeysByToolName, part } = params;
1252
+ const toolCallId = getTranscriptToolCallId(part);
1253
+ const toolName = typeof part["toolName"] === "string" ? part["toolName"] : void 0;
1254
+ const input = getTranscriptToolCallInput(part);
1255
+ const key = buildToolCallMatchKey({ toolCallId, toolName, input });
1256
+ if (!key) return;
1257
+ const placeholderKey = toolCallId == null ? buildToolCallMatchKey({ toolCallId: void 0, toolName, input: void 0 }) : void 0;
1258
+ const placeholder = placeholderKey && placeholderKey !== key ? spans.get(placeholderKey) : void 0;
1259
+ const existing = (_a = spans.get(key)) != null ? _a : placeholder;
1260
+ spans.set(key, {
1261
+ key,
1262
+ toolCallId: (_b = existing == null ? void 0 : existing.toolCallId) != null ? _b : toolCallId,
1263
+ toolName: (_c = existing == null ? void 0 : existing.toolName) != null ? _c : toolName,
1264
+ input: (_d = existing == null ? void 0 : existing.input) != null ? _d : input,
1265
+ result: existing == null ? void 0 : existing.result,
1266
+ status: existing == null ? void 0 : existing.status,
1267
+ errorMessage: existing == null ? void 0 : existing.errorMessage
1268
+ });
1269
+ if (placeholderKey && placeholderKey !== key) {
1270
+ spans.delete(placeholderKey);
1271
+ }
1272
+ if (!toolCallId && (existing == null ? void 0 : existing.result) === void 0) {
1273
+ rememberPendingTranscriptToolCallKey(pendingKeysByToolName, toolName, key);
1274
+ }
1275
+ }
1276
+ function mergeTranscriptToolResultPart(params) {
1277
+ var _a, _b, _c;
1278
+ const { spans, pendingKeysByToolName, part } = params;
1279
+ const toolCallId = getTranscriptToolCallId(part);
1280
+ const toolName = typeof part["toolName"] === "string" ? part["toolName"] : void 0;
1281
+ const result = getTranscriptToolResultValue(part);
1282
+ const fallbackKey = buildToolCallMatchKey({ toolCallId, toolName, input: void 0 });
1283
+ const key = toolCallId != null ? fallbackKey : (_a = takePendingTranscriptToolCallKey(pendingKeysByToolName, toolName)) != null ? _a : fallbackKey;
1284
+ if (!key) return;
1285
+ const existing = spans.get(key);
1286
+ const isError = isTranscriptToolResultError(part, result);
1287
+ spans.set(key, {
1288
+ key,
1289
+ toolCallId: (_b = existing == null ? void 0 : existing.toolCallId) != null ? _b : toolCallId,
1290
+ toolName: (_c = existing == null ? void 0 : existing.toolName) != null ? _c : toolName,
1291
+ input: existing == null ? void 0 : existing.input,
1292
+ result,
1293
+ status: isError ? "ERROR" : "OK",
1294
+ errorMessage: isError ? getTranscriptToolErrorMessage(result) : void 0
1295
+ });
1296
+ }
1297
+ function extractToolSpansFromMessages(messages) {
1298
+ const spans = /* @__PURE__ */ new Map();
1299
+ const pendingKeysByToolName = /* @__PURE__ */ new Map();
1300
+ for (const message of messages) {
1301
+ for (const part of getTranscriptMessageParts(message)) {
1302
+ if (part["type"] === "tool-call") {
1303
+ mergeTranscriptToolCallPart({ spans, pendingKeysByToolName, part });
1304
+ continue;
1305
+ }
1306
+ if (part["type"] === "tool-result" || part["type"] === "tool-error") {
1307
+ mergeTranscriptToolResultPart({ spans, pendingKeysByToolName, part });
1308
+ }
1309
+ }
1310
+ }
1311
+ return [...spans.values()];
1312
+ }
1171
1313
  function extractTextFromLmContent(content) {
1172
1314
  if (!Array.isArray(content)) return void 0;
1173
1315
  let result = "";
@@ -1183,7 +1325,7 @@ function extractToolCallsFromLmContent(content) {
1183
1325
  const calls = [];
1184
1326
  for (const part of content) {
1185
1327
  if (!isRecord(part) || part["type"] !== "tool-call") continue;
1186
- const toolCallId = typeof part["toolCallId"] === "string" ? part["toolCallId"] : void 0;
1328
+ const toolCallId = getTranscriptToolCallId(part);
1187
1329
  const toolName = typeof part["toolName"] === "string" ? part["toolName"] : void 0;
1188
1330
  if (toolCallId || toolName) {
1189
1331
  calls.push({ toolCallId, toolName, input: part["input"] });
@@ -1222,7 +1364,7 @@ function toOtlpAttr(key, value) {
1222
1364
  }
1223
1365
  function attrsFromTelemetryMetadata(metadata) {
1224
1366
  if (!metadata) return [];
1225
- return Object.entries(metadata).map(([k, v]) => {
1367
+ return Object.entries(metadata).filter(([k]) => !k.startsWith("raindrop.internal.")).map(([k, v]) => {
1226
1368
  const key = k === "raindrop.userId" ? "raindrop.ai.userId" : k;
1227
1369
  return toOtlpAttr(`ai.telemetry.metadata.${key}`, v);
1228
1370
  });
@@ -1311,6 +1453,50 @@ var AGENT_REPORTING_SIGNALS_DEFAULT = {
1311
1453
  }
1312
1454
  };
1313
1455
  var AGENT_REPORTING_TOOL_PREAMBLE = "You have a diagnostic tool for flagging serious failures for developer review. Only call this when you hit a significant, unrecoverable problem \u2014 not for minor hiccups or routine clarifications. If you can still help the user, do that instead of calling this tool. This tool is invisible to the user \u2014 never mention it or its output.";
1456
+ var pendingStoresByShipper = /* @__PURE__ */ new WeakMap();
1457
+ var PendingToolSpanStore = class _PendingToolSpanStore {
1458
+ constructor() {
1459
+ this.spans = /* @__PURE__ */ new Map();
1460
+ }
1461
+ static for(traceShipper, eventId) {
1462
+ let byEvent = pendingStoresByShipper.get(traceShipper);
1463
+ if (!byEvent) {
1464
+ byEvent = /* @__PURE__ */ new Map();
1465
+ pendingStoresByShipper.set(traceShipper, byEvent);
1466
+ }
1467
+ let store = byEvent.get(eventId);
1468
+ if (!store) {
1469
+ store = new _PendingToolSpanStore();
1470
+ byEvent.set(eventId, store);
1471
+ }
1472
+ return store;
1473
+ }
1474
+ resolve(resolvedToolSpans, ctx) {
1475
+ for (const [key, span] of this.spans) {
1476
+ const toolCall = resolvedToolSpans.get(key);
1477
+ if (!toolCall) continue;
1478
+ finishToolSpan(toolCall, span, ctx, span.startTimeUnixNano);
1479
+ this.spans.delete(key);
1480
+ }
1481
+ }
1482
+ remember(toolCall, rootSpan, ctx, startTimeUnixNano) {
1483
+ if (this.spans.has(toolCall.key)) return;
1484
+ this.spans.set(toolCall.key, startToolSpan(toolCall, rootSpan, ctx, startTimeUnixNano));
1485
+ }
1486
+ closeAll(traceShipper) {
1487
+ for (const [, span] of this.spans) {
1488
+ traceShipper.endSpan(span, { endTimeUnixNano: span.startTimeUnixNano });
1489
+ }
1490
+ this.spans.clear();
1491
+ }
1492
+ cleanup(traceShipper, eventId) {
1493
+ if (this.spans.size > 0) return;
1494
+ const byEvent = pendingStoresByShipper.get(traceShipper);
1495
+ if (!byEvent) return;
1496
+ byEvent.delete(eventId);
1497
+ if (byEvent.size === 0) pendingStoresByShipper.delete(traceShipper);
1498
+ }
1499
+ };
1314
1500
  var warnedMissingUserId = false;
1315
1501
  function warnMissingUserIdOnce() {
1316
1502
  if (warnedMissingUserId) return;
@@ -1337,6 +1523,10 @@ function extractRaindropMetadata(metadata) {
1337
1523
  if (typeof userId === "string" && userId) result.userId = userId;
1338
1524
  const eventId = metadata["raindrop.eventId"];
1339
1525
  if (typeof eventId === "string" && eventId) result.eventId = eventId;
1526
+ const eventIdGenerated = metadata["raindrop.internal.eventIdGenerated"];
1527
+ if (eventIdGenerated === true || eventIdGenerated === "true" || eventIdGenerated === "1") {
1528
+ result.eventIdGenerated = true;
1529
+ }
1340
1530
  const convoId = metadata["raindrop.convoId"];
1341
1531
  if (typeof convoId === "string" && convoId) result.convoId = convoId;
1342
1532
  const eventName = metadata["raindrop.eventName"];
@@ -1619,6 +1809,10 @@ function logFinalizeError(debug, err) {
1619
1809
  );
1620
1810
  }
1621
1811
  }
1812
+ function shouldKeepEventPending(params) {
1813
+ if (params.error != null || !params.canKeepEventPending) return false;
1814
+ return params.finishReason === "tool-calls" || params.finishReason === "tool_calls";
1815
+ }
1622
1816
  async function safeFinalize(finalize, debug, result, error) {
1623
1817
  try {
1624
1818
  await finalize(result, error);
@@ -1672,7 +1866,9 @@ function setupOperation(params) {
1672
1866
  const callTimeCtx = extractRaindropMetadata(telemetry == null ? void 0 : telemetry.metadata);
1673
1867
  const mergedCtx = mergeContexts(wrapTimeCtx, callTimeCtx);
1674
1868
  if (!mergedCtx.userId) warnMissingUserIdOnce();
1675
- const eventId = (_c = (_b = (_a = callTimeCtx.eventId) != null ? _a : mergedCtx.eventId) != null ? _b : inherited == null ? void 0 : inherited.eventId) != null ? _c : randomUUID();
1869
+ const hasCallTimeEventId = callTimeCtx.eventId != null;
1870
+ const hasWrapTimeEventId = wrapTimeCtx.eventId != null;
1871
+ const eventId = (_c = (_b = (_a = callTimeCtx.eventId) != null ? _a : wrapTimeCtx.eventId) != null ? _b : inherited == null ? void 0 : inherited.eventId) != null ? _c : randomUUID();
1676
1872
  const ctx = { ...mergedCtx, eventId };
1677
1873
  const inheritedParent = inherited && inherited.eventId === eventId ? { traceIdB64: inherited.traceIdB64, spanIdB64: inherited.spanIdB64 } : void 0;
1678
1874
  const outerOperationId = `ai.${operation}`;
@@ -1724,6 +1920,7 @@ function setupOperation(params) {
1724
1920
  return {
1725
1921
  eventId,
1726
1922
  ctx,
1923
+ canKeepEventPending: hasCallTimeEventId ? callTimeCtx.eventIdGenerated !== true : hasWrapTimeEventId,
1727
1924
  telemetry,
1728
1925
  rootSpan,
1729
1926
  wrappedArgs,
@@ -1746,6 +1943,12 @@ function createFinalize(params) {
1746
1943
  var _a, _b, _c, _d;
1747
1944
  const usage = extractUsageMetrics(result);
1748
1945
  const model = extractModel(result);
1946
+ const finishReason = extractFinishReason(result);
1947
+ const keepEventPending = shouldKeepEventPending({
1948
+ finishReason,
1949
+ error,
1950
+ canKeepEventPending: setup.canKeepEventPending
1951
+ });
1749
1952
  const inputAttachments = autoAttachmentEnabled ? extractInputAttachmentsFromArgs(arg) : void 0;
1750
1953
  const outputAttachments = autoAttachmentEnabled ? await extractOutputAttachmentsFromResult(result) : void 0;
1751
1954
  const baseMessages = coerceMessagesFromArgs(arg);
@@ -1767,7 +1970,18 @@ function createFinalize(params) {
1767
1970
  const output = patch.output;
1768
1971
  const finalModel = (_c = patch.model) != null ? _c : model;
1769
1972
  if (setup.rootSpan) {
1770
- const finishReason = extractFinishReason(result);
1973
+ const spanEndTimeUnixNano = nowUnixNanoString();
1974
+ const syntheticToolCallCount = emitTranscriptToolCallSpans({
1975
+ baseMessages,
1976
+ responseMessages,
1977
+ rootSpan: setup.rootSpan,
1978
+ eventId: setup.eventId,
1979
+ telemetry: setup.telemetry,
1980
+ toolCalls: setup.toolCalls,
1981
+ traceShipper,
1982
+ endTimeUnixNano: spanEndTimeUnixNano,
1983
+ keepEventPending
1984
+ });
1771
1985
  const providerMetadata = isRecord(result) ? result["providerMetadata"] : void 0;
1772
1986
  const resultToolCalls = isRecord(result) && Array.isArray(result["toolCalls"]) ? safeJsonWithUint8(result["toolCalls"]) : setup.toolCalls.length ? safeJsonWithUint8(setup.toolCalls) : void 0;
1773
1987
  traceShipper.endSpan(setup.rootSpan, {
@@ -1785,10 +1999,11 @@ function createFinalize(params) {
1785
1999
  attrInt("ai.usage.totalTokens", usage == null ? void 0 : usage.totalTokens),
1786
2000
  attrInt("ai.usage.reasoningTokens", usage == null ? void 0 : usage.reasoningTokens),
1787
2001
  attrInt("ai.usage.cachedInputTokens", usage == null ? void 0 : usage.cachedInputTokens),
1788
- attrInt("ai.toolCall.count", setup.toolCalls.length),
2002
+ attrInt("ai.toolCall.count", setup.toolCalls.length + syntheticToolCallCount),
1789
2003
  ...error ? [attrString("error.message", error instanceof Error ? error.message : String(error))] : []
1790
2004
  ],
1791
- error
2005
+ error,
2006
+ endTimeUnixNano: spanEndTimeUnixNano
1792
2007
  });
1793
2008
  }
1794
2009
  if (sendEvents) {
@@ -1801,7 +2016,7 @@ function createFinalize(params) {
1801
2016
  model: finalModel,
1802
2017
  properties: patch.properties,
1803
2018
  attachments: patch.attachments,
1804
- isPending: false
2019
+ isPending: keepEventPending
1805
2020
  }).catch((err) => {
1806
2021
  if (debug) {
1807
2022
  console.warn(
@@ -1812,6 +2027,89 @@ function createFinalize(params) {
1812
2027
  }
1813
2028
  };
1814
2029
  }
2030
+ function hasToolResult(toolCall) {
2031
+ return toolCall.result !== void 0 || toolCall.status === "ERROR";
2032
+ }
2033
+ function getExecutedToolCallKeys(toolCalls) {
2034
+ return new Set(
2035
+ toolCalls.map((tc) => buildToolCallMatchKey({ toolCallId: tc.id, toolName: tc.name, input: tc.args })).filter((key) => typeof key === "string" && key.length > 0)
2036
+ );
2037
+ }
2038
+ function getResolvedToolSpans(messages, executedKeys) {
2039
+ return new Map(
2040
+ extractToolSpansFromMessages(messages).filter((tc) => hasToolResult(tc) && !executedKeys.has(tc.key)).map((tc) => [tc.key, tc])
2041
+ );
2042
+ }
2043
+ function startToolSpan(toolCall, rootSpan, ctx, startTimeUnixNano) {
2044
+ var _a, _b, _c;
2045
+ const { operationName, resourceName } = opName("ai.toolCall", (_a = ctx.telemetry) == null ? void 0 : _a.functionId);
2046
+ return ctx.traceShipper.startSpan({
2047
+ name: "ai.toolCall",
2048
+ parent: { traceIdB64: rootSpan.ids.traceIdB64, spanIdB64: rootSpan.ids.spanIdB64 },
2049
+ eventId: ctx.eventId,
2050
+ operationId: "ai.toolCall",
2051
+ attributes: [
2052
+ attrString("operation.name", operationName),
2053
+ attrString("resource.name", resourceName),
2054
+ attrString("ai.telemetry.functionId", (_b = ctx.telemetry) == null ? void 0 : _b.functionId),
2055
+ attrString("ai.toolCall.name", toolCall.toolName),
2056
+ attrString("ai.toolCall.id", toolCall.toolCallId),
2057
+ ...((_c = ctx.telemetry) == null ? void 0 : _c.recordInputs) === false ? [] : [attrString("ai.toolCall.args", safeJsonWithUint8(toolCall.input))]
2058
+ ],
2059
+ startTimeUnixNano
2060
+ });
2061
+ }
2062
+ function finishToolSpan(toolCall, span, ctx, endTimeUnixNano) {
2063
+ var _a, _b, _c;
2064
+ const endAttributes = toolCall.status === "ERROR" ? [attrString("error.message", (_a = toolCall.errorMessage) != null ? _a : "Tool call failed")] : ((_b = ctx.telemetry) == null ? void 0 : _b.recordOutputs) === false ? [] : [attrString("ai.toolCall.result", safeJsonWithUint8(toolCall.result))];
2065
+ ctx.traceShipper.endSpan(span, {
2066
+ attributes: endAttributes,
2067
+ ...toolCall.status === "ERROR" ? { error: new Error((_c = toolCall.errorMessage) != null ? _c : "Tool call failed") } : {},
2068
+ endTimeUnixNano
2069
+ });
2070
+ }
2071
+ function emitToolSpan(toolCall, rootSpan, ctx, timeUnixNano) {
2072
+ const span = startToolSpan(toolCall, rootSpan, ctx, timeUnixNano);
2073
+ finishToolSpan(toolCall, span, ctx, timeUnixNano);
2074
+ }
2075
+ function emitTranscriptToolCallSpans(params) {
2076
+ const store = PendingToolSpanStore.for(params.traceShipper, params.eventId);
2077
+ const responseToolSpans = extractToolSpansFromMessages(params.responseMessages);
2078
+ if (responseToolSpans.length === 0 && params.baseMessages.length === 0) {
2079
+ if (!params.keepEventPending) {
2080
+ store.closeAll(params.traceShipper);
2081
+ }
2082
+ store.cleanup(params.traceShipper, params.eventId);
2083
+ return 0;
2084
+ }
2085
+ const ctx = {
2086
+ eventId: params.eventId,
2087
+ telemetry: params.telemetry,
2088
+ traceShipper: params.traceShipper
2089
+ };
2090
+ const executedKeys = getExecutedToolCallKeys(params.toolCalls);
2091
+ const resolvedSpans = getResolvedToolSpans(
2092
+ [...params.baseMessages, ...params.responseMessages],
2093
+ executedKeys
2094
+ );
2095
+ store.resolve(resolvedSpans, ctx);
2096
+ let syntheticToolCallCount = 0;
2097
+ for (const toolCall of responseToolSpans) {
2098
+ if (executedKeys.has(toolCall.key)) continue;
2099
+ executedKeys.add(toolCall.key);
2100
+ syntheticToolCallCount += 1;
2101
+ if (hasToolResult(toolCall) || !params.keepEventPending) {
2102
+ emitToolSpan(toolCall, params.rootSpan, ctx, params.endTimeUnixNano);
2103
+ } else {
2104
+ store.remember(toolCall, params.rootSpan, ctx, params.endTimeUnixNano);
2105
+ }
2106
+ }
2107
+ if (!params.keepEventPending) {
2108
+ store.closeAll(params.traceShipper);
2109
+ }
2110
+ store.cleanup(params.traceShipper, params.eventId);
2111
+ return syntheticToolCallCount;
2112
+ }
1815
2113
  function executeStreamingOperation(params) {
1816
2114
  const {
1817
2115
  operation,
@@ -2119,6 +2417,18 @@ function wrapAgentGenerate(generate, instance, agentSettings, className, aiSDK,
2119
2417
  const output = patch.output;
2120
2418
  const finalModel = (_c2 = patch.model) != null ? _c2 : model;
2121
2419
  if (rootSpan) {
2420
+ const spanEndTimeUnixNano = nowUnixNanoString();
2421
+ const syntheticToolCallCount = emitTranscriptToolCallSpans({
2422
+ baseMessages,
2423
+ responseMessages,
2424
+ rootSpan,
2425
+ eventId,
2426
+ telemetry,
2427
+ toolCalls,
2428
+ traceShipper: deps.traceShipper,
2429
+ endTimeUnixNano: spanEndTimeUnixNano,
2430
+ keepEventPending: false
2431
+ });
2122
2432
  const finishReason = extractFinishReason(result);
2123
2433
  const providerMetadata = isRecord(result) ? result["providerMetadata"] : void 0;
2124
2434
  const resultToolCalls = isRecord(result) && Array.isArray(result["toolCalls"]) ? safeJsonWithUint8(result["toolCalls"]) : toolCalls.length ? safeJsonWithUint8(toolCalls) : void 0;
@@ -2137,7 +2447,7 @@ function wrapAgentGenerate(generate, instance, agentSettings, className, aiSDK,
2137
2447
  attrInt("ai.usage.totalTokens", usage == null ? void 0 : usage.totalTokens),
2138
2448
  attrInt("ai.usage.reasoningTokens", usage == null ? void 0 : usage.reasoningTokens),
2139
2449
  attrInt("ai.usage.cachedInputTokens", usage == null ? void 0 : usage.cachedInputTokens),
2140
- attrInt("ai.toolCall.count", toolCalls.length),
2450
+ attrInt("ai.toolCall.count", toolCalls.length + syntheticToolCallCount),
2141
2451
  ...error ? [
2142
2452
  attrString(
2143
2453
  "error.message",
@@ -2145,7 +2455,8 @@ function wrapAgentGenerate(generate, instance, agentSettings, className, aiSDK,
2145
2455
  )
2146
2456
  ] : []
2147
2457
  ],
2148
- error
2458
+ error,
2459
+ endTimeUnixNano: spanEndTimeUnixNano
2149
2460
  });
2150
2461
  }
2151
2462
  if (sendEvents) {
@@ -2313,6 +2624,18 @@ function wrapAgentStream(stream, instance, agentSettings, className, aiSDK, deps
2313
2624
  const output = patch.output;
2314
2625
  const finalModel = (_c2 = patch.model) != null ? _c2 : model;
2315
2626
  if (rootSpan) {
2627
+ const spanEndTimeUnixNano = nowUnixNanoString();
2628
+ const syntheticToolCallCount = emitTranscriptToolCallSpans({
2629
+ baseMessages,
2630
+ responseMessages,
2631
+ rootSpan,
2632
+ eventId,
2633
+ telemetry,
2634
+ toolCalls,
2635
+ traceShipper: deps.traceShipper,
2636
+ endTimeUnixNano: spanEndTimeUnixNano,
2637
+ keepEventPending: false
2638
+ });
2316
2639
  const finishReason = extractFinishReason(result);
2317
2640
  const providerMetadata = isRecord(result) ? result["providerMetadata"] : void 0;
2318
2641
  const resultToolCalls = isRecord(result) && Array.isArray(result["toolCalls"]) ? safeJsonWithUint8(result["toolCalls"]) : toolCalls.length ? safeJsonWithUint8(toolCalls) : void 0;
@@ -2331,7 +2654,7 @@ function wrapAgentStream(stream, instance, agentSettings, className, aiSDK, deps
2331
2654
  attrInt("ai.usage.totalTokens", usage == null ? void 0 : usage.totalTokens),
2332
2655
  attrInt("ai.usage.reasoningTokens", usage == null ? void 0 : usage.reasoningTokens),
2333
2656
  attrInt("ai.usage.cachedInputTokens", usage == null ? void 0 : usage.cachedInputTokens),
2334
- attrInt("ai.toolCall.count", toolCalls.length),
2657
+ attrInt("ai.toolCall.count", toolCalls.length + syntheticToolCallCount),
2335
2658
  ...error ? [
2336
2659
  attrString(
2337
2660
  "error.message",
@@ -2339,7 +2662,8 @@ function wrapAgentStream(stream, instance, agentSettings, className, aiSDK, deps
2339
2662
  )
2340
2663
  ] : []
2341
2664
  ],
2342
- error
2665
+ error,
2666
+ endTimeUnixNano: spanEndTimeUnixNano
2343
2667
  });
2344
2668
  }
2345
2669
  if (sendEvents) {
@@ -2509,10 +2833,10 @@ function wrapToolExecute(name, tool, ctx, toolCalls) {
2509
2833
  lastValue = value;
2510
2834
  yield value;
2511
2835
  }
2512
- toolCalls.push({ name, args: toolArgs, result: lastValue, status: "OK" });
2836
+ toolCalls.push({ id: toolCallId, name, args: toolArgs, result: lastValue, status: "OK" });
2513
2837
  endToolSpan(toolSpan, lastValue);
2514
2838
  } catch (error) {
2515
- toolCalls.push({ name, args: toolArgs, status: "ERROR" });
2839
+ toolCalls.push({ id: toolCallId, name, args: toolArgs, status: "ERROR" });
2516
2840
  endToolSpan(toolSpan, void 0, error);
2517
2841
  throw error;
2518
2842
  }
@@ -2525,11 +2849,17 @@ function wrapToolExecute(name, tool, ctx, toolCalls) {
2525
2849
  const run = async () => {
2526
2850
  try {
2527
2851
  const awaitedResult = await result;
2528
- toolCalls.push({ name, args: toolArgs, result: awaitedResult, status: "OK" });
2852
+ toolCalls.push({
2853
+ id: toolCallId,
2854
+ name,
2855
+ args: toolArgs,
2856
+ result: awaitedResult,
2857
+ status: "OK"
2858
+ });
2529
2859
  endToolSpan(toolSpan, awaitedResult);
2530
2860
  return awaitedResult;
2531
2861
  } catch (error) {
2532
- toolCalls.push({ name, args: toolArgs, status: "ERROR" });
2862
+ toolCalls.push({ id: toolCallId, name, args: toolArgs, status: "ERROR" });
2533
2863
  endToolSpan(toolSpan, void 0, error);
2534
2864
  throw error;
2535
2865
  }
@@ -2915,15 +3245,44 @@ function extractNestedTokens(usage, key) {
2915
3245
 
2916
3246
  // src/index.ts
2917
3247
  function eventMetadata(options) {
2918
- var _a;
2919
3248
  const result = {};
2920
- result["raindrop.eventId"] = (_a = options.eventId) != null ? _a : randomUUID();
3249
+ if (options.eventId) {
3250
+ result["raindrop.eventId"] = options.eventId;
3251
+ } else {
3252
+ result["raindrop.eventId"] = randomUUID();
3253
+ result["raindrop.internal.eventIdGenerated"] = "true";
3254
+ }
2921
3255
  if (options.userId) result["raindrop.userId"] = options.userId;
2922
3256
  if (options.convoId) result["raindrop.convoId"] = options.convoId;
2923
3257
  if (options.eventName) result["raindrop.eventName"] = options.eventName;
2924
3258
  if (options.properties) result["raindrop.properties"] = JSON.stringify(options.properties);
2925
3259
  return result;
2926
3260
  }
3261
+ function deriveChatTurnMessageId(request) {
3262
+ const messages = Array.isArray(request.messages) ? request.messages : [];
3263
+ for (let i = messages.length - 1; i >= 0; i--) {
3264
+ const message = messages[i];
3265
+ if ((message == null ? void 0 : message.role) === "user" && typeof message.id === "string" && message.id.length > 0) {
3266
+ return message.id;
3267
+ }
3268
+ }
3269
+ if (typeof request.messageId === "string" && request.messageId.length > 0) {
3270
+ return request.messageId;
3271
+ }
3272
+ return void 0;
3273
+ }
3274
+ function eventMetadataFromChatRequest(options) {
3275
+ var _a, _b;
3276
+ const { request, ...rest } = options;
3277
+ const convoId = (_a = rest.convoId) != null ? _a : typeof request.id === "string" && request.id.length > 0 ? request.id : void 0;
3278
+ const turnMessageId = deriveChatTurnMessageId(request);
3279
+ const eventId = (_b = rest.eventId) != null ? _b : turnMessageId ? convoId ? `chat:${convoId}:${turnMessageId}` : `chat:${turnMessageId}` : void 0;
3280
+ return eventMetadata({
3281
+ ...rest,
3282
+ ...convoId ? { convoId } : {},
3283
+ ...eventId ? { eventId } : {}
3284
+ });
3285
+ }
2927
3286
  function envDebugEnabled() {
2928
3287
  var _a;
2929
3288
  if (typeof process === "undefined") return false;
@@ -3001,4 +3360,4 @@ function createRaindropAISDK(opts) {
3001
3360
  };
3002
3361
  }
3003
3362
 
3004
- export { _resetWarnedMissingUserId, createRaindropAISDK, currentSpan, eventMetadata, getContextManager, withCurrent };
3363
+ export { _resetWarnedMissingUserId, createRaindropAISDK, currentSpan, eventMetadata, eventMetadataFromChatRequest, getContextManager, withCurrent };
package/dist/index.d.mts CHANGED
@@ -81,6 +81,34 @@ type EventMetadataOptions = {
81
81
  * ```
82
82
  */
83
83
  declare function eventMetadata(options: EventMetadataOptions): Record<string, string>;
84
+ type AISDKChatRequestMessageLike = {
85
+ id?: string;
86
+ role?: string;
87
+ [key: string]: unknown;
88
+ };
89
+ type AISDKChatRequestLike = {
90
+ id?: string;
91
+ messageId?: string;
92
+ trigger?: string;
93
+ messages?: AISDKChatRequestMessageLike[];
94
+ [key: string]: unknown;
95
+ };
96
+ /**
97
+ * Creates stable Raindrop metadata from an AI SDK UI chat request body.
98
+ *
99
+ * This is useful for `useChat` applications that require multiple server round-trips
100
+ * for a single user turn, e.g. when the model triggers client-side tools.
101
+ *
102
+ * The helper derives:
103
+ * - `convoId` from the AI SDK chat request `id`
104
+ * - `eventId` from the latest user message id in `messages`
105
+ *
106
+ * Reusing the same derived `eventId` across tool handoff round-trips allows the
107
+ * wrapper to patch one logical event instead of creating a new event per POST.
108
+ */
109
+ declare function eventMetadataFromChatRequest(options: EventMetadataOptions & {
110
+ request: AISDKChatRequestLike;
111
+ }): Record<string, string>;
84
112
  type AgentCallMetadata = ReturnType<typeof eventMetadata>;
85
113
  type NormalizeUnknownAgentOptions<T> = T extends {
86
114
  options: infer CallOptions;
@@ -282,4 +310,4 @@ type RaindropAISDKClient = {
282
310
  };
283
311
  declare function createRaindropAISDK(opts: RaindropAISDKOptions): RaindropAISDKClient;
284
312
 
285
- export { type AISDKMessage, type AgentCallMetadata, type AgentWithMetadata, type Attachment, type BuildEventPatch, ContextManager, type ContextSpan, type EventBuilder, type EventMetadataOptions, type IdentifyInput, type RaindropAISDKClient, type RaindropAISDKContext, type RaindropAISDKOptions, type SelfDiagnosticsOptions, type SelfDiagnosticsSignalDefinition, type SelfDiagnosticsSignalDefinitions, type WrapAISDKOptions, type WrappedAI, type WrappedAISDK, _resetWarnedMissingUserId, createRaindropAISDK, currentSpan, eventMetadata, getContextManager, withCurrent };
313
+ export { type AISDKChatRequestLike, type AISDKChatRequestMessageLike, type AISDKMessage, type AgentCallMetadata, type AgentWithMetadata, type Attachment, type BuildEventPatch, ContextManager, type ContextSpan, type EventBuilder, type EventMetadataOptions, type IdentifyInput, type RaindropAISDKClient, type RaindropAISDKContext, type RaindropAISDKOptions, type SelfDiagnosticsOptions, type SelfDiagnosticsSignalDefinition, type SelfDiagnosticsSignalDefinitions, type WrapAISDKOptions, type WrappedAI, type WrappedAISDK, _resetWarnedMissingUserId, createRaindropAISDK, currentSpan, eventMetadata, eventMetadataFromChatRequest, getContextManager, withCurrent };