@astralbeam/sdk 0.13.5 → 0.13.7

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
- import { A as resolveApiUrl, O as astralBeamChatFetch, c as getCurrentUser, i as getChatConfig, k as isAstralBeamApiError, v as getRunChatUrl } from "./api-DiwSihrG.js";
1
+ import { A as resolveApiUrl, O as astralBeamChatFetch, c as getCurrentUser, i as getChatConfig, k as isAstralBeamApiError, v as getRunChatUrl } from "./api-CsRaydMf.js";
2
2
  import { d as JWTInvalid, h as strictDecoder, r as isObject, s as decode } from "./type_checks-C-WTHaWs.js";
3
3
  //#region \0rolldown/runtime.js
4
4
  var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
@@ -26,7 +26,7 @@ function decodeJwt(jwt) {
26
26
  return result;
27
27
  }
28
28
  //#endregion
29
- //#region node_modules/.deno/@tanstack+ai-client@0.28.0/node_modules/@tanstack/ai-client/dist/esm/response-stream.js
29
+ //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/response-stream.js
30
30
  var UnsupportedResponseStreamError = class extends Error {
31
31
  missingFeature;
32
32
  constructor(missingFeature) {
@@ -48,14 +48,46 @@ function createResponseStreamTextDecoder() {
48
48
  return new globalThis.TextDecoder();
49
49
  }
50
50
  //#endregion
51
- //#region node_modules/.deno/@tanstack+ai-client@0.28.0/node_modules/@tanstack/ai-client/dist/esm/sse-utils.js
51
+ //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/sse-utils.js
52
52
  function parseSseDataLine(line) {
53
53
  if (!line.startsWith("data:")) return line;
54
54
  const data = line.slice(5);
55
55
  return data.startsWith(" ") ? data.slice(1) : data;
56
56
  }
57
57
  //#endregion
58
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/utilities/ag-ui-usage.js
58
+ //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/message-date-normalizer.js
59
+ function validDate(value) {
60
+ if (value instanceof Date) return Number.isNaN(value.getTime()) ? void 0 : value;
61
+ if (typeof value !== "string") return void 0;
62
+ const date = new Date(value);
63
+ return Number.isNaN(date.getTime()) ? void 0 : date;
64
+ }
65
+ function normalizeMessageDates(message) {
66
+ const messageDate = validDate(message.createdAt);
67
+ const parts = message.parts.map((part) => {
68
+ if (part.type !== "tool-result") return part;
69
+ const date = validDate(part.createdAt);
70
+ const { createdAt: _ignored, ...rest } = part;
71
+ return date ? {
72
+ ...rest,
73
+ createdAt: date
74
+ } : rest;
75
+ });
76
+ const { createdAt: _ignored, ...rest } = message;
77
+ return messageDate ? {
78
+ ...rest,
79
+ parts,
80
+ createdAt: messageDate
81
+ } : {
82
+ ...rest,
83
+ parts
84
+ };
85
+ }
86
+ function normalizeMessagesDates(messages) {
87
+ return messages.map(normalizeMessageDates);
88
+ }
89
+ //#endregion
90
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/utilities/ag-ui-usage.js
59
91
  function definedDetails(value) {
60
92
  return Object.keys(value).length > 0 ? value : void 0;
61
93
  }
@@ -68,11 +100,22 @@ function rebuildTokenUsage(usage, leftover) {
68
100
  return fromSpecTokenUsage(void 0, leftover);
69
101
  }
70
102
  function fromSpecTokenUsage(usage, leftover) {
71
- const spec = usage?.[0];
72
- if (spec == null && leftover == null) return;
103
+ const spec = usage?.reduce((total, entry) => {
104
+ for (const key of [
105
+ "inputTokens",
106
+ "outputTokens",
107
+ "totalTokens",
108
+ "cachedInputTokens",
109
+ "cacheWriteInputTokens",
110
+ "reasoningTokens"
111
+ ]) if (entry[key] !== void 0) total[key] = (total[key] ?? 0) + entry[key];
112
+ return total;
113
+ }, {});
114
+ if ((usage === void 0 || usage.length === 0) && leftover == null) return;
73
115
  const { promptTokensDetails: leftoverPromptDetails, completionTokensDetails: leftoverCompletionDetails, ...leftoverRest } = leftover ?? {};
74
116
  const promptTokensDetails = definedDetails({
75
117
  ...spec?.cachedInputTokens !== void 0 ? { cachedTokens: spec.cachedInputTokens } : {},
118
+ ...spec?.cacheWriteInputTokens !== void 0 ? { cacheWriteTokens: spec.cacheWriteInputTokens } : {},
76
119
  ...leftoverPromptDetails
77
120
  });
78
121
  const completionTokensDetails = definedDetails({
@@ -89,7 +132,7 @@ function fromSpecTokenUsage(usage, leftover) {
89
132
  };
90
133
  }
91
134
  //#endregion
92
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/utilities/merge-metadata.js
135
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/utilities/merge-metadata.js
93
136
  function isPlainRecord$1(value) {
94
137
  return value != null && typeof value === "object" && !Array.isArray(value);
95
138
  }
@@ -129,8 +172,11 @@ function withTanstackMetadata(value, tanstack) {
129
172
  };
130
173
  }
131
174
  //#endregion
132
- //#region node_modules/.deno/@ag-ui+core@0.1.1-canary.beta.0/node_modules/@ag-ui/core/dist/events-BaoNrGbE.mjs
133
- let EventType$1 = /* @__PURE__ */ function(EventType) {
175
+ //#region node_modules/.deno/@ag-ui+core@1.0.0/node_modules/@ag-ui/core/dist/version-CTNE2I0_.mjs
176
+ /**
177
+ * The discriminator carried by every event.
178
+ */
179
+ let EventType = /* @__PURE__ */ function(EventType) {
134
180
  EventType["TEXT_MESSAGE_START"] = "TEXT_MESSAGE_START";
135
181
  EventType["TEXT_MESSAGE_CONTENT"] = "TEXT_MESSAGE_CONTENT";
136
182
  EventType["TEXT_MESSAGE_END"] = "TEXT_MESSAGE_END";
@@ -140,16 +186,6 @@ let EventType$1 = /* @__PURE__ */ function(EventType) {
140
186
  EventType["TOOL_CALL_END"] = "TOOL_CALL_END";
141
187
  EventType["TOOL_CALL_CHUNK"] = "TOOL_CALL_CHUNK";
142
188
  EventType["TOOL_CALL_RESULT"] = "TOOL_CALL_RESULT";
143
- /** @deprecated Use REASONING_START instead. Will be removed in 1.0.0. */
144
- EventType["THINKING_START"] = "THINKING_START";
145
- /** @deprecated Use REASONING_END instead. Will be removed in 1.0.0. */
146
- EventType["THINKING_END"] = "THINKING_END";
147
- /** @deprecated Use REASONING_MESSAGE_START instead. Will be removed in 1.0.0. */
148
- EventType["THINKING_TEXT_MESSAGE_START"] = "THINKING_TEXT_MESSAGE_START";
149
- /** @deprecated Use REASONING_MESSAGE_CONTENT instead. Will be removed in 1.0.0. */
150
- EventType["THINKING_TEXT_MESSAGE_CONTENT"] = "THINKING_TEXT_MESSAGE_CONTENT";
151
- /** @deprecated Use REASONING_MESSAGE_END instead. Will be removed in 1.0.0. */
152
- EventType["THINKING_TEXT_MESSAGE_END"] = "THINKING_TEXT_MESSAGE_END";
153
189
  EventType["STATE_SNAPSHOT"] = "STATE_SNAPSHOT";
154
190
  EventType["STATE_DELTA"] = "STATE_DELTA";
155
191
  EventType["MESSAGES_SNAPSHOT"] = "MESSAGES_SNAPSHOT";
@@ -169,10 +205,13 @@ let EventType$1 = /* @__PURE__ */ function(EventType) {
169
205
  EventType["REASONING_MESSAGE_CHUNK"] = "REASONING_MESSAGE_CHUNK";
170
206
  EventType["REASONING_END"] = "REASONING_END";
171
207
  EventType["REASONING_ENCRYPTED_VALUE"] = "REASONING_ENCRYPTED_VALUE";
208
+ EventType["SUBAGENT_STARTED"] = "SUBAGENT_STARTED";
209
+ EventType["SUBAGENT_FINISHED"] = "SUBAGENT_FINISHED";
210
+ EventType["SUBAGENT_ERROR"] = "SUBAGENT_ERROR";
172
211
  return EventType;
173
212
  }({});
174
213
  //#endregion
175
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/interrupt-serialization.js
214
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/interrupt-serialization.js
176
215
  var SHA256_K = new Uint32Array([
177
216
  1116352408,
178
217
  1899447441,
@@ -347,7 +386,7 @@ function cloneAndDeepFreezeJson(value) {
347
386
  return clone;
348
387
  }
349
388
  //#endregion
350
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/interrupts.js
389
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/interrupts.js
351
390
  function canonicalizeInterruptResolutions(resolutions) {
352
391
  const frozen = cloneAndDeepFreezeJson([...resolutions].sort((left, right) => left.interruptId.localeCompare(right.interruptId)));
353
392
  const canonicalResolutions = canonicalInterruptJson(frozen);
@@ -358,7 +397,7 @@ function canonicalizeInterruptResolutions(resolutions) {
358
397
  });
359
398
  }
360
399
  //#endregion
361
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/activities/chat/tools/schema-converter.js
400
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/activities/chat/tools/schema-converter.js
362
401
  /**
363
402
  * Build a JSONSchema object from any plain key/value source. The `JSONSchema`
364
403
  * interface's `[key: string]: any` index signature makes every property
@@ -410,6 +449,21 @@ function isStandardSchema(schema) {
410
449
  function pruneMap(map) {
411
450
  return Object.keys(map).length > 0 ? map : void 0;
412
451
  }
452
+ function coerceArrayItems(items) {
453
+ if (Array.isArray(items)) {
454
+ const nested = items.map((item) => makeStructuredOutputCompatible(item, item.required || []));
455
+ const itemMaps = nested.map((entry) => entry.nullWidening ?? {});
456
+ return {
457
+ schema: nested.map((entry) => entry.schema),
458
+ itemMap: itemMaps.some((entry) => Object.keys(entry).length > 0) ? itemMaps : void 0
459
+ };
460
+ }
461
+ const nested = makeStructuredOutputCompatible(items, items.required || []);
462
+ return {
463
+ schema: nested.schema,
464
+ itemMap: nested.nullWidening
465
+ };
466
+ }
413
467
  /**
414
468
  * Transform a JSON schema to be compatible with OpenAI's structured output requirements.
415
469
  * OpenAI requires:
@@ -447,15 +501,14 @@ function makeStructuredOutputCompatible(schema, originalRequired = []) {
447
501
  widenedHere = wasOptional;
448
502
  childMap = nested.nullWidening;
449
503
  } else if (prop.type === "array" && prop.items) {
450
- const items = Array.isArray(prop.items) ? prop.items[0] : prop.items;
451
- const nestedItems = items ? makeStructuredOutputCompatible(items, items.required || []) : void 0;
504
+ const nestedItems = coerceArrayItems(prop.items);
452
505
  properties[propName] = {
453
506
  ...prop,
454
- items: nestedItems ? nestedItems.schema : prop.items,
507
+ items: nestedItems.schema,
455
508
  ...wasOptional ? { type: ["array", "null"] } : {}
456
509
  };
457
510
  widenedHere = wasOptional;
458
- childMap = nestedItems?.nullWidening ? { items: nestedItems.nullWidening } : void 0;
511
+ childMap = nestedItems.itemMap ? { items: nestedItems.itemMap } : void 0;
459
512
  } else if (wasOptional) {
460
513
  if (prop.type && !Array.isArray(prop.type)) {
461
514
  properties[propName] = {
@@ -482,12 +535,9 @@ function makeStructuredOutputCompatible(schema, originalRequired = []) {
482
535
  if (Object.keys(propertyMaps).length > 0) map.properties = propertyMaps;
483
536
  }
484
537
  if (result.type === "array" && result.items) {
485
- const items = Array.isArray(result.items) ? result.items[0] : result.items;
486
- if (items) {
487
- const nestedItems = makeStructuredOutputCompatible(items, items.required || []);
488
- result.items = nestedItems.schema;
489
- if (nestedItems.nullWidening) map.items = nestedItems.nullWidening;
490
- }
538
+ const nestedItems = coerceArrayItems(result.items);
539
+ result.items = nestedItems.schema;
540
+ if (nestedItems.itemMap) map.items = nestedItems.itemMap;
491
541
  }
492
542
  return {
493
543
  schema: result,
@@ -589,38 +639,32 @@ function convertSchemaToJsonSchema(schema, options = {}) {
589
639
  return makeStructuredOutputCompatible(base, base.required || []).schema;
590
640
  }
591
641
  /**
592
- * Error thrown when Standard Schema validation fails. Carries the original
593
- * `issues` array so consumers (middleware `onError`, callers catching from
594
- * `chat({ outputSchema })`) can programmatically inspect each failure.
595
- */
596
- var StandardSchemaValidationError = class extends Error {
597
- name = "StandardSchemaValidationError";
598
- issues;
599
- constructor(issues) {
600
- super(`Validation failed: ${issues.map((i) => i.message || "Validation failed").join(", ")}`);
601
- this.issues = issues;
602
- }
603
- };
604
- /**
605
- * Synchronously validates data against a Standard Schema compliant schema.
606
- * Note: Some Standard Schema implementations may only support async validation.
607
- * In those cases, this function will throw.
642
+ * Validates data against a Standard Schema compliant schema.
608
643
  *
609
644
  * @param schema - Standard Schema compliant schema
610
645
  * @param data - Data to validate
611
- * @returns Parsed/validated data
612
- * @throws StandardSchemaValidationError if validation fails; Error if the
613
- * schema only supports async validation.
646
+ * @returns Validation result with success status, data or issues
614
647
  */
615
- function parseWithStandardSchema(schema, data) {
616
- if (!isStandardSchema(schema)) return data;
617
- const result = schema["~standard"].validate(data);
618
- if (result instanceof Promise) throw new Error("Schema validation returned a Promise. Use validateWithStandardSchema for async validation.");
619
- if (!result.issues) return result.value;
620
- throw new StandardSchemaValidationError(result.issues);
648
+ async function validateWithStandardSchema(schema, data) {
649
+ if (!isStandardSchema(schema)) return {
650
+ success: true,
651
+ data
652
+ };
653
+ const result = await schema["~standard"].validate(data);
654
+ if (!result.issues) return {
655
+ success: true,
656
+ data: result.value
657
+ };
658
+ return {
659
+ success: false,
660
+ issues: result.issues.map((issue) => ({
661
+ message: issue.message || "Validation failed",
662
+ path: issue.path?.map(String)
663
+ }))
664
+ };
621
665
  }
622
666
  //#endregion
623
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/activities/chat/tools/approval-schema.js
667
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/activities/chat/tools/approval-schema.js
624
668
  var jsonSchemaKeywords = /* @__PURE__ */ new Set([
625
669
  "$schema",
626
670
  "$id",
@@ -732,7 +776,7 @@ function hashSchemaInput(schema) {
732
776
  return digestInterruptJson(canonicalInterruptJson(schemaToWire(schema).jsonSchema ?? { standardValidator: "unserialized" }));
733
777
  }
734
778
  //#endregion
735
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/interrupt-resume.js
779
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/interrupt-resume.js
736
780
  /**
737
781
  * The `Interrupt.metadata` key under which this package's resume binding
738
782
  * travels.
@@ -853,7 +897,7 @@ function readInterruptBinding(descriptor) {
853
897
  };
854
898
  }
855
899
  //#endregion
856
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/interrupt-definition.js
900
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/interrupt-definition.js
857
901
  var INTERRUPT_PAYLOAD_METADATA_KEY = "tanstack:interruptPayload";
858
902
  function schemaJson(schema, name) {
859
903
  if (!isStandardJSONSchema(schema)) throw new TypeError(`${name} must be a Standard Schema with a JSON Schema converter.`);
@@ -878,7 +922,7 @@ function hashInterruptDefinitionSchema(schema) {
878
922
  return digestInterruptJson(schemaJson(schema, "Interrupt schema").canonicalJson);
879
923
  }
880
924
  //#endregion
881
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/generic-interrupt-continuation.js
925
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/generic-interrupt-continuation.js
882
926
  /**
883
927
  * `ResumeEntry.metadata` key for a first-party generic request.
884
928
  *
@@ -886,7 +930,7 @@ function hashInterruptDefinitionSchema(schema) {
886
930
  * The original request rides here so an ephemeral server can rebuild it.
887
931
  */
888
932
  var INTERRUPT_CONTINUATION_METADATA_KEY = "tanstack:interruptContinuation";
889
- function isRecord(value) {
933
+ function isRecord$3(value) {
890
934
  return value !== null && typeof value === "object" && !Array.isArray(value);
891
935
  }
892
936
  /** Put a parsed continuation on `ResumeEntry.metadata`. */
@@ -901,7 +945,7 @@ function wrapGenericInterruptContinuation(continuation) {
901
945
  function genericInterruptContinuationFromDescriptor(interrupt) {
902
946
  const binding = readUnopenedInterruptBinding(interrupt);
903
947
  if (binding?.kind !== "generic" || binding.definitionId === void 0 || binding.key === void 0 || binding.batchIndex === void 0) return;
904
- const metadata = isRecord(interrupt.metadata) ? interrupt.metadata : void 0;
948
+ const metadata = isRecord$3(interrupt.metadata) ? interrupt.metadata : void 0;
905
949
  const hasPayload = metadata !== void 0 && Object.prototype.hasOwnProperty.call(metadata, "tanstack:interruptPayload");
906
950
  return {
907
951
  v: 1,
@@ -917,174 +961,37 @@ function genericInterruptContinuationFromDescriptor(interrupt) {
917
961
  };
918
962
  }
919
963
  //#endregion
920
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/utilities/ag-ui-wire.js
964
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/utilities/provider-executed.js
921
965
  /**
922
- * Serialize TanStack `UIMessage`s into the AG-UI `RunAgentInput.messages`
923
- * wire shape. Anchors are spec-only (`id`, `role`, `name`, `content`,
924
- * `toolCalls`, `metadata`). Tool results and thinking parts on assistant
925
- * messages are additionally emitted as fan-out `{role:'tool',...}` and
926
- * `{role:'reasoning',...}` entries for strict AG-UI server consumers.
966
+ * Narrow a tool call's opaque `metadata` to the provider-executed convention.
967
+ * Returns the typed metadata when the call is provider-executed, else `null`.
968
+ *
969
+ * @see ProviderExecutedToolMetadata
927
970
  */
928
- function uiMessagesToWire(messages) {
929
- const wire = [];
930
- for (const msg of messages) {
931
- const parts = msg.parts ?? [];
932
- if (msg.role === "system") {
933
- wire.push(toAnchor(msg, { content: parts.length > 0 ? collectText(parts) : msg.content ?? "" }, parts));
934
- continue;
935
- }
936
- if (msg.role === "user") {
937
- wire.push(toAnchor(msg, { content: parts.length > 0 ? collectUserContent(parts) : msg.content ?? "" }, parts));
938
- continue;
939
- }
940
- for (const part of parts) if (part.type === "thinking") {
941
- const reasoning = {
942
- role: "reasoning",
943
- id: deriveReasoningId(msg.id, part),
944
- content: part.content
945
- };
946
- if (part.signature) reasoning.encryptedValue = part.signature;
947
- wire.push(reasoning);
948
- }
949
- const text = collectText(parts);
950
- const toolCalls = collectToolCalls(parts);
951
- wire.push(toAnchor(msg, {
952
- ...text !== "" && { content: text },
953
- ...toolCalls && { toolCalls }
954
- }, parts));
955
- for (const part of parts) if (part.type === "tool-result") wire.push({
956
- role: "tool",
957
- id: deriveToolMessageId(part.toolCallId),
958
- toolCallId: part.toolCallId,
959
- content: typeof part.content === "string" ? part.content : JSON.stringify(part.content),
960
- ...part.error !== void 0 && { error: part.error }
961
- });
962
- }
963
- return wire;
964
- }
965
- function toAnchor(msg, extras, parts) {
966
- const metadata = messageMetadata(msg, parts);
967
- const name = msg.name;
968
- return {
969
- id: msg.id,
970
- role: msg.role,
971
- ...name !== void 0 && { name },
972
- ...extras,
973
- ...metadata !== void 0 && { metadata }
974
- };
975
- }
976
- function messageMetadata(msg, parts) {
977
- const base = { ...msg.metadata ?? {} };
978
- const tanstack = { ...tanstackMetadata(msg) ?? {} };
979
- if (msg.createdAt) tanstack.createdAt = msg.createdAt.toISOString();
980
- const leftover = unfinishedStructuredOutput(parts);
981
- if (leftover) tanstack.structuredOutput = leftover;
982
- const toolCallMetadata = {};
983
- for (const part of parts) if (part.type === "tool-call" && part.metadata !== void 0) toolCallMetadata[part.id] = part.metadata;
984
- if (Object.keys(toolCallMetadata).length > 0) tanstack.toolCallMetadata = toolCallMetadata;
985
- const uiResources = parts.filter((p) => p.type === "ui-resource");
986
- if (uiResources.length > 0) tanstack.uiResources = uiResources;
987
- if (Object.keys(tanstack).length > 0) base.tanstack = tanstack;
988
- return Object.keys(base).length > 0 ? base : void 0;
989
- }
990
- function unfinishedStructuredOutput(parts) {
991
- for (const p of parts) if (p.type === "structured-output" && p.status !== "complete") return structuredOutputLeftover(p);
992
- }
993
- function structuredOutputLeftover(part) {
994
- return {
995
- status: part.status,
996
- raw: part.raw,
997
- ...part.errorMessage !== void 0 && { errorMessage: part.errorMessage }
998
- };
999
- }
1000
- function collectText(parts) {
1001
- const out = [];
1002
- for (const p of parts) if (p.type === "text") out.push(p.content);
1003
- else if (p.type === "structured-output" && p.status === "complete" && p.raw !== "") out.push(p.raw);
1004
- return out.join("");
1005
- }
1006
- function collectUserContent(parts) {
1007
- if (!parts.some((p) => p.type === "image" || p.type === "audio" || p.type === "video" || p.type === "document")) return collectText(parts);
1008
- const out = [];
1009
- for (const p of parts) if (p.type === "text") out.push({
1010
- type: "text",
1011
- text: p.content
1012
- });
1013
- else if (p.type === "image" || p.type === "audio" || p.type === "video" || p.type === "document") out.push(p);
1014
- return out;
1015
- }
1016
- function thoughtSignatureFromMetadata(metadata) {
1017
- if (metadata == null || typeof metadata !== "object" || Array.isArray(metadata)) return;
1018
- if (!("thoughtSignature" in metadata)) return void 0;
1019
- const value = metadata.thoughtSignature;
1020
- return typeof value === "string" && value !== "" ? value : void 0;
1021
- }
1022
- function collectToolCalls(parts) {
1023
- const calls = [];
1024
- for (const p of parts) if (p.type === "tool-call") {
1025
- const encryptedValue = thoughtSignatureFromMetadata(p.metadata);
1026
- calls.push({
1027
- id: p.id,
1028
- type: "function",
1029
- function: {
1030
- name: p.name,
1031
- arguments: p.arguments
1032
- },
1033
- ...encryptedValue !== void 0 ? { encryptedValue } : {}
1034
- });
1035
- }
1036
- return calls.length > 0 ? calls : void 0;
1037
- }
1038
- function deriveReasoningId(messageId, part) {
1039
- return `${messageId}-reasoning-${part.id ?? hashContent(part.content)}`;
1040
- }
1041
- function deriveToolMessageId(toolCallId) {
1042
- return `tool-${toolCallId}`;
1043
- }
1044
- function hashContent(s) {
1045
- let h = 0;
1046
- for (let i = 0; i < s.length; i++) h = h * 31 + s.charCodeAt(i) | 0;
1047
- return Math.abs(h).toString(36);
971
+ function getProviderExecutedMetadata(toolCall) {
972
+ const metadata = toolCall?.metadata;
973
+ if (typeof metadata === "object" && metadata !== null && metadata.providerExecuted === true) return metadata;
974
+ return null;
1048
975
  }
1049
- //#endregion
1050
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/utilities/restore-inbound-chunk.js
1051
976
  /**
1052
- * Rebuild TanStack `TokenUsage` (`promptTokens`) from spec `usage[]` plus
1053
- * leftover fields in `metadata.tanstack.usage`. Restore in-process aliases
1054
- * that the wire does not keep (`toolName`, `TOOL_CALL_END.input`). Mutates
1055
- * in place so WeakMap run-id stamps stay attached.
977
+ * True when a tool call was executed by the provider (e.g. Anthropic
978
+ * `web_search` / `web_fetch` server tools) rather than the agent loop. Such
979
+ * calls must not be routed to client-side execution and are already "complete".
1056
980
  */
1057
- function restorePublicUsage(chunk) {
1058
- if ((chunk.type === EventType$1.RUN_FINISHED || chunk.type === EventType$1.RUN_ERROR) && (Array.isArray(chunk.usage) || isTanstackUsage(chunk.usage))) {
1059
- const rebuilt = rebuildTokenUsage(chunk.usage, tanstackMetadata(chunk)?.usage);
1060
- if (rebuilt !== void 0) chunk.usage = rebuilt;
1061
- }
1062
- if (chunk.type === EventType$1.TOOL_CALL_START && chunk.toolName === void 0 && chunk.toolCallName) chunk.toolName = chunk.toolCallName;
1063
- if (chunk.type === EventType$1.TOOL_CALL_END && chunk.input === void 0) {
1064
- const input = tanstackMetadata(chunk)?.input;
1065
- if (input !== void 0) chunk.input = input;
1066
- }
1067
- return chunk;
981
+ function isProviderExecutedToolCall(toolCall) {
982
+ return getProviderExecutedMetadata(toolCall) !== null;
1068
983
  }
1069
984
  /**
1070
- * Rebuild the pre-wire chunk shape after SSE/HTTP/WS ingest.
1071
- * Copies `metadata.tanstack` extras back to top-level fields and rebuilds
1072
- * TanStack `TokenUsage` from spec `usage[]` when present.
985
+ * True when `id` is a `${parentId}-segment-${n}` message. The wire and the
986
+ * run loop split one provider turn into segments at thinking that follows a
987
+ * provider-executed tool call, to keep signed thinking order. Readers fold a
988
+ * segment back into its parent so the UI still shows one message.
1073
989
  */
1074
- function restoreInboundChunk(chunk) {
1075
- restorePublicUsage(chunk);
1076
- const tanstack = tanstackMetadata(chunk);
1077
- const next = chunk;
1078
- if (tanstack == null) return next;
1079
- for (const [key, value] of Object.entries(tanstack)) {
1080
- if (key === "usage" || key === "interruptErrors") continue;
1081
- if (next[key] === void 0 && value !== void 0) next[key] = value;
1082
- }
1083
- if (tanstack.interruptErrors !== void 0) next["tanstack:interruptErrors"] = tanstack.interruptErrors;
1084
- return next;
990
+ function isAssistantSegmentOf(id, parentId) {
991
+ return id?.startsWith(`${parentId}-segment-`) === true;
1085
992
  }
1086
993
  //#endregion
1087
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/utilities/tool-result.js
994
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/utilities/tool-result.js
1088
995
  var CONTENT_PART_TYPES = /* @__PURE__ */ new Set([
1089
996
  "text",
1090
997
  "image",
@@ -1092,10 +999,14 @@ var CONTENT_PART_TYPES = /* @__PURE__ */ new Set([
1092
999
  "video",
1093
1000
  "document"
1094
1001
  ]);
1002
+ function isToolResultOutcome(value) {
1003
+ return value === "cancelled" || value === "denied";
1004
+ }
1095
1005
  /**
1096
1006
  * Structural check for a single `ContentPart`. A text part must carry a string
1097
- * `content`; every other modality must carry a `source` with `type` of
1098
- * `'url' | 'data'` and a string `value`.
1007
+ * `content`. Every other part carries a source with a string `value`; a file
1008
+ * source's `value` is a non-empty opaque handle, and its optional `provider`
1009
+ * is a string.
1099
1010
  */
1100
1011
  function isContentPart$1(value) {
1101
1012
  if (typeof value !== "object" || value === null) return false;
@@ -1106,6 +1017,7 @@ function isContentPart$1(value) {
1106
1017
  if (typeof source !== "object" || source === null) return false;
1107
1018
  const src = source;
1108
1019
  if (typeof src.value !== "string") return false;
1020
+ if (src.type === "file") return src.value.length > 0 && (src.provider === void 0 || typeof src.provider === "string");
1109
1021
  if (src.type === "data") return typeof src.mimeType === "string";
1110
1022
  return src.type === "url";
1111
1023
  }
@@ -1119,6 +1031,16 @@ function isContentPartArray(value) {
1119
1031
  return Array.isArray(value) && value.length > 0 && value.every(isContentPart$1);
1120
1032
  }
1121
1033
  /**
1034
+ * Error text for a failed tool result: `output.error` when it is a string,
1035
+ * else the output itself when it is a string, else a generic message.
1036
+ * `StreamProcessor` and `chat()` history share it, so a reload shows the
1037
+ * same text as the live stream.
1038
+ */
1039
+ function toolResultErrorText(output) {
1040
+ if (output && typeof output === "object" && "error" in output && typeof output.error === "string") return output.error;
1041
+ return typeof output === "string" ? output : "Tool execution failed";
1042
+ }
1043
+ /**
1122
1044
  * Normalize a tool's return value for transport:
1123
1045
  * - string → unchanged
1124
1046
  * - ContentPart array → unchanged (multimodal, passed through to the adapter)
@@ -1130,28 +1052,114 @@ function normalizeToolResult(result) {
1130
1052
  return JSON.stringify(result);
1131
1053
  }
1132
1054
  //#endregion
1133
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/utilities/provider-executed.js
1134
- /**
1135
- * Narrow a tool call's opaque `metadata` to the provider-executed convention.
1136
- * Returns the typed metadata when the call is provider-executed, else `null`.
1137
- *
1138
- * @see ProviderExecutedToolMetadata
1139
- */
1140
- function getProviderExecutedMetadata(toolCall) {
1141
- const metadata = toolCall?.metadata;
1142
- if (typeof metadata === "object" && metadata !== null && metadata.providerExecuted === true) return metadata;
1143
- return null;
1055
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/utilities/subagent-wire.js
1056
+ function isRecord$2(value) {
1057
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1058
+ }
1059
+ function wireSubagentRunId(message) {
1060
+ if (!isRecord$2(message) || "parts" in message) return void 0;
1061
+ const id = message.subagentRunId;
1062
+ return typeof id === "string" && id !== "" ? id : void 0;
1063
+ }
1064
+ function wireSubagentInfo(message) {
1065
+ if (!isRecord$2(message) || !isRecord$2(message.metadata)) return void 0;
1066
+ const tanstack = message.metadata.tanstack;
1067
+ if (!isRecord$2(tanstack) || !isRecord$2(tanstack.subagent)) return void 0;
1068
+ const { name, status, error, interruptIds } = tanstack.subagent;
1069
+ if (typeof name !== "string" || !isStatus(status)) return void 0;
1070
+ const { description, parentSubagentRunId, parentToolCallId, metadata, placeholder } = tanstack.subagent;
1071
+ return {
1072
+ name,
1073
+ status,
1074
+ ...typeof description === "string" && { description },
1075
+ ...isRecord$2(error) && typeof error.message === "string" && { error: {
1076
+ message: error.message,
1077
+ ...typeof error.code === "string" && { code: error.code }
1078
+ } },
1079
+ ...Array.isArray(interruptIds) && { interruptIds: interruptIds.filter((id) => typeof id === "string") },
1080
+ ...typeof parentSubagentRunId === "string" && { parentSubagentRunId },
1081
+ ...typeof parentToolCallId === "string" && { parentToolCallId },
1082
+ ...isRecord$2(metadata) && { metadata },
1083
+ ...placeholder === true && { placeholder: true }
1084
+ };
1085
+ }
1086
+ function isStatus(value) {
1087
+ return value === "running" || value === "finished" || value === "error" || value === "suspended";
1088
+ }
1089
+ function untag(message) {
1090
+ if (!isRecord$2(message)) return message;
1091
+ const { subagentRunId: _id, ...rest } = message;
1092
+ const metadata = isRecord$2(rest.metadata) ? rest.metadata : void 0;
1093
+ const tanstack = isRecord$2(metadata?.tanstack) ? metadata.tanstack : void 0;
1094
+ if (!metadata || !tanstack || !("subagent" in tanstack)) return rest;
1095
+ const { subagent: _info, ...tanstackRest } = tanstack;
1096
+ return {
1097
+ ...rest,
1098
+ metadata: {
1099
+ ...metadata,
1100
+ tanstack: tanstackRest
1101
+ }
1102
+ };
1144
1103
  }
1145
1104
  /**
1146
- * True when a tool call was executed by the provider (e.g. Anthropic
1147
- * `web_search` / `web_fetch` server tools) rather than the agent loop. Such
1148
- * calls must not be routed to client-side execution and are already "complete".
1105
+ * Split wire messages into the parent's own messages and one group per direct
1106
+ * child. A nested child's messages stay inside its parent's group.
1149
1107
  */
1150
- function isProviderExecutedToolCall(toolCall) {
1151
- return getProviderExecutedMetadata(toolCall) !== null;
1108
+ function splitSubagentWire(messages) {
1109
+ const parentOf = /* @__PURE__ */ new Map();
1110
+ for (const message of messages) {
1111
+ const id = wireSubagentRunId(message);
1112
+ if (id !== void 0 && !parentOf.has(id)) parentOf.set(id, wireSubagentInfo(message)?.parentSubagentRunId);
1113
+ }
1114
+ const rootOf = (id) => {
1115
+ let current = id;
1116
+ for (let depth = 0; depth < 64; depth++) {
1117
+ const parent = parentOf.get(current);
1118
+ if (parent === void 0 || !parentOf.has(parent)) return current;
1119
+ current = parent;
1120
+ }
1121
+ return current;
1122
+ };
1123
+ const top = [];
1124
+ const groups = /* @__PURE__ */ new Map();
1125
+ for (const message of messages) {
1126
+ const id = wireSubagentRunId(message);
1127
+ if (id === void 0) {
1128
+ top.push(message);
1129
+ continue;
1130
+ }
1131
+ const root = rootOf(id);
1132
+ let group = groups.get(root);
1133
+ if (!group) {
1134
+ group = {
1135
+ id: root,
1136
+ info: {
1137
+ name: "subagent",
1138
+ status: "finished"
1139
+ },
1140
+ messages: [],
1141
+ hostIndex: top.length - 1
1142
+ };
1143
+ groups.set(root, group);
1144
+ }
1145
+ if (id !== root) {
1146
+ group.messages.push(message);
1147
+ continue;
1148
+ }
1149
+ const info = wireSubagentInfo(message);
1150
+ if (info) group.info = info;
1151
+ if (!info?.placeholder) group.messages.push(untag(message));
1152
+ }
1153
+ return {
1154
+ top,
1155
+ groups: [...groups.values()]
1156
+ };
1152
1157
  }
1153
1158
  //#endregion
1154
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/activities/chat/messages.js
1159
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/activities/chat/messages.js
1160
+ function isRecord$1(value) {
1161
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1162
+ }
1155
1163
  /**
1156
1164
  * Check if a MessagePart is a content part (text, image, audio, video, document)
1157
1165
  * that maps directly to a ModelMessage ContentPart.
@@ -1196,6 +1204,126 @@ function getTextContent(content) {
1196
1204
  if (typeof content === "string") return content;
1197
1205
  return content.filter((part) => part.type === "text").map((part) => part.content).join("");
1198
1206
  }
1207
+ function historyTextFromParts(parts) {
1208
+ const blocks = [];
1209
+ for (const part of parts) if (part.type === "text" && part.content !== "") blocks.push(part.content);
1210
+ else if (part.type === "structured-output" && part.status === "complete" && part.raw !== "") blocks.push(part.raw);
1211
+ else if (part.type === "subagent") {
1212
+ const nested = subagentHistoryText(part);
1213
+ if (nested !== "") blocks.push(nested);
1214
+ }
1215
+ return blocks.join("\n\n");
1216
+ }
1217
+ /** Child text for a later turn. The name stays so the next agent can tell the notes apart. */
1218
+ function subagentHistoryText(part) {
1219
+ const blocks = [];
1220
+ for (const message of part.subagent.messages) {
1221
+ if (!("parts" in message)) continue;
1222
+ const text = historyTextFromParts(message.parts).trim();
1223
+ if (text !== "") blocks.push(text);
1224
+ }
1225
+ if (blocks.length === 0) return "";
1226
+ return `${part.subagent.name}:\n${blocks.join("\n\n")}`;
1227
+ }
1228
+ function toolResultContent(content) {
1229
+ return Array.isArray(content) ? content : getTextContent(content);
1230
+ }
1231
+ function restoreToolResultOwnership(message) {
1232
+ if (!("role" in message) || message.role !== "tool") return message;
1233
+ const source = message;
1234
+ const owned = tanstackMetadata(source)?.toolResult;
1235
+ if (!isRecord$1(owned)) return message;
1236
+ if ("content" in owned && !isContentPartArray(owned.content)) return message;
1237
+ const next = { ...message };
1238
+ if (!("id" in owned)) Reflect.deleteProperty(next, "id");
1239
+ if (!("createdAt" in owned)) Reflect.deleteProperty(next, "createdAt");
1240
+ if (typeof owned.id === "string") Reflect.set(next, "id", owned.id);
1241
+ if (typeof owned.createdAt === "string") {
1242
+ const createdAt = coerceCreatedAt(owned.createdAt);
1243
+ if (createdAt) Reflect.set(next, "createdAt", createdAt);
1244
+ }
1245
+ if (isContentPartArray(owned.content)) Reflect.set(next, "content", owned.content);
1246
+ const sourceMetadata = Reflect.get(source, "metadata");
1247
+ if (isRecord$1(sourceMetadata)) {
1248
+ const tanstack = sourceMetadata.tanstack;
1249
+ if (isRecord$1(tanstack)) {
1250
+ const { toolResult: _toolResult, ...restTanstack } = tanstack;
1251
+ const restMetadata = { ...sourceMetadata };
1252
+ if (Object.keys(restTanstack).length) restMetadata.tanstack = restTanstack;
1253
+ else delete restMetadata.tanstack;
1254
+ if (Object.keys(restMetadata).length) Reflect.set(next, "metadata", restMetadata);
1255
+ else Reflect.deleteProperty(next, "metadata");
1256
+ }
1257
+ }
1258
+ return next;
1259
+ }
1260
+ /**
1261
+ * Rebuild a `Date` from a live `Date` or from an ISO string.
1262
+ * `JSON.stringify` turns `Date` into a string, so persistence reload
1263
+ * and AG-UI wire both land here as strings.
1264
+ */
1265
+ function coerceCreatedAt(value) {
1266
+ if (value instanceof Date) return Number.isNaN(value.getTime()) ? void 0 : value;
1267
+ if (typeof value !== "string") return void 0;
1268
+ const createdAt = new Date(value);
1269
+ return Number.isNaN(createdAt.getTime()) ? void 0 : createdAt;
1270
+ }
1271
+ function normalizeMessagePart(part) {
1272
+ if (part.type !== "tool-result") return part;
1273
+ const createdAt = coerceCreatedAt(part.createdAt);
1274
+ const { createdAt: _createdAt, ...rest } = part;
1275
+ return createdAt === void 0 ? rest : {
1276
+ ...rest,
1277
+ createdAt
1278
+ };
1279
+ }
1280
+ function createdAtFromMetadata(source) {
1281
+ return coerceCreatedAt(tanstackMetadata(source)?.createdAt);
1282
+ }
1283
+ function optionalCreatedAt(source) {
1284
+ const createdAt = coerceCreatedAt(source.createdAt);
1285
+ return createdAt !== void 0 ? { createdAt } : {};
1286
+ }
1287
+ function optionalName(source) {
1288
+ return source.name !== void 0 ? { name: source.name } : {};
1289
+ }
1290
+ function isUiResourcePart(value) {
1291
+ if (value == null || typeof value !== "object" || Array.isArray(value)) return false;
1292
+ if (!("type" in value) || value.type !== "ui-resource") return false;
1293
+ if (!("toolCallId" in value) || typeof value.toolCallId !== "string") return false;
1294
+ if (!("toolName" in value) || typeof value.toolName !== "string") return false;
1295
+ if (!("resource" in value) || value.resource == null || typeof value.resource !== "object" || Array.isArray(value.resource)) return false;
1296
+ const resource = value.resource;
1297
+ return "uri" in resource && typeof resource.uri === "string" && "mimeType" in resource && typeof resource.mimeType === "string";
1298
+ }
1299
+ function uiResourceKey(part) {
1300
+ return `${part.toolCallId}\0${part.toolName}\0${part.resource.uri}`;
1301
+ }
1302
+ function appendUiResources(ui, resources) {
1303
+ if (resources.length === 0) return ui;
1304
+ const seen = new Set(ui.parts.filter(isUiResourcePart).map((part) => uiResourceKey(part)));
1305
+ const extra = resources.filter((part) => !seen.has(uiResourceKey(part)));
1306
+ if (extra.length === 0) return ui;
1307
+ return {
1308
+ ...ui,
1309
+ parts: [...ui.parts, ...extra]
1310
+ };
1311
+ }
1312
+ function assistantMetadata(uiMessage) {
1313
+ const fromParts = uiMessage.parts.filter(isUiResourcePart);
1314
+ const current = uiMessage.metadata ?? {};
1315
+ const previous = tanstackMetadata(uiMessage);
1316
+ const tanstack = {};
1317
+ if (previous?.model !== void 0) tanstack.model = previous.model;
1318
+ if (previous?.runId !== void 0) tanstack.runId = previous.runId;
1319
+ if (previous?.run?.id !== void 0) tanstack.run = { id: previous.run.id };
1320
+ if (previous?.signature !== void 0) tanstack.signature = previous.signature;
1321
+ if (fromParts.length > 0) tanstack.uiResources = fromParts;
1322
+ const result = { ...current };
1323
+ if (Object.keys(tanstack).length > 0) result.tanstack = tanstack;
1324
+ else delete result.tanstack;
1325
+ return Object.keys(result).length > 0 ? result : void 0;
1326
+ }
1199
1327
  /**
1200
1328
  * Convert a UIMessage to ModelMessage(s)
1201
1329
  *
@@ -1231,7 +1359,9 @@ function buildUserOrToolMessage(uiMessage) {
1231
1359
  id: uiMessage.id,
1232
1360
  role: uiMessage.role,
1233
1361
  content: collapseContentParts(contentParts),
1234
- ...uiMessage.createdAt !== void 0 && { createdAt: uiMessage.createdAt }
1362
+ ...optionalName(uiMessage),
1363
+ ...optionalCreatedAt(uiMessage),
1364
+ ...uiMessage.metadata !== void 0 && { metadata: uiMessage.metadata }
1235
1365
  };
1236
1366
  }
1237
1367
  function createSegment() {
@@ -1257,20 +1387,29 @@ function buildAssistantMessages(uiMessage) {
1257
1387
  let current = createSegment();
1258
1388
  let pendingThinking = [];
1259
1389
  const emittedToolResultIds = /* @__PURE__ */ new Set();
1260
- function flushSegment() {
1390
+ const identityFields = {
1391
+ id: uiMessage.id,
1392
+ ...optionalName(uiMessage),
1393
+ ...optionalCreatedAt(uiMessage)
1394
+ };
1395
+ const metadata = assistantMetadata(uiMessage);
1396
+ const assistantFields = {
1397
+ ...identityFields,
1398
+ ...metadata !== void 0 && { metadata }
1399
+ };
1400
+ function flushSegment(force = false) {
1261
1401
  const content = collapseContentParts(current.contentParts);
1262
1402
  const hasContent = content !== null;
1263
1403
  const hasToolCalls = current.toolCalls.length > 0;
1264
1404
  const hasThinking = pendingThinking.length > 0;
1265
- if (hasContent || hasToolCalls || hasThinking) {
1405
+ if (force || hasContent || hasToolCalls || hasThinking) {
1266
1406
  messageList.push({
1267
- id: uiMessage.id,
1407
+ ...assistantFields,
1268
1408
  role: "assistant",
1269
1409
  content,
1270
1410
  ...hasToolCalls && { toolCalls: current.toolCalls },
1271
1411
  ...hasThinking && { thinking: pendingThinking },
1272
- ...current.structuredOutput && { structuredOutput: current.structuredOutput },
1273
- ...uiMessage.createdAt !== void 0 && { createdAt: uiMessage.createdAt }
1412
+ ...current.structuredOutput && { structuredOutput: current.structuredOutput }
1274
1413
  });
1275
1414
  pendingThinking = [];
1276
1415
  }
@@ -1296,20 +1435,24 @@ function buildAssistantMessages(uiMessage) {
1296
1435
  });
1297
1436
  break;
1298
1437
  case "tool-result":
1299
- flushSegment();
1438
+ flushSegment(messageList.length === 0);
1300
1439
  if ((part.state === "complete" || part.state === "error") && !emittedToolResultIds.has(part.toolCallId)) {
1440
+ const metadata = part.outcome === void 0 ? part.metadata : withTanstackMetadata(part, { toolResultOutcome: part.outcome }).metadata;
1301
1441
  messageList.push({
1302
- id: uiMessage.id,
1442
+ ...part.id !== void 0 && { id: part.id },
1443
+ ...optionalCreatedAt(part),
1303
1444
  role: "tool",
1304
1445
  content: part.content,
1305
1446
  toolCallId: part.toolCallId,
1306
- ...uiMessage.createdAt !== void 0 && { createdAt: uiMessage.createdAt }
1447
+ ...part.name !== void 0 && { name: part.name },
1448
+ ...metadata !== void 0 && { metadata },
1449
+ ...part.error !== void 0 && { error: part.error }
1307
1450
  });
1308
1451
  emittedToolResultIds.add(part.toolCallId);
1309
1452
  }
1310
1453
  break;
1311
1454
  case "thinking":
1312
- if (part.content) {
1455
+ if (part.content || part.signature) {
1313
1456
  if (current.toolCalls.some(isProviderExecutedToolCall)) flushSegment();
1314
1457
  pendingThinking.push({
1315
1458
  content: part.content,
@@ -1317,15 +1460,29 @@ function buildAssistantMessages(uiMessage) {
1317
1460
  });
1318
1461
  }
1319
1462
  break;
1320
- case "structured-output": if (part.status === "complete") {
1321
- const serialized = part.raw !== "" ? part.raw : part.data !== void 0 ? safeJsonStringify(part.data) : "";
1322
- if (serialized !== "") {
1463
+ case "structured-output":
1464
+ if (part.status === "complete") {
1465
+ const serialized = part.raw !== "" ? part.raw : part.data !== void 0 ? safeJsonStringify(part.data) : "";
1466
+ if (serialized !== "") {
1467
+ current.contentParts.push({
1468
+ type: "text",
1469
+ content: serialized
1470
+ });
1471
+ current.structuredOutput = part;
1472
+ }
1473
+ }
1474
+ break;
1475
+ case "ui-resource": break;
1476
+ case "subagent": {
1477
+ const block = part.subagent.parentToolCallId === void 0 ? subagentHistoryText(part) : "";
1478
+ if (block !== "") {
1479
+ const prefix = current.contentParts.length > 0 ? "\n\n" : "";
1323
1480
  current.contentParts.push({
1324
1481
  type: "text",
1325
- content: serialized
1482
+ content: `${prefix}${block}`
1326
1483
  });
1327
- current.structuredOutput = part;
1328
1484
  }
1485
+ break;
1329
1486
  }
1330
1487
  }
1331
1488
  flushSegment();
@@ -1333,18 +1490,15 @@ function buildAssistantMessages(uiMessage) {
1333
1490
  if (part.type !== "tool-call") continue;
1334
1491
  if (part.output !== void 0 && !emittedToolResultIds.has(part.id)) {
1335
1492
  messageList.push({
1336
- id: uiMessage.id,
1337
1493
  role: "tool",
1338
1494
  content: normalizeToolResult(part.output),
1339
- toolCallId: part.id,
1340
- ...uiMessage.createdAt !== void 0 && { createdAt: uiMessage.createdAt }
1495
+ toolCallId: part.id
1341
1496
  });
1342
1497
  emittedToolResultIds.add(part.id);
1343
1498
  }
1344
1499
  if (part.output === void 0 && part.state === "approval-responded" && part.approval?.approved !== void 0 && !emittedToolResultIds.has(part.id)) {
1345
1500
  const approved = part.approval.approved;
1346
1501
  messageList.push({
1347
- id: uiMessage.id,
1348
1502
  role: "tool",
1349
1503
  content: JSON.stringify({
1350
1504
  approved,
@@ -1352,16 +1506,15 @@ function buildAssistantMessages(uiMessage) {
1352
1506
  message: approved ? "User approved this action" : "User denied this action"
1353
1507
  }),
1354
1508
  toolCallId: part.id,
1355
- ...uiMessage.createdAt !== void 0 && { createdAt: uiMessage.createdAt }
1509
+ ...approved === false && { metadata: { tanstack: { toolResultOutcome: "denied" } } }
1356
1510
  });
1357
1511
  emittedToolResultIds.add(part.id);
1358
1512
  }
1359
1513
  }
1360
1514
  if (messageList.length === 0) messageList.push({
1361
- id: uiMessage.id,
1515
+ ...assistantFields,
1362
1516
  role: "assistant",
1363
- content: null,
1364
- ...uiMessage.createdAt !== void 0 && { createdAt: uiMessage.createdAt }
1517
+ content: null
1365
1518
  });
1366
1519
  return messageList;
1367
1520
  }
@@ -1379,22 +1532,43 @@ function buildAssistantMessages(uiMessage) {
1379
1532
  */
1380
1533
  function modelMessageToUIMessage(modelMessage, id) {
1381
1534
  const parts = [];
1535
+ const createdAt = coerceCreatedAt(modelMessage.createdAt);
1382
1536
  if (modelMessage.role === "assistant" && modelMessage.thinking?.length) for (const thinking of modelMessage.thinking) {
1383
- if (!thinking.content) continue;
1537
+ if (!thinking.content && !thinking.signature) continue;
1384
1538
  parts.push({
1385
1539
  type: "thinking",
1386
1540
  content: thinking.content,
1387
1541
  ...thinking.signature && { signature: thinking.signature }
1388
1542
  });
1389
1543
  }
1390
- if (modelMessage.role === "assistant" && modelMessage.structuredOutput) parts.push(modelMessage.structuredOutput);
1391
- else if (modelMessage.role === "tool" && modelMessage.toolCallId) parts.push({
1392
- type: "tool-result",
1393
- toolCallId: modelMessage.toolCallId,
1394
- content: getTextContent(modelMessage.content),
1395
- state: "complete"
1396
- });
1397
- else if (Array.isArray(modelMessage.content)) for (const part of modelMessage.content) parts.push(part);
1544
+ const structuredOutput = modelMessage.structuredOutput ?? snapshotStructuredOutput(tanstackMetadata(modelMessage)?.structuredOutput);
1545
+ if (modelMessage.role === "assistant" && structuredOutput) {
1546
+ if (typeof modelMessage.content === "string" && modelMessage.content && modelMessage.content !== structuredOutput.raw) {
1547
+ const suffix = structuredOutput.raw;
1548
+ const text = suffix !== "" && modelMessage.content.endsWith(suffix) ? modelMessage.content.slice(0, -suffix.length) : modelMessage.content;
1549
+ if (text) parts.push({
1550
+ type: "text",
1551
+ content: text
1552
+ });
1553
+ }
1554
+ parts.push(structuredOutput);
1555
+ } else if (modelMessage.role === "tool" && modelMessage.toolCallId) {
1556
+ const rawToolResultOutcome = tanstackMetadata(modelMessage)?.toolResultOutcome;
1557
+ const toolResultOutcome = isToolResultOutcome(rawToolResultOutcome) ? rawToolResultOutcome : void 0;
1558
+ const resultState = modelMessage.error === void 0 && toolResultOutcome === void 0 ? "complete" : "error";
1559
+ parts.push({
1560
+ type: "tool-result",
1561
+ toolCallId: modelMessage.toolCallId,
1562
+ content: toolResultContent(modelMessage.content),
1563
+ state: resultState,
1564
+ ...toolResultOutcome !== void 0 && { outcome: toolResultOutcome },
1565
+ ...modelMessage.id !== void 0 && { id: modelMessage.id },
1566
+ ...modelMessage.name !== void 0 && { name: modelMessage.name },
1567
+ ...modelMessage.metadata !== void 0 && { metadata: modelMessage.metadata },
1568
+ ...createdAt !== void 0 && { createdAt },
1569
+ ...modelMessage.error !== void 0 && { error: modelMessage.error }
1570
+ });
1571
+ } else if (Array.isArray(modelMessage.content)) for (const part of modelMessage.content) parts.push(part);
1398
1572
  else {
1399
1573
  const textContent = getTextContent(modelMessage.content);
1400
1574
  if (textContent) parts.push({
@@ -1419,12 +1593,16 @@ function modelMessageToUIMessage(modelMessage, id) {
1419
1593
  ...toolCall.metadata !== void 0 && { metadata: toolCall.metadata }
1420
1594
  });
1421
1595
  }
1422
- return {
1596
+ const ui = {
1423
1597
  id: id || generateMessageId(),
1424
1598
  role: modelMessage.role === "tool" ? "assistant" : modelMessage.role,
1425
1599
  parts,
1426
- ...modelMessage.createdAt !== void 0 && { createdAt: modelMessage.createdAt }
1600
+ ...optionalName(modelMessage),
1601
+ ...createdAt !== void 0 && { createdAt },
1602
+ ...modelMessage.metadata !== void 0 && { metadata: modelMessage.metadata }
1427
1603
  };
1604
+ const storedResources = tanstackMetadata(modelMessage)?.uiResources;
1605
+ return appendUiResources(ui, Array.isArray(storedResources) ? storedResources.filter(isUiResourcePart) : []);
1428
1606
  }
1429
1607
  /**
1430
1608
  * Normalize a single AG-UI `MESSAGES_SNAPSHOT` message into a `UIMessage`.
@@ -1455,25 +1633,38 @@ function aguiSnapshotMessageToUIMessage(message) {
1455
1633
  parts: aguiUserContentToParts(message.content)
1456
1634
  });
1457
1635
  case "assistant": {
1458
- const toolCallMetadata = tanstackMetadata(message)?.toolCallMetadata;
1636
+ const metadata = tanstackMetadata(message);
1637
+ const toolCallMetadata = metadata?.toolCallMetadata;
1638
+ const structuredOutput = snapshotStructuredOutput(metadata?.structuredOutput);
1459
1639
  const toolCalls = message.toolCalls?.map((toolCall) => {
1460
- const metadata = toolCallMetadata != null && typeof toolCallMetadata === "object" ? toolCallMetadata[toolCall.id] : void 0;
1461
- return metadata !== void 0 ? {
1640
+ const callMetadata = toolCallMetadata != null && typeof toolCallMetadata === "object" ? toolCallMetadata[toolCall.id] : void 0;
1641
+ return callMetadata !== void 0 ? {
1462
1642
  ...toolCall,
1463
- metadata
1643
+ metadata: callMetadata
1464
1644
  } : toolCall;
1465
1645
  });
1466
1646
  return applySnapshotMetadata(message, modelMessageToUIMessage({
1467
1647
  role: "assistant",
1468
1648
  content: message.content ?? null,
1469
- ...toolCalls && { toolCalls }
1649
+ ...optionalName(message),
1650
+ ...toolCalls && { toolCalls },
1651
+ ...structuredOutput && { structuredOutput }
1652
+ }, id));
1653
+ }
1654
+ case "tool": {
1655
+ message = restoreToolResultOwnership(message);
1656
+ const createdAt = coerceCreatedAt("createdAt" in message ? message.createdAt : void 0) ?? createdAtFromMetadata(message);
1657
+ return applySnapshotMetadata(message, modelMessageToUIMessage({
1658
+ role: "tool",
1659
+ content: isContentPartArray(message.content) ? message.content : aguiContentToContentParts(message.content),
1660
+ toolCallId: message.toolCallId,
1661
+ ..."name" in message && typeof message.name === "string" ? { name: message.name } : {},
1662
+ ..."id" in message && typeof message.id === "string" ? { id: message.id } : {},
1663
+ ..."metadata" in message && message.metadata != null ? { metadata: message.metadata } : {},
1664
+ ...createdAt !== void 0 ? { createdAt } : {},
1665
+ ...message.error !== void 0 && { error: message.error }
1470
1666
  }, id));
1471
1667
  }
1472
- case "tool": return applySnapshotMetadata(message, modelMessageToUIMessage({
1473
- role: "tool",
1474
- content: message.content,
1475
- toolCallId: message.toolCallId
1476
- }, id));
1477
1668
  case "system":
1478
1669
  case "developer": return applySnapshotMetadata(message, {
1479
1670
  id,
@@ -1485,12 +1676,13 @@ function aguiSnapshotMessageToUIMessage(message) {
1485
1676
  });
1486
1677
  case "reasoning": {
1487
1678
  const signature = encryptedValueFrom(message);
1679
+ const content = typeof message.content === "string" ? message.content : "";
1488
1680
  return applySnapshotMetadata(message, {
1489
1681
  id,
1490
1682
  role: "assistant",
1491
- parts: message.content ? [{
1683
+ parts: content || signature !== void 0 ? [{
1492
1684
  type: "thinking",
1493
- content: message.content,
1685
+ content,
1494
1686
  ...signature !== void 0 ? { signature } : {}
1495
1687
  }] : []
1496
1688
  });
@@ -1504,17 +1696,50 @@ function aguiSnapshotMessageToUIMessage(message) {
1504
1696
  }
1505
1697
  /** Copy snapshot metadata when it is a record. Rebuild createdAt from tanstack.createdAt. */
1506
1698
  function applySnapshotMetadata(source, ui) {
1507
- if (!("metadata" in source)) return ui;
1508
- const raw = source.metadata;
1509
- if (raw == null || typeof raw !== "object" || Array.isArray(raw)) return ui;
1510
- const metadata = raw;
1511
- const createdAtRaw = tanstackMetadata(metadata)?.createdAt;
1512
- const createdAt = typeof createdAtRaw === "string" ? new Date(createdAtRaw) : void 0;
1513
- const createdAtValid = createdAt !== void 0 && !Number.isNaN(createdAt.getTime());
1514
- return {
1699
+ const normalizedParts = ui.parts.map((part) => normalizeMessagePart(part));
1700
+ if (normalizedParts !== ui.parts) ui = {
1701
+ ...ui,
1702
+ parts: normalizedParts
1703
+ };
1704
+ const name = "name" in source && typeof source.name === "string" ? source.name : void 0;
1705
+ let next = name !== void 0 ? {
1515
1706
  ...ui,
1516
- metadata,
1517
- ...createdAtValid ? { createdAt } : {}
1707
+ name
1708
+ } : ui;
1709
+ let metadata;
1710
+ if ("metadata" in source) {
1711
+ const raw = source.metadata;
1712
+ if (raw != null && typeof raw === "object" && !Array.isArray(raw)) {
1713
+ metadata = raw;
1714
+ next = {
1715
+ ...next,
1716
+ metadata
1717
+ };
1718
+ }
1719
+ }
1720
+ const createdAt = coerceCreatedAt(next.createdAt) ?? (metadata !== void 0 ? createdAtFromMetadata(metadata) : void 0);
1721
+ if (createdAt !== void 0 && !Object.is(createdAt, next.createdAt)) next = {
1722
+ ...next,
1723
+ createdAt
1724
+ };
1725
+ else if (createdAt === void 0 && next.createdAt !== void 0) {
1726
+ const { createdAt: _invalid, ...rest } = next;
1727
+ next = rest;
1728
+ }
1729
+ const uiResources = tanstackMetadata(metadata)?.uiResources;
1730
+ const resources = Array.isArray(uiResources) ? uiResources.filter(isUiResourcePart) : [];
1731
+ return appendUiResources(next, resources);
1732
+ }
1733
+ function snapshotStructuredOutput(value) {
1734
+ if (value == null || value.status !== "streaming" && value.status !== "complete" && value.status !== "error" || typeof value.raw !== "string") return;
1735
+ return {
1736
+ type: "structured-output",
1737
+ status: value.status,
1738
+ raw: value.raw,
1739
+ ...value.partial !== void 0 ? { partial: value.partial } : {},
1740
+ ...value.data !== void 0 ? { data: value.data } : {},
1741
+ ...value.reasoning ? { reasoning: value.reasoning } : {},
1742
+ ...value.errorMessage !== void 0 ? { errorMessage: value.errorMessage } : {}
1518
1743
  };
1519
1744
  }
1520
1745
  /**
@@ -1523,21 +1748,26 @@ function applySnapshotMetadata(source, ui) {
1523
1748
  * AG-UI user content is either a plain string or a multimodal array whose text
1524
1749
  * entries use `{ type: 'text', text }` (vs. TanStack's `{ type: 'text', content }`).
1525
1750
  * Text entries are rewritten to the TanStack shape; image/audio/video/document
1526
- * entries already match `ContentPart` and pass through. `binary` entries have no
1527
- * TanStack equivalent and are dropped.
1751
+ * entries already match `ContentPart` and pass through.
1528
1752
  */
1529
1753
  function aguiUserContentToParts(content) {
1530
- if (typeof content === "string") return content ? [{
1531
- type: "text",
1532
- content
1533
- }] : [];
1534
- const parts = [];
1535
- for (const part of content) if (part.type === "text") parts.push({
1754
+ const converted = aguiContentToContentParts(content);
1755
+ return typeof converted === "string" ? converted ? [{
1536
1756
  type: "text",
1537
- content: part.text
1757
+ content: converted
1758
+ }] : [] : converted;
1759
+ }
1760
+ /** Convert wire content parts. Data, url, and file sources pass through. */
1761
+ function aguiContentToContentParts(content) {
1762
+ if (typeof content === "string") return content;
1763
+ return content.map((part) => {
1764
+ if (part.type !== "text") return part;
1765
+ const { text, ...rest } = part;
1766
+ return {
1767
+ ...rest,
1768
+ content: text
1769
+ };
1538
1770
  });
1539
- else if (part.type !== "binary") parts.push(part);
1540
- return parts;
1541
1771
  }
1542
1772
  /**
1543
1773
  * Normalize a message (UIMessage or ModelMessage) to a UIMessage
@@ -1548,14 +1778,17 @@ function aguiUserContentToParts(content) {
1548
1778
  * @returns A UIMessage with guaranteed id and createdAt
1549
1779
  */
1550
1780
  function normalizeToUIMessage(message, generateId) {
1551
- if ("parts" in message) return {
1552
- ...message,
1553
- id: message.id || generateId(),
1554
- createdAt: message.createdAt || /* @__PURE__ */ new Date()
1555
- };
1556
- else return {
1781
+ if ("parts" in message) {
1782
+ const parts = message.parts.map((part) => normalizeMessagePart(part));
1783
+ return {
1784
+ ...message,
1785
+ parts,
1786
+ id: message.id || generateId(),
1787
+ createdAt: coerceCreatedAt(message.createdAt) ?? /* @__PURE__ */ new Date()
1788
+ };
1789
+ } else return {
1557
1790
  ...modelMessageToUIMessage(message, generateId()),
1558
- createdAt: message.createdAt ?? /* @__PURE__ */ new Date()
1791
+ createdAt: coerceCreatedAt(message.createdAt) ?? /* @__PURE__ */ new Date()
1559
1792
  };
1560
1793
  }
1561
1794
  /**
@@ -1565,7 +1798,357 @@ function generateMessageId() {
1565
1798
  return `msg-${Date.now()}-${Math.random().toString(36).substring(7)}`;
1566
1799
  }
1567
1800
  //#endregion
1568
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/activities/chat/tools/tool-definition.js
1801
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/utilities/ag-ui-wire.js
1802
+ function isRecord(value) {
1803
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1804
+ }
1805
+ function rebuiltToolMetadata(metadata, createdAt, id, content, anchorOwnsUiResources = false, outcome) {
1806
+ const source = isRecord(metadata) ? metadata : {};
1807
+ const tanstack = isRecord(source.tanstack) ? { ...source.tanstack } : {};
1808
+ if (anchorOwnsUiResources) delete tanstack.uiResources;
1809
+ const date = coerceCreatedAt(createdAt);
1810
+ const toolResult = {
1811
+ ...id !== void 0 ? { id } : {},
1812
+ ...date && { createdAt: date.toISOString() },
1813
+ ...Array.isArray(content) && { content }
1814
+ };
1815
+ const result = {
1816
+ ...source,
1817
+ tanstack: {
1818
+ ...tanstack,
1819
+ ...outcome !== void 0 && { toolResultOutcome: outcome },
1820
+ toolResult
1821
+ }
1822
+ };
1823
+ return Object.keys(result).length ? result : void 0;
1824
+ }
1825
+ /**
1826
+ * Serialize TanStack `UIMessage`s and `ModelMessage`s into the AG-UI
1827
+ * `RunAgentInput.messages` wire shape. Anchors are spec-only (`id`, `role`,
1828
+ * `name`, `content`, `toolCalls`, `metadata`). Tool results and thinking parts
1829
+ * on assistant messages are additionally emitted as fan-out
1830
+ * `{role:'tool',...}` and `{role:'reasoning',...}` entries for strict AG-UI
1831
+ * server consumers. Set `includeSnapshotStructuredOutput` to retain complete
1832
+ * structured-output metadata for UI snapshots.
1833
+ */
1834
+ function uiMessagesToWire(messages, options) {
1835
+ const wire = [];
1836
+ const usedWireIds = new Set(messages.flatMap((message) => "id" in message && message.id && message.role !== "tool" ? [message.id] : []));
1837
+ const includeSnapshotStructuredOutput = options?.includeSnapshotStructuredOutput ?? false;
1838
+ const assistantIds = /* @__PURE__ */ new Set();
1839
+ for (const msg of messages) if (msg.role === "assistant" && msg.id !== void 0) assistantIds.add(msg.id);
1840
+ for (const msg of messages) {
1841
+ if (!("parts" in msg) && msg.role === "tool" && msg.toolCallId) {
1842
+ const id = uniqueToolWireId(toolWireId(msg.id, msg.toolCallId, assistantIds), usedWireIds);
1843
+ const metadata = rebuiltToolMetadata(msg.metadata, msg.createdAt, msg.id, msg.content);
1844
+ wire.push({
1845
+ role: "tool",
1846
+ id,
1847
+ ...msg.name !== void 0 && { name: msg.name },
1848
+ toolCallId: msg.toolCallId,
1849
+ content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
1850
+ ...msg.error !== void 0 && { error: msg.error },
1851
+ ...metadata !== void 0 && { metadata }
1852
+ });
1853
+ continue;
1854
+ }
1855
+ const uiMessage = "parts" in msg ? msg : modelMessageToUIMessage(msg, msg.id);
1856
+ const parts = uiMessage.parts;
1857
+ usedWireIds.add(uiMessage.id);
1858
+ if (msg.role === "system") {
1859
+ wire.push(toAnchor(uiMessage, "system", { content: parts.length > 0 ? collectText(parts) : msg.content ?? "" }, parts, includeSnapshotStructuredOutput));
1860
+ continue;
1861
+ }
1862
+ if (msg.role === "user") {
1863
+ wire.push(toAnchor(uiMessage, "user", { content: parts.length > 0 ? collectUserContent(parts) : msg.content ?? "" }, parts, includeSnapshotStructuredOutput));
1864
+ continue;
1865
+ }
1866
+ let current = {
1867
+ thinking: [],
1868
+ parts: []
1869
+ };
1870
+ const segments = [current];
1871
+ for (const part of parts) if (part.type === "thinking") if (current.parts.some((p) => p.type === "tool-call" && isProviderExecutedToolCall(p))) {
1872
+ current = {
1873
+ thinking: [part],
1874
+ parts: []
1875
+ };
1876
+ segments.push(current);
1877
+ } else current.thinking.push(part);
1878
+ else current.parts.push(part);
1879
+ segments.forEach((segment, index) => {
1880
+ for (const part of segment.thinking) {
1881
+ const reasoning = {
1882
+ role: "reasoning",
1883
+ id: uniqueWireId(deriveReasoningId(uiMessage.id, part), usedWireIds),
1884
+ content: part.content
1885
+ };
1886
+ if (part.signature) reasoning.encryptedValue = part.signature;
1887
+ wire.push(reasoning);
1888
+ }
1889
+ const text = collectText(segment.parts);
1890
+ const toolCalls = collectToolCalls(segment.parts);
1891
+ const anchorMessage = index === 0 ? uiMessage : {
1892
+ ...uiMessage,
1893
+ id: uniqueWireId(`${uiMessage.id}-segment-${index}`, usedWireIds)
1894
+ };
1895
+ wire.push(toAnchor(anchorMessage, "assistant", {
1896
+ ...text !== "" && { content: text },
1897
+ ...toolCalls && { toolCalls }
1898
+ }, segment.parts, includeSnapshotStructuredOutput));
1899
+ });
1900
+ const explicitToolResults = new Set(parts.flatMap((part) => part.type === "tool-result" ? [part.toolCallId] : []));
1901
+ for (const part of parts) if (part.type === "tool-result") {
1902
+ const id = uniqueToolWireId(part.id ?? deriveToolMessageId(part.toolCallId), usedWireIds);
1903
+ const metadata = rebuiltToolMetadata(part.metadata, part.createdAt, part.id, part.content, true, part.outcome);
1904
+ wire.push({
1905
+ role: "tool",
1906
+ id,
1907
+ toolCallId: part.toolCallId,
1908
+ ...part.name !== void 0 && { name: part.name },
1909
+ content: typeof part.content === "string" ? part.content : JSON.stringify(part.content),
1910
+ ...part.error !== void 0 && { error: part.error },
1911
+ ...metadata !== void 0 && { metadata }
1912
+ });
1913
+ } else if (part.type === "tool-call") {
1914
+ const approved = part.approval?.approved;
1915
+ if (explicitToolResults.has(part.id) || part.output === void 0 && (part.state !== "approval-responded" || approved === void 0)) continue;
1916
+ const result = part.output !== void 0 ? normalizeToolResult(part.output) : JSON.stringify({
1917
+ approved,
1918
+ ...approved && { pendingExecution: true },
1919
+ message: approved ? "User approved this action" : "User denied this action"
1920
+ });
1921
+ const content = typeof result === "string" ? result : JSON.stringify(result);
1922
+ wire.push({
1923
+ role: "tool",
1924
+ id: uniqueToolWireId(deriveToolMessageId(part.id), usedWireIds),
1925
+ toolCallId: part.id,
1926
+ content,
1927
+ metadata: rebuiltToolMetadata(void 0, void 0, void 0, result, false, part.approval?.approved === false ? "denied" : void 0)
1928
+ });
1929
+ }
1930
+ for (const part of parts) if (part.type === "subagent") wire.push(...subagentToWire(part, options));
1931
+ }
1932
+ return wire;
1933
+ }
1934
+ function toAnchor(msg, role, extras, parts, includeSnapshotStructuredOutput) {
1935
+ const metadata = messageMetadata(msg, parts, includeSnapshotStructuredOutput);
1936
+ const base = {
1937
+ id: msg.id,
1938
+ ...msg.name !== void 0 && { name: msg.name },
1939
+ ...metadata !== void 0 && { metadata }
1940
+ };
1941
+ if (role === "system") return {
1942
+ ...base,
1943
+ role,
1944
+ content: String(extras.content ?? "")
1945
+ };
1946
+ if (role === "user") return {
1947
+ ...base,
1948
+ role,
1949
+ content: extras.content ?? ""
1950
+ };
1951
+ return {
1952
+ ...base,
1953
+ role,
1954
+ ...typeof extras.content === "string" && { content: extras.content },
1955
+ ...extras.toolCalls !== void 0 && { toolCalls: extras.toolCalls }
1956
+ };
1957
+ }
1958
+ function messageMetadata(msg, parts, includeSnapshotStructuredOutput) {
1959
+ const base = { ...msg.metadata ?? {} };
1960
+ const previousTanstack = tanstackMetadata(msg);
1961
+ const tanstack = {};
1962
+ if (previousTanstack?.model !== void 0) tanstack.model = previousTanstack.model;
1963
+ if (previousTanstack?.runId !== void 0) tanstack.runId = previousTanstack.runId;
1964
+ if (previousTanstack?.run?.id !== void 0) tanstack.run = { id: previousTanstack.run.id };
1965
+ if (previousTanstack?.signature !== void 0) tanstack.signature = previousTanstack.signature;
1966
+ const createdAt = coerceCreatedAt(msg.createdAt);
1967
+ if (createdAt !== void 0) tanstack.createdAt = createdAt.toISOString();
1968
+ const structuredOutput = serializedStructuredOutput(parts, includeSnapshotStructuredOutput);
1969
+ if (structuredOutput) tanstack.structuredOutput = structuredOutput;
1970
+ const toolCallMetadata = {};
1971
+ for (const part of parts) if (part.type === "tool-call" && part.metadata !== void 0) toolCallMetadata[part.id] = part.metadata;
1972
+ if (Object.keys(toolCallMetadata).length > 0) tanstack.toolCallMetadata = toolCallMetadata;
1973
+ const uiResources = parts.filter((p) => p.type === "ui-resource");
1974
+ if (uiResources.length > 0) tanstack.uiResources = uiResources;
1975
+ if (Object.keys(tanstack).length > 0) base.tanstack = tanstack;
1976
+ else delete base.tanstack;
1977
+ return Object.keys(base).length > 0 ? base : void 0;
1978
+ }
1979
+ function serializedStructuredOutput(parts, includeSnapshotStructuredOutput) {
1980
+ for (const p of parts) if (p.type === "structured-output" && (includeSnapshotStructuredOutput || p.status !== "complete")) return structuredOutputMetadata(p, includeSnapshotStructuredOutput);
1981
+ }
1982
+ function structuredOutputMetadata(part, includeSnapshotStructuredOutput) {
1983
+ return {
1984
+ status: part.status,
1985
+ raw: part.raw,
1986
+ ...includeSnapshotStructuredOutput && part.partial !== void 0 ? { partial: part.partial } : {},
1987
+ ...includeSnapshotStructuredOutput && part.data !== void 0 ? { data: part.data } : {},
1988
+ ...includeSnapshotStructuredOutput && part.reasoning ? { reasoning: part.reasoning } : {},
1989
+ ...part.errorMessage !== void 0 && { errorMessage: part.errorMessage }
1990
+ };
1991
+ }
1992
+ function collectText(parts) {
1993
+ const out = [];
1994
+ for (const p of parts) if (p.type === "text") out.push(p.content);
1995
+ else if (p.type === "structured-output" && p.status === "complete" && p.raw !== "") out.push(p.raw);
1996
+ return out.join("");
1997
+ }
1998
+ /**
1999
+ * A child's messages, tagged with its AG-UI `subagentRunId`. The card data
2000
+ * rides in `metadata.tanstack.subagent`, so the other side can rebuild the
2001
+ * card and continue a suspended child.
2002
+ */
2003
+ function subagentToWire(part, options) {
2004
+ const { subagent } = part;
2005
+ const info = {
2006
+ name: subagent.name,
2007
+ status: subagent.status,
2008
+ ...subagent.description !== void 0 && { description: subagent.description },
2009
+ ...subagent.error !== void 0 && { error: subagent.error },
2010
+ ...subagent.interruptIds !== void 0 && { interruptIds: subagent.interruptIds },
2011
+ ...subagent.parentSubagentRunId !== void 0 && { parentSubagentRunId: subagent.parentSubagentRunId },
2012
+ ...subagent.parentToolCallId !== void 0 && { parentToolCallId: subagent.parentToolCallId },
2013
+ ...subagent.metadata !== void 0 && { metadata: subagent.metadata }
2014
+ };
2015
+ const child = uiMessagesToWire(subagent.messages, options);
2016
+ const own = child.some((message) => wireSubagentRunId(message) === void 0);
2017
+ return (own ? child : [{
2018
+ id: `subagent:${subagent.id}`,
2019
+ role: "assistant"
2020
+ }, ...child]).map((message, index) => {
2021
+ const nestedId = wireSubagentRunId(message);
2022
+ if (nestedId !== void 0) {
2023
+ const nested = wireSubagentInfo(message);
2024
+ if (!nested || nested.parentSubagentRunId !== void 0) return message;
2025
+ return withSubagentInfo(message, nestedId, {
2026
+ ...nested,
2027
+ parentSubagentRunId: subagent.id
2028
+ });
2029
+ }
2030
+ return withSubagentInfo(message, subagent.id, !own && index === 0 ? {
2031
+ ...info,
2032
+ placeholder: true
2033
+ } : info);
2034
+ });
2035
+ }
2036
+ function withSubagentInfo(message, subagentRunId, info) {
2037
+ const metadata = isRecord(message.metadata) ? message.metadata : {};
2038
+ const tanstack = isRecord(metadata.tanstack) ? metadata.tanstack : {};
2039
+ return {
2040
+ ...message,
2041
+ subagentRunId,
2042
+ metadata: {
2043
+ ...metadata,
2044
+ tanstack: {
2045
+ ...tanstack,
2046
+ subagent: info
2047
+ }
2048
+ }
2049
+ };
2050
+ }
2051
+ function collectUserContent(parts) {
2052
+ if (!parts.some((p) => p.type === "image" || p.type === "audio" || p.type === "video" || p.type === "document")) return collectText(parts);
2053
+ const out = [];
2054
+ for (const p of parts) if (p.type === "text") out.push({
2055
+ type: "text",
2056
+ text: p.content
2057
+ });
2058
+ else if (p.type === "image" || p.type === "audio" || p.type === "video" || p.type === "document") out.push(p);
2059
+ return out;
2060
+ }
2061
+ function thoughtSignatureFromMetadata(metadata) {
2062
+ if (metadata == null || typeof metadata !== "object" || Array.isArray(metadata)) return;
2063
+ if (!("thoughtSignature" in metadata)) return void 0;
2064
+ const value = metadata.thoughtSignature;
2065
+ return typeof value === "string" && value !== "" ? value : void 0;
2066
+ }
2067
+ function collectToolCalls(parts) {
2068
+ const calls = [];
2069
+ for (const p of parts) if (p.type === "tool-call") {
2070
+ const encryptedValue = thoughtSignatureFromMetadata(p.metadata);
2071
+ calls.push({
2072
+ id: p.id,
2073
+ type: "function",
2074
+ function: {
2075
+ name: p.name,
2076
+ arguments: p.arguments
2077
+ },
2078
+ ...encryptedValue !== void 0 ? { encryptedValue } : {}
2079
+ });
2080
+ }
2081
+ return calls.length > 0 ? calls : void 0;
2082
+ }
2083
+ function deriveReasoningId(messageId, part) {
2084
+ return `${messageId}-reasoning-${part.id ?? hashContent(part.content)}`;
2085
+ }
2086
+ function deriveToolMessageId(toolCallId) {
2087
+ return `tool-${toolCallId}`;
2088
+ }
2089
+ function uniqueToolWireId(id, used) {
2090
+ return uniqueWireId(id, used);
2091
+ }
2092
+ function uniqueWireId(id, used) {
2093
+ if (!used.has(id)) {
2094
+ used.add(id);
2095
+ return id;
2096
+ }
2097
+ let suffix = 2;
2098
+ while (used.has(`${id}-${suffix}`)) suffix++;
2099
+ const unique = `${id}-${suffix}`;
2100
+ used.add(unique);
2101
+ return unique;
2102
+ }
2103
+ function toolWireId(id, toolCallId, assistantIds) {
2104
+ const derived = deriveToolMessageId(toolCallId);
2105
+ if (id === void 0 || assistantIds.has(id)) return derived;
2106
+ return id;
2107
+ }
2108
+ function hashContent(s) {
2109
+ let h = 0;
2110
+ for (let i = 0; i < s.length; i++) h = h * 31 + s.charCodeAt(i) | 0;
2111
+ return Math.abs(h).toString(36);
2112
+ }
2113
+ //#endregion
2114
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/utilities/restore-inbound-chunk.js
2115
+ /**
2116
+ * Rebuild TanStack `TokenUsage` (`promptTokens`) from spec `usage[]` plus
2117
+ * leftover fields in `metadata.tanstack.usage`. Restore in-process aliases
2118
+ * that the wire does not keep (`toolName`, `TOOL_CALL_END.input`). Mutates
2119
+ * in place so WeakMap run-id stamps stay attached.
2120
+ */
2121
+ function restorePublicUsage(chunk) {
2122
+ if ((chunk.type === EventType.RUN_FINISHED || chunk.type === EventType.RUN_ERROR) && (Array.isArray(chunk.usage) || isTanstackUsage(chunk.usage))) {
2123
+ const rebuilt = rebuildTokenUsage(chunk.usage, tanstackMetadata(chunk)?.usage);
2124
+ if (rebuilt !== void 0) chunk.usage = rebuilt;
2125
+ }
2126
+ if (chunk.type === EventType.TOOL_CALL_START && chunk.toolName === void 0 && chunk.toolCallName) chunk.toolName = chunk.toolCallName;
2127
+ if (chunk.type === EventType.TOOL_CALL_END && chunk.input === void 0) {
2128
+ const input = tanstackMetadata(chunk)?.input;
2129
+ if (input !== void 0) chunk.input = input;
2130
+ }
2131
+ return chunk;
2132
+ }
2133
+ /**
2134
+ * Rebuild the pre-wire chunk shape after SSE/HTTP/WS ingest.
2135
+ * Copies `metadata.tanstack` extras back to top-level fields and rebuilds
2136
+ * TanStack `TokenUsage` from spec `usage[]` when present.
2137
+ */
2138
+ function restoreInboundChunk(chunk) {
2139
+ restorePublicUsage(chunk);
2140
+ const tanstack = tanstackMetadata(chunk);
2141
+ const next = chunk;
2142
+ if (tanstack == null) return next;
2143
+ for (const [key, value] of Object.entries(tanstack)) {
2144
+ if (key === "usage" || key === "interruptErrors") continue;
2145
+ if (next[key] === void 0 && value !== void 0) next[key] = value;
2146
+ }
2147
+ if (tanstack.interruptErrors !== void 0) next["tanstack:interruptErrors"] = tanstack.interruptErrors;
2148
+ return next;
2149
+ }
2150
+ //#endregion
2151
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/activities/chat/tools/tool-definition.js
1569
2152
  /**
1570
2153
  * Create an isomorphic tool definition that can be used directly or instantiated for server/client
1571
2154
  *
@@ -1627,6 +2210,7 @@ function toolDefinition(config) {
1627
2210
  const outputSchema = config.outputSchema;
1628
2211
  const approvalSchema = config.approvalSchema;
1629
2212
  const needsApproval = config.needsApproval;
2213
+ const execution = config.execution;
1630
2214
  return {
1631
2215
  __toolSide: "definition",
1632
2216
  ...config,
@@ -1634,6 +2218,7 @@ function toolDefinition(config) {
1634
2218
  outputSchema,
1635
2219
  approvalSchema,
1636
2220
  needsApproval,
2221
+ execution,
1637
2222
  server(execute) {
1638
2223
  return {
1639
2224
  __toolSide: "server",
@@ -1642,6 +2227,7 @@ function toolDefinition(config) {
1642
2227
  outputSchema,
1643
2228
  approvalSchema,
1644
2229
  needsApproval,
2230
+ execution,
1645
2231
  execute
1646
2232
  };
1647
2233
  },
@@ -1653,13 +2239,14 @@ function toolDefinition(config) {
1653
2239
  outputSchema,
1654
2240
  approvalSchema,
1655
2241
  needsApproval,
2242
+ execution,
1656
2243
  ...execute !== void 0 && { execute }
1657
2244
  };
1658
2245
  }
1659
2246
  };
1660
2247
  }
1661
2248
  //#endregion
1662
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/utilities/chunk-ids.js
2249
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/utilities/chunk-ids.js
1663
2250
  function stringField(value) {
1664
2251
  return typeof value === "string" && value !== "" ? value : void 0;
1665
2252
  }
@@ -1762,7 +2349,7 @@ var require_options = /* @__PURE__ */ __commonJSMin(((exports) => {
1762
2349
  exports.default = exports.Allow;
1763
2350
  }));
1764
2351
  //#endregion
1765
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/activities/chat/stream/json-parser.js
2352
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/activities/chat/stream/json-parser.js
1766
2353
  var import_dist = (/* @__PURE__ */ __commonJSMin(((exports) => {
1767
2354
  var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
1768
2355
  if (k2 === void 0) k2 = k;
@@ -1984,7 +2571,7 @@ function parsePartialJSON(jsonString) {
1984
2571
  return defaultJSONParser.parse(jsonString);
1985
2572
  }
1986
2573
  //#endregion
1987
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/activities/chat/stream/strategies.js
2574
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/activities/chat/stream/strategies.js
1988
2575
  /**
1989
2576
  * Immediate Strategy - emit on every chunk (default behavior)
1990
2577
  */
@@ -1994,7 +2581,7 @@ var ImmediateStrategy = class {
1994
2581
  }
1995
2582
  };
1996
2583
  //#endregion
1997
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/utilities/errors.js
2584
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/utilities/errors.js
1998
2585
  /**
1999
2586
  * Convert an AG-UI RUN_ERROR event to the Error shape exposed to consumers.
2000
2587
  * Preserves the provider code and sanitized raw event when available, while
@@ -2008,7 +2595,7 @@ function runErrorEventToError(chunk) {
2008
2595
  return error;
2009
2596
  }
2010
2597
  //#endregion
2011
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/activities/chat/stream/message-updaters.js
2598
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/activities/chat/stream/message-updaters.js
2012
2599
  /**
2013
2600
  * Message Updaters (Internal)
2014
2601
  *
@@ -2072,7 +2659,7 @@ function updateToolCallPart(messages, messageId, toolCall) {
2072
2659
  /**
2073
2660
  * Update or add a tool result part to a message.
2074
2661
  */
2075
- function updateToolResultPart(messages, messageId, toolCallId, content, state, error) {
2662
+ function updateToolResultPart(messages, messageId, toolCallId, content, state, error, outcome) {
2076
2663
  return messages.map((msg) => {
2077
2664
  if (msg.id !== messageId) return msg;
2078
2665
  const parts = [...msg.parts];
@@ -2082,6 +2669,7 @@ function updateToolResultPart(messages, messageId, toolCallId, content, state, e
2082
2669
  toolCallId,
2083
2670
  content,
2084
2671
  state,
2672
+ ...outcome !== void 0 && { outcome },
2085
2673
  ...error && { error }
2086
2674
  };
2087
2675
  if (resultPartIndex >= 0) parts[resultPartIndex] = toolResultPart;
@@ -2284,12 +2872,19 @@ function updateThinkingPart(messages, messageId, stepId, content, signature) {
2284
2872
  return messages.map((msg) => {
2285
2873
  if (msg.id !== messageId) return msg;
2286
2874
  const parts = [...msg.parts];
2287
- const thinkingPartIndex = parts.findIndex((p) => p.type === "thinking" && p.stepId === stepId);
2875
+ let thinkingPartIndex = parts.findIndex((p) => p.type === "thinking" && p.stepId === stepId);
2876
+ let adopted;
2877
+ if (thinkingPartIndex < 0) {
2878
+ thinkingPartIndex = parts.findIndex((p) => p.type === "thinking" && p.stepId === void 0);
2879
+ const candidate = parts[thinkingPartIndex];
2880
+ if (candidate?.type === "thinking") adopted = candidate;
2881
+ }
2882
+ const nextSignature = signature ?? adopted?.signature;
2288
2883
  const thinkingPart = {
2289
2884
  type: "thinking",
2290
2885
  content,
2291
2886
  stepId,
2292
- ...signature && { signature }
2887
+ ...nextSignature && { signature: nextSignature }
2293
2888
  };
2294
2889
  if (thinkingPartIndex >= 0) parts[thinkingPartIndex] = thinkingPart;
2295
2890
  else parts.push(thinkingPart);
@@ -2300,7 +2895,7 @@ function updateThinkingPart(messages, messageId, stepId, content, signature) {
2300
2895
  });
2301
2896
  }
2302
2897
  //#endregion
2303
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/activities/chat/stream/processor.js
2898
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/activities/chat/stream/processor.js
2304
2899
  /**
2305
2900
  * Unified Stream Processor
2306
2901
  *
@@ -2330,6 +2925,22 @@ function interruptBatchHasGeneric(interrupts) {
2330
2925
  });
2331
2926
  }
2332
2927
  /**
2928
+ * The canonical arguments string for a `TOOL_CALL_END.input`, or `undefined`
2929
+ * when JSON cannot carry it: `JSON.stringify` returns `undefined` (despite
2930
+ * its declared type) for a top-level function or symbol and throws on BigInt
2931
+ * and circular references. `null` is not tool arguments either, so it also
2932
+ * keeps the streamed value instead of writing `arguments = "null"`.
2933
+ */
2934
+ function serializeToolInput(input) {
2935
+ if (input === void 0 || input === null) return void 0;
2936
+ try {
2937
+ const serialized = JSON.stringify(input);
2938
+ return typeof serialized === "string" ? serialized : void 0;
2939
+ } catch {
2940
+ return;
2941
+ }
2942
+ }
2943
+ /**
2333
2944
  * StreamProcessor - State machine for processing AI response streams
2334
2945
  *
2335
2946
  * Manages the full UIMessage[] conversation and emits events on changes.
@@ -2359,12 +2970,17 @@ var StreamProcessor = class StreamProcessor {
2359
2970
  structuredMessageIds = /* @__PURE__ */ new Set();
2360
2971
  structuredOutputUpdateBatches = /* @__PURE__ */ new Map();
2361
2972
  activeRuns = /* @__PURE__ */ new Set();
2973
+ childProcessors = /* @__PURE__ */ new Map();
2974
+ childToolCalls = /* @__PURE__ */ new Map();
2362
2975
  finishReason = null;
2363
2976
  hasError = false;
2364
2977
  isDone = false;
2978
+ streamEndEmitted = false;
2365
2979
  recording = null;
2366
2980
  recordingStartTime = 0;
2981
+ subagentRunId;
2367
2982
  constructor(options = {}) {
2983
+ this.subagentRunId = options.subagentRunId;
2368
2984
  this.chunkStrategy = options.chunkStrategy || new ImmediateStrategy();
2369
2985
  this.events = options.events || {};
2370
2986
  this.jsonParser = options.jsonParser || defaultJSONParser;
@@ -2379,6 +2995,33 @@ var StreamProcessor = class StreamProcessor {
2379
2995
  this.emitMessagesChange();
2380
2996
  }
2381
2997
  /**
2998
+ * Put older UI messages at the front of the conversation.
2999
+ *
3000
+ * Skip a message if its id is already in the list. Keep the existing message.
3001
+ * Then emit the same messages-change event as `setMessages`.
3002
+ *
3003
+ * Use this for older history pages. The first hydrate window uses `setMessages`.
3004
+ *
3005
+ * @param messages Older UI messages in insertion order. The first item is the oldest.
3006
+ *
3007
+ * @example
3008
+ * ```ts
3009
+ * processor.setMessages([newest])
3010
+ * processor.prependMessages([oldest])
3011
+ * ```
3012
+ */
3013
+ prependMessages(messages) {
3014
+ const existingIds = new Set(this.messages.map((message) => message.id));
3015
+ const olderMessages = [];
3016
+ for (const message of messages) {
3017
+ if (existingIds.has(message.id)) continue;
3018
+ existingIds.add(message.id);
3019
+ olderMessages.push(message);
3020
+ }
3021
+ this.messages = [...olderMessages, ...this.messages];
3022
+ this.emitMessagesChange();
3023
+ }
3024
+ /**
2382
3025
  * Add a user message to the conversation.
2383
3026
  * Supports both simple string content and multimodal content arrays.
2384
3027
  *
@@ -2456,6 +3099,11 @@ var StreamProcessor = class StreamProcessor {
2456
3099
  addToolResult(toolCallId, output, error) {
2457
3100
  const messageWithToolCall = this.messages.find((msg) => msg.parts.some((p) => p.type === "tool-call" && p.id === toolCallId));
2458
3101
  if (!messageWithToolCall) {
3102
+ const owner = this.childOwningToolCall((part) => part.id === toolCallId);
3103
+ if (owner !== void 0) {
3104
+ this.updateChild(owner, (child) => child.addToolResult(toolCallId, output, error));
3105
+ return;
3106
+ }
2459
3107
  console.warn(`[StreamProcessor] Could not find message with tool call ${toolCallId}`);
2460
3108
  return;
2461
3109
  }
@@ -2470,6 +3118,11 @@ var StreamProcessor = class StreamProcessor {
2470
3118
  * Add an approval response (called by client after handling onApprovalRequest)
2471
3119
  */
2472
3120
  addToolApprovalResponse(approvalId, approved) {
3121
+ const owner = this.childOwningToolCall((part) => part.approval?.id === approvalId);
3122
+ if (owner !== void 0) {
3123
+ this.updateChild(owner, (child) => child.addToolApprovalResponse(approvalId, approved));
3124
+ return;
3125
+ }
2473
3126
  this.messages = updateToolCallApprovalResponse(this.messages, approvalId, approved);
2474
3127
  this.emitMessagesChange();
2475
3128
  }
@@ -2523,6 +3176,8 @@ var StreamProcessor = class StreamProcessor {
2523
3176
  this.structuredMessageIds.clear();
2524
3177
  this.structuredOutputUpdateBatches.clear();
2525
3178
  this.pendingManualMessageId = null;
3179
+ this.childProcessors.clear();
3180
+ this.childToolCalls.clear();
2526
3181
  this.emitMessagesChange();
2527
3182
  }
2528
3183
  /**
@@ -2551,7 +3206,17 @@ var StreamProcessor = class StreamProcessor {
2551
3206
  timestamp: Date.now(),
2552
3207
  index: this.recording.chunks.length
2553
3208
  });
3209
+ if (this.routeToChild(chunk)) return;
2554
3210
  switch (chunk.type) {
3211
+ case "SUBAGENT_STARTED":
3212
+ this.handleSubagentStartedEvent(chunk);
3213
+ break;
3214
+ case "SUBAGENT_FINISHED":
3215
+ this.handleSubagentFinishedEvent(chunk);
3216
+ break;
3217
+ case "SUBAGENT_ERROR":
3218
+ this.handleSubagentErrorEvent(chunk);
3219
+ break;
2555
3220
  case "TEXT_MESSAGE_START":
2556
3221
  this.handleTextMessageStartEvent(chunk);
2557
3222
  break;
@@ -2659,8 +3324,16 @@ var StreamProcessor = class StreamProcessor {
2659
3324
  const state = this.messageStates.get(id);
2660
3325
  if (state && state.role === "assistant") return id;
2661
3326
  }
3327
+ for (const [id, state] of [...this.messageStates].reverse()) if (state.role === "assistant") return id;
2662
3328
  return null;
2663
3329
  }
3330
+ resumeAssistantState(id, state) {
3331
+ this.activeMessageIds.add(id);
3332
+ if (state.isComplete || this.isDone) {
3333
+ state.isComplete = false;
3334
+ this.isDone = false;
3335
+ }
3336
+ }
2664
3337
  /**
2665
3338
  * Ensure an active assistant message exists, creating one if needed.
2666
3339
  * Used for backward compat when events arrive without prior TEXT_MESSAGE_START.
@@ -2673,18 +3346,24 @@ var StreamProcessor = class StreamProcessor {
2673
3346
  ensureAssistantMessage(preferredId) {
2674
3347
  if (preferredId) {
2675
3348
  const state = this.getMessageState(preferredId);
2676
- if (state) return {
2677
- messageId: preferredId,
2678
- state
2679
- };
3349
+ if (state) {
3350
+ this.resumeAssistantState(preferredId, state);
3351
+ return {
3352
+ messageId: preferredId,
3353
+ state
3354
+ };
3355
+ }
2680
3356
  }
2681
3357
  const activeId = this.getActiveAssistantMessageId();
2682
3358
  if (activeId) {
2683
3359
  const state = this.getMessageState(activeId);
2684
- if (state) return {
2685
- messageId: activeId,
2686
- state
2687
- };
3360
+ if (state) {
3361
+ this.resumeAssistantState(activeId, state);
3362
+ return {
3363
+ messageId: activeId,
3364
+ state
3365
+ };
3366
+ }
2688
3367
  }
2689
3368
  if (preferredId) {
2690
3369
  const existingMsg = this.messages.find((m) => m.id === preferredId);
@@ -2738,9 +3417,8 @@ var StreamProcessor = class StreamProcessor {
2738
3417
  tanstack: Object.fromEntries(Object.entries(incomingTanstack).filter(([key]) => key !== "content" && key !== "args"))
2739
3418
  } : incomingRecord;
2740
3419
  const metadata = mergeMetadata(message.metadata, toMerge);
2741
- const createdAtRaw = tanstackMetadata(incomingRecord)?.createdAt;
2742
- const createdAt = typeof createdAtRaw === "string" ? new Date(createdAtRaw) : void 0;
2743
- const createdAtValid = createdAt !== void 0 && !Number.isNaN(createdAt.getTime());
3420
+ const createdAt = coerceCreatedAt(tanstackMetadata(incomingRecord)?.createdAt);
3421
+ const createdAtValid = createdAt !== void 0;
2744
3422
  this.messages = this.messages.map((msg) => msg.id === messageId ? {
2745
3423
  ...msg,
2746
3424
  ...metadata !== void 0 ? { metadata } : {},
@@ -2748,6 +3426,164 @@ var StreamProcessor = class StreamProcessor {
2748
3426
  } : msg);
2749
3427
  this.emitMessagesChange();
2750
3428
  }
3429
+ findSubagentPart(subagentRunId) {
3430
+ for (const message of this.messages) for (const part of message.parts) if (part.type === "subagent" && part.subagent.id === subagentRunId) return {
3431
+ message,
3432
+ part
3433
+ };
3434
+ }
3435
+ /**
3436
+ * The direct child whose card holds `id`: the child itself, or a nested
3437
+ * child inside its messages.
3438
+ */
3439
+ childOwning(id) {
3440
+ const holds = (messages) => messages.some((message) => message.parts.some((part) => part.type === "subagent" && (part.subagent.id === id || holds(part.subagent.messages))));
3441
+ for (const message of this.messages) for (const part of message.parts) {
3442
+ if (part.type !== "subagent") continue;
3443
+ if (part.subagent.id === id || holds(part.subagent.messages)) return part.subagent.id;
3444
+ }
3445
+ }
3446
+ /** The direct child whose messages hold this tool call. */
3447
+ childOwningToolCall(matches) {
3448
+ const holds = (messages) => messages.some((message) => message.parts.some((part) => part.type === "tool-call" && matches(part) || part.type === "subagent" && holds(part.subagent.messages)));
3449
+ for (const message of this.messages) for (const part of message.parts) if (part.type === "subagent" && holds(part.subagent.messages)) return part.subagent.id;
3450
+ }
3451
+ /**
3452
+ * The processor for a direct child. The card's messages are the source of
3453
+ * truth. The processor re-reads them when something else replaced them.
3454
+ */
3455
+ childProcessor(subagentRunId) {
3456
+ const found = this.findSubagentPart(subagentRunId);
3457
+ if (!found) return void 0;
3458
+ const messages = found.part.subagent.messages;
3459
+ const existing = this.childProcessors.get(subagentRunId);
3460
+ if (existing) {
3461
+ if (existing.getMessages() !== messages) existing.messages = messages;
3462
+ return existing;
3463
+ }
3464
+ const child = new StreamProcessor({
3465
+ subagentRunId,
3466
+ initialMessages: messages,
3467
+ events: {
3468
+ onToolCall: (args) => this.events.onToolCall?.(args),
3469
+ onApprovalRequest: (args) => this.events.onApprovalRequest?.(args),
3470
+ onCustomEvent: (name, value, context) => this.events.onCustomEvent?.(name, value, context)
3471
+ }
3472
+ });
3473
+ this.childProcessors.set(subagentRunId, child);
3474
+ return child;
3475
+ }
3476
+ /** Run `update` on a direct child, then copy its messages onto the card. */
3477
+ updateChild(subagentRunId, update) {
3478
+ const child = this.childProcessor(subagentRunId);
3479
+ if (!child) return;
3480
+ update(child);
3481
+ const messages = child.getMessages();
3482
+ this.patchSubagent(subagentRunId, (subagent) => {
3483
+ subagent.messages = messages;
3484
+ });
3485
+ }
3486
+ handleSubagentStartedEvent(chunk) {
3487
+ if (chunk.type !== "SUBAGENT_STARTED") return;
3488
+ if (this.findSubagentPart(chunk.subagentRunId)) {
3489
+ this.childProcessors.delete(chunk.subagentRunId);
3490
+ this.patchSubagent(chunk.subagentRunId, (subagent) => {
3491
+ subagent.status = "running";
3492
+ delete subagent.interruptIds;
3493
+ delete subagent.error;
3494
+ if (chunk.metadata !== void 0) subagent.metadata = chunk.metadata;
3495
+ });
3496
+ return;
3497
+ }
3498
+ const { messageId } = this.ensureAssistantMessage();
3499
+ const part = {
3500
+ type: "subagent",
3501
+ subagent: {
3502
+ id: chunk.subagentRunId,
3503
+ name: chunk.name,
3504
+ ...chunk.description !== void 0 && { description: chunk.description },
3505
+ status: "running",
3506
+ ...chunk.parentSubagentRunId !== void 0 && { parentSubagentRunId: chunk.parentSubagentRunId },
3507
+ ...chunk.parentToolCallId !== void 0 && { parentToolCallId: chunk.parentToolCallId },
3508
+ ...chunk.metadata !== void 0 && { metadata: chunk.metadata },
3509
+ messages: []
3510
+ }
3511
+ };
3512
+ this.messages = this.messages.map((message) => message.id === messageId ? {
3513
+ ...message,
3514
+ parts: [...message.parts, part]
3515
+ } : message);
3516
+ this.emitMessagesChange();
3517
+ }
3518
+ handleSubagentFinishedEvent(chunk) {
3519
+ if (chunk.type !== "SUBAGENT_FINISHED") return;
3520
+ this.updateChild(chunk.subagentRunId, (child) => child.finalizeStream());
3521
+ this.patchSubagent(chunk.subagentRunId, (subagent) => {
3522
+ if (subagent.status === "error") return;
3523
+ if (chunk.outcome?.type === "suspended") {
3524
+ subagent.status = "suspended";
3525
+ subagent.interruptIds = chunk.outcome.interruptIds ?? [];
3526
+ return;
3527
+ }
3528
+ subagent.status = "finished";
3529
+ });
3530
+ }
3531
+ handleSubagentErrorEvent(chunk) {
3532
+ if (chunk.type !== "SUBAGENT_ERROR") return;
3533
+ this.updateChild(chunk.subagentRunId, (child) => child.finalizeStream());
3534
+ this.patchSubagent(chunk.subagentRunId, (subagent) => {
3535
+ subagent.status = "error";
3536
+ subagent.error = {
3537
+ message: chunk.message,
3538
+ ...chunk.code !== void 0 && { code: chunk.code }
3539
+ };
3540
+ });
3541
+ }
3542
+ patchSubagent(subagentRunId, patch) {
3543
+ this.messages = this.messages.map((message) => {
3544
+ const index = message.parts.findIndex((part) => part.type === "subagent" && part.subagent.id === subagentRunId);
3545
+ if (index === -1) return message;
3546
+ const part = message.parts[index];
3547
+ if (!part || part.type !== "subagent") return message;
3548
+ const next = {
3549
+ type: "subagent",
3550
+ subagent: { ...part.subagent }
3551
+ };
3552
+ patch(next.subagent);
3553
+ const parts = [...message.parts];
3554
+ parts[index] = next;
3555
+ return {
3556
+ ...message,
3557
+ parts
3558
+ };
3559
+ });
3560
+ this.emitMessagesChange();
3561
+ }
3562
+ /**
3563
+ * Send a child's chunk to that child's processor, so the card keeps text,
3564
+ * reasoning, tool calls, results, and nested children. A tool event without
3565
+ * `subagentRunId` follows its `TOOL_CALL_START`.
3566
+ */
3567
+ routeToChild(chunk) {
3568
+ let id;
3569
+ if (chunk.type === "SUBAGENT_STARTED") id = chunk.parentSubagentRunId;
3570
+ else if (chunk.type === "SUBAGENT_FINISHED" || chunk.type === "SUBAGENT_ERROR") {
3571
+ if (this.findSubagentPart(chunk.subagentRunId)) return false;
3572
+ id = chunk.subagentRunId;
3573
+ } else if ("subagentRunId" in chunk && chunk.subagentRunId) id = chunk.subagentRunId;
3574
+ else if ("toolCallId" in chunk && typeof chunk.toolCallId === "string") id = this.childToolCalls.get(chunk.toolCallId);
3575
+ if (id === void 0) return false;
3576
+ const owner = this.childOwning(id);
3577
+ if (owner === void 0) {
3578
+ if (id === this.subagentRunId) return false;
3579
+ console.warn(`[StreamProcessor] Dropped a chunk for unknown subagent ${id}`);
3580
+ return true;
3581
+ }
3582
+ if (this.findSubagentPart(owner)?.part.subagent.status === "error") return true;
3583
+ if (chunk.type === "TOOL_CALL_START") this.childToolCalls.set(chunk.toolCallId, owner);
3584
+ this.updateChild(owner, (child) => child.processChunk(chunk));
3585
+ return true;
3586
+ }
2751
3587
  /**
2752
3588
  * Handle TEXT_MESSAGE_START event
2753
3589
  */
@@ -2772,9 +3608,15 @@ var StreamProcessor = class StreamProcessor {
2772
3608
  this.activeMessageIds.add(messageId);
2773
3609
  for (const [toolCallId, mappedMessageId] of this.toolCallToMessage) if (mappedMessageId === pendingId) this.toolCallToMessage.set(toolCallId, messageId);
2774
3610
  }
2775
- if (!this.messageStates.has(messageId)) {
2776
- this.createMessageState(messageId, uiRole);
3611
+ let pendingState = this.messageStates.get(messageId);
3612
+ if (!pendingState) {
3613
+ pendingState = this.createMessageState(messageId, uiRole);
2777
3614
  this.activeMessageIds.add(messageId);
3615
+ } else if (pendingState.hasToolCallsSinceTextStart) {
3616
+ if (pendingState.currentSegmentText !== pendingState.lastEmittedText) this.emitTextUpdateForMessage(messageId);
3617
+ pendingState.currentSegmentText = "";
3618
+ pendingState.lastEmittedText = "";
3619
+ pendingState.hasToolCallsSinceTextStart = false;
2778
3620
  }
2779
3621
  this.mergeMessageMetadata(messageId, chunk.metadata);
2780
3622
  this.emitMessagesChange();
@@ -2824,8 +3666,9 @@ var StreamProcessor = class StreamProcessor {
2824
3666
  this.resetStreamState();
2825
3667
  const prevMessages = this.messages;
2826
3668
  const prevById = new Map(prevMessages.map((msg) => [msg.id, msg]));
2827
- const normalized = this.mergeReasoningFanOut(chunk.messages.map(aguiSnapshotMessageToUIMessage));
2828
- this.messages = this.reconcileSnapshotToolCalls(normalized, prevMessages).map((msg) => {
3669
+ const { top, groups } = splitSubagentWire(chunk.messages);
3670
+ const normalized = this.mergeReasoningFanOut(top.map(aguiSnapshotMessageToUIMessage));
3671
+ const reconciled = this.reconcileSnapshotToolCalls(normalized, prevMessages).map((msg) => {
2829
3672
  if (msg.metadata != null) return msg;
2830
3673
  const prev = prevById.get(msg.id);
2831
3674
  if (prev?.metadata == null) return msg;
@@ -2834,9 +3677,76 @@ var StreamProcessor = class StreamProcessor {
2834
3677
  metadata: prev.metadata
2835
3678
  };
2836
3679
  });
3680
+ this.messages = this.attachSnapshotSubagents(reconciled, top, groups, prevMessages);
2837
3681
  this.emitMessagesChange();
2838
3682
  }
2839
3683
  /**
3684
+ * Put subagent cards back on a snapshot. Child wire messages (tagged with
3685
+ * `subagentRunId`) become a card on the nearest assistant message before
3686
+ * them, or a new assistant message when there is none. A card that the
3687
+ * snapshot does not carry stays on its message, because a server snapshot
3688
+ * can hold the parent history only.
3689
+ */
3690
+ attachSnapshotSubagents(messages, top, groups, prevMessages) {
3691
+ const cards = [];
3692
+ const children = /* @__PURE__ */ new Map();
3693
+ for (const group of groups) {
3694
+ const child = new StreamProcessor({ subagentRunId: group.id });
3695
+ child.processChunk({
3696
+ type: EventType.MESSAGES_SNAPSHOT,
3697
+ messages: group.messages,
3698
+ timestamp: Date.now()
3699
+ });
3700
+ children.set(group.id, child);
3701
+ const { placeholder: _placeholder, ...info } = group.info;
3702
+ cards.push({
3703
+ hostId: top.slice(0, group.hostIndex + 1).findLast((message) => message.role === "assistant")?.id,
3704
+ part: {
3705
+ type: "subagent",
3706
+ subagent: {
3707
+ ...info,
3708
+ id: group.id,
3709
+ messages: child.getMessages()
3710
+ }
3711
+ },
3712
+ carried: false
3713
+ });
3714
+ }
3715
+ const inSnapshot = new Set(groups.map((group) => group.id));
3716
+ for (const message of prevMessages) for (const part of message.parts) {
3717
+ if (part.type !== "subagent" || inSnapshot.has(part.subagent.id)) continue;
3718
+ cards.push({
3719
+ hostId: message.id,
3720
+ ...part.subagent.parentToolCallId !== void 0 && { toolCallId: part.subagent.parentToolCallId },
3721
+ part,
3722
+ carried: true
3723
+ });
3724
+ const kept = this.childProcessors.get(part.subagent.id);
3725
+ if (kept) children.set(part.subagent.id, kept);
3726
+ }
3727
+ this.childProcessors.clear();
3728
+ for (const [id, child] of children) this.childProcessors.set(id, child);
3729
+ let out = messages;
3730
+ for (const card of cards) {
3731
+ let index = out.findIndex((message) => message.id === card.hostId);
3732
+ if (index === -1 && card.toolCallId !== void 0) index = out.findIndex((message) => message.parts.some((part) => part.type === "tool-call" && part.id === card.toolCallId));
3733
+ if (index === -1 && !card.carried) index = out.findLastIndex((message) => message.role === "assistant");
3734
+ if (index === -1) {
3735
+ if (!card.carried) out = [...out, {
3736
+ id: generateMessageId(),
3737
+ role: "assistant",
3738
+ parts: [card.part]
3739
+ }];
3740
+ continue;
3741
+ }
3742
+ out = out.map((message, position) => position !== index || message.parts.some((part) => part.type === "subagent" && part.subagent.id === card.part.subagent.id) ? message : {
3743
+ ...message,
3744
+ parts: [...message.parts, card.part]
3745
+ });
3746
+ }
3747
+ return out;
3748
+ }
3749
+ /**
2840
3750
  * Reconcile a freshly normalized snapshot with the pre-snapshot message
2841
3751
  * state so unreconstructable tool-call metadata is preserved.
2842
3752
  *
@@ -2871,7 +3781,7 @@ var StreamProcessor = class StreamProcessor {
2871
3781
  let pending = [];
2872
3782
  const thinkingParts = (msg) => msg.parts.filter((part) => part.type === "thinking");
2873
3783
  const isThinkingOnly = (msg) => msg.role === "assistant" && msg.parts.length > 0 && msg.parts.every((part) => part.type === "thinking");
2874
- const isToolResultOnly = (msg) => msg.role === "assistant" && msg.parts.length === 1 && msg.parts[0]?.type === "tool-result";
3784
+ const isToolResultOnly = (msg) => msg.role === "assistant" && msg.parts.some((part) => part.type === "tool-result") && msg.parts.every((part) => part.type === "tool-result" || part.type === "ui-resource");
2875
3785
  const flushPending = () => {
2876
3786
  out.push(...pending);
2877
3787
  pending = [];
@@ -2881,16 +3791,23 @@ var StreamProcessor = class StreamProcessor {
2881
3791
  pending.push(msg);
2882
3792
  continue;
2883
3793
  }
3794
+ let next = msg;
2884
3795
  if (msg.role === "assistant" && pending.length > 0 && !isToolResultOnly(msg)) {
2885
- out.push({
3796
+ next = {
2886
3797
  ...msg,
2887
3798
  parts: [...pending.flatMap(thinkingParts), ...msg.parts]
2888
- });
3799
+ };
2889
3800
  pending = [];
3801
+ } else flushPending();
3802
+ const prev = out.at(-1);
3803
+ if (prev?.role === "assistant" && next.role === "assistant" && isAssistantSegmentOf(next.id, prev.id)) {
3804
+ out[out.length - 1] = {
3805
+ ...prev,
3806
+ parts: [...prev.parts, ...next.parts]
3807
+ };
2890
3808
  continue;
2891
3809
  }
2892
- flushPending();
2893
- out.push(msg);
3810
+ out.push(next);
2894
3811
  }
2895
3812
  flushPending();
2896
3813
  return out;
@@ -2902,7 +3819,8 @@ var StreamProcessor = class StreamProcessor {
2902
3819
  for (const msg of snapshot) for (const part of msg.parts) if (part.type === "tool-call") snapshotToolCallIds.add(part.id);
2903
3820
  const reconciled = [];
2904
3821
  for (const msg of snapshot) {
2905
- const toolResultPart = msg.role === "assistant" && msg.parts.length === 1 ? msg.parts.find((p) => p.type === "tool-result") : void 0;
3822
+ const toolResultParts = msg.parts.filter((part) => part.type === "tool-result");
3823
+ const toolResultPart = msg.role === "assistant" && toolResultParts.length === 1 && msg.parts.every((part) => part.type === "tool-result" || part.type === "ui-resource") ? toolResultParts[0] : void 0;
2906
3824
  if (!toolResultPart) {
2907
3825
  reconciled.push(msg);
2908
3826
  continue;
@@ -2921,7 +3839,7 @@ var StreamProcessor = class StreamProcessor {
2921
3839
  snapshotToolCallIds.add(prev.id);
2922
3840
  } else console.warn(`[StreamProcessor] MESSAGES_SNAPSHOT contains a tool-result for "${toolResultPart.toolCallId}" but no matching tool-call exists in the snapshot or the pre-snapshot state; addToolResult("${toolResultPart.toolCallId}") will not be able to locate this call`);
2923
3841
  }
2924
- parts.push(toolResultPart);
3842
+ parts.push(...msg.parts);
2925
3843
  target.parts = parts;
2926
3844
  }
2927
3845
  return this.enrichSnapshotToolCallsFromResults(reconciled, prevToolCalls);
@@ -2955,7 +3873,7 @@ var StreamProcessor = class StreamProcessor {
2955
3873
  } catch {
2956
3874
  output = result.content;
2957
3875
  }
2958
- const errorText = result.state === "error" ? this.extractToolResultError(output) : void 0;
3876
+ const errorText = result.state === "error" ? toolResultErrorText(output) : void 0;
2959
3877
  next = {
2960
3878
  ...next,
2961
3879
  output: errorText ? { error: errorText } : output,
@@ -3001,6 +3919,7 @@ var StreamProcessor = class StreamProcessor {
3001
3919
  state.lastEmittedText = "";
3002
3920
  state.hasToolCallsSinceTextStart = false;
3003
3921
  }
3922
+ state.hasToolCallsSinceTextStart = false;
3004
3923
  const currentText = state.currentSegmentText;
3005
3924
  const delta = chunk.delta || "";
3006
3925
  const nextText = delta !== "" ? currentText + delta : currentText;
@@ -3092,11 +4011,13 @@ var StreamProcessor = class StreamProcessor {
3092
4011
  * Handle TOOL_CALL_END event — arguments are finalized (input-complete).
3093
4012
  * Tool output arrives on TOOL_CALL_RESULT, not on this event.
3094
4013
  *
3095
- * If TOOL_CALL_END carries parsed `input`, use it as the canonical arguments:
3096
- * back-fill the accumulated string when no TOOL_CALL_ARGS deltas were seen
3097
- * (adapters that deliver the whole input on END — e.g. Anthropic
3098
- * server_tool_use / web_search — issue #839) and override the rendered part's
3099
- * `input` with the canonical value.
4014
+ * If TOOL_CALL_END carries parsed `input`, it is the canonical arguments:
4015
+ * write it into the accumulated string and override the rendered part's
4016
+ * `input` with it. That covers adapters that deliver the whole input on END
4017
+ * (e.g. Anthropic server_tool_use / web_search — issue #839) and adapters
4018
+ * that stream the wire arguments and then normalize them (OpenAI strict-mode
4019
+ * null widening, undone by the adapter after #939), so `arguments` and
4020
+ * `input` never disagree on the persisted part.
3100
4021
  *
3101
4022
  * @see docs/chat-architecture.md#single-shot-tool-call-response — End-to-end flow
3102
4023
  */
@@ -3108,12 +4029,11 @@ var StreamProcessor = class StreamProcessor {
3108
4029
  const input = chunk.input !== void 0 ? chunk.input : tanstackMetadata(chunk)?.input;
3109
4030
  const existingToolCall = msgState.toolCalls.get(chunk.toolCallId);
3110
4031
  if (existingToolCall && existingToolCall.state !== "input-complete") {
3111
- if (input !== void 0 && !existingToolCall.arguments) try {
3112
- existingToolCall.arguments = JSON.stringify(input);
3113
- } catch {}
4032
+ const serializedInput = serializeToolInput(input);
4033
+ if (serializedInput !== void 0) existingToolCall.arguments = serializedInput;
3114
4034
  const index = msgState.toolCallOrder.indexOf(chunk.toolCallId);
3115
4035
  this.completeToolCall(messageId, index, existingToolCall);
3116
- if (input !== void 0) {
4036
+ if (serializedInput !== void 0) {
3117
4037
  existingToolCall.parsedArguments = input;
3118
4038
  this.messages = updateToolCallPart(this.messages, messageId, {
3119
4039
  id: existingToolCall.id,
@@ -3127,10 +4047,6 @@ var StreamProcessor = class StreamProcessor {
3127
4047
  }
3128
4048
  }
3129
4049
  }
3130
- extractToolResultError(output) {
3131
- if (output && typeof output === "object" && "error" in output && typeof output.error === "string") return output.error;
3132
- return typeof output === "string" ? output : "Tool execution failed";
3133
- }
3134
4050
  /**
3135
4051
  * Handle TOOL_CALL_RESULT event (AG-UI spec).
3136
4052
  *
@@ -3141,16 +4057,19 @@ var StreamProcessor = class StreamProcessor {
3141
4057
  handleToolCallResultEvent(chunk) {
3142
4058
  const messageId = this.toolCallToMessage.get(chunk.toolCallId) ?? this.messages.find((m) => m.parts.some((p) => p.type === "tool-call" && p.id === chunk.toolCallId))?.id;
3143
4059
  if (!messageId) return;
3144
- const isOutputError = chunk.state === "output-error" || tanstackMetadata(chunk)?.state === "output-error";
4060
+ const extra = chunk;
4061
+ const rawToolResultOutcome = tanstackMetadata(chunk)?.toolResultOutcome;
4062
+ const toolResultOutcome = isToolResultOutcome(rawToolResultOutcome) ? rawToolResultOutcome : void 0;
4063
+ const isOutputError = extra.state === "output-error" || tanstackMetadata(chunk)?.state === "output-error" || toolResultOutcome !== void 0;
3145
4064
  let output;
3146
4065
  try {
3147
- output = JSON.parse(chunk.content);
4066
+ output = typeof chunk.content === "string" ? JSON.parse(chunk.content) : chunk.content;
3148
4067
  } catch {
3149
4068
  output = chunk.content;
3150
4069
  }
3151
4070
  this.messages = updateToolCallWithOutput(this.messages, chunk.toolCallId, output, isOutputError ? "error" : void 0);
3152
4071
  const resultState = isOutputError ? "error" : "complete";
3153
- this.messages = updateToolResultPart(this.messages, messageId, chunk.toolCallId, chunk.content, resultState, resultState === "error" ? this.extractToolResultError(output) : void 0);
4072
+ this.messages = updateToolResultPart(this.messages, messageId, chunk.toolCallId, isContentPartArray(output) ? output : aguiContentToContentParts(chunk.content), resultState, resultState === "error" ? toolResultErrorText(output) : void 0, toolResultOutcome);
3154
4073
  this.emitMessagesChange();
3155
4074
  }
3156
4075
  /**
@@ -3178,26 +4097,44 @@ var StreamProcessor = class StreamProcessor {
3178
4097
  this.activeRuns.delete(chunk.runId);
3179
4098
  if (chunk.outcome?.type === "interrupt") this.handleInterrupts(chunk.outcome.interrupts);
3180
4099
  if (this.activeRuns.size === 0) {
3181
- this.isDone = true;
3182
4100
  this.completeAllToolCalls();
4101
+ if (this.finishReason === "tool_calls" && chunk.outcome?.type !== "interrupt") return;
4102
+ this.isDone = true;
3183
4103
  this.finalizeStream();
3184
4104
  }
3185
4105
  }
3186
- handleInterrupts(interrupts) {
4106
+ /**
4107
+ * Apply interrupt state to tool-call parts, then fire the client events.
4108
+ * A child's interrupt updates the child's card. `emit` is false there, so
4109
+ * the events fire once, from the top processor.
4110
+ */
4111
+ handleInterrupts(interrupts, emit = true) {
3187
4112
  const hasGeneric = interruptBatchHasGeneric(interrupts);
3188
4113
  for (const interrupt of interrupts) {
3189
4114
  const metadata = interrupt.metadata && typeof interrupt.metadata === "object" ? interrupt.metadata : {};
3190
4115
  const kind = typeof metadata.kind === "string" ? metadata.kind : void 0;
3191
4116
  const toolCallId = interrupt.toolCallId;
3192
4117
  if (!toolCallId) continue;
4118
+ const owner = interrupt.subagentRunId !== void 0 ? this.childOwning(interrupt.subagentRunId) : this.childOwningToolCall((part) => part.id === toolCallId);
4119
+ if (owner !== void 0) this.updateChild(owner, (child) => child.handleInterrupts([interrupt], false));
3193
4120
  const toolName = typeof metadata.toolName === "string" ? metadata.toolName : this.findToolCallName(toolCallId);
3194
4121
  const input = Object.hasOwn(metadata, "input") ? metadata.input : {};
3195
4122
  if (kind === "approval" || interrupt.reason === "approval_required") {
4123
+ if (owner !== void 0) {
4124
+ if (emit) this.events.onApprovalRequest?.({
4125
+ toolCallId,
4126
+ toolName,
4127
+ input,
4128
+ approvalId: interrupt.id
4129
+ });
4130
+ continue;
4131
+ }
3196
4132
  const resolvedMessageId = this.getActiveAssistantMessageId() ?? this.toolCallToMessage.get(toolCallId) ?? this.messages.find((m) => m.role === "assistant" && m.parts.some((p) => p.type === "tool-call" && p.id === toolCallId))?.id;
3197
4133
  if (resolvedMessageId) {
3198
4134
  this.messages = updateToolCallApproval(this.messages, resolvedMessageId, toolCallId, interrupt.id);
3199
4135
  this.emitMessagesChange();
3200
4136
  }
4137
+ if (!emit) continue;
3201
4138
  this.events.onApprovalRequest?.({
3202
4139
  toolCallId,
3203
4140
  toolName,
@@ -3207,7 +4144,7 @@ var StreamProcessor = class StreamProcessor {
3207
4144
  continue;
3208
4145
  }
3209
4146
  if (kind === "client_tool" || interrupt.reason === "client_tool_input") {
3210
- if (hasGeneric) continue;
4147
+ if (hasGeneric || !emit) continue;
3211
4148
  this.events.onToolCall?.({
3212
4149
  toolCallId,
3213
4150
  toolName,
@@ -3498,7 +4435,7 @@ var StreamProcessor = class StreamProcessor {
3498
4435
  } catch {
3499
4436
  toolCall.parsedArguments = void 0;
3500
4437
  }
3501
- if (this.isToolCallPartErrored(toolCall.id)) return;
4438
+ if (this.isToolCallPartTerminal(toolCall.id)) return;
3502
4439
  if (this.isToolCallPartAwaitingUserAction(toolCall.id)) return;
3503
4440
  this.messages = updateToolCallPart(this.messages, messageId, {
3504
4441
  id: toolCall.id,
@@ -3515,12 +4452,12 @@ var StreamProcessor = class StreamProcessor {
3515
4452
  return this.messages.some((msg) => msg.parts?.some((part) => part.type === "tool-call" && part.id === toolCallId && (part.state === "approval-requested" || part.state === "approval-responded")));
3516
4453
  }
3517
4454
  /**
3518
- * Whether the rendered tool-call part for the given id has reached the
3519
- * terminal 'error' state. Used to prevent the completion safety net from
3520
- * downgrading a failed call back to 'input-complete'.
4455
+ * Whether the rendered tool-call part for the given id has reached a
4456
+ * terminal 'error' or 'complete' state. Used to prevent the completion
4457
+ * safety net from downgrading a finished call back to 'input-complete'.
3521
4458
  */
3522
- isToolCallPartErrored(toolCallId) {
3523
- return this.messages.some((msg) => msg.parts?.some((part) => part.type === "tool-call" && part.id === toolCallId && part.state === "error"));
4459
+ isToolCallPartTerminal(toolCallId) {
4460
+ return this.messages.some((msg) => msg.parts?.some((part) => part.type === "tool-call" && part.id === toolCallId && (part.state === "error" || part.state === "complete")));
3524
4461
  }
3525
4462
  /**
3526
4463
  * Emit pending text update for a specific message.
@@ -3585,6 +4522,7 @@ var StreamProcessor = class StreamProcessor {
3585
4522
  * @see docs/chat-architecture.md#single-shot-text-response — Finalization step
3586
4523
  */
3587
4524
  finalizeStream() {
4525
+ this.isDone = true;
3588
4526
  let lastAssistantMessage;
3589
4527
  for (const messageId of this.activeMessageIds) {
3590
4528
  const state = this.getMessageState(messageId);
@@ -3610,7 +4548,10 @@ var StreamProcessor = class StreamProcessor {
3610
4548
  return;
3611
4549
  }
3612
4550
  }
3613
- if (lastAssistantMessage) this.events.onStreamEnd?.(lastAssistantMessage);
4551
+ if (lastAssistantMessage && !this.streamEndEmitted) {
4552
+ this.streamEndEmitted = true;
4553
+ this.events.onStreamEnd?.(lastAssistantMessage);
4554
+ }
3614
4555
  }
3615
4556
  /**
3616
4557
  * Get completed tool calls in API format (aggregated across all messages)
@@ -3702,6 +4643,7 @@ var StreamProcessor = class StreamProcessor {
3702
4643
  this.finishReason = null;
3703
4644
  this.hasError = false;
3704
4645
  this.isDone = false;
4646
+ this.streamEndEmitted = false;
3705
4647
  this.chunkStrategy.reset?.();
3706
4648
  }
3707
4649
  /**
@@ -3710,6 +4652,8 @@ var StreamProcessor = class StreamProcessor {
3710
4652
  reset() {
3711
4653
  this.resetStreamState();
3712
4654
  this.messages = [];
4655
+ this.childProcessors.clear();
4656
+ this.childToolCalls.clear();
3713
4657
  }
3714
4658
  /**
3715
4659
  * Check if a message contains only whitespace text and no other meaningful parts
@@ -3735,52 +4679,14 @@ function createReplayStream(recording) {
3735
4679
  } };
3736
4680
  }
3737
4681
  //#endregion
3738
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/client.js
3739
- var EventType = /* @__PURE__ */ function(EventType) {
3740
- EventType["TEXT_MESSAGE_START"] = "TEXT_MESSAGE_START";
3741
- EventType["TEXT_MESSAGE_CONTENT"] = "TEXT_MESSAGE_CONTENT";
3742
- EventType["TEXT_MESSAGE_END"] = "TEXT_MESSAGE_END";
3743
- EventType["TEXT_MESSAGE_CHUNK"] = "TEXT_MESSAGE_CHUNK";
3744
- EventType["TOOL_CALL_START"] = "TOOL_CALL_START";
3745
- EventType["TOOL_CALL_ARGS"] = "TOOL_CALL_ARGS";
3746
- EventType["TOOL_CALL_END"] = "TOOL_CALL_END";
3747
- EventType["TOOL_CALL_CHUNK"] = "TOOL_CALL_CHUNK";
3748
- EventType["TOOL_CALL_RESULT"] = "TOOL_CALL_RESULT";
3749
- EventType["THINKING_START"] = "THINKING_START";
3750
- EventType["THINKING_END"] = "THINKING_END";
3751
- EventType["THINKING_TEXT_MESSAGE_START"] = "THINKING_TEXT_MESSAGE_START";
3752
- EventType["THINKING_TEXT_MESSAGE_CONTENT"] = "THINKING_TEXT_MESSAGE_CONTENT";
3753
- EventType["THINKING_TEXT_MESSAGE_END"] = "THINKING_TEXT_MESSAGE_END";
3754
- EventType["STATE_SNAPSHOT"] = "STATE_SNAPSHOT";
3755
- EventType["STATE_DELTA"] = "STATE_DELTA";
3756
- EventType["MESSAGES_SNAPSHOT"] = "MESSAGES_SNAPSHOT";
3757
- EventType["ACTIVITY_SNAPSHOT"] = "ACTIVITY_SNAPSHOT";
3758
- EventType["ACTIVITY_DELTA"] = "ACTIVITY_DELTA";
3759
- EventType["RAW"] = "RAW";
3760
- EventType["CUSTOM"] = "CUSTOM";
3761
- EventType["RUN_STARTED"] = "RUN_STARTED";
3762
- EventType["RUN_FINISHED"] = "RUN_FINISHED";
3763
- EventType["RUN_ERROR"] = "RUN_ERROR";
3764
- EventType["STEP_STARTED"] = "STEP_STARTED";
3765
- EventType["STEP_FINISHED"] = "STEP_FINISHED";
3766
- EventType["REASONING_START"] = "REASONING_START";
3767
- EventType["REASONING_MESSAGE_START"] = "REASONING_MESSAGE_START";
3768
- EventType["REASONING_MESSAGE_CONTENT"] = "REASONING_MESSAGE_CONTENT";
3769
- EventType["REASONING_MESSAGE_END"] = "REASONING_MESSAGE_END";
3770
- EventType["REASONING_MESSAGE_CHUNK"] = "REASONING_MESSAGE_CHUNK";
3771
- EventType["REASONING_END"] = "REASONING_END";
3772
- EventType["REASONING_ENCRYPTED_VALUE"] = "REASONING_ENCRYPTED_VALUE";
3773
- return EventType;
3774
- }({});
3775
- //#endregion
3776
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/byok/providers.js
4682
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/byok/providers.js
3777
4683
  /** `[a-z][a-z0-9-]{0,63}` — lowercase, no underscores, max 64 chars. */
3778
4684
  var BYOK_PROVIDER_ID_PATTERN = /^[a-z][a-z0-9-]{0,63}$/;
3779
4685
  function isProviderId(value) {
3780
4686
  return typeof value === "string" && BYOK_PROVIDER_ID_PATTERN.test(value);
3781
4687
  }
3782
4688
  //#endregion
3783
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/byok/missing.js
4689
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/byok/missing.js
3784
4690
  function isByokMissingBody(value) {
3785
4691
  if (typeof value !== "object" || value === null) return false;
3786
4692
  if (!("error" in value)) return false;
@@ -3793,7 +4699,7 @@ function isByokMissingBody(value) {
3793
4699
  return true;
3794
4700
  }
3795
4701
  //#endregion
3796
- //#region node_modules/.deno/@tanstack+ai@0.49.1/node_modules/@tanstack/ai/dist/esm/byok/errors.js
4702
+ //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/byok/errors.js
3797
4703
  var ByokMissingError = class extends Error {
3798
4704
  provider;
3799
4705
  constructor(provider) {
@@ -3819,7 +4725,7 @@ var ByokUnresolvedProviderError = class extends Error {
3819
4725
  }
3820
4726
  };
3821
4727
  //#endregion
3822
- //#region node_modules/.deno/@tanstack+ai-client@0.28.0/node_modules/@tanstack/ai-client/dist/esm/connection-adapters.js
4728
+ //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/connection-adapters.js
3823
4729
  /**
3824
4730
  * Associates connect-wrapped chunks with the run they were produced under.
3825
4731
  * Content events (TEXT_MESSAGE_CONTENT, TOOL_CALL_*, …) carry no `runId` of
@@ -4088,15 +4994,33 @@ async function assertResponseOk(response) {
4088
4994
  }
4089
4995
  throw new Error(`HTTP error! status: ${response.status} ${response.statusText}`);
4090
4996
  }
4997
+ function chatHydrationSearchParams(threadId, options) {
4998
+ const values = { threadId };
4999
+ if (options?.limit !== void 0) values.limit = String(options.limit);
5000
+ if (options?.before !== void 0) values.before = options.before;
5001
+ return values;
5002
+ }
5003
+ function readChatHydrationPage(value) {
5004
+ if (value === null || typeof value !== "object") return { truncated: false };
5005
+ if (!("truncated" in value) || value.truncated !== true) return { truncated: false };
5006
+ if (!("cursor" in value) || typeof value.cursor !== "string" || value.cursor === "") return { truncated: false };
5007
+ return {
5008
+ truncated: true,
5009
+ cursor: value.cursor
5010
+ };
5011
+ }
4091
5012
  /**
4092
5013
  * GET the hydration endpoint for a thread and parse its JSON `{ messages,
4093
5014
  * activeRun }` body. This is the transport-agnostic reconnect probe: keyed on
4094
5015
  * the STABLE thread id, it returns the stored transcript and — if a run is still
4095
5016
  * generating — a cursor the caller tails via `joinRun`. Shared by every fetch/
4096
5017
  * XHR adapter so the client never has to know which transport is in use.
5018
+ *
5019
+ * Pass `limit` / `before` to page. Old servers that omit `page` are treated as
5020
+ * a full list (`truncated: false`).
4097
5021
  */
4098
- async function fetchThreadHydration(fetchClient, url, headers, credentials, threadId) {
4099
- const response = await fetchClient(withSearchParams(url, { threadId }), {
5022
+ async function fetchThreadHydration(fetchClient, url, headers, credentials, threadId, options) {
5023
+ const response = await fetchClient(withSearchParams(url, chatHydrationSearchParams(threadId, options)), {
4100
5024
  method: "GET",
4101
5025
  headers: {
4102
5026
  Accept: "application/json",
@@ -4112,9 +5036,10 @@ async function fetchThreadHydration(fetchClient, url, headers, credentials, thre
4112
5036
  pending: data.interrupts.pending
4113
5037
  } : null;
4114
5038
  return {
4115
- messages: Array.isArray(data.messages) ? data.messages : [],
5039
+ messages: Array.isArray(data.messages) ? normalizeMessagesDates(data.messages) : [],
4116
5040
  activeRun,
4117
- interrupts
5041
+ interrupts,
5042
+ page: readChatHydrationPage(data.page)
4118
5043
  };
4119
5044
  }
4120
5045
  /**
@@ -4248,12 +5173,24 @@ function normalizeConnectionAdapter(connection) {
4248
5173
  if (!hasConnect) throw new Error("Connection adapter must provide either connect or both subscribe and send");
4249
5174
  let activeBuffer = [];
4250
5175
  let activeWaiters = [];
5176
+ let activeSubscriber;
4251
5177
  function push(chunk, runId) {
4252
5178
  if (runId) chunkRunIds.set(chunk, runId);
4253
5179
  const waiter = activeWaiters.shift();
4254
5180
  if (waiter) waiter(chunk);
4255
5181
  else activeBuffer.push(chunk);
4256
5182
  }
5183
+ async function waitUntilSubscriberIdle(abortSignal) {
5184
+ const idle = () => activeSubscriber !== activeWaiters || activeBuffer.length === 0 && (activeWaiters.length > 0 || abortSignal?.aborted);
5185
+ for (let i = 0; i < 16 && !abortSignal?.aborted; i++) {
5186
+ if (idle()) return;
5187
+ await Promise.resolve();
5188
+ }
5189
+ while (!abortSignal?.aborted) {
5190
+ if (idle()) return;
5191
+ await new Promise((resolve) => setTimeout(resolve, 0));
5192
+ }
5193
+ }
4257
5194
  return {
4258
5195
  subscribe(abortSignal) {
4259
5196
  const myBuffer = activeBuffer.splice(0);
@@ -4261,19 +5198,24 @@ function normalizeConnectionAdapter(connection) {
4261
5198
  activeBuffer = myBuffer;
4262
5199
  activeWaiters = myWaiters;
4263
5200
  return (async function* () {
4264
- while (!abortSignal?.aborted) {
4265
- let chunk;
4266
- const buffered = myBuffer.shift();
4267
- if (buffered !== void 0) chunk = buffered;
4268
- else chunk = await new Promise((resolve) => {
4269
- const onAbort = () => resolve(null);
4270
- myWaiters.push((c) => {
4271
- abortSignal?.removeEventListener("abort", onAbort);
4272
- resolve(c);
5201
+ activeSubscriber = myWaiters;
5202
+ try {
5203
+ while (!abortSignal?.aborted) {
5204
+ let chunk;
5205
+ const buffered = myBuffer.shift();
5206
+ if (buffered !== void 0) chunk = buffered;
5207
+ else chunk = await new Promise((resolve) => {
5208
+ const onAbort = () => resolve(null);
5209
+ myWaiters.push((c) => {
5210
+ abortSignal?.removeEventListener("abort", onAbort);
5211
+ resolve(c);
5212
+ });
5213
+ abortSignal?.addEventListener("abort", onAbort, { once: true });
4273
5214
  });
4274
- abortSignal?.addEventListener("abort", onAbort, { once: true });
4275
- });
4276
- if (chunk !== null) yield chunk;
5215
+ if (chunk !== null) yield chunk;
5216
+ }
5217
+ } finally {
5218
+ if (activeSubscriber === myWaiters) activeSubscriber = void 0;
4277
5219
  }
4278
5220
  })();
4279
5221
  },
@@ -4311,11 +5253,12 @@ function normalizeConnectionAdapter(connection) {
4311
5253
  } catch {}
4312
5254
  throw err;
4313
5255
  }
5256
+ await waitUntilSubscriberIdle(abortSignal);
4314
5257
  },
4315
5258
  ...typeof connection.joinRun === "function" ? { joinRun: (runId, abortSignal) => connection.joinRun(runId, abortSignal) } : {},
4316
5259
  ...(() => {
4317
5260
  const hydrate = connection.hydrate;
4318
- return typeof hydrate === "function" ? { hydrate: (threadId) => hydrate(threadId) } : {};
5261
+ return typeof hydrate === "function" ? { hydrate: (threadId, options) => hydrate(threadId, options) } : {};
4319
5262
  })()
4320
5263
  };
4321
5264
  }
@@ -4413,10 +5356,10 @@ function fetchServerSentEvents(url, options = {}) {
4413
5356
  credentials: resolvedOptions.credentials || "same-origin"
4414
5357
  }, (response, sseSignal) => responseToSSEEvents(response, sseSignal, { runId })), signal, resolvedOptions.reconnect);
4415
5358
  },
4416
- async hydrate(threadId) {
5359
+ async hydrate(threadId, hydrateOptions) {
4417
5360
  const resolvedUrl = typeof url === "function" ? url() : url;
4418
5361
  const resolvedOptions = typeof options === "function" ? await options() : options;
4419
- return fetchThreadHydration(resolvedOptions.fetchClient ?? fetch, resolvedUrl, mergeHeaders(resolvedOptions.headers), resolvedOptions.credentials || "same-origin", threadId);
5362
+ return fetchThreadHydration(resolvedOptions.fetchClient ?? fetch, resolvedUrl, mergeHeaders(resolvedOptions.headers), resolvedOptions.credentials || "same-origin", threadId, hydrateOptions);
4420
5363
  },
4421
5364
  async hydrateGeneration(threadId) {
4422
5365
  const resolvedUrl = typeof url === "function" ? url() : url;
@@ -4477,17 +5420,17 @@ async function* abortableIterable(iterable, signal) {
4477
5420
  }
4478
5421
  }
4479
5422
  //#endregion
4480
- //#region node_modules/.deno/@tanstack+ai-client@0.28.0/node_modules/@tanstack/ai-client/dist/esm/interrupt-manager.js
5423
+ //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/interrupt-manager.js
4481
5424
  function isClientOwnedInterrupt(item) {
4482
5425
  return item.resumable;
4483
5426
  }
4484
5427
  function resolutionWithContinuation(item, resolution) {
4485
5428
  const continuation = genericInterruptContinuationFromDescriptor(item.descriptor);
4486
- if (!continuation) return resolution;
4487
- return {
5429
+ if (continuation) return {
4488
5430
  ...resolution,
4489
5431
  metadata: wrapGenericInterruptContinuation(continuation)
4490
5432
  };
5433
+ return resolution;
4491
5434
  }
4492
5435
  function isRootResolvableInterrupt(interrupt) {
4493
5436
  return "cancel" in interrupt && "clearResolution" in interrupt;
@@ -4807,11 +5750,51 @@ var InterruptManager = class {
4807
5750
  this.submitBatch(submission);
4808
5751
  }
4809
5752
  resolveClientToolOutput(toolCallId, output) {
5753
+ return this.resolveClientToolResult(toolCallId, {
5754
+ state: "output-available",
5755
+ output
5756
+ });
5757
+ }
5758
+ resolveClientToolError(toolCallId, errorText) {
5759
+ return this.resolveClientToolResult(toolCallId, {
5760
+ state: "output-error",
5761
+ errorText
5762
+ });
5763
+ }
5764
+ resolveClientToolResult(toolCallId, result) {
4810
5765
  const item = this.items.find((candidate) => candidate.kind === "client-tool-execution" && candidate.binding?.kind === "client-tool-execution" && candidate.binding.toolCallId === toolCallId || candidate.kind === "generic" && isClientToolExecutionReason(candidate.descriptor.reason) && candidate.descriptor.toolCallId === toolCallId && isLegacyClientToolMetadata(candidate.descriptor.metadata));
4811
5766
  if (!item) return false;
4812
- this.resolveItem(item.descriptor.id, output);
5767
+ if (item.kind !== "client-tool-execution") {
5768
+ this.resolveItem(item.descriptor.id, result.state === "output-error" ? { error: result.errorText } : result.output);
5769
+ return true;
5770
+ }
5771
+ if (result.state === "output-available") {
5772
+ this.resolveItem(item.descriptor.id, result.output);
5773
+ return true;
5774
+ }
5775
+ this.stageClientToolError(item, result.errorText);
4813
5776
  return true;
4814
5777
  }
5778
+ stageClientToolError(item, errorText) {
5779
+ this.assertItemMutable();
5780
+ this.invalidateRetry();
5781
+ if (!item.canResolve) {
5782
+ item.status = "error";
5783
+ item.error = this.itemError(item.descriptor.id, "invalid-response-schema", "The interrupt response schema is invalid and cannot be resolved.");
5784
+ this.publish();
5785
+ return;
5786
+ }
5787
+ item.validationGeneration++;
5788
+ item.resolution = cloneAndDeepFreezeJson(withTanstackMetadata(resolutionWithContinuation(item, {
5789
+ interruptId: item.descriptor.id,
5790
+ status: "resolved",
5791
+ payload: { error: errorText }
5792
+ }), { state: "output-error" }));
5793
+ item.status = "staged";
5794
+ item.error = void 0;
5795
+ this.publish();
5796
+ this.maybeSubmit();
5797
+ }
4815
5798
  resolveToolApprovalDecision(interruptId, approved) {
4816
5799
  const item = this.items.find((candidate) => candidate.descriptor.id === interruptId && (candidate.kind === "tool-approval" || candidate.kind === "generic" && candidate.descriptor.reason === "approval_required" && isLegacyApprovalMetadata(candidate.descriptor.metadata)));
4817
5800
  if (!item) return false;
@@ -5124,7 +6107,24 @@ var InterruptManager = class {
5124
6107
  } : result;
5125
6108
  return isPromiseLike(validation) ? Promise.resolve(validation).then(preserveInput) : preserveInput(validation);
5126
6109
  }
5127
- if (item.kind === "client-tool-execution") return validateWithSchema(item.tool?.outputSchema, payload, "invalid-tool-output");
6110
+ if (item.kind === "client-tool-execution") {
6111
+ const validation = validateWithSchema(item.tool?.outputSchema, payload, "invalid-tool-output");
6112
+ const canonicalize = (result) => {
6113
+ if (!("valid" in result)) return result;
6114
+ try {
6115
+ return {
6116
+ valid: true,
6117
+ payload: cloneAndDeepFreezeJson(result.payload)
6118
+ };
6119
+ } catch (error) {
6120
+ return {
6121
+ code: "invalid-tool-output",
6122
+ message: error instanceof Error ? error.message : String(error)
6123
+ };
6124
+ }
6125
+ };
6126
+ return isPromiseLike(validation) ? Promise.resolve(validation).then(canonicalize) : canonicalize(validation);
6127
+ }
5128
6128
  return this.validateApprovalCandidate(item, payload);
5129
6129
  }
5130
6130
  validateApprovalCandidate(item, payload) {
@@ -5362,7 +6362,20 @@ var InterruptManager = class {
5362
6362
  }
5363
6363
  };
5364
6364
  //#endregion
5365
- //#region node_modules/.deno/@tanstack+ai-client@0.28.0/node_modules/@tanstack/ai-client/dist/esm/byok/resolve.js
6365
+ //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/byok/client.js
6366
+ var registeredProviderIds = /* @__PURE__ */ new WeakMap();
6367
+ /**
6368
+ * Provider to stamp when the caller did not pass a slug.
6369
+ * One saved key wins. Else the only provider listed in `defineByok({ providers })`.
6370
+ */
6371
+ function byokFallbackProviderId(byok) {
6372
+ const savedIds = Object.entries(byok.keys()).filter((entry) => Boolean(entry[1])).map((entry) => entry[0]).filter(isProviderId);
6373
+ if (savedIds.length === 1) return savedIds[0];
6374
+ const registered = registeredProviderIds.get(byok) ?? [];
6375
+ if (registered.length === 1) return registered[0];
6376
+ }
6377
+ //#endregion
6378
+ //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/byok/resolve.js
5366
6379
  function resolveByokProviderId(byokProvider, ...candidates) {
5367
6380
  const fromFn = byokProvider?.();
5368
6381
  if (isProviderId(fromFn)) return fromFn;
@@ -5378,7 +6391,7 @@ async function prepareResolvedByokHeaders(byok, provider) {
5378
6391
  return byok.headers(provider);
5379
6392
  }
5380
6393
  //#endregion
5381
- //#region node_modules/.deno/@tanstack+ai-client@0.28.0/node_modules/@tanstack/ai-client/dist/esm/events.js
6394
+ //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/events.js
5382
6395
  /**
5383
6396
  * Abstract base class for ChatClient event emission
5384
6397
  */
@@ -5552,7 +6565,7 @@ var ChatClientEventEmitter = class {
5552
6565
  }
5553
6566
  };
5554
6567
  //#endregion
5555
- //#region node_modules/.deno/@tanstack+ai-client@0.28.0/node_modules/@tanstack/ai-client/dist/esm/devtools-noop.js
6568
+ //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/devtools-noop.js
5556
6569
  var NoOpChatClientEventEmitter = class extends ChatClientEventEmitter {
5557
6570
  emitEvent() {}
5558
6571
  };
@@ -5584,6 +6597,9 @@ var NoOpChatDevtoolsBridge = class {
5584
6597
  }
5585
6598
  observeChunk(_chunk) {}
5586
6599
  recordMemoryState(_value) {}
6600
+ recordCompactionEvent(_eventType, _value) {}
6601
+ recordCompactionState(_value) {}
6602
+ recordSkillsState(_value) {}
5587
6603
  beginRun(_runId, _threadId) {}
5588
6604
  getCurrentRunEventContext() {}
5589
6605
  getCurrentOrLastRunEventContext() {}
@@ -5594,7 +6610,7 @@ var NoOpChatDevtoolsBridge = class {
5594
6610
  };
5595
6611
  var createNoOpChatDevtoolsBridge = (options) => new NoOpChatDevtoolsBridge(options);
5596
6612
  //#endregion
5597
- //#region node_modules/.deno/@tanstack+ai-client@0.28.0/node_modules/@tanstack/ai-client/dist/esm/client-persistor.js
6613
+ //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/client-persistor.js
5598
6614
  /** Normalize a raw `getItem` result (legacy bare array or combined record). */
5599
6615
  function normalizePersistedState(raw) {
5600
6616
  if (Array.isArray(raw)) return { messages: raw };
@@ -5858,7 +6874,7 @@ var ChatPersistor = class {
5858
6874
  }
5859
6875
  };
5860
6876
  //#endregion
5861
- //#region node_modules/.deno/@tanstack+ai-client@0.28.0/node_modules/@tanstack/ai-client/dist/esm/cleared-stream-tracker.js
6877
+ //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/cleared-stream-tracker.js
5862
6878
  function getChunkToolCallId(chunk) {
5863
6879
  return "toolCallId" in chunk && typeof chunk.toolCallId === "string" ? chunk.toolCallId : void 0;
5864
6880
  }
@@ -5951,7 +6967,13 @@ var ClearedStreamTracker = class {
5951
6967
  }
5952
6968
  };
5953
6969
  //#endregion
5954
- //#region node_modules/.deno/@tanstack+ai-client@0.28.0/node_modules/@tanstack/ai-client/dist/esm/chat-client.js
6970
+ //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/chat-client.js
6971
+ var STREAM_PROCESSING_BUDGET_MS = 8;
6972
+ function yieldToHost() {
6973
+ const { scheduler } = globalThis;
6974
+ if (scheduler?.yield) return scheduler.yield();
6975
+ return new Promise((resolve) => setTimeout(resolve, 0));
6976
+ }
5955
6977
  function assertUniqueInterruptDefinitions(interrupts) {
5956
6978
  const ids = /* @__PURE__ */ new Set();
5957
6979
  for (const interrupt of interrupts ?? []) {
@@ -5966,6 +6988,16 @@ function resolveTransport(transport) {
5966
6988
  if (fetcher) return fetcherToConnectionAdapter(fetcher);
5967
6989
  throw new Error("ChatClient: either `connection` or `fetcher` is required.");
5968
6990
  }
6991
+ function connectionDrainsOnSend(connection) {
6992
+ return "connect" in connection;
6993
+ }
6994
+ function isIntermediateToolTurn(chunk) {
6995
+ if (chunk.type !== "RUN_FINISHED") return false;
6996
+ if (chunk.outcome?.type === "interrupt") return false;
6997
+ const extra = chunk;
6998
+ if (extra.finishReason !== void 0) return extra.finishReason === "tool_calls";
6999
+ return tanstackMetadata(chunk)?.finishReason === "tool_calls";
7000
+ }
5969
7001
  function normalizeQueueOption(option) {
5970
7002
  const base = {
5971
7003
  whenBusy: "queue",
@@ -6071,11 +7103,42 @@ var REJOIN_CONNECT_DEADLINE_MS = 2e3;
6071
7103
  var REJOIN_REBUILD_TRIGGERS = /* @__PURE__ */ new Set([
6072
7104
  "TEXT_MESSAGE_START",
6073
7105
  "TEXT_MESSAGE_CONTENT",
7106
+ "REASONING_MESSAGE_CONTENT",
6074
7107
  "TOOL_CALL_START",
6075
- "MESSAGES_SNAPSHOT"
7108
+ "MESSAGES_SNAPSHOT",
7109
+ "SUBAGENT_STARTED"
6076
7110
  ]);
7111
+ function rebuildsAssistantMessage(chunk) {
7112
+ if (chunk.type === "REASONING_ENCRYPTED_VALUE") return chunk.subtype === "message" && typeof chunk.encryptedValue === "string" && chunk.encryptedValue.length > 0;
7113
+ if (chunk.type === "STEP_FINISHED") return "signature" in chunk && Boolean(chunk.signature);
7114
+ return REJOIN_REBUILD_TRIGGERS.has(chunk.type);
7115
+ }
7116
+ /** Every subagent card in the messages, nested cards included. */
7117
+ function collectSubagentParts(messages) {
7118
+ const cards = [];
7119
+ for (const message of messages) for (const part of message.parts) {
7120
+ if (part.type !== "subagent") continue;
7121
+ cards.push(part);
7122
+ cards.push(...collectSubagentParts(part.subagent.messages));
7123
+ }
7124
+ return cards;
7125
+ }
7126
+ function readSubagentRunId(chunk) {
7127
+ if ("subagentRunId" in chunk && typeof chunk.subagentRunId === "string") return chunk.subagentRunId;
7128
+ }
7129
+ var MAX_RUN_LINEAGE_DEPTH = 64;
7130
+ function copyForDevtools(value) {
7131
+ if (Array.isArray(value)) return value.map(copyForDevtools);
7132
+ if (value instanceof Date) return new Date(value.getTime());
7133
+ if (value === null || typeof value !== "object") return value;
7134
+ const out = {};
7135
+ for (const [key, item] of Object.entries(value)) if (typeof item !== "function") out[key] = copyForDevtools(item);
7136
+ return out;
7137
+ }
6077
7138
  var ChatClient = class {
6078
7139
  processor;
7140
+ subagentHandles = /* @__PURE__ */ new Map();
7141
+ stoppedSubagentIds = /* @__PURE__ */ new Set();
6079
7142
  connection;
6080
7143
  uniqueId;
6081
7144
  threadId;
@@ -6083,6 +7146,8 @@ var ChatClient = class {
6083
7146
  clearedStreamTracker = new ClearedStreamTracker();
6084
7147
  currentRunId = null;
6085
7148
  lastResume = null;
7149
+ runParents = /* @__PURE__ */ new Map();
7150
+ supersededInterruptRunIds = /* @__PURE__ */ new Set();
6086
7151
  rejoinedRunId = null;
6087
7152
  interruptManager;
6088
7153
  activeInterruptSubmission;
@@ -6148,19 +7213,39 @@ var ChatClient = class {
6148
7213
  continuationPending = false;
6149
7214
  subscriptionAbortController = null;
6150
7215
  processingResolve = null;
7216
+ /**
7217
+ * `connect()` adapters push the full HTTP body into the subscribe queue, then
7218
+ * wait until that queue is idle. After `send()` returns, every chunk from this
7219
+ * request has been processed. Subscribe/send sockets do not drain that way.
7220
+ */
7221
+ connectionDrainsOnSend = false;
6151
7222
  errorReportedGeneration = null;
6152
7223
  streamGeneration = 0;
7224
+ continuationGeneration = 0;
7225
+ streamContinuationGeneration = 0;
6153
7226
  continuationSkipped = false;
6154
7227
  draining = false;
6155
7228
  sessionGenerating = false;
6156
7229
  activeRunIds = /* @__PURE__ */ new Set();
6157
7230
  /** Latched by `dispose()`; stops any late async callback starting new work. */
6158
7231
  disposed = false;
7232
+ /** The error a failed mount hydration set, cleared by the next successful one. */
7233
+ hydrationError;
6159
7234
  /** Whether a view is currently watching. See `attach` / `detach`. */
6160
7235
  tailing = false;
6161
7236
  /** Constructor inputs `attach()` needs on every re-attach, not just the first. */
6162
7237
  rejoinRunId;
6163
7238
  cachesMessages;
7239
+ /**
7240
+ * Newest-window size from `history.pageSize`. Only set when
7241
+ * `persistence === true`.
7242
+ */
7243
+ historyPageSize;
7244
+ hasOlderMessages = false;
7245
+ olderMessagesCursor;
7246
+ knownServerMessageIds = /* @__PURE__ */ new Set();
7247
+ loadOlderMessagesInFlight = false;
7248
+ historyGeneration = 0;
6164
7249
  devtoolsMounted = false;
6165
7250
  callbacksRef;
6166
7251
  constructor(options) {
@@ -6171,17 +7256,21 @@ var ChatClient = class {
6171
7256
  if (options.persistence === true) {
6172
7257
  if (!options.threadId) throw new Error("[TanStack AI] persistence needs a stable `threadId` to key on. Pass a threadId from your app (for example support-42).");
6173
7258
  cachesMessages = false;
7259
+ this.historyPageSize = options.history?.pageSize;
6174
7260
  } else if (options.persistence) {
6175
7261
  if (!options.threadId) throw new Error("[TanStack AI] persistence needs a stable `threadId` to key on. Pass a threadId from your app (for example support-42).");
6176
7262
  this.persistor = new ChatPersistor(options.persistence, options.threadId, (messages) => this.processor.setMessages(messages), (snapshot) => this.applyPersistedResume(snapshot));
6177
- }
7263
+ this.historyPageSize = void 0;
7264
+ } else this.historyPageSize = void 0;
6178
7265
  this.bodyOption = options.body || {};
6179
7266
  this.forwardedPropsOption = options.forwardedProps || {};
6180
7267
  this.byok = options.byok;
6181
7268
  this.byokProvider = options.byokProvider;
6182
7269
  this.context = options.context;
6183
7270
  this.queueConfig = normalizeQueueOption(options.queue);
6184
- this.connection = normalizeConnectionAdapter(resolveTransport(options));
7271
+ const transport = resolveTransport(options);
7272
+ this.connectionDrainsOnSend = connectionDrainsOnSend(transport);
7273
+ this.connection = normalizeConnectionAdapter(transport);
6185
7274
  this.clientToolsRef = { current: /* @__PURE__ */ new Map() };
6186
7275
  if (options.tools) for (const tool of options.tools) this.clientToolsRef.current.set(tool.name, tool);
6187
7276
  this.devtoolsBridge = (options.devtoolsBridgeFactory ?? createNoOpChatDevtoolsBridge)(this.buildDevtoolsBridgeOptions(options.devtools));
@@ -6229,8 +7318,10 @@ var ChatClient = class {
6229
7318
  ...initialMessages ? { initialMessages } : {},
6230
7319
  events: {
6231
7320
  onMessagesChange: (messages) => {
7321
+ this.syncSubagentHandles();
6232
7322
  this.persistor?.notifyMessagesChanged(messages);
6233
7323
  this.callbacksRef.current.onMessagesChange(messages);
7324
+ this.queueDevtoolsSnapshot();
6234
7325
  },
6235
7326
  onStreamStart: () => {
6236
7327
  this.setStatus("streaming");
@@ -6278,6 +7369,7 @@ var ChatClient = class {
6278
7369
  const clientTool = (this.activeClientTools ?? this.clientToolsRef.current).get(args.toolName);
6279
7370
  const executeFunc = clientTool?.execute;
6280
7371
  if (executeFunc) {
7372
+ const continuationGeneration = this.continuationGeneration;
6281
7373
  const runEventContext = this.devtoolsBridge.getCurrentRunEventContext();
6282
7374
  const executionPromise = (async () => {
6283
7375
  try {
@@ -6292,7 +7384,7 @@ var ChatClient = class {
6292
7384
  tool: args.toolName,
6293
7385
  output,
6294
7386
  state: "output-available"
6295
- }, clientTool, runEventContext);
7387
+ }, clientTool, continuationGeneration, runEventContext);
6296
7388
  } catch (error) {
6297
7389
  await this.addToolResultForClientTool({
6298
7390
  toolCallId: args.toolCallId,
@@ -6300,7 +7392,7 @@ var ChatClient = class {
6300
7392
  output: null,
6301
7393
  state: "output-error",
6302
7394
  errorText: error.message
6303
- }, clientTool, runEventContext);
7395
+ }, clientTool, continuationGeneration, runEventContext);
6304
7396
  } finally {
6305
7397
  this.pendingToolExecutions.delete(args.toolCallId);
6306
7398
  }
@@ -6315,10 +7407,13 @@ var ChatClient = class {
6315
7407
  },
6316
7408
  onCustomEvent: (eventType, data, context) => {
6317
7409
  if (eventType === "memory:state") this.devtoolsBridge.recordMemoryState(data);
7410
+ if (eventType === "compaction:started" || eventType === "compaction:state" || eventType === "compaction:ended") this.devtoolsBridge.recordCompactionEvent(eventType, data);
7411
+ if (eventType === "skills:state") this.devtoolsBridge.recordSkillsState(data);
6318
7412
  this.callbacksRef.current.onCustomEvent(eventType, data, context);
6319
7413
  }
6320
7414
  }
6321
7415
  });
7416
+ this.syncSubagentHandles();
6322
7417
  this.persistor?.hydrateAsync(persistedState);
6323
7418
  this.rejoinRunId = rejoinRunId;
6324
7419
  this.cachesMessages = cachesMessages;
@@ -6427,17 +7522,32 @@ var ChatClient = class {
6427
7522
  if (!hydrate) return;
6428
7523
  if (this.isLoading || this.abortController) return;
6429
7524
  if (this.disposed) return;
7525
+ const pageSize = this.historyPageSize;
7526
+ const hydrateOptions = pageSize === void 0 ? void 0 : { limit: pageSize };
6430
7527
  (async () => {
6431
7528
  let result;
7529
+ const generation = this.historyGeneration;
6432
7530
  try {
6433
- result = await hydrate(this.threadId);
6434
- } catch {
7531
+ result = await hydrate(this.threadId, hydrateOptions);
7532
+ } catch (cause) {
7533
+ if (generation === this.historyGeneration) this.failHydration(cause);
6435
7534
  return;
6436
7535
  }
7536
+ if (generation !== this.historyGeneration) return;
6437
7537
  if (this.disposed || !this.tailing) return;
6438
7538
  if (this.isLoading || this.abortController) return;
6439
- if (result.messages.length > 0) this.processor.setMessages(result.messages);
6440
- if (result.interrupts && result.interrupts.pending.length > 0) this.applyResumeSnapshot({
7539
+ if (this.hydrationError && this.error === this.hydrationError) {
7540
+ this.setError(void 0);
7541
+ this.setStatus("ready");
7542
+ }
7543
+ this.hydrationError = void 0;
7544
+ this.applyHydrationPage(result.page);
7545
+ if (result.messages.length > 0) {
7546
+ const windowMessages = normalizeMessagesDates(result.messages);
7547
+ this.processor.setMessages(windowMessages);
7548
+ this.rememberServerMessageIds(windowMessages);
7549
+ }
7550
+ if (result.interrupts && result.interrupts.pending.length > 0 && (!result.activeRun?.runId || result.activeRun.runId === result.interrupts.runId)) this.applyResumeSnapshot({
6441
7551
  resumeState: {
6442
7552
  threadId: this.threadId,
6443
7553
  runId: result.interrupts.runId
@@ -6447,6 +7557,29 @@ var ChatClient = class {
6447
7557
  else if (result.activeRun?.runId) this.maybeRejoinInFlight(result.activeRun.runId);
6448
7558
  })();
6449
7559
  }
7560
+ /**
7561
+ * Surface a mount-hydration failure (`persistence: true`) on the observable
7562
+ * fields, mirroring `GenerationClient.failHydration`, so "this thread failed
7563
+ * to load" is distinguishable from "this thread has no messages" and the app
7564
+ * can show an error / offer a retry. A genuine miss — the server having no
7565
+ * record for a fresh thread — resolves normally and never reaches here; only a
7566
+ * thrown transport / authorize-gate error does.
7567
+ *
7568
+ * Skipped when the view unmounted (`!tailing`) or a `sendMessage` took
7569
+ * ownership while the hydrate GET was in flight, so a live run's state always
7570
+ * wins over a stale mount-time failure — same guard as the success path above.
7571
+ */
7572
+ failHydration(cause) {
7573
+ if (this.disposed || !this.tailing) return;
7574
+ if (this.isLoading || this.abortController) return;
7575
+ const error = cause instanceof Error ? cause : new Error(String(cause));
7576
+ if (error instanceof ByokMissingError) this.byok?.request(error.provider, "missing");
7577
+ if (error instanceof ByokBlockedError && error.reason === "locked") this.byok?.request(error.provider, "locked");
7578
+ this.hydrationError = error;
7579
+ this.setStatus("error");
7580
+ this.setError(error);
7581
+ this.callbacksRef.current.onError(error);
7582
+ }
6450
7583
  mountDevtools() {
6451
7584
  this.ensureThreadId();
6452
7585
  if (this.devtoolsMounted) return;
@@ -6482,15 +7615,28 @@ var ChatClient = class {
6482
7615
  updateRunLifecycle(chunk, options) {
6483
7616
  if (chunk.type === "RUN_STARTED") {
6484
7617
  const chunkRunId = getChunkRunId$1(chunk) ?? chunk.runId;
7618
+ const parentRunId = "parentRunId" in chunk && typeof chunk.parentRunId === "string" ? chunk.parentRunId : void 0;
7619
+ if (parentRunId) {
7620
+ this.runParents.set(chunkRunId, parentRunId);
7621
+ if (this.supersededInterruptRunIds.has(chunkRunId)) this.markLineageAnswered(parentRunId);
7622
+ }
6485
7623
  this.activeResumeThreadId = "threadId" in chunk && typeof chunk.threadId === "string" ? chunk.threadId : this.activeResumeThreadId;
6486
7624
  this.activeResumeRunId = chunkRunId;
6487
- this.activeRunIds.add(chunkRunId);
6488
- this.clearedStreamTracker.onRunStarted(chunkRunId);
6489
- this.setSessionGenerating(true);
6490
- if (this.persistor && this.connection.joinRun && !this.lastResume) this.persistResumeSnapshot({
7625
+ const isAnsweredReplayStart = this.supersededInterruptRunIds.has(chunkRunId) && !this.isLoading;
7626
+ if (!isAnsweredReplayStart) {
7627
+ this.activeRunIds.add(chunkRunId);
7628
+ this.clearedStreamTracker.onRunStarted(chunkRunId);
7629
+ this.setSessionGenerating(true);
7630
+ }
7631
+ if (this.persistor && this.connection.joinRun && !this.lastResume && !isAnsweredReplayStart) this.persistResumeSnapshot({
6491
7632
  threadId: this.activeResumeThreadId ?? this.threadId,
6492
7633
  runId: chunkRunId
6493
7634
  });
7635
+ if (this.lastResume && this.supersededInterruptRunIds.has(this.lastResume.runId)) {
7636
+ this.lastResume = null;
7637
+ this.persistor?.persistResumeSnapshot(null);
7638
+ this.interruptManager.reset();
7639
+ }
6494
7640
  return;
6495
7641
  }
6496
7642
  if (chunk.type !== "RUN_FINISHED" && chunk.type !== "RUN_ERROR") return;
@@ -6503,7 +7649,8 @@ var ChatClient = class {
6503
7649
  this.clearedStreamTracker.onSessionRunError();
6504
7650
  }
6505
7651
  this.setSessionGenerating(this.activeRunIds.size > 0);
6506
- if (options?.resolveProcessing !== false) this.resolveProcessing();
7652
+ const skipProcessingResolve = chunk.type === "RUN_FINISHED" && isIntermediateToolTurn(chunk);
7653
+ if (options?.resolveProcessing !== false && !skipProcessingResolve) this.resolveProcessing();
6507
7654
  }
6508
7655
  /**
6509
7656
  * Track interrupt state off the stream's terminal events. A RUN_FINISHED with
@@ -6516,8 +7663,10 @@ var ChatClient = class {
6516
7663
  if (this.activeInterruptSubmission && chunk.type === "RUN_ERROR") return;
6517
7664
  const runId = getChunkRunId$1(chunk);
6518
7665
  const threadId = "threadId" in chunk && typeof chunk.threadId === "string" ? chunk.threadId : this.activeResumeThreadId;
7666
+ if (runId && !isIntermediateToolTurn(chunk) && (chunk.type === "RUN_ERROR" || chunk.type === "RUN_FINISHED" && chunk.outcome?.type !== "interrupt")) this.markLineageAnswered(runId);
6519
7667
  if (chunk.type === "RUN_FINISHED" && chunk.outcome?.type === "interrupt") {
6520
7668
  const interruptedRunId = this.currentRunId ?? runId ?? this.activeResumeRunId ?? "";
7669
+ if (interruptedRunId && this.supersededInterruptRunIds.has(interruptedRunId)) return;
6521
7670
  this.lastResume = {
6522
7671
  threadId: threadId ?? this.threadId,
6523
7672
  runId: interruptedRunId
@@ -6536,7 +7685,8 @@ var ChatClient = class {
6536
7685
  const isActiveStreamRunTerminal = Boolean(this.isLoading && runId && (runId === this.activeResumeRunId || runId === this.currentRunId));
6537
7686
  const isCurrentStreamTerminal = this.isLoading && chunk.type === "RUN_FINISHED" && !runId;
6538
7687
  const isActiveInterruptSubmissionTerminal = Boolean(this.activeInterruptSubmission && this.isLoading && chunk.type === "RUN_FINISHED" && chunk.outcome?.type !== "interrupt");
6539
- if (isRunlessSessionError || isTrackedRunTerminal || isCurrentRunTerminal || isActiveStreamRunTerminal || isCurrentStreamTerminal || isActiveInterruptSubmissionTerminal) {
7688
+ const isLineageDescendantTerminal = Boolean(this.lastResume && this.supersededInterruptRunIds.has(this.lastResume.runId));
7689
+ if (isRunlessSessionError || isTrackedRunTerminal || isCurrentRunTerminal || isActiveStreamRunTerminal || isCurrentStreamTerminal || isActiveInterruptSubmissionTerminal || isLineageDescendantTerminal) {
6540
7690
  this.lastResume = null;
6541
7691
  this.persistor?.persistResumeSnapshot(null);
6542
7692
  this.interruptManager.reset();
@@ -6544,6 +7694,17 @@ var ChatClient = class {
6544
7694
  }
6545
7695
  this.notifyResumeStateChange("live");
6546
7696
  }
7697
+ /** Walk parentRunId links and mark this run plus its ancestors as answered. */
7698
+ markLineageAnswered(runId) {
7699
+ let current = runId;
7700
+ let hops = 0;
7701
+ while (current !== void 0 && hops < MAX_RUN_LINEAGE_DEPTH) {
7702
+ if (this.supersededInterruptRunIds.has(current)) return;
7703
+ this.supersededInterruptRunIds.add(current);
7704
+ current = this.runParents.get(current);
7705
+ hops++;
7706
+ }
7707
+ }
6547
7708
  /**
6548
7709
  * The interrupt-resume state for the active/interrupted run (its run/thread
6549
7710
  * ids), or null when there is nothing to resume. Apps can persist this to
@@ -6594,10 +7755,14 @@ var ChatClient = class {
6594
7755
  resumeInterruptsUnsafe(resume, state) {
6595
7756
  const target = state ?? this.lastResume;
6596
7757
  if (!target) return Promise.resolve(false);
7758
+ return this.resumeInterruptsUnsafeForGeneration(resume, target, this.continuationGeneration);
7759
+ }
7760
+ resumeInterruptsUnsafeForGeneration(resume, target, continuationGeneration) {
7761
+ if (continuationGeneration !== this.continuationGeneration) return Promise.resolve(false);
6597
7762
  if (this.isLoading) return new Promise((resolve, reject) => {
6598
7763
  this.queuePostStreamAction(async () => {
6599
7764
  try {
6600
- resolve(await this.resumeInterruptsUnsafe(resume, target));
7765
+ resolve(await this.resumeInterruptsUnsafeForGeneration(resume, target, continuationGeneration));
6601
7766
  } catch (error) {
6602
7767
  reject(error);
6603
7768
  }
@@ -6613,6 +7778,7 @@ var ChatClient = class {
6613
7778
  return this.resumeInterruptsUnsafe(resume, state);
6614
7779
  }
6615
7780
  async submitInterruptBatch(submission) {
7781
+ const continuationGeneration = this.continuationGeneration;
6616
7782
  this.activeInterruptSubmission = submission;
6617
7783
  this.interruptSubmissionFailure = void 0;
6618
7784
  for (const resolution of submission.resolutions) {
@@ -6621,12 +7787,13 @@ var ChatClient = class {
6621
7787
  const approvalId = resolution.interruptId;
6622
7788
  this.processor.addToolApprovalResponse(approvalId, approved);
6623
7789
  }
6624
- const resumed = await this.resumeInterruptsUnsafe([...submission.resolutions], {
7790
+ const resumed = await this.resumeInterruptsUnsafeForGeneration([...submission.resolutions], {
6625
7791
  threadId: submission.threadId,
6626
7792
  runId: submission.interruptedRunId
6627
- }).finally(() => {
6628
- this.activeInterruptSubmission = void 0;
7793
+ }, continuationGeneration).finally(() => {
7794
+ if (this.activeInterruptSubmission === submission) this.activeInterruptSubmission = void 0;
6629
7795
  });
7796
+ if (continuationGeneration !== this.continuationGeneration) return;
6630
7797
  const failure = this.takeInterruptSubmissionFailure();
6631
7798
  if (failure !== void 0) throw { errors: failure.errors };
6632
7799
  if (!resumed) throw new Error("Interrupt continuation could not be started.");
@@ -6744,7 +7911,7 @@ var ChatClient = class {
6744
7911
  }
6745
7912
  getDevtoolsSnapshot() {
6746
7913
  return {
6747
- messages: this.processor.getMessages(),
7914
+ messages: copyForDevtools(this.processor.getMessages()),
6748
7915
  status: this.status,
6749
7916
  isLoading: this.isLoading,
6750
7917
  isSubscribed: this.isSubscribed,
@@ -6809,13 +7976,21 @@ var ChatClient = class {
6809
7976
  });
6810
7977
  }
6811
7978
  /**
6812
- * Consume chunks from the connection subscription.
7979
+ * Consume chunks from the connection subscription. Chunks are processed in
7980
+ * order; the loop yields to the host after each processing budget.
6813
7981
  */
6814
7982
  async consumeSubscription(signal) {
6815
7983
  const stream = this.connection.subscribe(signal);
7984
+ let chunkProcessingTime = 0;
6816
7985
  for await (const chunk of stream) {
6817
7986
  if (signal.aborted) break;
6818
- await this.processIncomingChunk(chunk);
7987
+ const startedAt = performance.now();
7988
+ this.processIncomingChunk(chunk);
7989
+ chunkProcessingTime += performance.now() - startedAt;
7990
+ if (chunkProcessingTime < STREAM_PROCESSING_BUDGET_MS) continue;
7991
+ chunkProcessingTime = 0;
7992
+ if (typeof document !== "undefined" && document.hidden) continue;
7993
+ await yieldToHost();
6819
7994
  }
6820
7995
  }
6821
7996
  /**
@@ -6837,7 +8012,7 @@ var ChatClient = class {
6837
8012
  * give up after {@link REJOIN_CONNECT_DEADLINE_MS} if no chunk arrives and
6838
8013
  * clear the dead pointer so it does not retry on the next load.
6839
8014
  *
6840
- * Replay chunks are processed WITHOUT the per-chunk yield the live path uses,
8015
+ * Replay chunks are processed WITHOUT the time-slice yield the live path uses,
6841
8016
  * so the buffered prefix snaps in and only the genuinely-live tail streams at
6842
8017
  * network speed — a reload looks like the run continued, not like it re-typed.
6843
8018
  */
@@ -6851,6 +8026,7 @@ var ChatClient = class {
6851
8026
  threadId: this.threadId,
6852
8027
  runId
6853
8028
  };
8029
+ this.streamContinuationGeneration = this.continuationGeneration;
6854
8030
  this.setIsLoading(true);
6855
8031
  this.setStatus("streaming");
6856
8032
  (async () => {
@@ -6867,11 +8043,11 @@ var ChatClient = class {
6867
8043
  attached = true;
6868
8044
  clearTimeout(connectTimer);
6869
8045
  }
6870
- if (!rebuilt && REJOIN_REBUILD_TRIGGERS.has(chunk.type)) {
8046
+ if (!rebuilt && rebuildsAssistantMessage(chunk)) {
6871
8047
  rebuilt = true;
6872
8048
  this.dropTrailingInFlightAssistant();
6873
8049
  }
6874
- await this.processIncomingChunk(chunk, { defer: false });
8050
+ this.processIncomingChunk(chunk);
6875
8051
  }
6876
8052
  if (this.pendingToolExecutions.size > 0) await Promise.all(this.pendingToolExecutions.values());
6877
8053
  } catch (error) {
@@ -6904,7 +8080,7 @@ var ChatClient = class {
6904
8080
  const last = messages[messages.length - 1];
6905
8081
  if (last && last.role === "assistant") this.processor.setMessages(messages.slice(0, -1));
6906
8082
  }
6907
- async processIncomingChunk(chunk, options) {
8083
+ processIncomingChunk(chunk) {
6908
8084
  chunk = restoreInboundChunk(chunk);
6909
8085
  if (chunk.type === "RUN_ERROR" && this.isActiveInterruptSubmissionFailure(chunk)) {
6910
8086
  const interruptErrors = tanstackMetadata(chunk)?.interruptErrors;
@@ -6922,10 +8098,17 @@ var ChatClient = class {
6922
8098
  }
6923
8099
  this.callbacksRef.current.onChunk(chunk);
6924
8100
  this.devtoolsBridge.observeChunk(chunk);
8101
+ const attributedId = readSubagentRunId(chunk);
8102
+ if (attributedId && chunk.type === EventType.SUBAGENT_STARTED) this.stoppedSubagentIds.delete(attributedId);
8103
+ if (attributedId && this.stoppedSubagentIds.has(attributedId)) {
8104
+ this.updateRunLifecycle(chunk);
8105
+ this.resolveJoinedRun(chunk);
8106
+ return;
8107
+ }
6925
8108
  this.processor.processChunk(chunk);
8109
+ this.syncSubagentHandles();
6926
8110
  this.updateRunLifecycle(chunk);
6927
8111
  this.observeInterruptState(chunk);
6928
- if (options?.defer !== false && (typeof document === "undefined" || !document.hidden)) await new Promise((resolve) => setTimeout(resolve, 0));
6929
8112
  this.resolveJoinedRun(chunk);
6930
8113
  }
6931
8114
  isActiveInterruptSubmissionFailure(chunk) {
@@ -7024,7 +8207,7 @@ var ChatClient = class {
7024
8207
  async sendMessage(content, body, sendOptions) {
7025
8208
  this.mountDevtools();
7026
8209
  if (typeof content === "string" && !content.trim()) return;
7027
- if (this.hasBlockingInterrupts()) throw new Error("ChatClient: cannot send normal input while pending interrupts exist. Use resumeInterrupts() instead.");
8210
+ if (this.hasPendingInterrupts()) throw new Error("ChatClient: cannot send normal input while pending interrupts exist. Use resumeInterrupts() instead.");
7028
8211
  const resolvedBody = {
7029
8212
  ...body,
7030
8213
  ...sendOptions?.body
@@ -7051,10 +8234,6 @@ var ChatClient = class {
7051
8234
  hasPendingInterrupts() {
7052
8235
  return this.interruptManager.getDescriptors().length > 0;
7053
8236
  }
7054
- /** True while an interrupt batch owns the next user turn. */
7055
- hasBlockingInterrupts() {
7056
- return this.activeInterruptSubmission !== void 0 || this.hasPendingInterrupts();
7057
- }
7058
8237
  /** True while a stream is active, a send is claiming the client, or the queue is draining. */
7059
8238
  isSendBusy() {
7060
8239
  return this.isLoading || this.sendInFlight || this.messageQueueDraining;
@@ -7146,7 +8325,7 @@ var ChatClient = class {
7146
8325
  */
7147
8326
  async append(message) {
7148
8327
  this.mountDevtools();
7149
- if (this.hasBlockingInterrupts()) throw new Error("ChatClient: cannot append normal input while pending interrupts exist. Use resumeInterrupts() instead.");
8328
+ if (this.hasPendingInterrupts()) throw new Error("ChatClient: cannot append normal input while pending interrupts exist. Use resumeInterrupts() instead.");
7150
8329
  const normalizedMessage = normalizeToUIMessage(message, generateMessageId);
7151
8330
  if (normalizedMessage.role === "system") return;
7152
8331
  const uiMessage = normalizedMessage;
@@ -7169,6 +8348,7 @@ var ChatClient = class {
7169
8348
  async streamResponse() {
7170
8349
  if (this.isLoading) return false;
7171
8350
  const generation = ++this.streamGeneration;
8351
+ this.streamContinuationGeneration = this.continuationGeneration;
7172
8352
  const resumeThreadId = this.pendingResumeThreadId;
7173
8353
  const resumeParentRunId = this.pendingResumeParentRunId;
7174
8354
  const resumeItems = this.pendingResumeItems;
@@ -7191,7 +8371,7 @@ var ChatClient = class {
7191
8371
  let activeDevtoolsRunId = null;
7192
8372
  let runTerminalEventEmitted = false;
7193
8373
  try {
7194
- const messages = this.processor.getMessages();
8374
+ const messages = this.messagesForSend(this.processor.getMessages());
7195
8375
  const clientTools = new Map(this.clientToolsRef.current);
7196
8376
  const runtimeContext = this.context;
7197
8377
  await this.callbacksRef.current.onResponse();
@@ -7212,7 +8392,7 @@ var ChatClient = class {
7212
8392
  const processingComplete = this.waitForProcessing();
7213
8393
  let byokHeaders;
7214
8394
  if (this.byok) {
7215
- const provider = resolveByokProviderId(this.byokProvider, mergedBody.provider);
8395
+ const provider = resolveByokProviderId(this.byokProvider, mergedBody.provider, byokFallbackProviderId(this.byok));
7216
8396
  byokHeaders = await prepareResolvedByokHeaders(this.byok, provider);
7217
8397
  }
7218
8398
  const runContext = {
@@ -7235,6 +8415,7 @@ var ChatClient = class {
7235
8415
  this.devtoolsBridge.emitSnapshot();
7236
8416
  await this.connection.send(messages, mergedBody, signal, runContext);
7237
8417
  if (generation !== this.streamGeneration || signal.aborted) return false;
8418
+ if (this.connectionDrainsOnSend) this.resolveProcessing();
7238
8419
  await processingComplete;
7239
8420
  if (generation !== this.streamGeneration) return false;
7240
8421
  if (this.status === "error") {
@@ -7283,6 +8464,7 @@ var ChatClient = class {
7283
8464
  }
7284
8465
  await this.drainPostStreamActions();
7285
8466
  if (streamCompletedSuccessfully) {
8467
+ this.rememberProcessorMessageIds();
7286
8468
  if (this.status !== "ready") this.setStatus("ready");
7287
8469
  if (!this.messageQueueDraining) await this.drainQueue();
7288
8470
  } else this.flushQueue();
@@ -7335,9 +8517,13 @@ var ChatClient = class {
7335
8517
  * Stop the current stream
7336
8518
  */
7337
8519
  stop() {
8520
+ this.continuationGeneration++;
7338
8521
  const hadLocalStream = this.abortController !== null;
7339
8522
  this.cancelInFlightStream({ setReadyStatus: true });
7340
8523
  this.discardPendingSends();
8524
+ this.lastResume = null;
8525
+ this.activeInterruptSubmission = void 0;
8526
+ this.interruptManager.reset();
7341
8527
  if (hadLocalStream) this.resetSessionGenerating();
7342
8528
  this.events.stopped();
7343
8529
  }
@@ -7357,8 +8543,10 @@ var ChatClient = class {
7357
8543
  } else if (this.activeRunIds.size > 0) this.resetSessionGenerating({ preserveClearedStreamTracking: true });
7358
8544
  this.persistor?.beginClear();
7359
8545
  this.processor.clearMessages();
8546
+ this.resetHistoryPaging();
7360
8547
  this.discardPendingSends();
7361
8548
  this.persistor?.remove();
8549
+ this.stoppedSubagentIds.clear();
7362
8550
  this.lastResume = null;
7363
8551
  this.interruptManager.reset();
7364
8552
  this.pendingResumeThreadId = null;
@@ -7372,13 +8560,15 @@ var ChatClient = class {
7372
8560
  */
7373
8561
  async addToolResult(result) {
7374
8562
  const clientTool = this.clientToolsRef.current.get(result.tool);
7375
- await this.addToolResultForClientTool(result, clientTool);
8563
+ await this.addToolResultForClientTool(result, clientTool, this.streamContinuationGeneration);
7376
8564
  }
7377
- async addToolResultForClientTool(result, clientTool, context) {
7378
- if (clientTool && result.state !== "output-error") try {
8565
+ async addToolResultForClientTool(result, clientTool, continuationGeneration, context) {
8566
+ if (result.state !== "output-error") try {
8567
+ let output = clientTool?.outputSchema && isStandardSchema(clientTool.outputSchema) ? await this.validateClientToolOutput(clientTool, result.output) : result.output;
8568
+ if (this.interruptManager.getDescriptors().some((interrupt) => interrupt.toolCallId === result.toolCallId)) output = cloneAndDeepFreezeJson(output);
7379
8569
  result = {
7380
8570
  ...result,
7381
- output: this.validateClientToolOutput(clientTool, result.output)
8571
+ output
7382
8572
  };
7383
8573
  } catch (error) {
7384
8574
  result = {
@@ -7389,18 +8579,20 @@ var ChatClient = class {
7389
8579
  };
7390
8580
  }
7391
8581
  this.events.toolResultAdded(result.toolCallId, result.tool, result.output, result.state || "output-available", context);
8582
+ if (continuationGeneration !== this.continuationGeneration) return;
7392
8583
  this.processor.addToolResult(result.toolCallId, result.output, result.state === "output-error" ? result.errorText || "Tool execution failed" : void 0);
7393
8584
  this.devtoolsBridge.emitSnapshot();
7394
- if (this.interruptManager.resolveClientToolOutput(result.toolCallId, result.state === "output-error" ? { error: result.errorText || "Tool execution failed" } : result.output)) return;
8585
+ if (result.state === "output-error" ? this.interruptManager.resolveClientToolError(result.toolCallId, result.errorText || "Tool execution failed") : this.interruptManager.resolveClientToolOutput(result.toolCallId, result.output)) return;
7395
8586
  if (this.isLoading) {
7396
- this.queuePostStreamAction(() => this.checkForContinuation());
8587
+ this.queuePostStreamAction(() => continuationGeneration === this.continuationGeneration ? this.checkForContinuation() : Promise.resolve());
7397
8588
  return;
7398
8589
  }
7399
8590
  await this.checkForContinuation();
7400
8591
  }
7401
- validateClientToolOutput(clientTool, output) {
7402
- if (clientTool.outputSchema && isStandardSchema(clientTool.outputSchema)) return parseWithStandardSchema(clientTool.outputSchema, output);
7403
- return output;
8592
+ async validateClientToolOutput(clientTool, output) {
8593
+ const validation = await validateWithStandardSchema(clientTool.outputSchema, output);
8594
+ if (!validation.success) throw new Error(validation.issues.map((issue) => issue.message).join(", "));
8595
+ return validation.data;
7404
8596
  }
7405
8597
  /**
7406
8598
  * Respond to a tool approval request
@@ -7431,7 +8623,11 @@ var ChatClient = class {
7431
8623
  * Queue an action to be executed after the current stream ends
7432
8624
  */
7433
8625
  queuePostStreamAction(action) {
7434
- this.postStreamActions.push(action);
8626
+ const continuationGeneration = this.continuationGeneration;
8627
+ this.postStreamActions.push(async () => {
8628
+ if (continuationGeneration !== this.continuationGeneration) return;
8629
+ await action();
8630
+ });
7435
8631
  }
7436
8632
  /**
7437
8633
  * Drain and execute all queued post-stream actions
@@ -7450,6 +8646,7 @@ var ChatClient = class {
7450
8646
  * Check if we should continue the flow and do so if needed
7451
8647
  */
7452
8648
  async checkForContinuation() {
8649
+ if (this.streamContinuationGeneration !== this.continuationGeneration) return;
7453
8650
  if (this.hasPendingInterrupts()) return;
7454
8651
  if (this.continuationPending || this.isLoading) {
7455
8652
  this.continuationSkipped = true;
@@ -7490,6 +8687,132 @@ var ChatClient = class {
7490
8687
  getMessages() {
7491
8688
  return this.processor.getMessages();
7492
8689
  }
8690
+ getSubagents() {
8691
+ return [...this.subagentHandles.values()];
8692
+ }
8693
+ devtoolsSnapshotQueued = false;
8694
+ /** One devtools snapshot per microtask, however many chunks arrive. */
8695
+ queueDevtoolsSnapshot() {
8696
+ if (this.devtoolsSnapshotQueued) return;
8697
+ this.devtoolsSnapshotQueued = true;
8698
+ queueMicrotask(() => {
8699
+ this.devtoolsSnapshotQueued = false;
8700
+ this.devtoolsBridge.emitSnapshot();
8701
+ });
8702
+ }
8703
+ syncSubagentHandles() {
8704
+ const cards = collectSubagentParts(this.processor.getMessages());
8705
+ const present = new Set(cards.map((part) => part.subagent.id));
8706
+ for (const id of this.subagentHandles.keys()) if (!present.has(id)) this.subagentHandles.delete(id);
8707
+ for (const part of cards) {
8708
+ const id = part.subagent.id;
8709
+ const existing = this.subagentHandles.get(id);
8710
+ if (existing) {
8711
+ if (part.subagent === existing) continue;
8712
+ const { stop } = existing;
8713
+ for (const key of Object.keys(existing)) Reflect.deleteProperty(existing, key);
8714
+ Object.assign(existing, part.subagent, { stop });
8715
+ part.subagent = existing;
8716
+ continue;
8717
+ }
8718
+ const handle = {
8719
+ ...part.subagent,
8720
+ stop: () => {
8721
+ this.stopSubagent(id);
8722
+ }
8723
+ };
8724
+ this.subagentHandles.set(id, handle);
8725
+ part.subagent = handle;
8726
+ }
8727
+ }
8728
+ stopSubagent(id) {
8729
+ const handle = this.subagentHandles.get(id);
8730
+ if (!handle || handle.status !== "running") return;
8731
+ this.stoppedSubagentIds.add(id);
8732
+ this.processor.processChunk({
8733
+ type: EventType.SUBAGENT_ERROR,
8734
+ subagentRunId: id,
8735
+ message: "Stopped",
8736
+ timestamp: Date.now()
8737
+ });
8738
+ this.syncSubagentHandles();
8739
+ handle.status = "error";
8740
+ handle.error = { message: "Stopped" };
8741
+ this.abortController?.abort();
8742
+ }
8743
+ /**
8744
+ * True when the last hydrate or older-page response said more messages exist.
8745
+ */
8746
+ getHasOlderMessages() {
8747
+ return this.hasOlderMessages;
8748
+ }
8749
+ /**
8750
+ * Fetch the next older window and put it in front of the painted messages.
8751
+ *
8752
+ * No-op when there is no older page, no cursor, or a page load is already
8753
+ * running. A network failure rejects and leaves the painted messages as they
8754
+ * are; {@link getHasOlderMessages} stays true.
8755
+ */
8756
+ async loadOlderMessages() {
8757
+ const hydrate = this.connection.hydrate;
8758
+ if (hydrate === void 0) return;
8759
+ if (this.disposed) return;
8760
+ if (!this.hasOlderMessages) return;
8761
+ const cursor = this.olderMessagesCursor;
8762
+ if (cursor === void 0) return;
8763
+ if (this.loadOlderMessagesInFlight) return;
8764
+ const hydrateOptions = { before: cursor };
8765
+ if (this.historyPageSize !== void 0) hydrateOptions.limit = this.historyPageSize;
8766
+ this.loadOlderMessagesInFlight = true;
8767
+ const generation = this.historyGeneration;
8768
+ try {
8769
+ const result = await hydrate(this.threadId, hydrateOptions);
8770
+ if (this.disposed) return;
8771
+ if (generation !== this.historyGeneration) return;
8772
+ const olderMessages = normalizeMessagesDates(result.messages);
8773
+ if (olderMessages.length === 0) throw new Error("Older page was empty");
8774
+ this.applyHydrationPage(result.page);
8775
+ this.processor.prependMessages(olderMessages);
8776
+ this.rememberServerMessageIds(olderMessages);
8777
+ } finally {
8778
+ this.loadOlderMessagesInFlight = false;
8779
+ }
8780
+ }
8781
+ resetHistoryPaging() {
8782
+ this.hasOlderMessages = false;
8783
+ this.olderMessagesCursor = void 0;
8784
+ this.knownServerMessageIds.clear();
8785
+ this.historyGeneration++;
8786
+ }
8787
+ applyHydrationPage(page) {
8788
+ if (page?.truncated === true) {
8789
+ this.hasOlderMessages = true;
8790
+ this.olderMessagesCursor = page.cursor;
8791
+ return;
8792
+ }
8793
+ this.hasOlderMessages = false;
8794
+ this.olderMessagesCursor = void 0;
8795
+ }
8796
+ rememberServerMessageIds(messages) {
8797
+ const ids = messages.map((message) => message.id);
8798
+ for (const id of ids) this.knownServerMessageIds.add(id);
8799
+ }
8800
+ rememberProcessorMessageIds() {
8801
+ if (this.historyPageSize === void 0) return;
8802
+ this.rememberServerMessageIds(this.processor.getMessages());
8803
+ }
8804
+ messagesForSend(messages) {
8805
+ if (this.historyPageSize === void 0) return messages;
8806
+ const unknownMessages = [];
8807
+ for (const message of messages) {
8808
+ if (this.knownServerMessageIds.has(message.id)) continue;
8809
+ unknownMessages.push(message);
8810
+ }
8811
+ if (unknownMessages.length > 0) return unknownMessages;
8812
+ const lastUserIndex = messages.findLastIndex((message) => message.role === "user");
8813
+ if (lastUserIndex === -1) return [];
8814
+ return messages.slice(lastUserIndex);
8815
+ }
7493
8816
  /**
7494
8817
  * True when an interrupt (or another direct send) claimed the client during
7495
8818
  * a drain. Read via a method so cross-await mutations are not constant-folded
@@ -7633,10 +8956,12 @@ var ChatClient = class {
7633
8956
  this.resetSessionGenerating();
7634
8957
  this.setIsSubscribed(false);
7635
8958
  this.setConnectionStatus("disconnected");
7636
- this.connection = normalizeConnectionAdapter(resolveTransport({
8959
+ const transport = resolveTransport({
7637
8960
  connection: options.connection,
7638
8961
  fetcher: options.fetcher
7639
- }));
8962
+ });
8963
+ this.connectionDrainsOnSend = connectionDrainsOnSend(transport);
8964
+ this.connection = normalizeConnectionAdapter(transport);
7640
8965
  if (wasSubscribed) this.subscribe();
7641
8966
  }
7642
8967
  if (options.body !== void 0) this.bodyOption = options.body;
@@ -8379,35 +9704,49 @@ function createAstralBeamChat(options, deferStart = false) {
8379
9704
  throw error instanceof Error && isAstralBeamApiError(error.cause) ? error.cause : error;
8380
9705
  }
8381
9706
  };
8382
- const client = new ChatClient({
8383
- connection,
8384
- tools: declareTools(),
8385
- forwardedProps: forwardedProps(),
8386
- onMessagesChange: (messages) => {
8387
- update({
8388
- messages,
8389
- sandbox: collectSandboxActivity(messages),
8390
- error: client.getError()
8391
- });
8392
- },
8393
- onStatusChange: (status) => update({
8394
- status,
8395
- error: client.getError()
8396
- }),
8397
- onCustomEvent: (eventType, data) => {
8398
- const value = data?.state;
8399
- if (eventType === "astralbeam.sandbox.status" && (value === "starting" || value === "ready" || value === "error")) {
8400
- debug?.("sandbox", `sandbox ${value}`);
8401
- update({ sandboxStatus: value });
8402
- return;
8403
- }
8404
- debug?.("stream", `custom event "${eventType}"`, data);
8405
- },
8406
- onChunk: (chunk) => live.streamCallbacks?.onChunk?.(chunk),
8407
- onResponse: (response) => live.streamCallbacks?.onResponse?.(response),
8408
- onFinish: (message) => live.streamCallbacks?.onFinish?.(message),
8409
- onError: (error) => live.streamCallbacks?.onError?.(error)
8410
- });
9707
+ const createClient = () => {
9708
+ const next = new ChatClient({
9709
+ connection,
9710
+ tools: declareTools(),
9711
+ forwardedProps: forwardedProps(),
9712
+ onMessagesChange: (messages) => {
9713
+ if (client !== next) return;
9714
+ update({
9715
+ messages,
9716
+ sandbox: collectSandboxActivity(messages),
9717
+ error: next.getError()
9718
+ });
9719
+ },
9720
+ onStatusChange: (status) => {
9721
+ if (client === next) update({
9722
+ status,
9723
+ error: next.getError()
9724
+ });
9725
+ },
9726
+ onCustomEvent: (eventType, data) => {
9727
+ if (client !== next) return;
9728
+ const value = data?.state;
9729
+ if (eventType === "astralbeam.sandbox.status" && (value === "starting" || value === "ready" || value === "error")) {
9730
+ debug?.("sandbox", `sandbox ${value}`);
9731
+ update({ sandboxStatus: value });
9732
+ return;
9733
+ }
9734
+ debug?.("stream", `custom event "${eventType}"`, data);
9735
+ },
9736
+ onChunk: (chunk) => live.streamCallbacks?.onChunk?.(chunk),
9737
+ onResponse: (response) => live.streamCallbacks?.onResponse?.(response),
9738
+ onFinish: (message) => live.streamCallbacks?.onFinish?.(message),
9739
+ onError: (error) => live.streamCallbacks?.onError?.(error)
9740
+ });
9741
+ return next;
9742
+ };
9743
+ let client = createClient();
9744
+ const replaceClient = () => {
9745
+ client.stop();
9746
+ const previous = client;
9747
+ client = createClient();
9748
+ previous.dispose();
9749
+ };
8411
9750
  const settleDanglingToolCalls = () => {
8412
9751
  for (const message of state.messages) for (const part of message.parts) {
8413
9752
  if (part.type !== "tool-call" || isSettledToolCall(part)) continue;
@@ -8438,8 +9777,7 @@ function createAstralBeamChat(options, deferStart = false) {
8438
9777
  if (auth.status === "ready") {
8439
9778
  const nextIdentity = authenticationIdentity(auth.currentUser);
8440
9779
  if (identity !== void 0 && identity !== nextIdentity) {
8441
- client.stop();
8442
- client.clear();
9780
+ replaceClient();
8443
9781
  disposeRenders();
8444
9782
  update({
8445
9783
  messages: [],
@@ -8507,10 +9845,11 @@ function createAstralBeamChat(options, deferStart = false) {
8507
9845
  reload: () => client.reload(),
8508
9846
  reset: () => {
8509
9847
  debug?.("status", "conversation reset");
8510
- client.clear();
9848
+ replaceClient();
8511
9849
  disposeRenders();
8512
9850
  update({
8513
9851
  messages: [],
9852
+ status: "ready",
8514
9853
  sandbox: {
8515
9854
  files: [],
8516
9855
  commands: []