@anuma/sdk 6.39.1 → 6.39.3

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.
@@ -6702,6 +6702,93 @@ function composeHooks(hooks) {
6702
6702
  };
6703
6703
  }
6704
6704
 
6705
+ // src/lib/chat/streamIdleTimeout.ts
6706
+ var StreamIdleTimeoutError = class extends Error {
6707
+ constructor(idleTimeoutMs) {
6708
+ super(`Stream timed out after ${idleTimeoutMs} ms without activity.`);
6709
+ this.name = "StreamIdleTimeoutError";
6710
+ this.idleTimeoutMs = idleTimeoutMs;
6711
+ }
6712
+ };
6713
+ function withStreamIdleTimeout(transport, idleTimeoutMs) {
6714
+ if (!(idleTimeoutMs > 0) || idleTimeoutMs === Infinity) return transport;
6715
+ return (options) => ({
6716
+ stream: (async function* () {
6717
+ const controller = new AbortController();
6718
+ let timer;
6719
+ let settled = false;
6720
+ let completed = false;
6721
+ let iterator;
6722
+ let interruption;
6723
+ let rejectPending;
6724
+ const disarm = () => {
6725
+ if (timer !== void 0) clearTimeout(timer);
6726
+ timer = void 0;
6727
+ };
6728
+ const interrupt = (error) => {
6729
+ if (interruption) return;
6730
+ interruption = error;
6731
+ rejectPending?.(error);
6732
+ controller.abort();
6733
+ };
6734
+ const arm = () => {
6735
+ if (settled || controller.signal.aborted) return;
6736
+ disarm();
6737
+ timer = setTimeout(() => {
6738
+ interrupt(new StreamIdleTimeoutError(idleTimeoutMs));
6739
+ }, idleTimeoutMs);
6740
+ };
6741
+ const onAbort = () => {
6742
+ controller.abort();
6743
+ };
6744
+ options.signal?.addEventListener("abort", onAbort, { once: true });
6745
+ try {
6746
+ if (options.signal?.aborted) {
6747
+ const error = new Error("Request aborted");
6748
+ error.name = "AbortError";
6749
+ throw error;
6750
+ }
6751
+ arm();
6752
+ iterator = transport({
6753
+ ...options,
6754
+ signal: controller.signal,
6755
+ onActivity: () => {
6756
+ arm();
6757
+ options.onActivity?.();
6758
+ }
6759
+ }).stream[Symbol.asyncIterator]();
6760
+ while (true) {
6761
+ if (interruption) throw interruption;
6762
+ const next = await new Promise((resolve, reject) => {
6763
+ rejectPending = reject;
6764
+ Promise.resolve(iterator.next()).then(resolve, reject);
6765
+ });
6766
+ rejectPending = void 0;
6767
+ if (next.done) {
6768
+ completed = true;
6769
+ break;
6770
+ }
6771
+ arm();
6772
+ yield next.value;
6773
+ }
6774
+ } finally {
6775
+ settled = true;
6776
+ rejectPending = void 0;
6777
+ disarm();
6778
+ options.signal?.removeEventListener("abort", onAbort);
6779
+ if (!completed) {
6780
+ controller.abort();
6781
+ try {
6782
+ void iterator?.return?.().catch(() => {
6783
+ });
6784
+ } catch {
6785
+ }
6786
+ }
6787
+ }
6788
+ })()
6789
+ });
6790
+ }
6791
+
6705
6792
  // src/lib/chat/useChat/utils.ts
6706
6793
  var import_jsonrepair = require("jsonrepair");
6707
6794
  init_logger();
@@ -8892,7 +8979,20 @@ var defaultTransport = (options) => {
8892
8979
  } catch {
8893
8980
  }
8894
8981
  }
