@mastra/ai-sdk 1.7.2 → 1.8.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @mastra/ai-sdk
2
2
 
3
+ ## 1.8.0-alpha.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added optional SSE heartbeats to `chatRoute()` so streams can remain active through infrastructure with idle timeouts. ([#20852](https://github.com/mastra-ai/mastra/pull/20852))
8
+
9
+ ```typescript
10
+ chatRoute({
11
+ path: '/chat/:agentId',
12
+ heartbeatMs: 15_000,
13
+ });
14
+ ```
15
+
16
+ - Fixed nested agent streams to emit compact snapshots until each step completes, and added `data-tool-agent-step` as a new stream part type (exported as `AgentStepDataPart`) that carries the full completed-step detail for consumers that observe suspension without a finish event. ([#20576](https://github.com/mastra-ai/mastra/pull/20576))
17
+
18
+ ### Patch Changes
19
+
20
+ - Updated dependencies [[`e7109ee`](https://github.com/mastra-ai/mastra/commit/e7109ee6f731bacc79c885906f3c7dca8d8f013a), [`772c0c8`](https://github.com/mastra-ai/mastra/commit/772c0c897cec383258de2e6178147f8014767c7b), [`578bf2e`](https://github.com/mastra-ai/mastra/commit/578bf2e6a88e9d5b8bf502204e15a95dfbb679ae), [`06b2d87`](https://github.com/mastra-ai/mastra/commit/06b2d87e63bcdd0ed59215c6789692b9b12de376), [`ac01d63`](https://github.com/mastra-ai/mastra/commit/ac01d6355974aec73fdb8781449ed12bac582094), [`a810a05`](https://github.com/mastra-ai/mastra/commit/a810a058f62ad407cfc1701e0be36ae91145d7cf), [`f8da216`](https://github.com/mastra-ai/mastra/commit/f8da21633e7eb0e31c9ce0fc30567870d19416d3), [`6104347`](https://github.com/mastra-ai/mastra/commit/61043473ba6bfd0a25156824e853e13165562e6c), [`45bfb88`](https://github.com/mastra-ai/mastra/commit/45bfb88fd52f1dd3be20e2a38905777c96499c90), [`e3b9307`](https://github.com/mastra-ai/mastra/commit/e3b9307098daefbfae2a52ae2ef51bc9fc701190), [`d6834c5`](https://github.com/mastra-ai/mastra/commit/d6834c5a7866b16734d23900163c2414ed70d791), [`c52d346`](https://github.com/mastra-ai/mastra/commit/c52d3462ec831a5d95926ecd3d3373f5928ad2e5), [`0023e79`](https://github.com/mastra-ai/mastra/commit/0023e7919431078280abd11c89d1edeae35fcc69), [`c2ad51e`](https://github.com/mastra-ai/mastra/commit/c2ad51e2467f901eecba8c9f4a45e22a50bd7c18), [`3dc97ea`](https://github.com/mastra-ai/mastra/commit/3dc97ea415fad353b48a13095fad1835933cc12a), [`3d01cd3`](https://github.com/mastra-ai/mastra/commit/3d01cd387321b6f9c5cac31d487c84bf51b19c78), [`7bf3086`](https://github.com/mastra-ai/mastra/commit/7bf308663f0115ca74ad20554ade740f06640859), [`a8dd139`](https://github.com/mastra-ai/mastra/commit/a8dd1391a9fe9a6632c25809ef236980afa9a020), [`e5786be`](https://github.com/mastra-ai/mastra/commit/e5786be02bb903073082bd9d6da880ebaacc343f), [`2093fbd`](https://github.com/mastra-ai/mastra/commit/2093fbd53bb744bae19ec89f6d73db9a66fbe8a7), [`e7a5da4`](https://github.com/mastra-ai/mastra/commit/e7a5da4ef8e4dd452d2f232961b4e682a85ffe43), [`7b4393d`](https://github.com/mastra-ai/mastra/commit/7b4393d557411fdcf07b0e30e5acaf7cc85154ae)]:
21
+ - @mastra/core@1.58.0-alpha.1
22
+
3
23
  ## 1.7.2
4
24
 
5
25
  ### Patch Changes
@@ -96,6 +96,8 @@ export type chatRouteOptions<OUTPUT = undefined> = {
96
96
  sendFinish?: boolean;
97
97
  sendReasoning?: boolean;
98
98
  sendSources?: boolean;
99
+ /** Target interval for periodic SSE comment heartbeats. Values up to 0 disable heartbeats. `NaN`, positive infinity, and values above 2,147,483,647 throw a `RangeError`. */
100
+ heartbeatMs?: number;
99
101
  onError?: (error: unknown) => string;
100
102
  };
101
103
  /**
@@ -111,11 +113,13 @@ export type chatRouteOptions<OUTPUT = undefined> = {
111
113
  * @param {boolean} [options.sendFinish=true] - Whether to send finish events in the stream
112
114
  * @param {boolean} [options.sendReasoning=false] - Whether to include reasoning steps in the stream
113
115
  * @param {boolean} [options.sendSources=false] - Whether to include source citations in the stream
116
+ * @param {number} [options.heartbeatMs] - Target interval for periodic SSE comment heartbeats. Already-buffered source events and stream lifecycle signals take priority. Values up to 0 disable heartbeats. `NaN`, positive infinity, and values above 2,147,483,647 throw a `RangeError`.
114
117
  * @param {(error: unknown) => string} [options.onError] - Custom error serializer streamed to the client. When omitted, errors are passed through a default serializer that strips sensitive fields (e.g. `APICallError.requestBodyValues`, which holds the system prompt) before they reach the client.
115
118
  *
116
119
  * @returns {ReturnType<typeof registerApiRoute>} A registered API route handler
117
120
  *
118
121
  * @throws {Error} When path doesn't include `:agentId` and no fixed agent is specified
122
+ * @throws {RangeError} When `heartbeatMs` is `NaN`, positive infinity, or greater than 2,147,483,647
119
123
  * @throws {Error} When agent ID is missing at runtime
120
124
  * @throws {Error} When specified agent is not found in Mastra instance
121
125
  *
@@ -138,10 +142,10 @@ export type chatRouteOptions<OUTPUT = undefined> = {
138
142
  * @remarks
139
143
  * - The route handler expects a JSON body with a `messages` array
140
144
  * - Messages should follow the format: `{ role: 'user' | 'assistant' | 'system', content: string }`
141
- * - The response is a Server-Sent Events (SSE) stream compatible with AI SDK v5
145
+ * - The response is a Server-Sent Events (SSE) stream compatible with the selected AI SDK version
142
146
  * - If both `agent` and `:agentId` are present, a warning is logged and the fixed `agent` takes precedence
143
147
  * - Request context from the incoming request overrides `defaultOptions.requestContext` if both are present
144
148
  */
145
- export declare function chatRoute<OUTPUT = undefined>({ path, agent, defaultOptions, experimentalTransform, version, agentVersion, sendStart, sendFinish, sendReasoning, sendSources, onError, }: chatRouteOptions<OUTPUT>): ReturnType<typeof registerApiRoute>;
149
+ export declare function chatRoute<OUTPUT = undefined>({ path, agent, defaultOptions, experimentalTransform, version, agentVersion, sendStart, sendFinish, sendReasoning, sendSources, heartbeatMs, onError, }: chatRouteOptions<OUTPUT>): ReturnType<typeof registerApiRoute>;
146
150
  export {};
147
151
  //# sourceMappingURL=chat-route.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-route.d.ts","sourceRoot":"","sources":["../src/chat-route.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chat-route.d.ts","sourceRoot":"","sources":["../src/chat-route.ts"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -9427,6 +9427,7 @@ function convertFullStreamChunkToUIMessageStream({ part, messageMetadataValue, s
9427
9427
  //#endregion
9428
9428
  //#region src/transformers.ts
9429
9429
  const PRIMITIVE_CACHE_SYMBOL = Symbol("primitive-cache");
9430
+ const COMPLETED_STEPS_SYMBOL = Symbol("completed-steps-cache");
9430
9431
  function cloneWorkflowStep(step, includeOutput) {
9431
9432
  return {
9432
9433
  name: step.name,
@@ -9554,7 +9555,8 @@ function createAgentStreamToAISDKTransformer(convertMastraChunkToAISDK, { lastMe
9554
9555
  if (transformedChunk) if (transformedChunk.type === "tool-agent") {
9555
9556
  const payload = transformedChunk.payload;
9556
9557
  const agentTransformed = transformAgent(payload, bufferedSteps);
9557
- if (agentTransformed) controller.enqueue(agentTransformed);
9558
+ if (agentTransformed) if (Array.isArray(agentTransformed)) for (const part of agentTransformed) controller.enqueue(part);
9559
+ else controller.enqueue(agentTransformed);
9558
9560
  } else if (transformedChunk.type === "tool-workflow") {
9559
9561
  const payload = transformedChunk.payload;
9560
9562
  const workflowChunk = transformWorkflow(payload, bufferedSteps, true, void 0, void 0, convertMastraChunkToAISDK);
@@ -9637,30 +9639,7 @@ function AgentStreamToAISDKV6Transformer({ lastMessageId, sendStart = true, send
9637
9639
  });
9638
9640
  }
9639
9641
  function ensureAgentRunState(bufferedSteps, runId) {
9640
- if (!bufferedSteps.has(runId)) bufferedSteps.set(runId, {
9641
- id: "",
9642
- object: null,
9643
- finishReason: null,
9644
- usage: null,
9645
- warnings: [],
9646
- text: "",
9647
- reasoning: [],
9648
- sources: [],
9649
- files: [],
9650
- toolCalls: [],
9651
- pendingToolCalls: [],
9652
- toolResults: [],
9653
- request: {},
9654
- response: {
9655
- id: "",
9656
- timestamp: /* @__PURE__ */ new Date(),
9657
- modelId: "",
9658
- messages: []
9659
- },
9660
- providerMetadata: void 0,
9661
- steps: [],
9662
- status: "running"
9663
- });
9642
+ if (!bufferedSteps.has(runId)) bufferedSteps.set(runId, createAgentRunState());
9664
9643
  return bufferedSteps.get(runId);
9665
9644
  }
9666
9645
  function upsertPendingToolCall(pendingToolCalls = [], toolCallId, updates) {
@@ -9696,36 +9675,109 @@ function appendPendingToolCallArgs(pendingToolCalls = [], payload) {
9696
9675
  function removePendingToolCall(pendingToolCalls = [], toolCallId) {
9697
9676
  return pendingToolCalls.filter((call) => call.toolCallId !== toolCallId);
9698
9677
  }
9678
+ function createAgentResponseState() {
9679
+ return {
9680
+ id: "",
9681
+ timestamp: /* @__PURE__ */ new Date(),
9682
+ modelId: "",
9683
+ messages: []
9684
+ };
9685
+ }
9686
+ function createAgentRunState(id = "") {
9687
+ return {
9688
+ id,
9689
+ object: null,
9690
+ finishReason: null,
9691
+ usage: null,
9692
+ warnings: [],
9693
+ text: "",
9694
+ reasoning: [],
9695
+ sources: [],
9696
+ files: [],
9697
+ toolCalls: [],
9698
+ pendingToolCalls: [],
9699
+ toolResults: [],
9700
+ request: {},
9701
+ response: createAgentResponseState(),
9702
+ providerMetadata: void 0,
9703
+ steps: [],
9704
+ status: "running"
9705
+ };
9706
+ }
9707
+ function cloneAgentResponse(response, { includeMessages }) {
9708
+ if (!response) return response;
9709
+ return {
9710
+ ...response,
9711
+ ...Object.prototype.hasOwnProperty.call(response, "messages") ? { messages: includeMessages ? response.messages : [] } : {},
9712
+ ...Object.prototype.hasOwnProperty.call(response, "dbMessages") ? { dbMessages: includeMessages ? response.dbMessages : [] } : {},
9713
+ ...Object.prototype.hasOwnProperty.call(response, "uiMessages") ? { uiMessages: includeMessages ? response.uiMessages : [] } : {}
9714
+ };
9715
+ }
9716
+ function cloneAgentStep(step, { includeDetails }) {
9717
+ if (includeDetails) return {
9718
+ ...step,
9719
+ response: cloneAgentResponse(step.response, { includeMessages: true })
9720
+ };
9721
+ return {
9722
+ ...step,
9723
+ object: null,
9724
+ files: [],
9725
+ sources: [],
9726
+ toolCalls: [],
9727
+ pendingToolCalls: [],
9728
+ toolResults: [],
9729
+ dynamicToolCalls: [],
9730
+ dynamicToolResults: [],
9731
+ staticToolCalls: [],
9732
+ staticToolResults: [],
9733
+ text: "",
9734
+ reasoning: [],
9735
+ content: Array.isArray(step.content) ? [] : step.content,
9736
+ reasoningText: typeof step.reasoningText === "string" ? "" : step.reasoningText,
9737
+ response: cloneAgentResponse(step.response, { includeMessages: false })
9738
+ };
9739
+ }
9740
+ function serializeAgentRun(current, { includeCompletedStepDetails, includeResponseMessages }) {
9741
+ const { _textOffset: _to, _reasoningOffset: _ro, ...data } = current;
9742
+ return {
9743
+ ...data,
9744
+ response: cloneAgentResponse(data.response, { includeMessages: includeResponseMessages }),
9745
+ steps: data.steps.map((step) => cloneAgentStep(step, { includeDetails: includeCompletedStepDetails }))
9746
+ };
9747
+ }
9748
+ function createAgentDataPart(args) {
9749
+ const { current, runId, includeCompletedStepDetails, includeResponseMessages } = args;
9750
+ return {
9751
+ type: "data-tool-agent",
9752
+ id: runId,
9753
+ data: serializeAgentRun(current, {
9754
+ includeCompletedStepDetails,
9755
+ includeResponseMessages
9756
+ })
9757
+ };
9758
+ }
9759
+ function createAgentStepDataPart(args) {
9760
+ const { runId, stepIndex, step } = args;
9761
+ return {
9762
+ type: "data-tool-agent-step",
9763
+ id: `${runId}:${stepIndex}`,
9764
+ data: {
9765
+ runId,
9766
+ stepIndex,
9767
+ step: cloneAgentStep(step, { includeDetails: true })
9768
+ }
9769
+ };
9770
+ }
9699
9771
  function transformAgent(payload, bufferedSteps) {
9700
9772
  let hasChanged = false;
9773
+ let completedStep = null;
9701
9774
  switch (payload.type) {
9702
- case "start":
9703
- bufferedSteps.set(payload.runId, {
9704
- id: payload.payload.id,
9705
- object: null,
9706
- finishReason: null,
9707
- usage: null,
9708
- warnings: [],
9709
- text: "",
9710
- reasoning: [],
9711
- sources: [],
9712
- files: [],
9713
- toolCalls: [],
9714
- pendingToolCalls: [],
9715
- toolResults: [],
9716
- request: {},
9717
- response: {
9718
- id: "",
9719
- timestamp: /* @__PURE__ */ new Date(),
9720
- modelId: "",
9721
- messages: []
9722
- },
9723
- providerMetadata: void 0,
9724
- steps: [],
9725
- status: "running"
9726
- });
9775
+ case "start": {
9776
+ const startState = createAgentRunState(payload.payload.id);
9777
+ bufferedSteps.set(payload.runId, startState);
9727
9778
  hasChanged = true;
9728
9779
  break;
9780
+ }
9729
9781
  case "tool-call-input-streaming-start": {
9730
9782
  const toolInputStartRun = ensureAgentRunState(bufferedSteps, payload.runId);
9731
9783
  const existing = toolInputStartRun.pendingToolCalls?.find((call) => call.toolCallId === payload.payload.toolCallId);
@@ -9856,6 +9908,7 @@ function transformAgent(payload, bufferedSteps) {
9856
9908
  break;
9857
9909
  case "step-finish": {
9858
9910
  const stepRun = ensureAgentRunState(bufferedSteps, payload.runId);
9911
+ const stepIndex = stepRun.steps.length;
9859
9912
  const { steps: _steps, _textOffset, _reasoningOffset, ...stepRunWithoutSteps } = stepRun;
9860
9913
  const textOffset = _textOffset || 0;
9861
9914
  const reasoningOffset = _reasoningOffset || 0;
@@ -9897,18 +9950,28 @@ function transformAgent(payload, bufferedSteps) {
9897
9950
  _textOffset: stepRun.text.length,
9898
9951
  _reasoningOffset: stepRun.reasoning.length
9899
9952
  });
9953
+ completedStep = {
9954
+ stepIndex,
9955
+ step: stepResult
9956
+ };
9900
9957
  hasChanged = true;
9901
9958
  break;
9902
9959
  }
9903
9960
  default: break;
9904
9961
  }
9905
9962
  if (hasChanged) {
9906
- const { _textOffset: _to, _reasoningOffset: _ro, ...data } = bufferedSteps.get(payload.runId);
9907
- return {
9908
- type: "data-tool-agent",
9909
- id: payload.runId,
9910
- data
9911
- };
9963
+ const snapshot = createAgentDataPart({
9964
+ current: bufferedSteps.get(payload.runId),
9965
+ runId: payload.runId,
9966
+ includeCompletedStepDetails: payload.type === "finish",
9967
+ includeResponseMessages: payload.type === "finish"
9968
+ });
9969
+ if (completedStep) return [snapshot, createAgentStepDataPart({
9970
+ runId: payload.runId,
9971
+ stepIndex: completedStep.stepIndex,
9972
+ step: completedStep.step
9973
+ })];
9974
+ return snapshot;
9912
9975
  }
9913
9976
  return null;
9914
9977
  }
@@ -10334,10 +10397,24 @@ function transformNetwork(payload, bufferedNetworks, isNested) {
10334
10397
  const step = current.steps.find((step) => step.id === stepId);
10335
10398
  if (!step) return null;
10336
10399
  step[PRIMITIVE_CACHE_SYMBOL] = step[PRIMITIVE_CACHE_SYMBOL] || /* @__PURE__ */ new Map();
10400
+ if (payload.payload.type === "start") delete step[COMPLETED_STEPS_SYMBOL];
10337
10401
  const result = transformAgent(payload.payload, step[PRIMITIVE_CACHE_SYMBOL]);
10338
- if (result) {
10339
- const { request, response, ...data } = result.data;
10402
+ const snapshot = Array.isArray(result) ? result[0] : result;
10403
+ if (snapshot) {
10404
+ const { request, response, ...data } = snapshot.data;
10405
+ if (Array.isArray(result)) {
10406
+ const { stepIndex, step: completedStepDetail } = result[1].data;
10407
+ step[COMPLETED_STEPS_SYMBOL] = step[COMPLETED_STEPS_SYMBOL] || /* @__PURE__ */ new Map();
10408
+ step[COMPLETED_STEPS_SYMBOL].set(stepIndex, completedStepDetail);
10409
+ }
10340
10410
  step.task = data;
10411
+ const completedSteps = step[COMPLETED_STEPS_SYMBOL];
10412
+ if (completedSteps && completedSteps.size > 0 && Array.isArray(data.steps)) {
10413
+ for (const [stepIndex, completedStepDetail] of completedSteps) if (stepIndex < data.steps.length) data.steps[stepIndex] = {
10414
+ ...data.steps[stepIndex],
10415
+ ...completedStepDetail
10416
+ };
10417
+ }
10341
10418
  }
10342
10419
  bufferedNetworks.set(payload.runId, current);
10343
10420
  return {
@@ -10461,6 +10538,124 @@ function toAISdkStream(stream, options = {
10461
10538
  }));
10462
10539
  }
10463
10540
  //#endregion
10541
+ //#region src/sse-heartbeat.ts
10542
+ const SSE_HEARTBEAT_BYTES = new TextEncoder().encode(": heartbeat\n\n");
10543
+ const LF_BYTE = 10;
10544
+ const MAX_TIMEOUT_MS = 2147483647;
10545
+ /** Throws when an enabled heartbeat interval cannot be scheduled with a timer. */
10546
+ function assertValidHeartbeatMs(heartbeatMs) {
10547
+ if (heartbeatMs !== void 0 && !(heartbeatMs <= 0) && (!Number.isFinite(heartbeatMs) || heartbeatMs > MAX_TIMEOUT_MS)) throw new RangeError(`heartbeatMs must be a finite number no greater than ${MAX_TIMEOUT_MS}`);
10548
+ }
10549
+ /**
10550
+ * Adds periodic SSE comment heartbeats to an AI SDK response body.
10551
+ * AI SDK serialization uses LF-delimited frames, which this private wrapper preserves and relies on.
10552
+ */
10553
+ function withSseHeartbeat(response, heartbeatMs) {
10554
+ assertValidHeartbeatMs(heartbeatMs);
10555
+ if (heartbeatMs === void 0 || heartbeatMs <= 0 || !response.body) return response;
10556
+ const reader = response.body.getReader();
10557
+ let heartbeatTimeout;
10558
+ let wakePull;
10559
+ let readResult;
10560
+ let readError;
10561
+ let hasReadError = false;
10562
+ let reading = false;
10563
+ let finished = false;
10564
+ let readerReleased = false;
10565
+ let atFrameBoundary = true;
10566
+ let lastByte;
10567
+ let nextHeartbeatAt = performance.now() + heartbeatMs;
10568
+ const clearHeartbeat = () => {
10569
+ if (heartbeatTimeout !== void 0) {
10570
+ clearTimeout(heartbeatTimeout);
10571
+ heartbeatTimeout = void 0;
10572
+ }
10573
+ };
10574
+ const releaseReader = () => {
10575
+ if (readerReleased) return;
10576
+ readerReleased = true;
10577
+ reader.releaseLock();
10578
+ };
10579
+ const updateFrameBoundary = (chunk) => {
10580
+ if (chunk.byteLength === 0) return;
10581
+ atFrameBoundary = chunk.byteLength === 1 ? lastByte === LF_BYTE && chunk[0] === LF_BYTE : chunk[chunk.byteLength - 2] === LF_BYTE && chunk[chunk.byteLength - 1] === LF_BYTE;
10582
+ lastByte = chunk[chunk.byteLength - 1];
10583
+ };
10584
+ const startRead = () => {
10585
+ if (reading || readResult || hasReadError || finished) return;
10586
+ reading = true;
10587
+ reader.read().then((result) => {
10588
+ reading = false;
10589
+ readResult = result;
10590
+ wakePull?.("read");
10591
+ }, (error) => {
10592
+ reading = false;
10593
+ readError = error;
10594
+ hasReadError = true;
10595
+ wakePull?.("read");
10596
+ });
10597
+ };
10598
+ const forwardRead = (controller) => {
10599
+ if (hasReadError) {
10600
+ finished = true;
10601
+ releaseReader();
10602
+ controller.error(readError);
10603
+ return;
10604
+ }
10605
+ if (!readResult) return;
10606
+ const result = readResult;
10607
+ readResult = void 0;
10608
+ if (result.done) {
10609
+ finished = true;
10610
+ releaseReader();
10611
+ controller.close();
10612
+ return;
10613
+ }
10614
+ updateFrameBoundary(result.value);
10615
+ controller.enqueue(result.value);
10616
+ };
10617
+ const stream = new ReadableStream({
10618
+ async pull(controller) {
10619
+ if (finished) return;
10620
+ startRead();
10621
+ if (readResult || hasReadError) {
10622
+ forwardRead(controller);
10623
+ return;
10624
+ }
10625
+ const next = await new Promise((resolve) => {
10626
+ wakePull = resolve;
10627
+ if (atFrameBoundary) heartbeatTimeout = setTimeout(() => resolve("heartbeat"), Math.max(0, nextHeartbeatAt - performance.now()));
10628
+ });
10629
+ wakePull = void 0;
10630
+ clearHeartbeat();
10631
+ if (finished) return;
10632
+ if (readResult || hasReadError) {
10633
+ forwardRead(controller);
10634
+ return;
10635
+ }
10636
+ if (next === "heartbeat") {
10637
+ controller.enqueue(SSE_HEARTBEAT_BYTES.slice());
10638
+ nextHeartbeatAt = performance.now() + heartbeatMs;
10639
+ }
10640
+ },
10641
+ async cancel(reason) {
10642
+ if (finished) return;
10643
+ finished = true;
10644
+ clearHeartbeat();
10645
+ try {
10646
+ await reader.cancel(reason);
10647
+ } finally {
10648
+ releaseReader();
10649
+ }
10650
+ }
10651
+ });
10652
+ return new Response(stream, {
10653
+ status: response.status,
10654
+ statusText: response.statusText,
10655
+ headers: response.headers
10656
+ });
10657
+ }
10658
+ //#endregion
10464
10659
  //#region src/chat-route.ts
10465
10660
  /**
10466
10661
  * Collects every approval response across all assistant messages in a v6
@@ -10663,11 +10858,13 @@ async function handleChatStream({ mastra, agentId, agentVersion, params, default
10663
10858
  * @param {boolean} [options.sendFinish=true] - Whether to send finish events in the stream
10664
10859
  * @param {boolean} [options.sendReasoning=false] - Whether to include reasoning steps in the stream
10665
10860
  * @param {boolean} [options.sendSources=false] - Whether to include source citations in the stream
10861
+ * @param {number} [options.heartbeatMs] - Target interval for periodic SSE comment heartbeats. Already-buffered source events and stream lifecycle signals take priority. Values up to 0 disable heartbeats. `NaN`, positive infinity, and values above 2,147,483,647 throw a `RangeError`.
10666
10862
  * @param {(error: unknown) => string} [options.onError] - Custom error serializer streamed to the client. When omitted, errors are passed through a default serializer that strips sensitive fields (e.g. `APICallError.requestBodyValues`, which holds the system prompt) before they reach the client.
10667
10863
  *
10668
10864
  * @returns {ReturnType<typeof registerApiRoute>} A registered API route handler
10669
10865
  *
10670
10866
  * @throws {Error} When path doesn't include `:agentId` and no fixed agent is specified
10867
+ * @throws {RangeError} When `heartbeatMs` is `NaN`, positive infinity, or greater than 2,147,483,647
10671
10868
  * @throws {Error} When agent ID is missing at runtime
10672
10869
  * @throws {Error} When specified agent is not found in Mastra instance
10673
10870
  *
@@ -10690,12 +10887,13 @@ async function handleChatStream({ mastra, agentId, agentVersion, params, default
10690
10887
  * @remarks
10691
10888
  * - The route handler expects a JSON body with a `messages` array
10692
10889
  * - Messages should follow the format: `{ role: 'user' | 'assistant' | 'system', content: string }`
10693
- * - The response is a Server-Sent Events (SSE) stream compatible with AI SDK v5
10890
+ * - The response is a Server-Sent Events (SSE) stream compatible with the selected AI SDK version
10694
10891
  * - If both `agent` and `:agentId` are present, a warning is logged and the fixed `agent` takes precedence
10695
10892
  * - Request context from the incoming request overrides `defaultOptions.requestContext` if both are present
10696
10893
  */
10697
- function chatRoute({ path = "/chat/:agentId", agent, defaultOptions, experimentalTransform, version = "v5", agentVersion, sendStart = true, sendFinish = true, sendReasoning = false, sendSources = false, onError }) {
10894
+ function chatRoute({ path = "/chat/:agentId", agent, defaultOptions, experimentalTransform, version = "v5", agentVersion, sendStart = true, sendFinish = true, sendReasoning = false, sendSources = false, heartbeatMs, onError }) {
10698
10895
  if (!agent && !path.includes("/:agentId")) throw new Error("Path must include :agentId to route to the correct agent or pass the agent explicitly");
10896
+ assertValidHeartbeatMs(heartbeatMs);
10699
10897
  return (0, _mastra_core_server.registerApiRoute)(path, {
10700
10898
  method: "POST",
10701
10899
  openapi: {
@@ -10824,11 +11022,13 @@ function chatRoute({ path = "/chat/:agentId", agent, defaultOptions, experimenta
10824
11022
  sendSources,
10825
11023
  onError
10826
11024
  };
10827
- if (version === "v6") return createUIMessageStreamResponse({ stream: await handleChatStream({
11025
+ let response;
11026
+ if (version === "v6") response = createUIMessageStreamResponse({ stream: await handleChatStream({
10828
11027
  ...handlerOptions,
10829
11028
  version: "v6"
10830
11029
  }) });
10831
- return createUIMessageStreamResponse$1({ stream: await handleChatStream(handlerOptions) });
11030
+ else response = createUIMessageStreamResponse$1({ stream: await handleChatStream(handlerOptions) });
11031
+ return withSseHeartbeat(response, heartbeatMs);
10832
11032
  }
10833
11033
  });
10834
11034
  }