@raindrop-ai/ai-sdk 0.0.29 → 0.0.30

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.
@@ -391,6 +391,19 @@ type RaindropTelemetryIntegrationOptions = {
391
391
  eventShipper: EventShipper;
392
392
  sendTraces?: boolean;
393
393
  sendEvents?: boolean;
394
+ /**
395
+ * When `true` (default), generations started inside a tool's `execute`
396
+ * callback on the same async branch are wrapped in a synthetic `TOOL_CALL`
397
+ * + `agent.subagent` LLM marker so observability backends can render them
398
+ * as an agent block (and so the inner LLM's `track_partial` is suppressed).
399
+ *
400
+ * This is the right semantic for any framework that lowers sub-agents to
401
+ * AI SDK tools (Ash, Mastra, Claude Agent SDK, hand-rolled tool loops),
402
+ * not Ash-specific. Set to `false` if you have a tool whose `execute`
403
+ * happens to call `streamText`/`generateText` for unrelated reasons (e.g.
404
+ * RAG enrichment) and don't want it labelled as a sub-agent.
405
+ */
406
+ subagentWrapping?: boolean;
394
407
  debug?: boolean;
395
408
  context?: {
396
409
  userId?: string;
@@ -405,9 +418,26 @@ declare class RaindropTelemetryIntegration implements TelemetryIntegration {
405
418
  private readonly eventShipper;
406
419
  private readonly sendTraces;
407
420
  private readonly sendEvents;
421
+ private readonly subagentWrapping;
408
422
  private readonly debug;
409
423
  private readonly defaultContext;
410
424
  private readonly callStates;
425
+ /**
426
+ * Per-tool-call snapshot of the parent-tool ALS context taken right
427
+ * before `enterParentToolContext` overwrites it in `toolExecutionStart`.
428
+ * Kept at the integration level (rather than on `CallState`) so the
429
+ * snapshot survives even when the parent generation has no tracked
430
+ * `CallState` — e.g. the AI SDK dispatches a tool callback for a
431
+ * `callId` we never registered via `onStart`. Without this, the
432
+ * unconditional ALS enter in `toolExecutionStart` would leave
433
+ * `toolExecutionEnd` no way to restore the prior context, so it would
434
+ * clear and wipe whatever the outer scope had set.
435
+ *
436
+ * Keyed by `toolCallId` because that's the only identifier guaranteed
437
+ * to round-trip between `toolExecutionStart` and `toolExecutionEnd`
438
+ * (the `event.callId` can be the same for parallel sibling tools).
439
+ */
440
+ private readonly priorParentContexts;
411
441
  constructor(opts: RaindropTelemetryIntegrationOptions);
412
442
  private getState;
413
443
  private cleanup;
@@ -510,6 +540,92 @@ declare function readRaindropCallMetadataFromArgs(args: readonly unknown[]): Rai
510
540
 
511
541
  declare function _resetWarnedMissingUserId(): void;
512
542
 
543
+ /**
544
+ * Parent tool execution context propagation.
545
+ *
546
+ * Problem
547
+ * ───────
548
+ * When a tool's `execute` callback recursively calls `streamText` /
549
+ * `generateText` (which is exactly how Ash lowers sub-agents, but also a
550
+ * common pattern in hand-rolled agent loops, Mastra, Claude Agent SDK,
551
+ * etc.), the inner generation's telemetry events are dispatched with no
552
+ * connection back to the outer tool call. The AI SDK fires `onStart` for
553
+ * the inner generation with its own fresh `callId`; nothing on the event
554
+ * tells the integration "this generation was triggered from inside tool
555
+ * X's execute callback."
556
+ *
557
+ * Without that linkage, observability tools have to fall back to
558
+ * regex-matching the prompt to guess whether a call is a sub-agent
559
+ * dispatch — fragile, framework-specific, and easily spoofable by user
560
+ * input.
561
+ *
562
+ * Solution
563
+ * ────────
564
+ * We maintain an `AsyncLocalStorage` slot that holds the currently-active
565
+ * parent tool call. The integration's `onToolExecutionStart` enters the
566
+ * slot before the AI SDK awaits `tool.execute(...)`; the inner
567
+ * generation's `onStart` reads from the slot to recover parent linkage.
568
+ * `onToolExecutionEnd` clears the slot so subsequent code in the same
569
+ * async branch doesn't see stale state.
570
+ *
571
+ * Async-context scoping
572
+ * ─────────────────────
573
+ * `enterWith` modifies the *current* async context, which propagates
574
+ * downward into any `await` continuation and into child async resources
575
+ * (including the awaited `tool.execute` and anything it calls). Sibling
576
+ * branches are unaffected: when the AI SDK dispatches tool calls via
577
+ * `Promise.all(map(async (tc) => ...))`, each map callback runs in its
578
+ * own async branch, so `enterWith` in one branch is invisible to the
579
+ * others. This is exactly the behaviour we need for parallel sub-agent
580
+ * dispatch.
581
+ *
582
+ * On runtimes that don't expose `AsyncLocalStorage` (browser, Cloudflare
583
+ * Workers without `nodejs_compat`), we fall back to a synchronous stack
584
+ * via `runWithParentToolContext` — see the `SyncFallbackStorage` in
585
+ * `call-metadata.ts` for the established pattern. `enterWith` becomes a
586
+ * silent no-op on the sync fallback, so async tool execution on those
587
+ * runtimes won't have parent linkage and the inner generation will
588
+ * render as a top-level call instead of being grouped under its caller.
589
+ */
590
+ type ParentToolContext = {
591
+ /** The `callId` of the generation whose step kicked off this tool call. */
592
+ parentCallId: string;
593
+ /** The tool-call id from the parent generation's step. */
594
+ toolCallId: string;
595
+ /** The tool name the parent generation invoked. */
596
+ toolName: string;
597
+ };
598
+ /** Test helper — drop the storage so tests start from a fresh slot. */
599
+ declare function _resetParentToolContextStorage(): void;
600
+ /**
601
+ * Returns the active parent tool context, or `undefined` if no tool is
602
+ * currently executing in this async branch.
603
+ */
604
+ declare function getCurrentParentToolContext(): ParentToolContext | undefined;
605
+ /**
606
+ * Enter a parent tool context on the current async branch. Subsequent
607
+ * `await`-ed work (including the parent's `tool.execute` body) inherits
608
+ * the context until {@link clearParentToolContext} runs. No-op on
609
+ * runtimes without `enterWith` support (browser/edge sync fallback).
610
+ */
611
+ declare function enterParentToolContext(ctx: ParentToolContext): void;
612
+ /**
613
+ * Clear the parent tool context on the current async branch. Pairs with
614
+ * {@link enterParentToolContext}. No-op on runtimes without `enterWith`
615
+ * support.
616
+ *
617
+ * `enterWith(undefined)` is supported by Node's `AsyncLocalStorage` at
618
+ * runtime; the `as never` cast bypasses TypeScript's stricter typing on
619
+ * the shared global declaration (`store: T`).
620
+ */
621
+ declare function clearParentToolContext(): void;
622
+ /**
623
+ * Run `fn` with `ctx` bound as the parent tool context. The sync-fallback
624
+ * equivalent of `enter`/`clear` — useful in tests and any code path
625
+ * where you want strict block scoping.
626
+ */
627
+ declare function runWithParentToolContext<R>(ctx: ParentToolContext, fn: () => R): R;
628
+
513
629
  /**
514
630
  * Options for creating event metadata for call-time context override.
515
631
  */
@@ -860,8 +976,17 @@ type RaindropAISDKClient = {
860
976
  /**
861
977
  * Create a TelemetryIntegration instance for direct registration with AI SDK v7+.
862
978
  * Use with `registerTelemetryIntegration()` from the `ai` package.
979
+ *
980
+ * Accepts the same `context` it has always accepted, or a full options
981
+ * object exposing `{ context, subagentWrapping }`. Sub-agent wrapping
982
+ * defaults to `true`; pass `subagentWrapping: false` to opt out for
983
+ * consumers whose `tool.execute` happens to call `generateText` for
984
+ * non-agentic reasons (e.g. RAG enrichment).
863
985
  */
864
- createTelemetryIntegration(context?: RaindropTelemetryIntegrationOptions["context"]): RaindropTelemetryIntegration;
986
+ createTelemetryIntegration(contextOrOptions?: RaindropTelemetryIntegrationOptions["context"] | {
987
+ context?: RaindropTelemetryIntegrationOptions["context"];
988
+ subagentWrapping?: boolean;
989
+ }): RaindropTelemetryIntegration;
865
990
  events: {
866
991
  patch(eventId: string, patch: EventPatch): Promise<void>;
867
992
  addAttachments(eventId: string, attachments: Attachment[]): Promise<void>;
@@ -933,4 +1058,4 @@ type RaindropAISDKClient = {
933
1058
  };
934
1059
  declare function createRaindropAISDK(opts: RaindropAISDKOptions): RaindropAISDKClient;
935
1060
 
936
- export { type AISDKChatRequestLike, type AISDKChatRequestMessageLike, type AISDKMessage, type AgentCallMetadata, type AgentWithMetadata, type Attachment, type BuildEventPatch, ContextManager, type ContextSpan, type CreateSpanArgs, DEFAULT_REDACT_ATTRIBUTE_KEYS, DEFAULT_SECRET_KEY_NAMES, type EndSpanArgs, type EventBuilder, type EventMetadataOptions, type IdentifyInput, type OtlpAnyValue, type OtlpSpan, REDACTED_PLACEHOLDER, type RaindropAISDKClient, type RaindropAISDKContext, type RaindropAISDKOptions, type RaindropCallMetadata, RaindropTelemetryIntegration, type RaindropTelemetryIntegrationOptions, type SelfDiagnosticsOptions, type SelfDiagnosticsSignalDefinition, type SelfDiagnosticsSignalDefinitions, type StartSpanArgs, type TraceSpan, type TransformSpanHook, type WrapAISDKOptions, type WrappedAI, type WrappedAISDK, _resetRaindropCallMetadataStorage, _resetWarnedMissingUserId, createRaindropAISDK, currentSpan, defaultTransformSpan, eventMetadata, eventMetadataFromChatRequest, getContextManager, getCurrentRaindropCallMetadata, readRaindropCallMetadataFromArgs, redactJsonAttributeValue, redactSecretsInObject, runWithRaindropCallMetadata, withCurrent };
1061
+ export { type AISDKChatRequestLike, type AISDKChatRequestMessageLike, type AISDKMessage, type AgentCallMetadata, type AgentWithMetadata, type Attachment, type BuildEventPatch, ContextManager, type ContextSpan, type CreateSpanArgs, DEFAULT_REDACT_ATTRIBUTE_KEYS, DEFAULT_SECRET_KEY_NAMES, type EndSpanArgs, type EventBuilder, type EventMetadataOptions, type IdentifyInput, type OtlpAnyValue, type OtlpSpan, type ParentToolContext, REDACTED_PLACEHOLDER, type RaindropAISDKClient, type RaindropAISDKContext, type RaindropAISDKOptions, type RaindropCallMetadata, RaindropTelemetryIntegration, type RaindropTelemetryIntegrationOptions, type SelfDiagnosticsOptions, type SelfDiagnosticsSignalDefinition, type SelfDiagnosticsSignalDefinitions, type StartSpanArgs, type TraceSpan, type TransformSpanHook, type WrapAISDKOptions, type WrappedAI, type WrappedAISDK, _resetParentToolContextStorage, _resetRaindropCallMetadataStorage, _resetWarnedMissingUserId, clearParentToolContext, createRaindropAISDK, currentSpan, defaultTransformSpan, enterParentToolContext, eventMetadata, eventMetadataFromChatRequest, getContextManager, getCurrentParentToolContext, getCurrentRaindropCallMetadata, readRaindropCallMetadataFromArgs, redactJsonAttributeValue, redactSecretsInObject, runWithParentToolContext, runWithRaindropCallMetadata, withCurrent };
@@ -391,6 +391,19 @@ type RaindropTelemetryIntegrationOptions = {
391
391
  eventShipper: EventShipper;
392
392
  sendTraces?: boolean;
393
393
  sendEvents?: boolean;
394
+ /**
395
+ * When `true` (default), generations started inside a tool's `execute`
396
+ * callback on the same async branch are wrapped in a synthetic `TOOL_CALL`
397
+ * + `agent.subagent` LLM marker so observability backends can render them
398
+ * as an agent block (and so the inner LLM's `track_partial` is suppressed).
399
+ *
400
+ * This is the right semantic for any framework that lowers sub-agents to
401
+ * AI SDK tools (Ash, Mastra, Claude Agent SDK, hand-rolled tool loops),
402
+ * not Ash-specific. Set to `false` if you have a tool whose `execute`
403
+ * happens to call `streamText`/`generateText` for unrelated reasons (e.g.
404
+ * RAG enrichment) and don't want it labelled as a sub-agent.
405
+ */
406
+ subagentWrapping?: boolean;
394
407
  debug?: boolean;
395
408
  context?: {
396
409
  userId?: string;
@@ -405,9 +418,26 @@ declare class RaindropTelemetryIntegration implements TelemetryIntegration {
405
418
  private readonly eventShipper;
406
419
  private readonly sendTraces;
407
420
  private readonly sendEvents;
421
+ private readonly subagentWrapping;
408
422
  private readonly debug;
409
423
  private readonly defaultContext;
410
424
  private readonly callStates;
425
+ /**
426
+ * Per-tool-call snapshot of the parent-tool ALS context taken right
427
+ * before `enterParentToolContext` overwrites it in `toolExecutionStart`.
428
+ * Kept at the integration level (rather than on `CallState`) so the
429
+ * snapshot survives even when the parent generation has no tracked
430
+ * `CallState` — e.g. the AI SDK dispatches a tool callback for a
431
+ * `callId` we never registered via `onStart`. Without this, the
432
+ * unconditional ALS enter in `toolExecutionStart` would leave
433
+ * `toolExecutionEnd` no way to restore the prior context, so it would
434
+ * clear and wipe whatever the outer scope had set.
435
+ *
436
+ * Keyed by `toolCallId` because that's the only identifier guaranteed
437
+ * to round-trip between `toolExecutionStart` and `toolExecutionEnd`
438
+ * (the `event.callId` can be the same for parallel sibling tools).
439
+ */
440
+ private readonly priorParentContexts;
411
441
  constructor(opts: RaindropTelemetryIntegrationOptions);
412
442
  private getState;
413
443
  private cleanup;
@@ -510,6 +540,92 @@ declare function readRaindropCallMetadataFromArgs(args: readonly unknown[]): Rai
510
540
 
511
541
  declare function _resetWarnedMissingUserId(): void;
512
542
 
543
+ /**
544
+ * Parent tool execution context propagation.
545
+ *
546
+ * Problem
547
+ * ───────
548
+ * When a tool's `execute` callback recursively calls `streamText` /
549
+ * `generateText` (which is exactly how Ash lowers sub-agents, but also a
550
+ * common pattern in hand-rolled agent loops, Mastra, Claude Agent SDK,
551
+ * etc.), the inner generation's telemetry events are dispatched with no
552
+ * connection back to the outer tool call. The AI SDK fires `onStart` for
553
+ * the inner generation with its own fresh `callId`; nothing on the event
554
+ * tells the integration "this generation was triggered from inside tool
555
+ * X's execute callback."
556
+ *
557
+ * Without that linkage, observability tools have to fall back to
558
+ * regex-matching the prompt to guess whether a call is a sub-agent
559
+ * dispatch — fragile, framework-specific, and easily spoofable by user
560
+ * input.
561
+ *
562
+ * Solution
563
+ * ────────
564
+ * We maintain an `AsyncLocalStorage` slot that holds the currently-active
565
+ * parent tool call. The integration's `onToolExecutionStart` enters the
566
+ * slot before the AI SDK awaits `tool.execute(...)`; the inner
567
+ * generation's `onStart` reads from the slot to recover parent linkage.
568
+ * `onToolExecutionEnd` clears the slot so subsequent code in the same
569
+ * async branch doesn't see stale state.
570
+ *
571
+ * Async-context scoping
572
+ * ─────────────────────
573
+ * `enterWith` modifies the *current* async context, which propagates
574
+ * downward into any `await` continuation and into child async resources
575
+ * (including the awaited `tool.execute` and anything it calls). Sibling
576
+ * branches are unaffected: when the AI SDK dispatches tool calls via
577
+ * `Promise.all(map(async (tc) => ...))`, each map callback runs in its
578
+ * own async branch, so `enterWith` in one branch is invisible to the
579
+ * others. This is exactly the behaviour we need for parallel sub-agent
580
+ * dispatch.
581
+ *
582
+ * On runtimes that don't expose `AsyncLocalStorage` (browser, Cloudflare
583
+ * Workers without `nodejs_compat`), we fall back to a synchronous stack
584
+ * via `runWithParentToolContext` — see the `SyncFallbackStorage` in
585
+ * `call-metadata.ts` for the established pattern. `enterWith` becomes a
586
+ * silent no-op on the sync fallback, so async tool execution on those
587
+ * runtimes won't have parent linkage and the inner generation will
588
+ * render as a top-level call instead of being grouped under its caller.
589
+ */
590
+ type ParentToolContext = {
591
+ /** The `callId` of the generation whose step kicked off this tool call. */
592
+ parentCallId: string;
593
+ /** The tool-call id from the parent generation's step. */
594
+ toolCallId: string;
595
+ /** The tool name the parent generation invoked. */
596
+ toolName: string;
597
+ };
598
+ /** Test helper — drop the storage so tests start from a fresh slot. */
599
+ declare function _resetParentToolContextStorage(): void;
600
+ /**
601
+ * Returns the active parent tool context, or `undefined` if no tool is
602
+ * currently executing in this async branch.
603
+ */
604
+ declare function getCurrentParentToolContext(): ParentToolContext | undefined;
605
+ /**
606
+ * Enter a parent tool context on the current async branch. Subsequent
607
+ * `await`-ed work (including the parent's `tool.execute` body) inherits
608
+ * the context until {@link clearParentToolContext} runs. No-op on
609
+ * runtimes without `enterWith` support (browser/edge sync fallback).
610
+ */
611
+ declare function enterParentToolContext(ctx: ParentToolContext): void;
612
+ /**
613
+ * Clear the parent tool context on the current async branch. Pairs with
614
+ * {@link enterParentToolContext}. No-op on runtimes without `enterWith`
615
+ * support.
616
+ *
617
+ * `enterWith(undefined)` is supported by Node's `AsyncLocalStorage` at
618
+ * runtime; the `as never` cast bypasses TypeScript's stricter typing on
619
+ * the shared global declaration (`store: T`).
620
+ */
621
+ declare function clearParentToolContext(): void;
622
+ /**
623
+ * Run `fn` with `ctx` bound as the parent tool context. The sync-fallback
624
+ * equivalent of `enter`/`clear` — useful in tests and any code path
625
+ * where you want strict block scoping.
626
+ */
627
+ declare function runWithParentToolContext<R>(ctx: ParentToolContext, fn: () => R): R;
628
+
513
629
  /**
514
630
  * Options for creating event metadata for call-time context override.
515
631
  */
@@ -860,8 +976,17 @@ type RaindropAISDKClient = {
860
976
  /**
861
977
  * Create a TelemetryIntegration instance for direct registration with AI SDK v7+.
862
978
  * Use with `registerTelemetryIntegration()` from the `ai` package.
979
+ *
980
+ * Accepts the same `context` it has always accepted, or a full options
981
+ * object exposing `{ context, subagentWrapping }`. Sub-agent wrapping
982
+ * defaults to `true`; pass `subagentWrapping: false` to opt out for
983
+ * consumers whose `tool.execute` happens to call `generateText` for
984
+ * non-agentic reasons (e.g. RAG enrichment).
863
985
  */
864
- createTelemetryIntegration(context?: RaindropTelemetryIntegrationOptions["context"]): RaindropTelemetryIntegration;
986
+ createTelemetryIntegration(contextOrOptions?: RaindropTelemetryIntegrationOptions["context"] | {
987
+ context?: RaindropTelemetryIntegrationOptions["context"];
988
+ subagentWrapping?: boolean;
989
+ }): RaindropTelemetryIntegration;
865
990
  events: {
866
991
  patch(eventId: string, patch: EventPatch): Promise<void>;
867
992
  addAttachments(eventId: string, attachments: Attachment[]): Promise<void>;
@@ -933,4 +1058,4 @@ type RaindropAISDKClient = {
933
1058
  };
934
1059
  declare function createRaindropAISDK(opts: RaindropAISDKOptions): RaindropAISDKClient;
935
1060
 
936
- export { type AISDKChatRequestLike, type AISDKChatRequestMessageLike, type AISDKMessage, type AgentCallMetadata, type AgentWithMetadata, type Attachment, type BuildEventPatch, ContextManager, type ContextSpan, type CreateSpanArgs, DEFAULT_REDACT_ATTRIBUTE_KEYS, DEFAULT_SECRET_KEY_NAMES, type EndSpanArgs, type EventBuilder, type EventMetadataOptions, type IdentifyInput, type OtlpAnyValue, type OtlpSpan, REDACTED_PLACEHOLDER, type RaindropAISDKClient, type RaindropAISDKContext, type RaindropAISDKOptions, type RaindropCallMetadata, RaindropTelemetryIntegration, type RaindropTelemetryIntegrationOptions, type SelfDiagnosticsOptions, type SelfDiagnosticsSignalDefinition, type SelfDiagnosticsSignalDefinitions, type StartSpanArgs, type TraceSpan, type TransformSpanHook, type WrapAISDKOptions, type WrappedAI, type WrappedAISDK, _resetRaindropCallMetadataStorage, _resetWarnedMissingUserId, createRaindropAISDK, currentSpan, defaultTransformSpan, eventMetadata, eventMetadataFromChatRequest, getContextManager, getCurrentRaindropCallMetadata, readRaindropCallMetadataFromArgs, redactJsonAttributeValue, redactSecretsInObject, runWithRaindropCallMetadata, withCurrent };
1061
+ export { type AISDKChatRequestLike, type AISDKChatRequestMessageLike, type AISDKMessage, type AgentCallMetadata, type AgentWithMetadata, type Attachment, type BuildEventPatch, ContextManager, type ContextSpan, type CreateSpanArgs, DEFAULT_REDACT_ATTRIBUTE_KEYS, DEFAULT_SECRET_KEY_NAMES, type EndSpanArgs, type EventBuilder, type EventMetadataOptions, type IdentifyInput, type OtlpAnyValue, type OtlpSpan, type ParentToolContext, REDACTED_PLACEHOLDER, type RaindropAISDKClient, type RaindropAISDKContext, type RaindropAISDKOptions, type RaindropCallMetadata, RaindropTelemetryIntegration, type RaindropTelemetryIntegrationOptions, type SelfDiagnosticsOptions, type SelfDiagnosticsSignalDefinition, type SelfDiagnosticsSignalDefinitions, type StartSpanArgs, type TraceSpan, type TransformSpanHook, type WrapAISDKOptions, type WrappedAI, type WrappedAISDK, _resetParentToolContextStorage, _resetRaindropCallMetadataStorage, _resetWarnedMissingUserId, clearParentToolContext, createRaindropAISDK, currentSpan, defaultTransformSpan, enterParentToolContext, eventMetadata, eventMetadataFromChatRequest, getContextManager, getCurrentParentToolContext, getCurrentRaindropCallMetadata, readRaindropCallMetadataFromArgs, redactJsonAttributeValue, redactSecretsInObject, runWithParentToolContext, runWithRaindropCallMetadata, withCurrent };
@@ -1019,7 +1019,7 @@ async function* asyncGeneratorWithCurrent(span, gen) {
1019
1019
  // package.json
1020
1020
  var package_default = {
1021
1021
  name: "@raindrop-ai/ai-sdk",
1022
- version: "0.0.29"};
1022
+ version: "0.0.30"};
1023
1023
 
1024
1024
  // src/internal/version.ts
1025
1025
  var libraryName = package_default.name;
@@ -1735,13 +1735,76 @@ function readRaindropCallMetadataFromArgs(args) {
1735
1735
  return meta;
1736
1736
  }
1737
1737
 
1738
+ // src/internal/parent-tool-context.ts
1739
+ var SyncFallbackStorage2 = class {
1740
+ constructor() {
1741
+ this._stack = [];
1742
+ }
1743
+ getStore() {
1744
+ return this._stack[this._stack.length - 1];
1745
+ }
1746
+ run(store, callback) {
1747
+ this._stack.push(store);
1748
+ try {
1749
+ return callback();
1750
+ } finally {
1751
+ this._stack.pop();
1752
+ }
1753
+ }
1754
+ // `enterWith` is intentionally absent on the sync fallback — the
1755
+ // event-driven enter/clear pattern only makes sense when the runtime
1756
+ // can propagate state across awaits, which the sync stack cannot.
1757
+ };
1758
+ var _storage2 = null;
1759
+ function getStorage2() {
1760
+ if (_storage2) return _storage2;
1761
+ const Ctor = globalThis.RAINDROP_ASYNC_LOCAL_STORAGE;
1762
+ _storage2 = Ctor ? new Ctor() : new SyncFallbackStorage2();
1763
+ return _storage2;
1764
+ }
1765
+ function _resetParentToolContextStorage() {
1766
+ _storage2 = null;
1767
+ }
1768
+ function getCurrentParentToolContext() {
1769
+ return getStorage2().getStore();
1770
+ }
1771
+ function enterParentToolContext(ctx) {
1772
+ var _a;
1773
+ const storage = getStorage2();
1774
+ (_a = storage.enterWith) == null ? void 0 : _a.call(storage, ctx);
1775
+ }
1776
+ function clearParentToolContext() {
1777
+ var _a;
1778
+ const storage = getStorage2();
1779
+ (_a = storage.enterWith) == null ? void 0 : _a.call(storage, void 0);
1780
+ }
1781
+ function runWithParentToolContext(ctx, fn) {
1782
+ return getStorage2().run(ctx, fn);
1783
+ }
1784
+
1738
1785
  // src/internal/raindrop-telemetry-integration.ts
1739
1786
  var RaindropTelemetryIntegration = class {
1740
1787
  constructor(opts) {
1741
1788
  this.callStates = /* @__PURE__ */ new Map();
1789
+ /**
1790
+ * Per-tool-call snapshot of the parent-tool ALS context taken right
1791
+ * before `enterParentToolContext` overwrites it in `toolExecutionStart`.
1792
+ * Kept at the integration level (rather than on `CallState`) so the
1793
+ * snapshot survives even when the parent generation has no tracked
1794
+ * `CallState` — e.g. the AI SDK dispatches a tool callback for a
1795
+ * `callId` we never registered via `onStart`. Without this, the
1796
+ * unconditional ALS enter in `toolExecutionStart` would leave
1797
+ * `toolExecutionEnd` no way to restore the prior context, so it would
1798
+ * clear and wipe whatever the outer scope had set.
1799
+ *
1800
+ * Keyed by `toolCallId` because that's the only identifier guaranteed
1801
+ * to round-trip between `toolExecutionStart` and `toolExecutionEnd`
1802
+ * (the `event.callId` can be the same for parallel sibling tools).
1803
+ */
1804
+ this.priorParentContexts = /* @__PURE__ */ new Map();
1742
1805
  // ── onStart ─────────────────────────────────────────────────────────────
1743
1806
  this.onStart = (event) => {
1744
- var _a, _b, _c, _d;
1807
+ var _a, _b, _c, _d, _e;
1745
1808
  if (event.isEnabled === false) return;
1746
1809
  const isEmbed = event.operationId === "ai.embed" || event.operationId === "ai.embedMany";
1747
1810
  const recordInputs = event.recordInputs !== false;
@@ -1760,6 +1823,47 @@ var RaindropTelemetryIntegration = class {
1760
1823
  event.operationId,
1761
1824
  functionId
1762
1825
  );
1826
+ const parentToolContext = !isEmbed && this.subagentWrapping ? getCurrentParentToolContext() : void 0;
1827
+ const metadataSubagentName = !isEmbed && this.subagentWrapping && parentToolContext === void 0 ? typeof (metadata == null ? void 0 : metadata["ash.subagent.name"]) === "string" ? metadata["ash.subagent.name"] : void 0 : void 0;
1828
+ const subagentName = (_e = parentToolContext == null ? void 0 : parentToolContext.toolName) != null ? _e : metadataSubagentName;
1829
+ let subagentToolCallSpan;
1830
+ let rootParentOverride;
1831
+ if (subagentName && this.sendTraces) {
1832
+ const parentAttrs = parentToolContext ? [
1833
+ attrString("raindrop.parent.callId", parentToolContext.parentCallId),
1834
+ attrString("raindrop.parent.toolCallId", parentToolContext.toolCallId),
1835
+ attrString("raindrop.parent.toolName", parentToolContext.toolName)
1836
+ ] : [];
1837
+ subagentToolCallSpan = this.traceShipper.startSpan({
1838
+ name: subagentName,
1839
+ parent: inheritedParent,
1840
+ eventId,
1841
+ operationId: "ai.toolCall",
1842
+ attributes: [
1843
+ attrString("operation.name", "ai.toolCall"),
1844
+ attrString("resource.name", subagentName),
1845
+ attrString("raindrop.span.kind", "tool_call"),
1846
+ attrString("ai.toolCall.name", subagentName),
1847
+ attrString("raindrop.subagent.name", subagentName),
1848
+ attrString("raindrop.agent.role", "subagent"),
1849
+ ...parentAttrs
1850
+ ]
1851
+ });
1852
+ const subagentParentRef = this.spanParentRef(subagentToolCallSpan);
1853
+ const markerSpan = this.traceShipper.startSpan({
1854
+ name: "agent.subagent",
1855
+ parent: subagentParentRef,
1856
+ eventId,
1857
+ operationId: "agent.subagent",
1858
+ attributes: [
1859
+ attrString("operation.name", "agent.subagent"),
1860
+ attrString("raindrop.span.kind", "llm_call"),
1861
+ attrString("raindrop.subagent.name", subagentName)
1862
+ ]
1863
+ });
1864
+ this.traceShipper.endSpan(markerSpan);
1865
+ rootParentOverride = subagentParentRef;
1866
+ }
1763
1867
  let rootSpan;
1764
1868
  if (this.sendTraces) {
1765
1869
  const promptAttrs = !isEmbed && recordInputs ? [
@@ -1780,7 +1884,7 @@ var RaindropTelemetryIntegration = class {
1780
1884
  ] : [attrString("ai.value", safeJsonWithUint8(event.value))] : [];
1781
1885
  rootSpan = this.traceShipper.startSpan({
1782
1886
  name: event.operationId,
1783
- parent: inheritedParent,
1887
+ parent: rootParentOverride != null ? rootParentOverride : inheritedParent,
1784
1888
  eventId,
1785
1889
  operationId: event.operationId,
1786
1890
  attributes: [
@@ -1808,18 +1912,21 @@ var RaindropTelemetryIntegration = class {
1808
1912
  operationId: event.operationId,
1809
1913
  eventId,
1810
1914
  rootSpan,
1811
- rootParent: rootSpan ? this.spanParentRef(rootSpan) : inheritedParent,
1915
+ rootParent: rootSpan ? this.spanParentRef(rootSpan) : rootParentOverride != null ? rootParentOverride : inheritedParent,
1812
1916
  stepSpan: void 0,
1813
1917
  stepParent: void 0,
1814
1918
  toolSpans: /* @__PURE__ */ new Map(),
1815
1919
  embedSpans: /* @__PURE__ */ new Map(),
1920
+ parentContextToolCallIds: /* @__PURE__ */ new Set(),
1816
1921
  recordInputs,
1817
1922
  recordOutputs,
1818
1923
  functionId,
1819
1924
  metadata,
1820
1925
  accumulatedText: "",
1821
1926
  inputText: isEmbed ? void 0 : this.extractInputText(event),
1822
- toolCallCount: 0
1927
+ toolCallCount: 0,
1928
+ subagentName,
1929
+ subagentToolCallSpan
1823
1930
  });
1824
1931
  };
1825
1932
  // ── onStepStart ─────────────────────────────────────────────────────────
@@ -2043,6 +2150,9 @@ var RaindropTelemetryIntegration = class {
2043
2150
  } else {
2044
2151
  this.finishGenerate(event, state);
2045
2152
  }
2153
+ if (state.subagentToolCallSpan) {
2154
+ this.traceShipper.endSpan(state.subagentToolCallSpan);
2155
+ }
2046
2156
  this.cleanup(event.callId);
2047
2157
  };
2048
2158
  // ── onError ─────────────────────────────────────────────────────────────
@@ -2060,6 +2170,10 @@ var RaindropTelemetryIntegration = class {
2060
2170
  this.traceShipper.endSpan(embedSpan, { error: actualError });
2061
2171
  }
2062
2172
  state.embedSpans.clear();
2173
+ for (const toolCallId of state.parentContextToolCallIds) {
2174
+ this.priorParentContexts.delete(toolCallId);
2175
+ }
2176
+ state.parentContextToolCallIds.clear();
2063
2177
  for (const toolSpan of state.toolSpans.values()) {
2064
2178
  this.traceShipper.endSpan(toolSpan, { error: actualError });
2065
2179
  }
@@ -2067,6 +2181,9 @@ var RaindropTelemetryIntegration = class {
2067
2181
  if (state.rootSpan) {
2068
2182
  this.traceShipper.endSpan(state.rootSpan, { error: actualError });
2069
2183
  }
2184
+ if (state.subagentToolCallSpan) {
2185
+ this.traceShipper.endSpan(state.subagentToolCallSpan, { error: actualError });
2186
+ }
2070
2187
  this.cleanup(event.callId);
2071
2188
  };
2072
2189
  // ── executeTool ─────────────────────────────────────────────────────────
@@ -2091,6 +2208,7 @@ var RaindropTelemetryIntegration = class {
2091
2208
  this.eventShipper = opts.eventShipper;
2092
2209
  this.sendTraces = opts.sendTraces !== false;
2093
2210
  this.sendEvents = opts.sendEvents !== false;
2211
+ this.subagentWrapping = opts.subagentWrapping !== false;
2094
2212
  this.debug = opts.debug === true;
2095
2213
  this.defaultContext = opts.context;
2096
2214
  }
@@ -2172,9 +2290,20 @@ var RaindropTelemetryIntegration = class {
2172
2290
  // (see https://github.com/vercel/ai/pull/14589). Event shape is identical.
2173
2291
  // Both names are exposed and forward to a single implementation.
2174
2292
  toolExecutionStart(event) {
2293
+ const { toolCall } = event;
2294
+ const priorParent = getCurrentParentToolContext();
2295
+ this.priorParentContexts.set(
2296
+ toolCall.toolCallId,
2297
+ priorParent != null ? priorParent : null
2298
+ );
2299
+ enterParentToolContext({
2300
+ parentCallId: event.callId,
2301
+ toolCallId: toolCall.toolCallId,
2302
+ toolName: toolCall.toolName
2303
+ });
2175
2304
  const state = this.getState(event.callId);
2305
+ state == null ? void 0 : state.parentContextToolCallIds.add(toolCall.toolCallId);
2176
2306
  if (!(state == null ? void 0 : state.stepParent)) return;
2177
- const { toolCall } = event;
2178
2307
  const { operationName, resourceName } = opName(
2179
2308
  "ai.toolCall",
2180
2309
  state.functionId
@@ -2198,6 +2327,17 @@ var RaindropTelemetryIntegration = class {
2198
2327
  this.emitLive(state, "tool_start", toolCall.toolName, { args: toolCall.input });
2199
2328
  }
2200
2329
  toolExecutionEnd(event) {
2330
+ var _a;
2331
+ const toolCallId = (_a = event.toolCall) == null ? void 0 : _a.toolCallId;
2332
+ if (toolCallId && this.priorParentContexts.has(toolCallId)) {
2333
+ const prior = this.priorParentContexts.get(toolCallId);
2334
+ this.priorParentContexts.delete(toolCallId);
2335
+ if (prior) {
2336
+ enterParentToolContext(prior);
2337
+ } else {
2338
+ clearParentToolContext();
2339
+ }
2340
+ }
2201
2341
  const state = this.getState(event.callId);
2202
2342
  if (!state) return;
2203
2343
  const toolSpan = state.toolSpans.get(event.toolCall.toolCallId);
@@ -2261,7 +2401,8 @@ var RaindropTelemetryIntegration = class {
2261
2401
  );
2262
2402
  this.traceShipper.endSpan(state.rootSpan, { attributes: outputAttrs });
2263
2403
  }
2264
- if (this.sendEvents) {
2404
+ const suppressSubagentEvent = state.subagentName !== void 0 && state.subagentToolCallSpan !== void 0;
2405
+ if (this.sendEvents && !suppressSubagentEvent) {
2265
2406
  const callMeta = this.extractRaindropMetadata(state.metadata);
2266
2407
  const userId = (_f = callMeta.userId) != null ? _f : (_e = this.defaultContext) == null ? void 0 : _e.userId;
2267
2408
  if (userId) {
@@ -4423,12 +4564,16 @@ function createRaindropAISDK(opts) {
4423
4564
  const writeKey = opts.writeKey;
4424
4565
  const eventsRequested = ((_a = opts.events) == null ? void 0 : _a.enabled) !== false;
4425
4566
  const tracesRequested = ((_b = opts.traces) == null ? void 0 : _b.enabled) !== false;
4426
- const eventsEnabled = eventsRequested && !!writeKey;
4427
- const tracesEnabled = tracesRequested && !!writeKey;
4428
4567
  const envDebug = envDebugEnabled();
4429
- if (!writeKey && (eventsRequested || tracesRequested)) {
4568
+ const localWorkshopInput = opts.localWorkshopUrl === false ? null : opts.localWorkshopUrl;
4569
+ const resolvedLocalDebuggerUrl = resolveLocalDebuggerBaseUrl(localWorkshopInput);
4570
+ const localDebuggerUrl = localWorkshopInput === null ? null : resolvedLocalDebuggerUrl != null ? resolvedLocalDebuggerUrl : void 0;
4571
+ const hasDestination = !!writeKey || !!resolvedLocalDebuggerUrl;
4572
+ const eventsEnabled = eventsRequested && hasDestination;
4573
+ const tracesEnabled = tracesRequested && hasDestination;
4574
+ if (!hasDestination && (eventsRequested || tracesRequested)) {
4430
4575
  console.warn(
4431
- "[raindrop-ai/ai-sdk] writeKey not provided; telemetry shipping is disabled"
4576
+ "[raindrop-ai/ai-sdk] writeKey not provided and no local Workshop reachable; telemetry shipping is disabled"
4432
4577
  );
4433
4578
  }
4434
4579
  const eventShipper = new EventShipper2({
@@ -4436,9 +4581,9 @@ function createRaindropAISDK(opts) {
4436
4581
  endpoint: opts.endpoint,
4437
4582
  enabled: eventsEnabled,
4438
4583
  debug: ((_c = opts.events) == null ? void 0 : _c.debug) === true || envDebug,
4439
- partialFlushMs: (_d = opts.events) == null ? void 0 : _d.partialFlushMs
4584
+ partialFlushMs: (_d = opts.events) == null ? void 0 : _d.partialFlushMs,
4585
+ localDebuggerUrl
4440
4586
  });
4441
- const localDebuggerUrl = opts.localWorkshopUrl === false ? null : opts.localWorkshopUrl;
4442
4587
  const traceShipper = new TraceShipper2({
4443
4588
  writeKey,
4444
4589
  endpoint: opts.endpoint,
@@ -4460,12 +4605,30 @@ function createRaindropAISDK(opts) {
4460
4605
  traceShipper
4461
4606
  });
4462
4607
  },
4463
- createTelemetryIntegration(context) {
4608
+ createTelemetryIntegration(contextOrOptions) {
4609
+ const FLAT_CONTEXT_KEYS = [
4610
+ "userId",
4611
+ "eventId",
4612
+ "eventName",
4613
+ "convoId",
4614
+ "properties"
4615
+ ];
4616
+ let context;
4617
+ let subagentWrapping;
4618
+ if (contextOrOptions === void 0) ; else if ("context" in contextOrOptions) {
4619
+ context = contextOrOptions.context;
4620
+ subagentWrapping = contextOrOptions.subagentWrapping;
4621
+ } else if ("subagentWrapping" in contextOrOptions && !FLAT_CONTEXT_KEYS.some((k) => k in contextOrOptions)) {
4622
+ subagentWrapping = contextOrOptions.subagentWrapping;
4623
+ } else {
4624
+ context = contextOrOptions;
4625
+ }
4464
4626
  return new RaindropTelemetryIntegration({
4465
4627
  traceShipper,
4466
4628
  eventShipper,
4467
4629
  sendTraces: tracesEnabled,
4468
4630
  sendEvents: eventsEnabled,
4631
+ subagentWrapping,
4469
4632
  debug: envDebug,
4470
4633
  context
4471
4634
  });
@@ -4577,17 +4740,22 @@ exports.DEFAULT_REDACT_ATTRIBUTE_KEYS = DEFAULT_REDACT_ATTRIBUTE_KEYS;
4577
4740
  exports.DEFAULT_SECRET_KEY_NAMES = DEFAULT_SECRET_KEY_NAMES;
4578
4741
  exports.REDACTED_PLACEHOLDER = REDACTED_PLACEHOLDER;
4579
4742
  exports.RaindropTelemetryIntegration = RaindropTelemetryIntegration;
4743
+ exports._resetParentToolContextStorage = _resetParentToolContextStorage;
4580
4744
  exports._resetRaindropCallMetadataStorage = _resetRaindropCallMetadataStorage;
4581
4745
  exports._resetWarnedMissingUserId = _resetWarnedMissingUserId;
4746
+ exports.clearParentToolContext = clearParentToolContext;
4582
4747
  exports.createRaindropAISDK = createRaindropAISDK;
4583
4748
  exports.currentSpan = currentSpan;
4584
4749
  exports.defaultTransformSpan = defaultTransformSpan;
4750
+ exports.enterParentToolContext = enterParentToolContext;
4585
4751
  exports.eventMetadata = eventMetadata;
4586
4752
  exports.eventMetadataFromChatRequest = eventMetadataFromChatRequest;
4587
4753
  exports.getContextManager = getContextManager;
4754
+ exports.getCurrentParentToolContext = getCurrentParentToolContext;
4588
4755
  exports.getCurrentRaindropCallMetadata = getCurrentRaindropCallMetadata;
4589
4756
  exports.readRaindropCallMetadataFromArgs = readRaindropCallMetadataFromArgs;
4590
4757
  exports.redactJsonAttributeValue = redactJsonAttributeValue;
4591
4758
  exports.redactSecretsInObject = redactSecretsInObject;
4759
+ exports.runWithParentToolContext = runWithParentToolContext;
4592
4760
  exports.runWithRaindropCallMetadata = runWithRaindropCallMetadata;
4593
4761
  exports.withCurrent = withCurrent;