8895
- return response;
8982
+ if (!response.body || !options.onActivity) return response;
8983
+ const body = response.body.pipeThrough(
8984
+ new TransformStream({
8985
+ transform(chunk, controller) {
8986
+ if (chunk.byteLength > 0) options.onActivity?.();
8987
+ controller.enqueue(chunk);
8988
+ }
8989
+ })
8990
+ );
8991
+ return new Response(body, {
8992
+ status: response.status,
8993
+ statusText: response.statusText,
8994
+ headers: response.headers
8995
+ });
8896
8996
  };
8897
8997
  return createSseClient({
8898
8998
  method: options.method ?? "POST",
@@ -8949,6 +9049,7 @@ async function runToolLoop(options) {
8949
9049
  conversationId,
8950
9050
  smoothing,
8951
9051
  signal,
9052
+ idleTimeoutMs = 12e4,
8952
9053
  resumable,
8953
9054
  detachSignal,
8954
9055
  onStreamMeta,
@@ -8962,13 +9063,14 @@ async function runToolLoop(options) {
8962
9063
  onStepFinish,
8963
9064
  onRequest,
8964
9065
  onStreamRetry,
8965
- transport: makeStreamingRequest = defaultTransport,
9066
+ transport = defaultTransport,
8966
9067
  preProcessors,
8967
9068
  maxConnectorCalls = 2,
8968
9069
  hooks: hooksOption,
8969
9070
  piiRedaction,
8970
9071
  onPiiRedacted
8971
9072
  } = options;
9073
+ const makeStreamingRequest = withStreamIdleTimeout(transport, idleTimeoutMs);
8972
9074
  const hooks = Array.isArray(hooksOption) ? composeHooks(hooksOption) : hooksOption;
8973
9075
  const runId = generateRunId();
8974
9076
  let messages = options.messages;
@@ -24607,6 +24709,7 @@ function useChat(options) {
24607
24709
  apiType: defaultApiType = "auto",
24608
24710
  smoothing,
24609
24711
  preProcessors,
24712
+ hooks,
24610
24713
  piiRedaction,
24611
24714
  onPiiRedacted,
24612
24715
  resumable = false,
@@ -24860,6 +24963,7 @@ function useChat(options) {
24860
24963
  onToolCallArgumentsDelta,
24861
24964
  onStepFinish,
24862
24965
  preProcessors,
24966
+ hooks,
24863
24967
  piiRedaction: requestPiiRedaction ?? resolvedPiiRedaction,
24864
24968
  onPiiRedacted
24865
24969
  });
@@ -24899,6 +25003,7 @@ function useChat(options) {
24899
25003
  defaultApiType,
24900
25004
  smoothing,
24901
25005
  preProcessors,
25006
+ hooks,
24902
25007
  resolvedPiiRedaction,
24903
25008
  onPiiRedacted,
24904
25009
  resumable,
@@ -25869,6 +25974,7 @@ function useChatStorage(options) {
25869
25974
  embeddingModel = DEFAULT_API_EMBEDDING_MODEL,
25870
25975
  minContentLength = DEFAULT_MIN_CONTENT_LENGTH,
25871
25976
  preProcessors,
25977
+ hooks,
25872
25978
  smoothing,
25873
25979
  resumable = false,
25874
25980
  onCancelResult,
@@ -26328,6 +26434,7 @@ function useChatStorage(options) {
26328
26434
  onError,
26329
26435
  apiType,
26330
26436
  preProcessors,
26437
+ hooks,
26331
26438
  smoothing,
26332
26439
  resumable,
26333
26440
  onCancelResult,
@@ -2002,6 +2002,127 @@ declare function createStreamingDeAnonymizer(redactor: PiiRedactor, emit: (chunk
2002
2002
  flush: () => void;
2003
2003
  };
2004
2004
 
2005
+ type LlmTokenUsage = {
2006
+ inputTokens?: number;
2007
+ outputTokens?: number;
2008
+ };
2009
+ type ModelCallStartEvent = {
2010
+ runId: string;
2011
+ stepIndex: number;
2012
+ model: string;
2013
+ messages: Array<LlmapiMessage>;
2014
+ tools: Array<LlmapiChatCompletionTool>;
2015
+ requestBody: Record<string, unknown>;
2016
+ };
2017
+ type ModelCallEndEvent = {
2018
+ runId: string;
2019
+ stepIndex: number;
2020
+ content: string;
2021
+ toolCalls: Array<{
2022
+ id: string;
2023
+ name: string;
2024
+ arguments: string;
2025
+ }>;
2026
+ usage?: LlmTokenUsage;
2027
+ finishReason?: string;
2028
+ error?: string;
2029
+ };
2030
+ type ToolUseStartEvent = {
2031
+ runId: string;
2032
+ stepIndex: number;
2033
+ toolCallId: string;
2034
+ name: string;
2035
+ rawArguments: string;
2036
+ parsedArguments?: Record<string, unknown>;
2037
+ };
2038
+ type ToolUseEndEvent = {
2039
+ runId: string;
2040
+ stepIndex: number;
2041
+ toolCallId: string;
2042
+ name: string;
2043
+ result?: unknown;
2044
+ error?: string;
2045
+ errorType?: "parse" | "timeout" | "execution" | "cancelled";
2046
+ };
2047
+ type RunStartEvent = {
2048
+ runId: string;
2049
+ model: string;
2050
+ messages: Array<LlmapiMessage>;
2051
+ tools: Array<LlmapiChatCompletionTool>;
2052
+ };
2053
+ type RunEndEvent = {
2054
+ runId: string;
2055
+ finalContent: string;
2056
+ /** Number of LLM rounds executed. */
2057
+ totalSteps: number;
2058
+ };
2059
+ type RunErrorEvent = {
2060
+ runId: string;
2061
+ error: string;
2062
+ /**
2063
+ * Only `"model"` today. Tool failures surface on {@link RunHooks.afterToolUse}
2064
+ * with `errorType`, not on `onRunError`, so a tool stage isn't needed yet.
2065
+ * Left as a union to leave room for `"validation"` or `"tool"` later
2066
+ * without a breaking change.
2067
+ */
2068
+ stage: "model";
2069
+ /**
2070
+ * The underlying `Error` when one is available. `RunErrorEvent` predates
2071
+ * this field, so `error` (a string message) is kept for backwards
2072
+ * compatibility; new consumers should prefer `errorObject` when present
2073
+ * for stack traces and `cause`.
2074
+ */
2075
+ errorObject?: Error;
2076
+ };
2077
+ type RunHooks = {
2078
+ /**
2079
+ * Fires once per `runToolLoop` invocation, before any pre-processor or
2080
+ * model call. The `messages` payload is the raw caller-supplied
2081
+ * conversation — pre-processors that enrich it (e.g. web search context)
2082
+ * run afterwards, so `beforeModelCall.messages` reflects the
2083
+ * post-enrichment state.
2084
+ */
2085
+ onRunStart?: (e: RunStartEvent) => Promise<void> | void;
2086
+ /**
2087
+ * Fires exactly once per run on clean completion. Mutually exclusive
2088
+ * with `onRunError` — a run that completes fires `onRunEnd`; a run that
2089
+ * errors or is aborted fires `onRunError`. Consumers can rely on
2090
+ * exactly one terminal hook per `onRunStart`.
2091
+ */
2092
+ onRunEnd?: (e: RunEndEvent) => Promise<void> | void;
2093
+ /**
2094
+ * Fires exactly once per run when the loop errors or is aborted.
2095
+ * `stage` is `"model"` for stream/provider errors and for aborts;
2096
+ * tool execution failures surface as structured `{ error, errorType }`
2097
+ * results on `afterToolUse` rather than firing `onRunError`.
2098
+ * Mutually exclusive with `onRunEnd`.
2099
+ */
2100
+ onRunError?: (e: RunErrorEvent) => Promise<void> | void;
2101
+ beforeModelCall?: (e: ModelCallStartEvent) => Promise<void> | void;
2102
+ afterModelCall?: (e: ModelCallEndEvent) => Promise<void> | void;
2103
+ /**
2104
+ * Fires before each tool the model invoked — including server-side
2105
+ * tools that have no client executor. Today this is observe-only.
2106
+ * TODO(follow-up PR): allow returning { args?, abort?: { reason } } to
2107
+ * mutate arguments or short-circuit the tool call.
2108
+ *
2109
+ * Pairing with {@link afterToolUse} is asymmetric: server-side tools
2110
+ * routed via `onToolCall` get `beforeToolUse` but no `afterToolUse`,
2111
+ * because they don't execute in-process. Tools blocked by a failed
2112
+ * dependency or a dependency cycle DO get `afterToolUse` with
2113
+ * `errorType: "execution"` — consumers tallying matched pairs should
2114
+ * filter on executor-backed tools to avoid spurious imbalances.
2115
+ */
2116
+ beforeToolUse?: (e: ToolUseStartEvent) => Promise<void> | void;
2117
+ /**
2118
+ * Fires after each executor-backed tool finishes (success or failure)
2119
+ * and after each dependency-blocked tool is skipped (`errorType:
2120
+ * "execution"`). Does NOT fire for server-side tools routed via
2121
+ * `onToolCall`. See {@link beforeToolUse} for the pairing contract.
2122
+ */
2123
+ afterToolUse?: (e: ToolUseEndEvent) => Promise<void> | void;
2124
+ };
2125
+
2005
2126
  /**
2006
2127
  * Configuration for stream output smoothing.
2007
2128
  *
@@ -2196,6 +2317,8 @@ type BaseSendMessageArgs = ResponsesApiOptions & {
2196
2317
  type BaseUseChatOptions = {
2197
2318
  getToken?: () => Promise<string | null>;
2198
2319
  baseUrl?: string;
2320
+ /** Run lifecycle hooks for telemetry and tracing, for example from `createMetricsHooks` */
2321
+ hooks?: RunHooks | RunHooks[];
2199
2322
  /**
2200
2323
  * Callback function to be called when a new data chunk is received.
2201
2324
  */
@@ -2469,12 +2592,10 @@ type StreamingTransportOptions = {
2469
2592
  inferenceId: string;
2470
2593
  }) => void;
2471
2594
  /**
2472
- * Fires whenever bytes arrive on the wire — data frames AND the keep-alive
2473
- * comment lines (`: ...`) the SSE parser otherwise discards. A pure liveness
2474
- * signal: a consumer running an idle watchdog uses it to tell a slow-but-alive
2475
- * stream (the server still heart-beating through a long reasoning silence)
2476
- * apart from a dead connection (no bytes at all). Not every transport emits it
2477
- * — the xhr transport does; treat its absence as "no extra liveness info".
2595
+ * Fires when the transport receives bytes, including SSE keep-alive comments.
2596
+ * Consumers can use this callback to reset the idle timeout.
2597
+ * The fetch and XHR transports emit it.
2598
+ * Custom transports can omit it if they cannot report byte activity.
2478
2599
  */
2479
2600
  onActivity?: () => void;
2480
2601
  };
@@ -7135,6 +7256,8 @@ interface CreateConversationOptions {
7135
7256
  * @inline
7136
7257
  */
7137
7258
  interface BaseUseChatStorageOptions {
7259
+ /** Run lifecycle hooks for telemetry and tracing, for example from `createMetricsHooks` */
7260
+ hooks?: RunHooks | RunHooks[];
7138
7261
  /** WatermelonDB database instance for storing conversations and messages */
7139
7262
  database: Database;
7140
7263
  /** Route memory reads, writes, recall and the memory tools through this store instead of the database's vault; folders are unsupported. With `piiRedaction` on, build the store with `embeddingOptions.maskInput` (e.g. `(text) => redactor.maskText(text)` with any `PiiRedactor`) or its memory calls throw. */
@@ -2002,6 +2002,127 @@ declare function createStreamingDeAnonymizer(redactor: PiiRedactor, emit: (chunk
2002
2002
  flush: () => void;
2003
2003
  };
2004
2004
 
2005
+ type LlmTokenUsage = {
2006
+ inputTokens?: number;
2007
+ outputTokens?: number;
2008
+ };
2009
+ type ModelCallStartEvent = {
2010
+ runId: string;
2011
+ stepIndex: number;
2012
+ model: string;
2013
+ messages: Array<LlmapiMessage>;
2014
+ tools: Array<LlmapiChatCompletionTool>;
2015
+ requestBody: Record<string, unknown>;
2016
+ };
2017
+ type ModelCallEndEvent = {
2018
+ runId: string;
2019
+ stepIndex: number;
2020
+ content: string;
2021
+ toolCalls: Array<{
2022
+ id: string;
2023
+ name: string;
2024
+ arguments: string;
2025
+ }>;
2026
+ usage?: LlmTokenUsage;
2027
+ finishReason?: string;
2028
+ error?: string;
2029
+ };
2030
+ type ToolUseStartEvent = {
2031
+ runId: string;
2032
+ stepIndex: number;
2033
+ toolCallId: string;
2034
+ name: string;
2035
+ rawArguments: string;
2036
+ parsedArguments?: Record<string, unknown>;
2037
+ };
2038
+ type ToolUseEndEvent = {
2039
+ runId: string;
2040
+ stepIndex: number;
2041
+ toolCallId: string;
2042
+ name: string;
2043
+ result?: unknown;
2044
+ error?: string;
2045
+ errorType?: "parse" | "timeout" | "execution" | "cancelled";
2046
+ };
2047
+ type RunStartEvent = {
2048
+ runId: string;
2049
+ model: string;
2050
+ messages: Array<LlmapiMessage>;
2051
+ tools: Array<LlmapiChatCompletionTool>;
2052
+ };
2053
+ type RunEndEvent = {
2054
+ runId: string;
2055
+ finalContent: string;
2056
+ /** Number of LLM rounds executed. */
2057
+ totalSteps: number;
2058
+ };
2059
+ type RunErrorEvent = {
2060
+ runId: string;
2061
+ error: string;
2062
+ /**
2063
+ * Only `"model"` today. Tool failures surface on {@link RunHooks.afterToolUse}
2064
+ * with `errorType`, not on `onRunError`, so a tool stage isn't needed yet.
2065
+ * Left as a union to leave room for `"validation"` or `"tool"` later
2066
+ * without a breaking change.
2067
+ */
2068
+ stage: "model";
2069
+ /**
2070
+ * The underlying `Error` when one is available. `RunErrorEvent` predates
2071
+ * this field, so `error` (a string message) is kept for backwards
2072
+ * compatibility; new consumers should prefer `errorObject` when present
2073
+ * for stack traces and `cause`.
2074
+ */
2075
+ errorObject?: Error;
2076
+ };
2077
+ type RunHooks = {
2078
+ /**
2079
+ * Fires once per `runToolLoop` invocation, before any pre-processor or
2080
+ * model call. The `messages` payload is the raw caller-supplied
2081
+ * conversation — pre-processors that enrich it (e.g. web search context)
2082
+ * run afterwards, so `beforeModelCall.messages` reflects the
2083
+ * post-enrichment state.
2084
+ */
2085
+ onRunStart?: (e: RunStartEvent) => Promise<void> | void;
2086
+ /**
2087
+ * Fires exactly once per run on clean completion. Mutually exclusive
2088
+ * with `onRunError` — a run that completes fires `onRunEnd`; a run that
2089
+ * errors or is aborted fires `onRunError`. Consumers can rely on
2090
+ * exactly one terminal hook per `onRunStart`.
2091
+ */
2092
+ onRunEnd?: (e: RunEndEvent) => Promise<void> | void;
2093
+ /**
2094
+ * Fires exactly once per run when the loop errors or is aborted.
2095
+ * `stage` is `"model"` for stream/provider errors and for aborts;
2096
+ * tool execution failures surface as structured `{ error, errorType }`
2097
+ * results on `afterToolUse` rather than firing `onRunError`.
2098
+ * Mutually exclusive with `onRunEnd`.
2099
+ */
2100
+ onRunError?: (e: RunErrorEvent) => Promise<void> | void;
2101
+ beforeModelCall?: (e: ModelCallStartEvent) => Promise<void> | void;
2102
+ afterModelCall?: (e: ModelCallEndEvent) => Promise<void> | void;
2103
+ /**
2104
+ * Fires before each tool the model invoked — including server-side
2105
+ * tools that have no client executor. Today this is observe-only.
2106
+ * TODO(follow-up PR): allow returning { args?, abort?: { reason } } to
2107
+ * mutate arguments or short-circuit the tool call.
2108
+ *
2109
+ * Pairing with {@link afterToolUse} is asymmetric: server-side tools
2110
+ * routed via `onToolCall` get `beforeToolUse` but no `afterToolUse`,
2111
+ * because they don't execute in-process. Tools blocked by a failed
2112
+ * dependency or a dependency cycle DO get `afterToolUse` with
2113
+ * `errorType: "execution"` — consumers tallying matched pairs should
2114
+ * filter on executor-backed tools to avoid spurious imbalances.
2115
+ */
2116
+ beforeToolUse?: (e: ToolUseStartEvent) => Promise<void> | void;
2117
+ /**
2118
+ * Fires after each executor-backed tool finishes (success or failure)
2119
+ * and after each dependency-blocked tool is skipped (`errorType:
2120
+ * "execution"`). Does NOT fire for server-side tools routed via
2121
+ * `onToolCall`. See {@link beforeToolUse} for the pairing contract.
2122
+ */
2123
+ afterToolUse?: (e: ToolUseEndEvent) => Promise<void> | void;
2124
+ };
2125
+
2005
2126
  /**
2006
2127
  * Configuration for stream output smoothing.
2007
2128
  *
@@ -2196,6 +2317,8 @@ type BaseSendMessageArgs = ResponsesApiOptions & {
2196
2317
  type BaseUseChatOptions = {
2197
2318
  getToken?: () => Promise<string | null>;
2198
2319
  baseUrl?: string;
2320
+ /** Run lifecycle hooks for telemetry and tracing, for example from `createMetricsHooks` */
2321
+ hooks?: RunHooks | RunHooks[];
2199
2322
  /**
2200
2323
  * Callback function to be called when a new data chunk is received.
2201
2324
  */
@@ -2469,12 +2592,10 @@ type StreamingTransportOptions = {
2469
2592
  inferenceId: string;
2470
2593
  }) => void;
2471
2594
  /**
2472
- * Fires whenever bytes arrive on the wire — data frames AND the keep-alive
2473
- * comment lines (`: ...`) the SSE parser otherwise discards. A pure liveness
2474
- * signal: a consumer running an idle watchdog uses it to tell a slow-but-alive
2475
- * stream (the server still heart-beating through a long reasoning silence)
2476
- * apart from a dead connection (no bytes at all). Not every transport emits it
2477
- * — the xhr transport does; treat its absence as "no extra liveness info".
2595
+ * Fires when the transport receives bytes, including SSE keep-alive comments.
2596
+ * Consumers can use this callback to reset the idle timeout.
2597
+ * The fetch and XHR transports emit it.
2598
+ * Custom transports can omit it if they cannot report byte activity.
2478
2599
  */
2479
2600
  onActivity?: () => void;
2480
2601
  };
@@ -7135,6 +7256,8 @@ interface CreateConversationOptions {
7135
7256
  * @inline
7136
7257
  */
7137
7258
  interface BaseUseChatStorageOptions {
7259
+ /** Run lifecycle hooks for telemetry and tracing, for example from `createMetricsHooks` */
7260
+ hooks?: RunHooks | RunHooks[];
7138
7261
  /** WatermelonDB database instance for storing conversations and messages */
7139
7262
  database: Database;
7140
7263
  /** Route memory reads, writes, recall and the memory tools through this store instead of the database's vault; folders are unsupported. With `piiRedaction` on, build the store with `embeddingOptions.maskInput` (e.g. `(text) => redactor.maskText(text)` with any `PiiRedactor`) or its memory calls throw. */
@@ -5115,6 +5115,93 @@ function composeHooks(hooks) {
5115
5115
  };
5116
5116
  }
5117
5117
 
5118
+ // src/lib/chat/streamIdleTimeout.ts
5119
+ var StreamIdleTimeoutError = class extends Error {
5120
+ constructor(idleTimeoutMs) {
5121
+ super(`Stream timed out after ${idleTimeoutMs} ms without activity.`);
5122
+ this.name = "StreamIdleTimeoutError";
5123
+ this.idleTimeoutMs = idleTimeoutMs;
5124
+ }
5125
+ };
5126
+ function withStreamIdleTimeout(transport, idleTimeoutMs) {
5127
+ if (!(idleTimeoutMs > 0) || idleTimeoutMs === Infinity) return transport;
5128
+ return (options) => ({
5129
+ stream: (async function* () {
5130
+ const controller = new AbortController();
5131
+ let timer;
5132
+ let settled = false;
5133
+ let completed = false;
5134
+ let iterator;
5135
+ let interruption;
5136
+ let rejectPending;
5137
+ const disarm = () => {
5138
+ if (timer !== void 0) clearTimeout(timer);
5139
+ timer = void 0;
5140
+ };
5141
+ const interrupt = (error) => {
5142
+ if (interruption) return;
5143
+ interruption = error;
5144
+ rejectPending?.(error);
5145
+ controller.abort();
5146
+ };
5147
+ const arm = () => {
5148
+ if (settled || controller.signal.aborted) return;
5149
+ disarm();
5150
+ timer = setTimeout(() => {
5151
+ interrupt(new StreamIdleTimeoutError(idleTimeoutMs));
5152
+ }, idleTimeoutMs);
5153
+ };
5154
+ const onAbort = () => {
5155
+ controller.abort();
5156
+ };
5157
+ options.signal?.addEventListener("abort", onAbort, { once: true });
5158
+ try {
5159
+ if (options.signal?.aborted) {
5160
+ const error = new Error("Request aborted");
5161
+ error.name = "AbortError";
5162
+ throw error;
5163
+ }
5164
+ arm();
5165
+ iterator = transport({
5166
+ ...options,
5167
+ signal: controller.signal,
5168
+ onActivity: () => {
5169
+ arm();
5170
+ options.onActivity?.();
5171
+ }
5172
+ }).stream[Symbol.asyncIterator]();
5173
+ while (true) {
5174
+ if (interruption) throw interruption;
5175
+ const next = await new Promise((resolve, reject) => {
5176
+ rejectPending = reject;
5177
+ Promise.resolve(iterator.next()).then(resolve, reject);
5178
+ });
5179
+ rejectPending = void 0;
5180
+ if (next.done) {
5181
+ completed = true;
5182
+ break;
5183
+ }
5184
+ arm();
5185
+ yield next.value;
5186
+ }
5187
+ } finally {
5188
+ settled = true;
5189
+ rejectPending = void 0;
5190
+ disarm();
5191
+ options.signal?.removeEventListener("abort", onAbort);
5192
+ if (!completed) {
5193
+ controller.abort();
5194
+ try {
5195
+ void iterator?.return?.().catch(() => {
5196
+ });
5197
+ } catch {
5198
+ }
5199
+ }
5200
+ }
5201
+ })()
5202
+ });
5203
+ }
5204
+
5118
5205
  // src/lib/chat/useChat/utils.ts
5119
5206
  import { jsonrepair } from "jsonrepair";
5120
5207
  function parseToolArguments(raw) {
@@ -7304,7 +7391,20 @@ var defaultTransport = (options) => {
7304
7391
  } catch {
7305
7392
  }
7306
7393
  }
7307
- return response;
7394
+ if (!response.body || !options.onActivity) return response;
7395
+ const body = response.body.pipeThrough(
7396
+ new TransformStream({
7397
+ transform(chunk, controller) {
7398
+ if (chunk.byteLength > 0) options.onActivity?.();
7399
+ controller.enqueue(chunk);
7400
+ }
7401
+ })
7402
+ );
7403
+ return new Response(body, {
7404
+ status: response.status,
7405
+ statusText: response.statusText,
7406
+ headers: response.headers
7407
+ });
7308
7408
  };
7309
7409
  return createSseClient({
7310
7410
  method: options.method ?? "POST",
@@ -7361,6 +7461,7 @@ async function runToolLoop(options) {
7361
7461
  conversationId,
7362
7462
  smoothing,
7363
7463
  signal,
7464
+ idleTimeoutMs = 12e4,
7364
7465
  resumable,
7365
7466
  detachSignal,
7366
7467
  onStreamMeta,
@@ -7374,13 +7475,14 @@ async function runToolLoop(options) {
7374
7475
  onStepFinish,
7375
7476
  onRequest,
7376
7477
  onStreamRetry,
7377
- transport: makeStreamingRequest = defaultTransport,
7478
+ transport = defaultTransport,
7378
7479
  preProcessors,
7379
7480
  maxConnectorCalls = 2,
7380
7481
  hooks: hooksOption,
7381
7482
  piiRedaction,
7382
7483
  onPiiRedacted
7383
7484
  } = options;
7485
+ const makeStreamingRequest = withStreamIdleTimeout(transport, idleTimeoutMs);
7384
7486
  const hooks = Array.isArray(hooksOption) ? composeHooks(hooksOption) : hooksOption;
7385
7487
  const runId = generateRunId();
7386
7488
  let messages = options.messages;
@@ -22960,6 +23062,7 @@ function useChat(options) {
22960
23062
  apiType: defaultApiType = "auto",
22961
23063
  smoothing,
22962
23064
  preProcessors,
23065
+ hooks,
22963
23066
  piiRedaction,
22964
23067
  onPiiRedacted,
22965
23068
  resumable = false,
@@ -23213,6 +23316,7 @@ function useChat(options) {
23213
23316
  onToolCallArgumentsDelta,
23214
23317
  onStepFinish,
23215
23318
  preProcessors,
23319
+ hooks,
23216
23320
  piiRedaction: requestPiiRedaction ?? resolvedPiiRedaction,
23217
23321
  onPiiRedacted
23218
23322
  });
@@ -23252,6 +23356,7 @@ function useChat(options) {
23252
23356
  defaultApiType,
23253
23357
  smoothing,
23254
23358
  preProcessors,
23359
+ hooks,
23255
23360
  resolvedPiiRedaction,
23256
23361
  onPiiRedacted,
23257
23362
  resumable,
@@ -24212,6 +24317,7 @@ function useChatStorage(options) {
24212
24317
  embeddingModel = DEFAULT_API_EMBEDDING_MODEL,
24213
24318
  minContentLength = DEFAULT_MIN_CONTENT_LENGTH,
24214
24319
  preProcessors,
24320
+ hooks,
24215
24321
  smoothing,
24216
24322
  resumable = false,
24217
24323
  onCancelResult,
@@ -24671,6 +24777,7 @@ function useChatStorage(options) {
24671
24777
  onError,
24672
24778
  apiType,
24673
24779
  preProcessors,
24780
+ hooks,
24674
24781
  smoothing,
24675
24782
  resumable,
24676
24783
  onCancelResult,