@astralbeam/sdk 0.15.2 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,17 @@
1
- import { A as resolveApiUrl, O as astralBeamChatFetch, c as getCurrentUser, i as getChatConfig, k as isAstralBeamApiError, v as getRunChatUrl } from "./api-r11gRy91.js";
1
+ import { Ct as isAstralBeamApiError, O as getCurrentUser, S as getChatFile, St as astralBeamChatFetch, X as getRunChatUrl, Y as getResolveChatToolResultUrl, b as getChatAttachment, ct as listChatThreads, g as createChatThread, ot as listChatMessages, vt as updateChatThread, wt as resolveApiUrl, x as getChatConfig, xt as uploadChatFile, y as deleteChatThread } from "./api-BaIPbj-Q.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
+ var __defProp = Object.defineProperty;
4
5
  var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
6
+ var __exportAll = (all, no_symbols) => {
7
+ let target = {};
8
+ for (var name in all) __defProp(target, name, {
9
+ get: all[name],
10
+ enumerable: true
11
+ });
12
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
13
+ return target;
14
+ };
5
15
  //#endregion
6
16
  //#region node_modules/.deno/jose@6.2.9/node_modules/jose/dist/webapi/util/decode_jwt.js
7
17
  function decodeJwt(jwt) {
@@ -26,7 +36,7 @@ function decodeJwt(jwt) {
26
36
  return result;
27
37
  }
28
38
  //#endregion
29
- //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/response-stream.js
39
+ //#region node_modules/.deno/@tanstack+ai-client@0.39.2/node_modules/@tanstack/ai-client/dist/esm/response-stream.js
30
40
  var UnsupportedResponseStreamError = class extends Error {
31
41
  missingFeature;
32
42
  constructor(missingFeature) {
@@ -48,14 +58,14 @@ function createResponseStreamTextDecoder() {
48
58
  return new globalThis.TextDecoder();
49
59
  }
50
60
  //#endregion
51
- //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/sse-utils.js
61
+ //#region node_modules/.deno/@tanstack+ai-client@0.39.2/node_modules/@tanstack/ai-client/dist/esm/sse-utils.js
52
62
  function parseSseDataLine(line) {
53
63
  if (!line.startsWith("data:")) return line;
54
64
  const data = line.slice(5);
55
65
  return data.startsWith(" ") ? data.slice(1) : data;
56
66
  }
57
67
  //#endregion
58
- //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/message-date-normalizer.js
68
+ //#region node_modules/.deno/@tanstack+ai-client@0.39.2/node_modules/@tanstack/ai-client/dist/esm/message-date-normalizer.js
59
69
  function validDate(value) {
60
70
  if (value instanceof Date) return Number.isNaN(value.getTime()) ? void 0 : value;
61
71
  if (typeof value !== "string") return void 0;
@@ -87,7 +97,7 @@ function normalizeMessagesDates(messages) {
87
97
  return messages.map(normalizeMessageDates);
88
98
  }
89
99
  //#endregion
90
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/utilities/ag-ui-usage.js
100
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/utilities/ag-ui-usage.js
91
101
  function definedDetails(value) {
92
102
  return Object.keys(value).length > 0 ? value : void 0;
93
103
  }
@@ -132,7 +142,7 @@ function fromSpecTokenUsage(usage, leftover) {
132
142
  };
133
143
  }
134
144
  //#endregion
135
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/utilities/merge-metadata.js
145
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/utilities/merge-metadata.js
136
146
  function isPlainRecord$1(value) {
137
147
  return value != null && typeof value === "object" && !Array.isArray(value);
138
148
  }
@@ -211,7 +221,19 @@ let EventType = /* @__PURE__ */ function(EventType) {
211
221
  return EventType;
212
222
  }({});
213
223
  //#endregion
214
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/interrupt-serialization.js
224
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/utilities/reasoning-encrypted-value.js
225
+ /**
226
+ * Id prefix for the reasoning message of a redacted thinking block (Anthropic
227
+ * `redacted_thinking`). AG-UI's `encryptedValue` does not say what kind of
228
+ * bytes it holds (ag-ui-protocol/ag-ui#2884), so the id that `entityId` points
229
+ * to says it. AG-UI clients keep message ids, but they drop event metadata.
230
+ */
231
+ var REDACTED_THINKING_ID_PREFIX = "redacted_thinking-";
232
+ function isRedactedThinkingId(id) {
233
+ return typeof id === "string" && id.startsWith("redacted_thinking-");
234
+ }
235
+ //#endregion
236
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/interrupt-serialization.js
215
237
  var SHA256_K = new Uint32Array([
216
238
  1116352408,
217
239
  1899447441,
@@ -386,7 +408,7 @@ function cloneAndDeepFreezeJson(value) {
386
408
  return clone;
387
409
  }
388
410
  //#endregion
389
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/interrupts.js
411
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/interrupts.js
390
412
  function canonicalizeInterruptResolutions(resolutions) {
391
413
  const frozen = cloneAndDeepFreezeJson([...resolutions].sort((left, right) => left.interruptId.localeCompare(right.interruptId)));
392
414
  const canonicalResolutions = canonicalInterruptJson(frozen);
@@ -397,7 +419,7 @@ function canonicalizeInterruptResolutions(resolutions) {
397
419
  });
398
420
  }
399
421
  //#endregion
400
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/activities/chat/tools/schema-converter.js
422
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/activities/chat/tools/schema-converter.js
401
423
  /**
402
424
  * Build a JSONSchema object from any plain key/value source. The `JSONSchema`
403
425
  * interface's `[key: string]: any` index signature makes every property
@@ -550,16 +572,17 @@ function makeStructuredOutputCompatible(schema, originalRequired = []) {
550
572
  *
551
573
  * - Standard JSON Schemas are rebuilt structurally (dropping `$schema`, which
552
574
  * LLM providers ignore) and given the explicit `type`/`properties`/`required`
553
- * defaults object shapes need downstream.
575
+ * defaults object shapes need downstream. `io` picks the `input` (default)
576
+ * or `output` view.
554
577
  * - Plain `JSONSchema` inputs are rebuilt into the typed view; non-object inputs
555
578
  * are surfaced untouched (they can't be widened).
556
579
  * - Standard Schema validators lacking a `~standard.jsonSchema` converter throw
557
580
  * with actionable guidance, rather than shipping `{ '~standard': … }` to the
558
581
  * provider and producing an opaque downstream error.
559
582
  */
560
- function toTypedJsonSchema(schema) {
583
+ function toTypedJsonSchema(schema, io = "input") {
561
584
  if (isStandardJSONSchema(schema)) {
562
- const result = toJsonSchema$2(schema["~standard"].jsonSchema.input({ target: "draft-07" }));
585
+ const result = toJsonSchema$2(schema["~standard"].jsonSchema[io]({ target: "draft-07" }));
563
586
  if ("properties" in result && !result.type) result.type = "object";
564
587
  if (result.type === "object" && !("properties" in result)) result.properties = {};
565
588
  if (result.type === "object" && !("required" in result)) result.required = [];
@@ -631,9 +654,9 @@ function toTypedJsonSchema(schema) {
631
654
  */
632
655
  function convertSchemaToJsonSchema(schema, options = {}) {
633
656
  if (!schema) return void 0;
634
- const { forStructuredOutput = false } = options;
657
+ const { forStructuredOutput = false, io } = options;
635
658
  if (!forStructuredOutput && !isStandardJSONSchema(schema) && !isStandardSchema(schema)) return schema;
636
- const base = toTypedJsonSchema(schema);
659
+ const base = toTypedJsonSchema(schema, io);
637
660
  if (!base || typeof base !== "object") return base;
638
661
  if (!forStructuredOutput) return base;
639
662
  return makeStructuredOutputCompatible(base, base.required || []).schema;
@@ -664,7 +687,7 @@ async function validateWithStandardSchema(schema, data) {
664
687
  };
665
688
  }
666
689
  //#endregion
667
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/activities/chat/tools/approval-schema.js
690
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/activities/chat/tools/approval-schema.js
668
691
  var jsonSchemaKeywords = /* @__PURE__ */ new Set([
669
692
  "$schema",
670
693
  "$id",
@@ -776,7 +799,7 @@ function hashSchemaInput(schema) {
776
799
  return digestInterruptJson(canonicalInterruptJson(schemaToWire(schema).jsonSchema ?? { standardValidator: "unserialized" }));
777
800
  }
778
801
  //#endregion
779
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/interrupt-resume.js
802
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/interrupt-resume.js
780
803
  /**
781
804
  * The `Interrupt.metadata` key under which this package's resume binding
782
805
  * travels.
@@ -897,7 +920,7 @@ function readInterruptBinding(descriptor) {
897
920
  };
898
921
  }
899
922
  //#endregion
900
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/interrupt-definition.js
923
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/interrupt-definition.js
901
924
  var INTERRUPT_PAYLOAD_METADATA_KEY = "tanstack:interruptPayload";
902
925
  function schemaJson(schema, name) {
903
926
  if (!isStandardJSONSchema(schema)) throw new TypeError(`${name} must be a Standard Schema with a JSON Schema converter.`);
@@ -922,7 +945,7 @@ function hashInterruptDefinitionSchema(schema) {
922
945
  return digestInterruptJson(schemaJson(schema, "Interrupt schema").canonicalJson);
923
946
  }
924
947
  //#endregion
925
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/generic-interrupt-continuation.js
948
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/generic-interrupt-continuation.js
926
949
  /**
927
950
  * `ResumeEntry.metadata` key for a first-party generic request.
928
951
  *
@@ -961,7 +984,7 @@ function genericInterruptContinuationFromDescriptor(interrupt) {
961
984
  };
962
985
  }
963
986
  //#endregion
964
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/utilities/provider-executed.js
987
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/utilities/provider-executed.js
965
988
  /**
966
989
  * Narrow a tool call's opaque `metadata` to the provider-executed convention.
967
990
  * Returns the typed metadata when the call is provider-executed, else `null`.
@@ -991,7 +1014,7 @@ function isAssistantSegmentOf(id, parentId) {
991
1014
  return id?.startsWith(`${parentId}-segment-`) === true;
992
1015
  }
993
1016
  //#endregion
994
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/utilities/tool-result.js
1017
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/utilities/tool-result.js
995
1018
  var CONTENT_PART_TYPES = /* @__PURE__ */ new Set([
996
1019
  "text",
997
1020
  "image",
@@ -1052,7 +1075,7 @@ function normalizeToolResult(result) {
1052
1075
  return JSON.stringify(result);
1053
1076
  }
1054
1077
  //#endregion
1055
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/utilities/subagent-wire.js
1078
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/utilities/subagent-wire.js
1056
1079
  function isRecord$2(value) {
1057
1080
  return typeof value === "object" && value !== null && !Array.isArray(value);
1058
1081
  }
@@ -1156,7 +1179,7 @@ function splitSubagentWire(messages) {
1156
1179
  };
1157
1180
  }
1158
1181
  //#endregion
1159
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/activities/chat/messages.js
1182
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/activities/chat/messages.js
1160
1183
  function isRecord$1(value) {
1161
1184
  return typeof value === "object" && value !== null && !Array.isArray(value);
1162
1185
  }
@@ -1184,6 +1207,10 @@ function encryptedValueFrom(value) {
1184
1207
  }
1185
1208
  return nonEmptyString(tanstackMetadata(value)?.signature);
1186
1209
  }
1210
+ /** `{ redacted: true }` when a reasoning message's id marks a redacted block. */
1211
+ function redactedFrom(value) {
1212
+ return "id" in value && isRedactedThinkingId(value.id) ? { redacted: true } : {};
1213
+ }
1187
1214
  /**
1188
1215
  * Collapse an array of ContentParts into the most compact ModelMessage content:
1189
1216
  * - Empty array → null
@@ -1345,6 +1372,7 @@ function assistantMetadata(uiMessage) {
1345
1372
  */
1346
1373
  function uiMessageToModelMessages(uiMessage) {
1347
1374
  if (uiMessage.role === "system") return [];
1375
+ if (uiMessage.role === "activity") return [];
1348
1376
  if (uiMessage.role !== "assistant") return [buildUserOrToolMessage(uiMessage)];
1349
1377
  return buildAssistantMessages(uiMessage);
1350
1378
  }
@@ -1456,7 +1484,8 @@ function buildAssistantMessages(uiMessage) {
1456
1484
  if (current.toolCalls.some(isProviderExecutedToolCall)) flushSegment();
1457
1485
  pendingThinking.push({
1458
1486
  content: part.content,
1459
- ...part.signature && { signature: part.signature }
1487
+ ...part.signature && { signature: part.signature },
1488
+ ...part.redacted && { redacted: true }
1460
1489
  });
1461
1490
  }
1462
1491
  break;
@@ -1473,6 +1502,7 @@ function buildAssistantMessages(uiMessage) {
1473
1502
  }
1474
1503
  break;
1475
1504
  case "ui-resource": break;
1505
+ case "activity": break;
1476
1506
  case "subagent": {
1477
1507
  const block = part.subagent.parentToolCallId === void 0 ? subagentHistoryText(part) : "";
1478
1508
  if (block !== "") {
@@ -1538,7 +1568,8 @@ function modelMessageToUIMessage(modelMessage, id) {
1538
1568
  parts.push({
1539
1569
  type: "thinking",
1540
1570
  content: thinking.content,
1541
- ...thinking.signature && { signature: thinking.signature }
1571
+ ...thinking.signature && { signature: thinking.signature },
1572
+ ...thinking.redacted && { redacted: true }
1542
1573
  });
1543
1574
  }
1544
1575
  const structuredOutput = modelMessage.structuredOutput ?? snapshotStructuredOutput(tanstackMetadata(modelMessage)?.structuredOutput);
@@ -1683,10 +1714,20 @@ function aguiSnapshotMessageToUIMessage(message) {
1683
1714
  parts: content || signature !== void 0 ? [{
1684
1715
  type: "thinking",
1685
1716
  content,
1686
- ...signature !== void 0 ? { signature } : {}
1717
+ ...signature !== void 0 ? { signature } : {},
1718
+ ...redactedFrom(message)
1687
1719
  }] : []
1688
1720
  });
1689
1721
  }
1722
+ case "activity": return applySnapshotMetadata(message, {
1723
+ id,
1724
+ role: "activity",
1725
+ parts: [{
1726
+ type: "activity",
1727
+ activityType: message.activityType,
1728
+ content: structuredClone(message.content)
1729
+ }]
1730
+ });
1690
1731
  default: return applySnapshotMetadata(message, {
1691
1732
  id,
1692
1733
  role: "assistant",
@@ -1732,15 +1773,23 @@ function applySnapshotMetadata(source, ui) {
1732
1773
  }
1733
1774
  function snapshotStructuredOutput(value) {
1734
1775
  if (value == null || value.status !== "streaming" && value.status !== "complete" && value.status !== "error" || typeof value.raw !== "string") return;
1735
- return {
1776
+ const base = {
1736
1777
  type: "structured-output",
1737
- status: value.status,
1738
1778
  raw: value.raw,
1739
1779
  ...value.partial !== void 0 ? { partial: value.partial } : {},
1740
- ...value.data !== void 0 ? { data: value.data } : {},
1741
1780
  ...value.reasoning ? { reasoning: value.reasoning } : {},
1742
1781
  ...value.errorMessage !== void 0 ? { errorMessage: value.errorMessage } : {}
1743
1782
  };
1783
+ if (value.status !== "complete") return {
1784
+ ...base,
1785
+ status: value.status
1786
+ };
1787
+ if (value.data === void 0) return void 0;
1788
+ return {
1789
+ ...base,
1790
+ status: "complete",
1791
+ data: value.data
1792
+ };
1744
1793
  }
1745
1794
  /**
1746
1795
  * Convert AG-UI user message content into `UIMessage` parts.
@@ -1798,7 +1847,7 @@ function generateMessageId() {
1798
1847
  return `msg-${Date.now()}-${Math.random().toString(36).substring(7)}`;
1799
1848
  }
1800
1849
  //#endregion
1801
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/utilities/ag-ui-wire.js
1850
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/utilities/ag-ui-wire.js
1802
1851
  function isRecord(value) {
1803
1852
  return typeof value === "object" && value !== null && !Array.isArray(value);
1804
1853
  }
@@ -1829,15 +1878,35 @@ function rebuiltToolMetadata(metadata, createdAt, id, content, anchorOwnsUiResou
1829
1878
  * on assistant messages are additionally emitted as fan-out
1830
1879
  * `{role:'tool',...}` and `{role:'reasoning',...}` entries for strict AG-UI
1831
1880
  * server consumers. Set `includeSnapshotStructuredOutput` to retain complete
1832
- * structured-output metadata for UI snapshots.
1881
+ * structured-output metadata for UI snapshots. Set `includeActivity` to emit
1882
+ * AG-UI `ActivityMessage` rows (MESSAGES_SNAPSHOT only — default omit so
1883
+ * RunAgentInput never carries activity).
1833
1884
  */
1834
1885
  function uiMessagesToWire(messages, options) {
1835
1886
  const wire = [];
1836
1887
  const usedWireIds = new Set(messages.flatMap((message) => "id" in message && message.id && message.role !== "tool" ? [message.id] : []));
1837
1888
  const includeSnapshotStructuredOutput = options?.includeSnapshotStructuredOutput ?? false;
1889
+ const includeActivity = options?.includeActivity ?? false;
1838
1890
  const assistantIds = /* @__PURE__ */ new Set();
1839
1891
  for (const msg of messages) if (msg.role === "assistant" && msg.id !== void 0) assistantIds.add(msg.id);
1840
1892
  for (const msg of messages) {
1893
+ if (msg.role === "activity") {
1894
+ if (includeActivity && "parts" in msg) {
1895
+ const part = msg.parts.find((p) => p.type === "activity");
1896
+ if (part) {
1897
+ const activity = {
1898
+ id: msg.id,
1899
+ role: "activity",
1900
+ activityType: part.activityType,
1901
+ content: structuredClone(part.content),
1902
+ ...msg.metadata != null && { metadata: msg.metadata },
1903
+ ...part.subagentRunId !== void 0 && { subagentRunId: part.subagentRunId }
1904
+ };
1905
+ wire.push(activity);
1906
+ }
1907
+ }
1908
+ continue;
1909
+ }
1841
1910
  if (!("parts" in msg) && msg.role === "tool" && msg.toolCallId) {
1842
1911
  const id = uniqueToolWireId(toolWireId(msg.id, msg.toolCallId, assistantIds), usedWireIds);
1843
1912
  const metadata = rebuiltToolMetadata(msg.metadata, msg.createdAt, msg.id, msg.content);
@@ -2081,7 +2150,8 @@ function collectToolCalls(parts) {
2081
2150
  return calls.length > 0 ? calls : void 0;
2082
2151
  }
2083
2152
  function deriveReasoningId(messageId, part) {
2084
- return `${messageId}-reasoning-${part.id ?? hashContent(part.content)}`;
2153
+ const id = `${messageId}-reasoning-${part.id ?? hashContent(part.content)}`;
2154
+ return part.type === "thinking" && part.redacted ? `${REDACTED_THINKING_ID_PREFIX}${id}` : id;
2085
2155
  }
2086
2156
  function deriveToolMessageId(toolCallId) {
2087
2157
  return `tool-${toolCallId}`;
@@ -2111,7 +2181,7 @@ function hashContent(s) {
2111
2181
  return Math.abs(h).toString(36);
2112
2182
  }
2113
2183
  //#endregion
2114
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/utilities/restore-inbound-chunk.js
2184
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/utilities/restore-inbound-chunk.js
2115
2185
  /**
2116
2186
  * Rebuild TanStack `TokenUsage` (`promptTokens`) from spec `usage[]` plus
2117
2187
  * leftover fields in `metadata.tanstack.usage`. Restore in-process aliases
@@ -2148,7 +2218,7 @@ function restoreInboundChunk(chunk) {
2148
2218
  return next;
2149
2219
  }
2150
2220
  //#endregion
2151
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/activities/chat/tools/tool-definition.js
2221
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/activities/chat/tools/tool-definition.js
2152
2222
  /**
2153
2223
  * Create an isomorphic tool definition that can be used directly or instantiated for server/client
2154
2224
  *
@@ -2246,7 +2316,7 @@ function toolDefinition(config) {
2246
2316
  };
2247
2317
  }
2248
2318
  //#endregion
2249
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/utilities/chunk-ids.js
2319
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/utilities/chunk-ids.js
2250
2320
  function stringField(value) {
2251
2321
  return typeof value === "string" && value !== "" ? value : void 0;
2252
2322
  }
@@ -2349,7 +2419,7 @@ var require_options = /* @__PURE__ */ __commonJSMin(((exports) => {
2349
2419
  exports.default = exports.Allow;
2350
2420
  }));
2351
2421
  //#endregion
2352
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/activities/chat/stream/json-parser.js
2422
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/activities/chat/stream/json-parser.js
2353
2423
  var import_dist = (/* @__PURE__ */ __commonJSMin(((exports) => {
2354
2424
  var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
2355
2425
  if (k2 === void 0) k2 = k;
@@ -2571,7 +2641,7 @@ function parsePartialJSON(jsonString) {
2571
2641
  return defaultJSONParser.parse(jsonString);
2572
2642
  }
2573
2643
  //#endregion
2574
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/activities/chat/stream/strategies.js
2644
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/activities/chat/stream/strategies.js
2575
2645
  /**
2576
2646
  * Immediate Strategy - emit on every chunk (default behavior)
2577
2647
  */
@@ -2581,7 +2651,7 @@ var ImmediateStrategy = class {
2581
2651
  }
2582
2652
  };
2583
2653
  //#endregion
2584
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/utilities/errors.js
2654
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/utilities/errors.js
2585
2655
  /**
2586
2656
  * Convert an AG-UI RUN_ERROR event to the Error shape exposed to consumers.
2587
2657
  * Preserves the provider code and sanitized raw event when available, while
@@ -2595,7 +2665,730 @@ function runErrorEventToError(chunk) {
2595
2665
  return error;
2596
2666
  }
2597
2667
  //#endregion
2598
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/activities/chat/stream/message-updaters.js
2668
+ //#region node_modules/.deno/fast-json-patch@3.1.1/node_modules/fast-json-patch/module/helpers.mjs
2669
+ /*!
2670
+ * https://github.com/Starcounter-Jack/JSON-Patch
2671
+ * (c) 2017-2022 Joachim Wester
2672
+ * MIT licensed
2673
+ */
2674
+ var __extends = (function() {
2675
+ var extendStatics = function(d, b) {
2676
+ extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d, b) {
2677
+ d.__proto__ = b;
2678
+ } || function(d, b) {
2679
+ for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
2680
+ };
2681
+ return extendStatics(d, b);
2682
+ };
2683
+ return function(d, b) {
2684
+ extendStatics(d, b);
2685
+ function __() {
2686
+ this.constructor = d;
2687
+ }
2688
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
2689
+ };
2690
+ })();
2691
+ var _hasOwnProperty = Object.prototype.hasOwnProperty;
2692
+ function hasOwnProperty(obj, key) {
2693
+ return _hasOwnProperty.call(obj, key);
2694
+ }
2695
+ function _objectKeys(obj) {
2696
+ if (Array.isArray(obj)) {
2697
+ var keys_1 = new Array(obj.length);
2698
+ for (var k = 0; k < keys_1.length; k++) keys_1[k] = "" + k;
2699
+ return keys_1;
2700
+ }
2701
+ if (Object.keys) return Object.keys(obj);
2702
+ var keys = [];
2703
+ for (var i in obj) if (hasOwnProperty(obj, i)) keys.push(i);
2704
+ return keys;
2705
+ }
2706
+ /**
2707
+ * Deeply clone the object.
2708
+ * https://jsperf.com/deep-copy-vs-json-stringify-json-parse/25 (recursiveDeepCopy)
2709
+ * @param {any} obj value to clone
2710
+ * @return {any} cloned obj
2711
+ */
2712
+ function _deepClone(obj) {
2713
+ switch (typeof obj) {
2714
+ case "object": return JSON.parse(JSON.stringify(obj));
2715
+ case "undefined": return null;
2716
+ default: return obj;
2717
+ }
2718
+ }
2719
+ function isInteger(str) {
2720
+ var i = 0;
2721
+ var len = str.length;
2722
+ var charCode;
2723
+ while (i < len) {
2724
+ charCode = str.charCodeAt(i);
2725
+ if (charCode >= 48 && charCode <= 57) {
2726
+ i++;
2727
+ continue;
2728
+ }
2729
+ return false;
2730
+ }
2731
+ return true;
2732
+ }
2733
+ /**
2734
+ * Escapes a json pointer path
2735
+ * @param path The raw pointer
2736
+ * @return the Escaped path
2737
+ */
2738
+ function escapePathComponent(path) {
2739
+ if (path.indexOf("/") === -1 && path.indexOf("~") === -1) return path;
2740
+ return path.replace(/~/g, "~0").replace(/\//g, "~1");
2741
+ }
2742
+ /**
2743
+ * Unescapes a json pointer path
2744
+ * @param path The escaped pointer
2745
+ * @return The unescaped path
2746
+ */
2747
+ function unescapePathComponent(path) {
2748
+ return path.replace(/~1/g, "/").replace(/~0/g, "~");
2749
+ }
2750
+ /**
2751
+ * Recursively checks whether an object has any undefined values inside.
2752
+ */
2753
+ function hasUndefined(obj) {
2754
+ if (obj === void 0) return true;
2755
+ if (obj) {
2756
+ if (Array.isArray(obj)) {
2757
+ for (var i_1 = 0, len = obj.length; i_1 < len; i_1++) if (hasUndefined(obj[i_1])) return true;
2758
+ } else if (typeof obj === "object") {
2759
+ var objKeys = _objectKeys(obj);
2760
+ var objKeysLength = objKeys.length;
2761
+ for (var i = 0; i < objKeysLength; i++) if (hasUndefined(obj[objKeys[i]])) return true;
2762
+ }
2763
+ }
2764
+ return false;
2765
+ }
2766
+ function patchErrorMessageFormatter(message, args) {
2767
+ var messageParts = [message];
2768
+ for (var key in args) {
2769
+ var value = typeof args[key] === "object" ? JSON.stringify(args[key], null, 2) : args[key];
2770
+ if (typeof value !== "undefined") messageParts.push(key + ": " + value);
2771
+ }
2772
+ return messageParts.join("\n");
2773
+ }
2774
+ var PatchError = function(_super) {
2775
+ __extends(PatchError, _super);
2776
+ function PatchError(message, name, index, operation, tree) {
2777
+ var _newTarget = this.constructor;
2778
+ var _this = _super.call(this, patchErrorMessageFormatter(message, {
2779
+ name,
2780
+ index,
2781
+ operation,
2782
+ tree
2783
+ })) || this;
2784
+ _this.name = name;
2785
+ _this.index = index;
2786
+ _this.operation = operation;
2787
+ _this.tree = tree;
2788
+ Object.setPrototypeOf(_this, _newTarget.prototype);
2789
+ _this.message = patchErrorMessageFormatter(message, {
2790
+ name,
2791
+ index,
2792
+ operation,
2793
+ tree
2794
+ });
2795
+ return _this;
2796
+ }
2797
+ return PatchError;
2798
+ }(Error);
2799
+ //#endregion
2800
+ //#region node_modules/.deno/fast-json-patch@3.1.1/node_modules/fast-json-patch/module/core.mjs
2801
+ var core_exports = /* @__PURE__ */ __exportAll({
2802
+ JsonPatchError: () => JsonPatchError,
2803
+ _areEquals: () => _areEquals,
2804
+ applyOperation: () => applyOperation,
2805
+ applyPatch: () => applyPatch,
2806
+ applyReducer: () => applyReducer,
2807
+ deepClone: () => deepClone,
2808
+ getValueByPointer: () => getValueByPointer,
2809
+ validate: () => validate,
2810
+ validator: () => validator
2811
+ });
2812
+ var JsonPatchError = PatchError;
2813
+ var deepClone = _deepClone;
2814
+ var objOps = {
2815
+ add: function(obj, key, document) {
2816
+ obj[key] = this.value;
2817
+ return { newDocument: document };
2818
+ },
2819
+ remove: function(obj, key, document) {
2820
+ var removed = obj[key];
2821
+ delete obj[key];
2822
+ return {
2823
+ newDocument: document,
2824
+ removed
2825
+ };
2826
+ },
2827
+ replace: function(obj, key, document) {
2828
+ var removed = obj[key];
2829
+ obj[key] = this.value;
2830
+ return {
2831
+ newDocument: document,
2832
+ removed
2833
+ };
2834
+ },
2835
+ move: function(obj, key, document) {
2836
+ var removed = getValueByPointer(document, this.path);
2837
+ if (removed) removed = _deepClone(removed);
2838
+ var originalValue = applyOperation(document, {
2839
+ op: "remove",
2840
+ path: this.from
2841
+ }).removed;
2842
+ applyOperation(document, {
2843
+ op: "add",
2844
+ path: this.path,
2845
+ value: originalValue
2846
+ });
2847
+ return {
2848
+ newDocument: document,
2849
+ removed
2850
+ };
2851
+ },
2852
+ copy: function(obj, key, document) {
2853
+ var valueToCopy = getValueByPointer(document, this.from);
2854
+ applyOperation(document, {
2855
+ op: "add",
2856
+ path: this.path,
2857
+ value: _deepClone(valueToCopy)
2858
+ });
2859
+ return { newDocument: document };
2860
+ },
2861
+ test: function(obj, key, document) {
2862
+ return {
2863
+ newDocument: document,
2864
+ test: _areEquals(obj[key], this.value)
2865
+ };
2866
+ },
2867
+ _get: function(obj, key, document) {
2868
+ this.value = obj[key];
2869
+ return { newDocument: document };
2870
+ }
2871
+ };
2872
+ var arrOps = {
2873
+ add: function(arr, i, document) {
2874
+ if (isInteger(i)) arr.splice(i, 0, this.value);
2875
+ else arr[i] = this.value;
2876
+ return {
2877
+ newDocument: document,
2878
+ index: i
2879
+ };
2880
+ },
2881
+ remove: function(arr, i, document) {
2882
+ return {
2883
+ newDocument: document,
2884
+ removed: arr.splice(i, 1)[0]
2885
+ };
2886
+ },
2887
+ replace: function(arr, i, document) {
2888
+ var removed = arr[i];
2889
+ arr[i] = this.value;
2890
+ return {
2891
+ newDocument: document,
2892
+ removed
2893
+ };
2894
+ },
2895
+ move: objOps.move,
2896
+ copy: objOps.copy,
2897
+ test: objOps.test,
2898
+ _get: objOps._get
2899
+ };
2900
+ /**
2901
+ * Retrieves a value from a JSON document by a JSON pointer.
2902
+ * Returns the value.
2903
+ *
2904
+ * @param document The document to get the value from
2905
+ * @param pointer an escaped JSON pointer
2906
+ * @return The retrieved value
2907
+ */
2908
+ function getValueByPointer(document, pointer) {
2909
+ if (pointer == "") return document;
2910
+ var getOriginalDestination = {
2911
+ op: "_get",
2912
+ path: pointer
2913
+ };
2914
+ applyOperation(document, getOriginalDestination);
2915
+ return getOriginalDestination.value;
2916
+ }
2917
+ /**
2918
+ * Apply a single JSON Patch Operation on a JSON document.
2919
+ * Returns the {newDocument, result} of the operation.
2920
+ * It modifies the `document` and `operation` objects - it gets the values by reference.
2921
+ * If you would like to avoid touching your values, clone them:
2922
+ * `jsonpatch.applyOperation(document, jsonpatch._deepClone(operation))`.
2923
+ *
2924
+ * @param document The document to patch
2925
+ * @param operation The operation to apply
2926
+ * @param validateOperation `false` is without validation, `true` to use default jsonpatch's validation, or you can pass a `validateOperation` callback to be used for validation.
2927
+ * @param mutateDocument Whether to mutate the original document or clone it before applying
2928
+ * @param banPrototypeModifications Whether to ban modifications to `__proto__`, defaults to `true`.
2929
+ * @return `{newDocument, result}` after the operation
2930
+ */
2931
+ function applyOperation(document, operation, validateOperation, mutateDocument, banPrototypeModifications, index) {
2932
+ if (validateOperation === void 0) validateOperation = false;
2933
+ if (mutateDocument === void 0) mutateDocument = true;
2934
+ if (banPrototypeModifications === void 0) banPrototypeModifications = true;
2935
+ if (index === void 0) index = 0;
2936
+ if (validateOperation) if (typeof validateOperation == "function") validateOperation(operation, 0, document, operation.path);
2937
+ else validator(operation, 0);
2938
+ if (operation.path === "") {
2939
+ var returnValue = { newDocument: document };
2940
+ if (operation.op === "add") {
2941
+ returnValue.newDocument = operation.value;
2942
+ return returnValue;
2943
+ } else if (operation.op === "replace") {
2944
+ returnValue.newDocument = operation.value;
2945
+ returnValue.removed = document;
2946
+ return returnValue;
2947
+ } else if (operation.op === "move" || operation.op === "copy") {
2948
+ returnValue.newDocument = getValueByPointer(document, operation.from);
2949
+ if (operation.op === "move") returnValue.removed = document;
2950
+ return returnValue;
2951
+ } else if (operation.op === "test") {
2952
+ returnValue.test = _areEquals(document, operation.value);
2953
+ if (returnValue.test === false) throw new JsonPatchError("Test operation failed", "TEST_OPERATION_FAILED", index, operation, document);
2954
+ returnValue.newDocument = document;
2955
+ return returnValue;
2956
+ } else if (operation.op === "remove") {
2957
+ returnValue.removed = document;
2958
+ returnValue.newDocument = null;
2959
+ return returnValue;
2960
+ } else if (operation.op === "_get") {
2961
+ operation.value = document;
2962
+ return returnValue;
2963
+ } else if (validateOperation) throw new JsonPatchError("Operation `op` property is not one of operations defined in RFC-6902", "OPERATION_OP_INVALID", index, operation, document);
2964
+ else return returnValue;
2965
+ } else {
2966
+ if (!mutateDocument) document = _deepClone(document);
2967
+ var keys = (operation.path || "").split("/");
2968
+ var obj = document;
2969
+ var t = 1;
2970
+ var len = keys.length;
2971
+ var existingPathFragment = void 0;
2972
+ var key = void 0;
2973
+ var validateFunction = void 0;
2974
+ if (typeof validateOperation == "function") validateFunction = validateOperation;
2975
+ else validateFunction = validator;
2976
+ while (true) {
2977
+ key = keys[t];
2978
+ if (key && key.indexOf("~") != -1) key = unescapePathComponent(key);
2979
+ if (banPrototypeModifications && (key == "__proto__" || key == "prototype" && t > 0 && keys[t - 1] == "constructor")) throw new TypeError("JSON-Patch: modifying `__proto__` or `constructor/prototype` prop is banned for security reasons, if this was on purpose, please set `banPrototypeModifications` flag false and pass it to this function. More info in fast-json-patch README");
2980
+ if (validateOperation) {
2981
+ if (existingPathFragment === void 0) {
2982
+ if (obj[key] === void 0) existingPathFragment = keys.slice(0, t).join("/");
2983
+ else if (t == len - 1) existingPathFragment = operation.path;
2984
+ if (existingPathFragment !== void 0) validateFunction(operation, 0, document, existingPathFragment);
2985
+ }
2986
+ }
2987
+ t++;
2988
+ if (Array.isArray(obj)) {
2989
+ if (key === "-") key = obj.length;
2990
+ else if (validateOperation && !isInteger(key)) throw new JsonPatchError("Expected an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index", "OPERATION_PATH_ILLEGAL_ARRAY_INDEX", index, operation, document);
2991
+ else if (isInteger(key)) key = ~~key;
2992
+ if (t >= len) {
2993
+ if (validateOperation && operation.op === "add" && key > obj.length) throw new JsonPatchError("The specified index MUST NOT be greater than the number of elements in the array", "OPERATION_VALUE_OUT_OF_BOUNDS", index, operation, document);
2994
+ var returnValue = arrOps[operation.op].call(operation, obj, key, document);
2995
+ if (returnValue.test === false) throw new JsonPatchError("Test operation failed", "TEST_OPERATION_FAILED", index, operation, document);
2996
+ return returnValue;
2997
+ }
2998
+ } else if (t >= len) {
2999
+ var returnValue = objOps[operation.op].call(operation, obj, key, document);
3000
+ if (returnValue.test === false) throw new JsonPatchError("Test operation failed", "TEST_OPERATION_FAILED", index, operation, document);
3001
+ return returnValue;
3002
+ }
3003
+ obj = obj[key];
3004
+ if (validateOperation && t < len && (!obj || typeof obj !== "object")) throw new JsonPatchError("Cannot perform operation at the desired path", "OPERATION_PATH_UNRESOLVABLE", index, operation, document);
3005
+ }
3006
+ }
3007
+ }
3008
+ /**
3009
+ * Apply a full JSON Patch array on a JSON document.
3010
+ * Returns the {newDocument, result} of the patch.
3011
+ * It modifies the `document` object and `patch` - it gets the values by reference.
3012
+ * If you would like to avoid touching your values, clone them:
3013
+ * `jsonpatch.applyPatch(document, jsonpatch._deepClone(patch))`.
3014
+ *
3015
+ * @param document The document to patch
3016
+ * @param patch The patch to apply
3017
+ * @param validateOperation `false` is without validation, `true` to use default jsonpatch's validation, or you can pass a `validateOperation` callback to be used for validation.
3018
+ * @param mutateDocument Whether to mutate the original document or clone it before applying
3019
+ * @param banPrototypeModifications Whether to ban modifications to `__proto__`, defaults to `true`.
3020
+ * @return An array of `{newDocument, result}` after the patch
3021
+ */
3022
+ function applyPatch(document, patch, validateOperation, mutateDocument, banPrototypeModifications) {
3023
+ if (mutateDocument === void 0) mutateDocument = true;
3024
+ if (banPrototypeModifications === void 0) banPrototypeModifications = true;
3025
+ if (validateOperation) {
3026
+ if (!Array.isArray(patch)) throw new JsonPatchError("Patch sequence must be an array", "SEQUENCE_NOT_AN_ARRAY");
3027
+ }
3028
+ if (!mutateDocument) document = _deepClone(document);
3029
+ var results = new Array(patch.length);
3030
+ for (var i = 0, length_1 = patch.length; i < length_1; i++) {
3031
+ results[i] = applyOperation(document, patch[i], validateOperation, true, banPrototypeModifications, i);
3032
+ document = results[i].newDocument;
3033
+ }
3034
+ results.newDocument = document;
3035
+ return results;
3036
+ }
3037
+ /**
3038
+ * Apply a single JSON Patch Operation on a JSON document.
3039
+ * Returns the updated document.
3040
+ * Suitable as a reducer.
3041
+ *
3042
+ * @param document The document to patch
3043
+ * @param operation The operation to apply
3044
+ * @return The updated document
3045
+ */
3046
+ function applyReducer(document, operation, index) {
3047
+ var operationResult = applyOperation(document, operation);
3048
+ if (operationResult.test === false) throw new JsonPatchError("Test operation failed", "TEST_OPERATION_FAILED", index, operation, document);
3049
+ return operationResult.newDocument;
3050
+ }
3051
+ /**
3052
+ * Validates a single operation. Called from `jsonpatch.validate`. Throws `JsonPatchError` in case of an error.
3053
+ * @param {object} operation - operation object (patch)
3054
+ * @param {number} index - index of operation in the sequence
3055
+ * @param {object} [document] - object where the operation is supposed to be applied
3056
+ * @param {string} [existingPathFragment] - comes along with `document`
3057
+ */
3058
+ function validator(operation, index, document, existingPathFragment) {
3059
+ if (typeof operation !== "object" || operation === null || Array.isArray(operation)) throw new JsonPatchError("Operation is not an object", "OPERATION_NOT_AN_OBJECT", index, operation, document);
3060
+ else if (!objOps[operation.op]) throw new JsonPatchError("Operation `op` property is not one of operations defined in RFC-6902", "OPERATION_OP_INVALID", index, operation, document);
3061
+ else if (typeof operation.path !== "string") throw new JsonPatchError("Operation `path` property is not a string", "OPERATION_PATH_INVALID", index, operation, document);
3062
+ else if (operation.path.indexOf("/") !== 0 && operation.path.length > 0) throw new JsonPatchError("Operation `path` property must start with \"/\"", "OPERATION_PATH_INVALID", index, operation, document);
3063
+ else if ((operation.op === "move" || operation.op === "copy") && typeof operation.from !== "string") throw new JsonPatchError("Operation `from` property is not present (applicable in `move` and `copy` operations)", "OPERATION_FROM_REQUIRED", index, operation, document);
3064
+ else if ((operation.op === "add" || operation.op === "replace" || operation.op === "test") && operation.value === void 0) throw new JsonPatchError("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)", "OPERATION_VALUE_REQUIRED", index, operation, document);
3065
+ else if ((operation.op === "add" || operation.op === "replace" || operation.op === "test") && hasUndefined(operation.value)) throw new JsonPatchError("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)", "OPERATION_VALUE_CANNOT_CONTAIN_UNDEFINED", index, operation, document);
3066
+ else if (document) {
3067
+ if (operation.op == "add") {
3068
+ var pathLen = operation.path.split("/").length;
3069
+ var existingPathLen = existingPathFragment.split("/").length;
3070
+ if (pathLen !== existingPathLen + 1 && pathLen !== existingPathLen) throw new JsonPatchError("Cannot perform an `add` operation at the desired path", "OPERATION_PATH_CANNOT_ADD", index, operation, document);
3071
+ } else if (operation.op === "replace" || operation.op === "remove" || operation.op === "_get") {
3072
+ if (operation.path !== existingPathFragment) throw new JsonPatchError("Cannot perform the operation at a path that does not exist", "OPERATION_PATH_UNRESOLVABLE", index, operation, document);
3073
+ } else if (operation.op === "move" || operation.op === "copy") {
3074
+ var error = validate([{
3075
+ op: "_get",
3076
+ path: operation.from,
3077
+ value: void 0
3078
+ }], document);
3079
+ if (error && error.name === "OPERATION_PATH_UNRESOLVABLE") throw new JsonPatchError("Cannot perform the operation from a path that does not exist", "OPERATION_FROM_UNRESOLVABLE", index, operation, document);
3080
+ }
3081
+ }
3082
+ }
3083
+ /**
3084
+ * Validates a sequence of operations. If `document` parameter is provided, the sequence is additionally validated against the object document.
3085
+ * If error is encountered, returns a JsonPatchError object
3086
+ * @param sequence
3087
+ * @param document
3088
+ * @returns {JsonPatchError|undefined}
3089
+ */
3090
+ function validate(sequence, document, externalValidator) {
3091
+ try {
3092
+ if (!Array.isArray(sequence)) throw new JsonPatchError("Patch sequence must be an array", "SEQUENCE_NOT_AN_ARRAY");
3093
+ if (document) applyPatch(_deepClone(document), _deepClone(sequence), externalValidator || true);
3094
+ else {
3095
+ externalValidator = externalValidator || validator;
3096
+ for (var i = 0; i < sequence.length; i++) externalValidator(sequence[i], i, document, void 0);
3097
+ }
3098
+ } catch (e) {
3099
+ if (e instanceof JsonPatchError) return e;
3100
+ else throw e;
3101
+ }
3102
+ }
3103
+ function _areEquals(a, b) {
3104
+ if (a === b) return true;
3105
+ if (a && b && typeof a == "object" && typeof b == "object") {
3106
+ var arrA = Array.isArray(a), arrB = Array.isArray(b), i, length, key;
3107
+ if (arrA && arrB) {
3108
+ length = a.length;
3109
+ if (length != b.length) return false;
3110
+ for (i = length; i-- !== 0;) if (!_areEquals(a[i], b[i])) return false;
3111
+ return true;
3112
+ }
3113
+ if (arrA != arrB) return false;
3114
+ var keys = Object.keys(a);
3115
+ length = keys.length;
3116
+ if (length !== Object.keys(b).length) return false;
3117
+ for (i = length; i-- !== 0;) if (!b.hasOwnProperty(keys[i])) return false;
3118
+ for (i = length; i-- !== 0;) {
3119
+ key = keys[i];
3120
+ if (!_areEquals(a[key], b[key])) return false;
3121
+ }
3122
+ return true;
3123
+ }
3124
+ return a !== a && b !== b;
3125
+ }
3126
+ //#endregion
3127
+ //#region node_modules/.deno/fast-json-patch@3.1.1/node_modules/fast-json-patch/module/duplex.mjs
3128
+ /*!
3129
+ * https://github.com/Starcounter-Jack/JSON-Patch
3130
+ * (c) 2017-2021 Joachim Wester
3131
+ * MIT license
3132
+ */
3133
+ var duplex_exports = /* @__PURE__ */ __exportAll({
3134
+ compare: () => compare,
3135
+ generate: () => generate,
3136
+ observe: () => observe,
3137
+ unobserve: () => unobserve
3138
+ });
3139
+ var beforeDict = /* @__PURE__ */ new WeakMap();
3140
+ var Mirror = function() {
3141
+ function Mirror(obj) {
3142
+ this.observers = /* @__PURE__ */ new Map();
3143
+ this.obj = obj;
3144
+ }
3145
+ return Mirror;
3146
+ }();
3147
+ var ObserverInfo = function() {
3148
+ function ObserverInfo(callback, observer) {
3149
+ this.callback = callback;
3150
+ this.observer = observer;
3151
+ }
3152
+ return ObserverInfo;
3153
+ }();
3154
+ function getMirror(obj) {
3155
+ return beforeDict.get(obj);
3156
+ }
3157
+ function getObserverFromMirror(mirror, callback) {
3158
+ return mirror.observers.get(callback);
3159
+ }
3160
+ function removeObserverFromMirror(mirror, observer) {
3161
+ mirror.observers.delete(observer.callback);
3162
+ }
3163
+ /**
3164
+ * Detach an observer from an object
3165
+ */
3166
+ function unobserve(root, observer) {
3167
+ observer.unobserve();
3168
+ }
3169
+ /**
3170
+ * Observes changes made to an object, which can then be retrieved using generate
3171
+ */
3172
+ function observe(obj, callback) {
3173
+ var patches = [];
3174
+ var observer;
3175
+ var mirror = getMirror(obj);
3176
+ if (!mirror) {
3177
+ mirror = new Mirror(obj);
3178
+ beforeDict.set(obj, mirror);
3179
+ } else {
3180
+ var observerInfo = getObserverFromMirror(mirror, callback);
3181
+ observer = observerInfo && observerInfo.observer;
3182
+ }
3183
+ if (observer) return observer;
3184
+ observer = {};
3185
+ mirror.value = _deepClone(obj);
3186
+ if (callback) {
3187
+ observer.callback = callback;
3188
+ observer.next = null;
3189
+ var dirtyCheck = function() {
3190
+ generate(observer);
3191
+ };
3192
+ var fastCheck = function() {
3193
+ clearTimeout(observer.next);
3194
+ observer.next = setTimeout(dirtyCheck);
3195
+ };
3196
+ if (typeof window !== "undefined") {
3197
+ window.addEventListener("mouseup", fastCheck);
3198
+ window.addEventListener("keyup", fastCheck);
3199
+ window.addEventListener("mousedown", fastCheck);
3200
+ window.addEventListener("keydown", fastCheck);
3201
+ window.addEventListener("change", fastCheck);
3202
+ }
3203
+ }
3204
+ observer.patches = patches;
3205
+ observer.object = obj;
3206
+ observer.unobserve = function() {
3207
+ generate(observer);
3208
+ clearTimeout(observer.next);
3209
+ removeObserverFromMirror(mirror, observer);
3210
+ if (typeof window !== "undefined") {
3211
+ window.removeEventListener("mouseup", fastCheck);
3212
+ window.removeEventListener("keyup", fastCheck);
3213
+ window.removeEventListener("mousedown", fastCheck);
3214
+ window.removeEventListener("keydown", fastCheck);
3215
+ window.removeEventListener("change", fastCheck);
3216
+ }
3217
+ };
3218
+ mirror.observers.set(callback, new ObserverInfo(callback, observer));
3219
+ return observer;
3220
+ }
3221
+ /**
3222
+ * Generate an array of patches from an observer
3223
+ */
3224
+ function generate(observer, invertible) {
3225
+ if (invertible === void 0) invertible = false;
3226
+ var mirror = beforeDict.get(observer.object);
3227
+ _generate(mirror.value, observer.object, observer.patches, "", invertible);
3228
+ if (observer.patches.length) applyPatch(mirror.value, observer.patches);
3229
+ var temp = observer.patches;
3230
+ if (temp.length > 0) {
3231
+ observer.patches = [];
3232
+ if (observer.callback) observer.callback(temp);
3233
+ }
3234
+ return temp;
3235
+ }
3236
+ function _generate(mirror, obj, patches, path, invertible) {
3237
+ if (obj === mirror) return;
3238
+ if (typeof obj.toJSON === "function") obj = obj.toJSON();
3239
+ var newKeys = _objectKeys(obj);
3240
+ var oldKeys = _objectKeys(mirror);
3241
+ var deleted = false;
3242
+ for (var t = oldKeys.length - 1; t >= 0; t--) {
3243
+ var key = oldKeys[t];
3244
+ var oldVal = mirror[key];
3245
+ if (hasOwnProperty(obj, key) && !(obj[key] === void 0 && oldVal !== void 0 && Array.isArray(obj) === false)) {
3246
+ var newVal = obj[key];
3247
+ if (typeof oldVal == "object" && oldVal != null && typeof newVal == "object" && newVal != null && Array.isArray(oldVal) === Array.isArray(newVal)) _generate(oldVal, newVal, patches, path + "/" + escapePathComponent(key), invertible);
3248
+ else if (oldVal !== newVal) {
3249
+ if (invertible) patches.push({
3250
+ op: "test",
3251
+ path: path + "/" + escapePathComponent(key),
3252
+ value: _deepClone(oldVal)
3253
+ });
3254
+ patches.push({
3255
+ op: "replace",
3256
+ path: path + "/" + escapePathComponent(key),
3257
+ value: _deepClone(newVal)
3258
+ });
3259
+ }
3260
+ } else if (Array.isArray(mirror) === Array.isArray(obj)) {
3261
+ if (invertible) patches.push({
3262
+ op: "test",
3263
+ path: path + "/" + escapePathComponent(key),
3264
+ value: _deepClone(oldVal)
3265
+ });
3266
+ patches.push({
3267
+ op: "remove",
3268
+ path: path + "/" + escapePathComponent(key)
3269
+ });
3270
+ deleted = true;
3271
+ } else {
3272
+ if (invertible) patches.push({
3273
+ op: "test",
3274
+ path,
3275
+ value: mirror
3276
+ });
3277
+ patches.push({
3278
+ op: "replace",
3279
+ path,
3280
+ value: obj
3281
+ });
3282
+ }
3283
+ }
3284
+ if (!deleted && newKeys.length == oldKeys.length) return;
3285
+ for (var t = 0; t < newKeys.length; t++) {
3286
+ var key = newKeys[t];
3287
+ if (!hasOwnProperty(mirror, key) && obj[key] !== void 0) patches.push({
3288
+ op: "add",
3289
+ path: path + "/" + escapePathComponent(key),
3290
+ value: _deepClone(obj[key])
3291
+ });
3292
+ }
3293
+ }
3294
+ /**
3295
+ * Create an array of patches from the differences in two objects
3296
+ */
3297
+ function compare(tree1, tree2, invertible) {
3298
+ if (invertible === void 0) invertible = false;
3299
+ var patches = [];
3300
+ _generate(tree1, tree2, patches, "", invertible);
3301
+ return patches;
3302
+ }
3303
+ //#endregion
3304
+ //#region node_modules/.deno/fast-json-patch@3.1.1/node_modules/fast-json-patch/index.mjs
3305
+ /**
3306
+ * Default export for backwards compat
3307
+ */
3308
+ var fast_json_patch_default = Object.assign({}, core_exports, duplex_exports, {
3309
+ JsonPatchError: PatchError,
3310
+ deepClone: _deepClone,
3311
+ escapePathComponent,
3312
+ unescapePathComponent
3313
+ });
3314
+ //#endregion
3315
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/activities/chat/activity-records.js
3316
+ function isActivityPart(part) {
3317
+ return part.type === "activity";
3318
+ }
3319
+ function isActivityContent(value) {
3320
+ return value != null && typeof value === "object" && !Array.isArray(value);
3321
+ }
3322
+ function applyActivitySnapshotToUIMessages(messages, chunk) {
3323
+ const { messageId, activityType, content } = chunk;
3324
+ const replace = chunk.replace ?? true;
3325
+ const existingIndex = messages.findIndex((m) => m.id === messageId);
3326
+ const existing = existingIndex >= 0 ? messages[existingIndex] : void 0;
3327
+ if (existing && existing.role !== "activity") {
3328
+ console.warn(`ACTIVITY_SNAPSHOT: Message '${messageId}' is not an activity message`);
3329
+ return messages;
3330
+ }
3331
+ if (existing && !replace) return messages;
3332
+ const metadata = mergeMetadata(existing?.role === "activity" ? existing.metadata : void 0, chunk.metadata);
3333
+ const next = {
3334
+ id: messageId,
3335
+ role: "activity",
3336
+ parts: [{
3337
+ type: "activity",
3338
+ activityType,
3339
+ content: structuredClone(content ?? {})
3340
+ }],
3341
+ ...metadata != null ? { metadata } : {},
3342
+ ...existing?.role === "activity" && existing.createdAt != null ? { createdAt: existing.createdAt } : {},
3343
+ ...existing?.role === "activity" && existing.name != null ? { name: existing.name } : {}
3344
+ };
3345
+ if (existingIndex === -1) return [...messages, next];
3346
+ return messages.map((msg, index) => index === existingIndex ? next : msg);
3347
+ }
3348
+ function applyActivityDeltaToUIMessages(messages, chunk) {
3349
+ const { messageId, activityType, patch } = chunk;
3350
+ const existingIndex = messages.findIndex((m) => m.id === messageId);
3351
+ if (existingIndex === -1) {
3352
+ console.warn(`ACTIVITY_DELTA: No activity message '${messageId}'`);
3353
+ return messages;
3354
+ }
3355
+ const existing = messages[existingIndex];
3356
+ if (existing == null || existing.role !== "activity") {
3357
+ console.warn(`ACTIVITY_DELTA: Message '${messageId}' is not an activity message`);
3358
+ return messages;
3359
+ }
3360
+ const activityPart = existing.parts.find(isActivityPart);
3361
+ if (activityPart && activityPart.activityType !== activityType) {
3362
+ console.warn(`ACTIVITY_DELTA: activityType '${activityType}' does not match '${activityPart.activityType}' for '${messageId}'`);
3363
+ return messages;
3364
+ }
3365
+ const baseContent = structuredClone(activityPart?.content ?? {});
3366
+ try {
3367
+ const result = fast_json_patch_default.applyPatch(baseContent, patch ?? [], true, false);
3368
+ if (!isActivityContent(result.newDocument)) {
3369
+ console.warn(`ACTIVITY_DELTA: patched content for '${messageId}' is not an object`);
3370
+ return messages;
3371
+ }
3372
+ const nextPart = {
3373
+ type: "activity",
3374
+ activityType,
3375
+ content: structuredClone(result.newDocument)
3376
+ };
3377
+ const parts = activityPart ? existing.parts.map((part) => part.type === "activity" ? nextPart : part) : [nextPart];
3378
+ const metadata = mergeMetadata(existing.metadata, chunk.metadata);
3379
+ return messages.map((msg, index) => index === existingIndex ? {
3380
+ ...msg,
3381
+ parts,
3382
+ ...metadata != null ? { metadata } : {}
3383
+ } : msg);
3384
+ } catch (error) {
3385
+ const errorMessage = error instanceof Error ? error.message : String(error);
3386
+ console.warn(`Failed to apply activity patch for '${messageId}': ${errorMessage}`);
3387
+ return messages;
3388
+ }
3389
+ }
3390
+ //#endregion
3391
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/activities/chat/stream/message-updaters.js
2599
3392
  /**
2600
3393
  * Message Updaters (Internal)
2601
3394
  *
@@ -2880,11 +3673,13 @@ function updateThinkingPart(messages, messageId, stepId, content, signature) {
2880
3673
  if (candidate?.type === "thinking") adopted = candidate;
2881
3674
  }
2882
3675
  const nextSignature = signature ?? adopted?.signature;
3676
+ const redacted = isRedactedThinkingId(stepId) || adopted?.redacted === true;
2883
3677
  const thinkingPart = {
2884
3678
  type: "thinking",
2885
3679
  content,
2886
3680
  stepId,
2887
- ...nextSignature && { signature: nextSignature }
3681
+ ...nextSignature && { signature: nextSignature },
3682
+ ...redacted && { redacted: true }
2888
3683
  };
2889
3684
  if (thinkingPartIndex >= 0) parts[thinkingPartIndex] = thinkingPart;
2890
3685
  else parts.push(thinkingPart);
@@ -2895,7 +3690,7 @@ function updateThinkingPart(messages, messageId, stepId, content, signature) {
2895
3690
  });
2896
3691
  }
2897
3692
  //#endregion
2898
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/activities/chat/stream/processor.js
3693
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/activities/chat/stream/processor.js
2899
3694
  /**
2900
3695
  * Unified Stream Processor
2901
3696
  *
@@ -2916,6 +3711,28 @@ function updateThinkingPart(messages, messageId, stepId, content, signature) {
2916
3711
  * adapter contract, single-shot flows, and expected UIMessage output.
2917
3712
  */
2918
3713
  var STRUCTURED_OUTPUT_UPDATE_BATCH_SIZE = 12;
3714
+ /**
3715
+ * Events that leave this processor's open *_CHUNK stream open.
3716
+ * RAW, activity events, and REASONING_ENCRYPTED_VALUE match the AG-UI client.
3717
+ * Subagent lifecycle events stay here too: the child processor owns that
3718
+ * lane, and SUBAGENT_FINISHED or SUBAGENT_ERROR finalizes the child.
3719
+ */
3720
+ var CHUNK_PASS_THROUGH = /* @__PURE__ */ new Set([
3721
+ "RAW",
3722
+ "ACTIVITY_SNAPSHOT",
3723
+ "ACTIVITY_DELTA",
3724
+ "REASONING_ENCRYPTED_VALUE",
3725
+ "SUBAGENT_STARTED",
3726
+ "SUBAGENT_FINISHED",
3727
+ "SUBAGENT_ERROR"
3728
+ ]);
3729
+ /** A run-level event closes every open shorthand lane, including children. */
3730
+ var RUN_LEVEL_CLOSES_LANES = /* @__PURE__ */ new Set([
3731
+ "RUN_STARTED",
3732
+ "RUN_FINISHED",
3733
+ "RUN_ERROR",
3734
+ "MESSAGES_SNAPSHOT"
3735
+ ]);
2919
3736
  function interruptBatchHasGeneric(interrupts) {
2920
3737
  return interrupts.some((interrupt) => {
2921
3738
  const metadata = interrupt.metadata;
@@ -2941,6 +3758,19 @@ function serializeToolInput(input) {
2941
3758
  }
2942
3759
  }
2943
3760
  /**
3761
+ * The input of a tool call from its raw arguments: `{}` for an empty string,
3762
+ * `undefined` when the arguments are not complete JSON (for example cut off)
3763
+ * or are `null`.
3764
+ */
3765
+ function parseToolArguments(args) {
3766
+ if (args.trim() === "") return {};
3767
+ try {
3768
+ return JSON.parse(args) ?? void 0;
3769
+ } catch {
3770
+ return;
3771
+ }
3772
+ }
3773
+ /**
2944
3774
  * StreamProcessor - State machine for processing AI response streams
2945
3775
  *
2946
3776
  * Manages the full UIMessage[] conversation and emits events on changes.
@@ -2967,9 +3797,12 @@ var StreamProcessor = class StreamProcessor {
2967
3797
  toolCallToMessage = /* @__PURE__ */ new Map();
2968
3798
  pendingManualMessageId = null;
2969
3799
  pendingThinkingStepId = null;
3800
+ openChunk = null;
2970
3801
  structuredMessageIds = /* @__PURE__ */ new Set();
2971
3802
  structuredOutputUpdateBatches = /* @__PURE__ */ new Map();
2972
3803
  activeRuns = /* @__PURE__ */ new Set();
3804
+ runToolCallIds = [];
3805
+ toolCallsSentToClient = /* @__PURE__ */ new Set();
2973
3806
  childProcessors = /* @__PURE__ */ new Map();
2974
3807
  childToolCalls = /* @__PURE__ */ new Map();
2975
3808
  finishReason = null;
@@ -3176,6 +4009,7 @@ var StreamProcessor = class StreamProcessor {
3176
4009
  this.structuredMessageIds.clear();
3177
4010
  this.structuredOutputUpdateBatches.clear();
3178
4011
  this.pendingManualMessageId = null;
4012
+ this.openChunk = null;
3179
4013
  this.childProcessors.clear();
3180
4014
  this.childToolCalls.clear();
3181
4015
  this.emitMessagesChange();
@@ -3196,7 +4030,7 @@ var StreamProcessor = class StreamProcessor {
3196
4030
  *
3197
4031
  * Central dispatch for all AG-UI events. Each event type maps to a specific
3198
4032
  * handler. Events not listed in the switch are intentionally ignored
3199
- * (STEP_STARTED, STATE_SNAPSHOT, STATE_DELTA).
4033
+ * (STATE_SNAPSHOT, STATE_DELTA).
3200
4034
  *
3201
4035
  * @see docs/chat-architecture.md#adapter-contract — Expected event types and ordering
3202
4036
  */
@@ -3207,6 +4041,10 @@ var StreamProcessor = class StreamProcessor {
3207
4041
  index: this.recording.chunks.length
3208
4042
  });
3209
4043
  if (this.routeToChild(chunk)) return;
4044
+ for (const event of this.expandChunk(chunk)) this.dispatchChunk(event);
4045
+ }
4046
+ /** Send one event, after expandChunk(), to its handler. */
4047
+ dispatchChunk(chunk) {
3210
4048
  switch (chunk.type) {
3211
4049
  case "SUBAGENT_STARTED":
3212
4050
  this.handleSubagentStartedEvent(chunk);
@@ -3266,7 +4104,178 @@ var StreamProcessor = class StreamProcessor {
3266
4104
  case "TOOL_CALL_RESULT":
3267
4105
  this.handleToolCallResultEvent(chunk);
3268
4106
  break;
3269
- case "STEP_STARTED": this.handleStepStartedEvent(chunk);
4107
+ case "STEP_STARTED":
4108
+ this.handleStepStartedEvent(chunk);
4109
+ break;
4110
+ case "ACTIVITY_SNAPSHOT":
4111
+ this.handleActivitySnapshotEvent(chunk);
4112
+ break;
4113
+ case "ACTIVITY_DELTA": this.handleActivityDeltaEvent(chunk);
4114
+ }
4115
+ }
4116
+ /**
4117
+ * AG-UI lets a producer send one TEXT_MESSAGE_CHUNK, TOOL_CALL_CHUNK or
4118
+ * REASONING_MESSAGE_CHUNK in place of the START / CONTENT (ARGS) / END
4119
+ * events. A chunk with a new id opens a stream. A chunk with the same id or
4120
+ * no id continues it. Any other event closes it, except the ones in
4121
+ * CHUNK_PASS_THROUGH. Expand the shorthand into the START / CONTENT (ARGS)
4122
+ * / END events, so both forms build the same message.
4123
+ */
4124
+ expandChunk(chunk) {
4125
+ const open = this.openChunk;
4126
+ const timestamp = chunk.timestamp;
4127
+ switch (chunk.type) {
4128
+ case "TEXT_MESSAGE_CHUNK": {
4129
+ const id = this.chunkStreamId("TEXT_MESSAGE", chunk.messageId, chunk.type);
4130
+ if (id === void 0) return this.closeChunk();
4131
+ const events = [];
4132
+ if (open?.family !== "TEXT_MESSAGE" || open.id !== id) {
4133
+ events.push(...this.closeChunk(), {
4134
+ type: EventType.TEXT_MESSAGE_START,
4135
+ messageId: id,
4136
+ role: chunk.role ?? "assistant",
4137
+ ...chunk.name !== void 0 ? { name: chunk.name } : {},
4138
+ timestamp
4139
+ });
4140
+ this.openChunk = {
4141
+ family: "TEXT_MESSAGE",
4142
+ id
4143
+ };
4144
+ }
4145
+ if (chunk.delta !== void 0 || chunk.metadata !== void 0) events.push({
4146
+ type: EventType.TEXT_MESSAGE_CONTENT,
4147
+ messageId: id,
4148
+ delta: chunk.delta ?? "",
4149
+ metadata: chunk.metadata,
4150
+ timestamp
4151
+ });
4152
+ return events;
4153
+ }
4154
+ case "TOOL_CALL_CHUNK": {
4155
+ const id = this.chunkStreamId("TOOL_CALL", chunk.toolCallId, chunk.type);
4156
+ if (id === void 0) return this.closeChunk();
4157
+ const events = [];
4158
+ if (open?.family !== "TOOL_CALL" || open.id !== id) {
4159
+ if (chunk.toolCallName === void 0) {
4160
+ console.warn(`[StreamProcessor] Dropped TOOL_CALL_CHUNK ${id}: the first chunk of a tool call needs a toolCallName`);
4161
+ return this.closeChunk();
4162
+ }
4163
+ events.push(...this.closeChunk(), {
4164
+ type: EventType.TOOL_CALL_START,
4165
+ toolCallId: id,
4166
+ toolCallName: chunk.toolCallName,
4167
+ parentMessageId: chunk.parentMessageId,
4168
+ metadata: chunk.metadata,
4169
+ timestamp
4170
+ });
4171
+ this.openChunk = {
4172
+ family: "TOOL_CALL",
4173
+ id
4174
+ };
4175
+ }
4176
+ if (chunk.delta !== void 0 || chunk.metadata !== void 0) events.push({
4177
+ type: EventType.TOOL_CALL_ARGS,
4178
+ toolCallId: id,
4179
+ delta: chunk.delta ?? "",
4180
+ ...chunk.metadata !== void 0 ? { metadata: chunk.metadata } : {},
4181
+ timestamp
4182
+ });
4183
+ return events;
4184
+ }
4185
+ case "REASONING_MESSAGE_CHUNK": {
4186
+ const id = this.chunkStreamId("REASONING_MESSAGE", chunk.messageId, chunk.type);
4187
+ if (id === void 0) return this.closeChunk();
4188
+ const events = [];
4189
+ if (open?.family !== "REASONING_MESSAGE" || open.id !== id) {
4190
+ events.push(...this.closeChunk(), {
4191
+ type: EventType.REASONING_MESSAGE_START,
4192
+ messageId: id,
4193
+ role: "reasoning",
4194
+ timestamp
4195
+ });
4196
+ this.openChunk = {
4197
+ family: "REASONING_MESSAGE",
4198
+ id
4199
+ };
4200
+ }
4201
+ if (chunk.delta !== void 0 || chunk.metadata !== void 0) events.push({
4202
+ type: EventType.REASONING_MESSAGE_CONTENT,
4203
+ messageId: id,
4204
+ delta: chunk.delta ?? "",
4205
+ ...chunk.metadata !== void 0 ? { metadata: chunk.metadata } : {},
4206
+ timestamp
4207
+ });
4208
+ return events;
4209
+ }
4210
+ default: {
4211
+ const events = open && !CHUNK_PASS_THROUGH.has(chunk.type) ? this.closeChunk() : [];
4212
+ if (RUN_LEVEL_CLOSES_LANES.has(chunk.type)) this.closeChildChunkLanes();
4213
+ return [...events, chunk];
4214
+ }
4215
+ }
4216
+ }
4217
+ /** Synthesize *_END on every nested shorthand lane and apply those events there. */
4218
+ closeChildChunkLanes() {
4219
+ for (const child of this.childProcessors.values()) {
4220
+ for (const event of child.closeChunk()) child.dispatchChunk(event);
4221
+ child.closeChildChunkLanes();
4222
+ }
4223
+ }
4224
+ /**
4225
+ * The child an untagged chunk with no id continues: the only child whose
4226
+ * subtree has an open chunk stream of its kind, when this processor has
4227
+ * none. The AG-UI client resolves a chunk that tags only its opener the
4228
+ * same way.
4229
+ */
4230
+ childContinuing(chunk) {
4231
+ let family;
4232
+ if (chunk.type === "TEXT_MESSAGE_CHUNK" && chunk.messageId === void 0) family = "TEXT_MESSAGE";
4233
+ else if (chunk.type === "REASONING_MESSAGE_CHUNK" && chunk.messageId === void 0) family = "REASONING_MESSAGE";
4234
+ else if (chunk.type === "TOOL_CALL_CHUNK" && chunk.toolCallId === void 0) family = "TOOL_CALL";
4235
+ else return;
4236
+ if (this.openChunkCount(family) !== 1) return void 0;
4237
+ for (const [id, child] of this.childProcessors) if (child.openChunkCount(family) === 1) return id;
4238
+ }
4239
+ /**
4240
+ * How many open chunk streams of a kind this processor and all its nested
4241
+ * children hold.
4242
+ */
4243
+ openChunkCount(family) {
4244
+ let count = this.openChunk?.family === family ? 1 : 0;
4245
+ for (const child of this.childProcessors.values()) count += child.openChunkCount(family);
4246
+ return count;
4247
+ }
4248
+ /**
4249
+ * The id a chunk opens or continues. A chunk with no id continues. If it
4250
+ * continues nothing, the caller drops it and closes the open stream, so a
4251
+ * later chunk with no id cannot continue that stream either.
4252
+ */
4253
+ chunkStreamId(family, id, type) {
4254
+ if (id !== void 0) return id;
4255
+ if (this.openChunk?.family === family) return this.openChunk.id;
4256
+ console.warn(`[StreamProcessor] Dropped ${type}: it has no id and continues nothing`);
4257
+ }
4258
+ closeChunk() {
4259
+ const open = this.openChunk;
4260
+ if (!open) return [];
4261
+ this.openChunk = null;
4262
+ const timestamp = Date.now();
4263
+ switch (open.family) {
4264
+ case "TEXT_MESSAGE": return [{
4265
+ type: EventType.TEXT_MESSAGE_END,
4266
+ messageId: open.id,
4267
+ timestamp
4268
+ }];
4269
+ case "TOOL_CALL": return [{
4270
+ type: EventType.TOOL_CALL_END,
4271
+ toolCallId: open.id,
4272
+ timestamp
4273
+ }];
4274
+ case "REASONING_MESSAGE": return [{
4275
+ type: EventType.REASONING_MESSAGE_END,
4276
+ messageId: open.id,
4277
+ timestamp
4278
+ }];
3270
4279
  }
3271
4280
  }
3272
4281
  /**
@@ -3343,7 +4352,7 @@ var StreamProcessor = class StreamProcessor {
3343
4352
  * MESSAGES_SNAPSHOT) but whose transient state was cleared. In that case we
3344
4353
  * hydrate state from the existing message rather than creating a duplicate.
3345
4354
  */
3346
- ensureAssistantMessage(preferredId) {
4355
+ ensureAssistantMessage(preferredId, fallbackToActive = true) {
3347
4356
  if (preferredId) {
3348
4357
  const state = this.getMessageState(preferredId);
3349
4358
  if (state) {
@@ -3354,7 +4363,7 @@ var StreamProcessor = class StreamProcessor {
3354
4363
  };
3355
4364
  }
3356
4365
  }
3357
- const activeId = this.getActiveAssistantMessageId();
4366
+ const activeId = fallbackToActive ? this.getActiveAssistantMessageId() : null;
3358
4367
  if (activeId) {
3359
4368
  const state = this.getMessageState(activeId);
3360
4369
  if (state) {
@@ -3367,7 +4376,8 @@ var StreamProcessor = class StreamProcessor {
3367
4376
  }
3368
4377
  if (preferredId) {
3369
4378
  const existingMsg = this.messages.find((m) => m.id === preferredId);
3370
- if (existingMsg) {
4379
+ if (existingMsg?.role === "activity") preferredId = void 0;
4380
+ else if (existingMsg) {
3371
4381
  const state = this.createMessageState(preferredId, existingMsg.role);
3372
4382
  this.activeMessageIds.add(preferredId);
3373
4383
  const lastPart = existingMsg.parts.length > 0 ? existingMsg.parts[existingMsg.parts.length - 1] : null;
@@ -3392,7 +4402,7 @@ var StreamProcessor = class StreamProcessor {
3392
4402
  this.messages = [...this.messages, assistantMessage];
3393
4403
  const state = this.createMessageState(id, "assistant");
3394
4404
  this.activeMessageIds.add(id);
3395
- this.pendingManualMessageId = id;
4405
+ if (!preferredId) this.pendingManualMessageId = id;
3396
4406
  this.events.onStreamStart?.();
3397
4407
  this.emitMessagesChange();
3398
4408
  return {
@@ -3562,7 +4572,9 @@ var StreamProcessor = class StreamProcessor {
3562
4572
  /**
3563
4573
  * Send a child's chunk to that child's processor, so the card keeps text,
3564
4574
  * reasoning, tool calls, results, and nested children. A tool event without
3565
- * `subagentRunId` follows its `TOOL_CALL_START`.
4575
+ * `subagentRunId` follows its `TOOL_CALL_START` or `TOOL_CALL_CHUNK`. An
4576
+ * untagged chunk with no id follows a child's open chunk stream (see
4577
+ * childContinuing()).
3566
4578
  */
3567
4579
  routeToChild(chunk) {
3568
4580
  let id;
@@ -3572,6 +4584,7 @@ var StreamProcessor = class StreamProcessor {
3572
4584
  id = chunk.subagentRunId;
3573
4585
  } else if ("subagentRunId" in chunk && chunk.subagentRunId) id = chunk.subagentRunId;
3574
4586
  else if ("toolCallId" in chunk && typeof chunk.toolCallId === "string") id = this.childToolCalls.get(chunk.toolCallId);
4587
+ else id = this.childContinuing(chunk);
3575
4588
  if (id === void 0) return false;
3576
4589
  const owner = this.childOwning(id);
3577
4590
  if (owner === void 0) {
@@ -3580,34 +4593,45 @@ var StreamProcessor = class StreamProcessor {
3580
4593
  return true;
3581
4594
  }
3582
4595
  if (this.findSubagentPart(owner)?.part.subagent.status === "error") return true;
3583
- if (chunk.type === "TOOL_CALL_START") this.childToolCalls.set(chunk.toolCallId, owner);
4596
+ if ((chunk.type === "TOOL_CALL_START" || chunk.type === "TOOL_CALL_CHUNK") && chunk.toolCallId !== void 0) this.childToolCalls.set(chunk.toolCallId, owner);
3584
4597
  this.updateChild(owner, (child) => child.processChunk(chunk));
3585
4598
  return true;
3586
4599
  }
4600
+ renameMessage(fromId, toId) {
4601
+ this.messages = this.messages.map((msg) => msg.id === fromId ? {
4602
+ ...msg,
4603
+ id: toId
4604
+ } : msg);
4605
+ const existingState = this.messageStates.get(fromId);
4606
+ if (existingState) {
4607
+ existingState.id = toId;
4608
+ this.messageStates.delete(fromId);
4609
+ this.messageStates.set(toId, existingState);
4610
+ }
4611
+ this.activeMessageIds.delete(fromId);
4612
+ this.activeMessageIds.add(toId);
4613
+ for (const [toolCallId, mappedMessageId] of this.toolCallToMessage) if (mappedMessageId === fromId) this.toolCallToMessage.set(toolCallId, toId);
4614
+ if (this.structuredMessageIds.delete(fromId)) this.structuredMessageIds.add(toId);
4615
+ const batch = this.structuredOutputUpdateBatches.get(fromId);
4616
+ if (batch) {
4617
+ this.structuredOutputUpdateBatches.delete(fromId);
4618
+ this.structuredOutputUpdateBatches.set(toId, batch);
4619
+ }
4620
+ }
3587
4621
  /**
3588
4622
  * Handle TEXT_MESSAGE_START event
3589
4623
  */
3590
4624
  handleTextMessageStartEvent(chunk) {
3591
4625
  const { messageId, role } = chunk;
4626
+ if (this.messages.some((m) => m.id === messageId && m.role === "activity")) {
4627
+ console.warn(`TEXT_MESSAGE_START: Dropped text for '${messageId}', the id of an activity message`);
4628
+ return;
4629
+ }
3592
4630
  const uiRole = role === "user" || role === "system" ? role : "assistant";
3593
4631
  if (this.pendingManualMessageId) {
3594
4632
  const pendingId = this.pendingManualMessageId;
3595
4633
  this.pendingManualMessageId = null;
3596
- if (pendingId !== messageId) {
3597
- this.messages = this.messages.map((msg) => msg.id === pendingId ? {
3598
- ...msg,
3599
- id: messageId
3600
- } : msg);
3601
- const existingState = this.messageStates.get(pendingId);
3602
- if (existingState) {
3603
- existingState.id = messageId;
3604
- this.messageStates.delete(pendingId);
3605
- this.messageStates.set(messageId, existingState);
3606
- }
3607
- this.activeMessageIds.delete(pendingId);
3608
- this.activeMessageIds.add(messageId);
3609
- for (const [toolCallId, mappedMessageId] of this.toolCallToMessage) if (mappedMessageId === pendingId) this.toolCallToMessage.set(toolCallId, messageId);
3610
- }
4634
+ if (pendingId !== messageId) this.renameMessage(pendingId, messageId);
3611
4635
  let pendingState = this.messageStates.get(messageId);
3612
4636
  if (!pendingState) {
3613
4637
  pendingState = this.createMessageState(messageId, uiRole);
@@ -3618,6 +4642,7 @@ var StreamProcessor = class StreamProcessor {
3618
4642
  pendingState.lastEmittedText = "";
3619
4643
  pendingState.hasToolCallsSinceTextStart = false;
3620
4644
  }
4645
+ this.applySenderName(messageId, chunk.name);
3621
4646
  this.mergeMessageMetadata(messageId, chunk.metadata);
3622
4647
  this.emitMessagesChange();
3623
4648
  return;
@@ -3632,6 +4657,7 @@ var StreamProcessor = class StreamProcessor {
3632
4657
  existingState.lastEmittedText = "";
3633
4658
  existingState.hasToolCallsSinceTextStart = false;
3634
4659
  }
4660
+ this.applySenderName(messageId, chunk.name);
3635
4661
  this.mergeMessageMetadata(messageId, chunk.metadata);
3636
4662
  return;
3637
4663
  }
@@ -3644,10 +4670,18 @@ var StreamProcessor = class StreamProcessor {
3644
4670
  this.messages = [...this.messages, newMessage];
3645
4671
  this.createMessageState(messageId, uiRole);
3646
4672
  this.activeMessageIds.add(messageId);
4673
+ this.applySenderName(messageId, chunk.name);
3647
4674
  this.mergeMessageMetadata(messageId, chunk.metadata);
3648
4675
  this.events.onStreamStart?.();
3649
4676
  this.emitMessagesChange();
3650
4677
  }
4678
+ applySenderName(messageId, name) {
4679
+ if (name === void 0) return;
4680
+ this.messages = this.messages.map((msg) => msg.id === messageId ? {
4681
+ ...msg,
4682
+ name
4683
+ } : msg);
4684
+ }
3651
4685
  /**
3652
4686
  * Handle TEXT_MESSAGE_END event
3653
4687
  */
@@ -3677,10 +4711,31 @@ var StreamProcessor = class StreamProcessor {
3677
4711
  metadata: prev.metadata
3678
4712
  };
3679
4713
  });
3680
- this.messages = this.attachSnapshotSubagents(reconciled, top, groups, prevMessages);
4714
+ this.messages = this.mergeOmittedActivity(prevMessages, this.attachSnapshotSubagents(reconciled, top, groups, prevMessages));
3681
4715
  this.emitMessagesChange();
3682
4716
  }
3683
4717
  /**
4718
+ * Snapshot is authoritative for user/assistant/system. Activity omitted
4719
+ * from the snapshot (TanStack chat() still builds snapshots from
4720
+ * ModelMessage[]) stays in the transcript so persist/reconnect cannot
4721
+ * wipe it. Snapshot activity rows win when the id is present.
4722
+ *
4723
+ * Keep every snapshot row, including two assistants that share an id
4724
+ * (agent-loop retry reuses `currentMessageId`). A Map keyed by id would
4725
+ * drop the earlier error tool-call (#1192).
4726
+ */
4727
+ mergeOmittedActivity(prevMessages, snapshot) {
4728
+ const snapshotIds = new Set(snapshot.map((msg) => msg.id));
4729
+ const omitted = prevMessages.filter((msg) => msg.role === "activity" && !snapshotIds.has(msg.id));
4730
+ if (omitted.length === 0) return snapshot;
4731
+ const out = [...snapshot];
4732
+ for (const activity of omitted) {
4733
+ const prevIndex = prevMessages.findIndex((msg) => msg.id === activity.id);
4734
+ out.splice(Math.min(Math.max(prevIndex, 0), out.length), 0, activity);
4735
+ }
4736
+ return out;
4737
+ }
4738
+ /**
3684
4739
  * Put subagent cards back on a snapshot. Child wire messages (tagged with
3685
4740
  * `subagentRunId`) become a card on the nearest assistant message before
3686
4741
  * them, or a new assistant message when there is none. A card that the
@@ -3900,6 +4955,10 @@ var StreamProcessor = class StreamProcessor {
3900
4955
  * @see docs/chat-architecture.md#uimessage-part-ordering-invariants — Replace vs. push logic
3901
4956
  */
3902
4957
  handleTextMessageContentEvent(chunk) {
4958
+ if (this.messages.some((m) => m.id === chunk.messageId && m.role === "activity")) {
4959
+ console.warn(`TEXT_MESSAGE_CONTENT: Dropped text for '${chunk.messageId}', the id of an activity message`);
4960
+ return;
4961
+ }
3903
4962
  const { messageId, state } = this.ensureAssistantMessage(chunk.messageId);
3904
4963
  this.mergeMessageMetadata(messageId, chunk.metadata);
3905
4964
  if (this.structuredMessageIds.has(messageId)) {
@@ -3943,8 +5002,25 @@ var StreamProcessor = class StreamProcessor {
3943
5002
  * @see docs/chat-architecture.md#adapter-contract — Ordering requirements
3944
5003
  */
3945
5004
  handleToolCallStartEvent(chunk) {
5005
+ const pendingId = this.pendingManualMessageId;
5006
+ const parentId = chunk.parentMessageId;
5007
+ if (pendingId && parentId && parentId !== pendingId) {
5008
+ const parent = this.messages.find((m) => m.id === parentId);
5009
+ const placeholder = this.messages.find((m) => m.id === pendingId);
5010
+ if (!parent) {
5011
+ this.pendingManualMessageId = null;
5012
+ this.renameMessage(pendingId, parentId);
5013
+ } else if (parent.role === "assistant" && placeholder) {
5014
+ this.pendingManualMessageId = null;
5015
+ this.messages = this.messages.filter((m) => m.id !== pendingId).map((m) => m.id === parentId ? {
5016
+ ...m,
5017
+ parts: [...m.parts, ...placeholder.parts]
5018
+ } : m);
5019
+ this.renameMessage(pendingId, parentId);
5020
+ }
5021
+ }
3946
5022
  const targetMessageId = chunk.parentMessageId ?? this.getActiveAssistantMessageId();
3947
- const { messageId, state } = this.ensureAssistantMessage(targetMessageId ?? void 0);
5023
+ const { messageId, state } = this.ensureAssistantMessage(targetMessageId ?? void 0, chunk.parentMessageId === void 0);
3948
5024
  state.hasToolCallsSinceTextStart = true;
3949
5025
  const toolCallId = chunk.toolCallId;
3950
5026
  if (!state.toolCalls.get(toolCallId)) {
@@ -3962,6 +5038,7 @@ var StreamProcessor = class StreamProcessor {
3962
5038
  };
3963
5039
  state.toolCalls.set(toolCallId, newToolCall);
3964
5040
  state.toolCallOrder.push(toolCallId);
5041
+ this.runToolCallIds.push(toolCallId);
3965
5042
  this.toolCallToMessage.set(toolCallId, messageId);
3966
5043
  this.messages = updateToolCallPart(this.messages, messageId, {
3967
5044
  id: chunk.toolCallId,
@@ -3995,6 +5072,7 @@ var StreamProcessor = class StreamProcessor {
3995
5072
  const existingToolCall = state.toolCalls.get(toolCallId);
3996
5073
  if (!existingToolCall) return;
3997
5074
  const wasAwaitingInput = existingToolCall.state === "awaiting-input";
5075
+ if (chunk.metadata !== void 0) existingToolCall.metadata = mergeMetadata(existingToolCall.metadata, chunk.metadata);
3998
5076
  existingToolCall.arguments += chunk.delta || "";
3999
5077
  if (wasAwaitingInput && chunk.delta) existingToolCall.state = "input-streaming";
4000
5078
  existingToolCall.parsedArguments = this.jsonParser.parse(existingToolCall.arguments);
@@ -4002,7 +5080,8 @@ var StreamProcessor = class StreamProcessor {
4002
5080
  id: existingToolCall.id,
4003
5081
  name: existingToolCall.name,
4004
5082
  arguments: existingToolCall.arguments,
4005
- state: existingToolCall.state
5083
+ state: existingToolCall.state,
5084
+ ...existingToolCall.metadata !== void 0 && { metadata: existingToolCall.metadata }
4006
5085
  });
4007
5086
  this.emitMessagesChange();
4008
5087
  this.events.onToolCallStateChange?.(messageId, existingToolCall.id, existingToolCall.state, existingToolCall.arguments);
@@ -4079,6 +5158,10 @@ var StreamProcessor = class StreamProcessor {
4079
5158
  * runs are still active before finalizing.
4080
5159
  */
4081
5160
  handleRunStartedEvent(chunk) {
5161
+ if (this.activeRuns.size === 0) {
5162
+ this.runToolCallIds = [];
5163
+ this.toolCallsSentToClient.clear();
5164
+ }
4082
5165
  this.activeRuns.add(chunk.runId);
4083
5166
  }
4084
5167
  /**
@@ -4099,11 +5182,39 @@ var StreamProcessor = class StreamProcessor {
4099
5182
  if (this.activeRuns.size === 0) {
4100
5183
  this.completeAllToolCalls();
4101
5184
  if (this.finishReason === "tool_calls" && chunk.outcome?.type !== "interrupt") return;
5185
+ if (!chunk.outcome || chunk.outcome.type === "success") this.handlePendingToolCalls(chunk.outcome?.pendingToolCallIds);
4102
5186
  this.isDone = true;
4103
5187
  this.finalizeStream();
4104
5188
  }
4105
5189
  }
4106
5190
  /**
5191
+ * AG-UI ends a run that calls a frontend tool with a success outcome and
5192
+ * leaves the call unanswered. The pending calls are the ones named in
5193
+ * `pendingToolCallIds` that this run started, or else every call this run
5194
+ * started that has no result. Fire onToolCall once for each, so the client
5195
+ * runs it and continues.
5196
+ */
5197
+ handlePendingToolCalls(pendingToolCallIds) {
5198
+ const ids = pendingToolCallIds?.length ? pendingToolCallIds.filter((id) => this.runToolCallIds.includes(id)) : this.runToolCallIds;
5199
+ const parts = this.messages.flatMap((msg) => msg.parts ?? []);
5200
+ for (const toolCallId of ids) {
5201
+ if (this.toolCallsSentToClient.has(toolCallId)) continue;
5202
+ const part = parts.find((p) => p.type === "tool-call" && p.id === toolCallId);
5203
+ if (!part || isProviderExecutedToolCall(part) || part.output !== void 0 || parts.some((p) => p.type === "tool-result" && p.toolCallId === toolCallId) || this.isToolCallPartTerminal(toolCallId) || this.isToolCallPartAwaitingUserAction(toolCallId)) continue;
5204
+ const input = part.input ?? parseToolArguments(part.arguments);
5205
+ if (input === void 0) {
5206
+ console.warn(`[StreamProcessor] Did not run tool call ${toolCallId}: the arguments of ${part.name} are not complete JSON`);
5207
+ continue;
5208
+ }
5209
+ this.toolCallsSentToClient.add(toolCallId);
5210
+ this.events.onToolCall?.({
5211
+ toolCallId,
5212
+ toolName: part.name,
5213
+ input
5214
+ });
5215
+ }
5216
+ }
5217
+ /**
4107
5218
  * Apply interrupt state to tool-call parts, then fire the client events.
4108
5219
  * A child's interrupt updates the child's card. `emit` is false there, so
4109
5220
  * the events fire once, from the top processor.
@@ -4145,6 +5256,7 @@ var StreamProcessor = class StreamProcessor {
4145
5256
  }
4146
5257
  if (kind === "client_tool" || interrupt.reason === "client_tool_input") {
4147
5258
  if (hasGeneric || !emit) continue;
5259
+ this.toolCallsSentToClient.add(toolCallId);
4148
5260
  this.events.onToolCall?.({
4149
5261
  toolCallId,
4150
5262
  toolName,
@@ -4205,10 +5317,28 @@ var StreamProcessor = class StreamProcessor {
4205
5317
  this.pendingThinkingStepId = stepId;
4206
5318
  }
4207
5319
  /**
4208
- * Handle STEP_FINISHED event.
4209
- *
4210
- * Thinking *content* comes from REASONING_MESSAGE_CONTENT, not STEP_FINISHED.
4211
- * But some adapters (e.g. BytePlus thinking-summary) carry the provider
5320
+ * Handle ACTIVITY_SNAPSHOT. Creates or replaces a frontend-only activity
5321
+ * message. Does not create MessageStreamState (that path feeds
5322
+ * ProcessorResult.content).
5323
+ */
5324
+ handleActivitySnapshotEvent(chunk) {
5325
+ this.messages = applyActivitySnapshotToUIMessages(this.messages, chunk);
5326
+ this.emitMessagesChange();
5327
+ }
5328
+ /**
5329
+ * Handle ACTIVITY_DELTA. Applies RFC 6902 patches to an existing activity
5330
+ * `content`. Missing or non-activity ids are no-ops. Patch failure leaves
5331
+ * the previous content in place.
5332
+ */
5333
+ handleActivityDeltaEvent(chunk) {
5334
+ this.messages = applyActivityDeltaToUIMessages(this.messages, chunk);
5335
+ this.emitMessagesChange();
5336
+ }
5337
+ /**
5338
+ * Handle STEP_FINISHED event.
5339
+ *
5340
+ * Thinking *content* comes from REASONING_MESSAGE_CONTENT, not STEP_FINISHED.
5341
+ * But some adapters (e.g. BytePlus thinking-summary) carry the provider
4212
5342
  * signature blob ONLY on the STEP_FINISHED event, so still extract that here
4213
5343
  * and attach it to the thinking step the reasoning events already built.
4214
5344
  */
@@ -4233,6 +5363,7 @@ var StreamProcessor = class StreamProcessor {
4233
5363
  */
4234
5364
  handleReasoningMessageContentEvent(chunk) {
4235
5365
  const { messageId, state } = this.ensureAssistantMessage(this.getActiveAssistantMessageId() ?? void 0);
5366
+ this.mergeMessageMetadata(messageId, chunk.metadata);
4236
5367
  state.hasSeenReasoningEvents = true;
4237
5368
  const delta = chunk.delta || "";
4238
5369
  this.consumePendingThinkingStep(state);
@@ -4345,6 +5476,7 @@ var StreamProcessor = class StreamProcessor {
4345
5476
  }
4346
5477
  if (chunk.name === "tool-input-available" && chunk.value) {
4347
5478
  const { toolCallId, toolName, input } = chunk.value;
5479
+ this.toolCallsSentToClient.add(toolCallId);
4348
5480
  this.events.onToolCall?.({
4349
5481
  toolCallId,
4350
5482
  toolName,
@@ -4523,6 +5655,8 @@ var StreamProcessor = class StreamProcessor {
4523
5655
  */
4524
5656
  finalizeStream() {
4525
5657
  this.isDone = true;
5658
+ for (const event of this.closeChunk()) this.dispatchChunk(event);
5659
+ this.closeChildChunkLanes();
4526
5660
  let lastAssistantMessage;
4527
5661
  for (const messageId of this.activeMessageIds) {
4528
5662
  const state = this.getMessageState(messageId);
@@ -4640,6 +5774,7 @@ var StreamProcessor = class StreamProcessor {
4640
5774
  this.structuredOutputUpdateBatches.clear();
4641
5775
  this.pendingManualMessageId = null;
4642
5776
  this.pendingThinkingStepId = null;
5777
+ this.openChunk = null;
4643
5778
  this.finishReason = null;
4644
5779
  this.hasError = false;
4645
5780
  this.isDone = false;
@@ -4679,14 +5814,14 @@ function createReplayStream(recording) {
4679
5814
  } };
4680
5815
  }
4681
5816
  //#endregion
4682
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/byok/providers.js
5817
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/byok/providers.js
4683
5818
  /** `[a-z][a-z0-9-]{0,63}` — lowercase, no underscores, max 64 chars. */
4684
5819
  var BYOK_PROVIDER_ID_PATTERN = /^[a-z][a-z0-9-]{0,63}$/;
4685
5820
  function isProviderId(value) {
4686
5821
  return typeof value === "string" && BYOK_PROVIDER_ID_PATTERN.test(value);
4687
5822
  }
4688
5823
  //#endregion
4689
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/byok/missing.js
5824
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/byok/missing.js
4690
5825
  function isByokMissingBody(value) {
4691
5826
  if (typeof value !== "object" || value === null) return false;
4692
5827
  if (!("error" in value)) return false;
@@ -4699,7 +5834,7 @@ function isByokMissingBody(value) {
4699
5834
  return true;
4700
5835
  }
4701
5836
  //#endregion
4702
- //#region node_modules/.deno/@tanstack+ai@0.63.0/node_modules/@tanstack/ai/dist/esm/byok/errors.js
5837
+ //#region node_modules/.deno/@tanstack+ai@0.68.0/node_modules/@tanstack/ai/dist/esm/byok/errors.js
4703
5838
  var ByokMissingError = class extends Error {
4704
5839
  provider;
4705
5840
  constructor(provider) {
@@ -4725,7 +5860,7 @@ var ByokUnresolvedProviderError = class extends Error {
4725
5860
  }
4726
5861
  };
4727
5862
  //#endregion
4728
- //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/connection-adapters.js
5863
+ //#region node_modules/.deno/@tanstack+ai-client@0.39.2/node_modules/@tanstack/ai-client/dist/esm/connection-adapters.js
4729
5864
  /**
4730
5865
  * Associates connect-wrapped chunks with the run they were produced under.
4731
5866
  * Content events (TEXT_MESSAGE_CONTENT, TOOL_CALL_*, …) carry no `runId` of
@@ -4918,6 +6053,9 @@ async function* readStreamLines(reader, abortSignal) {
4918
6053
  buffer += decoder.decode();
4919
6054
  if (buffer.trim() && !abortSignal?.aborted) throw new StreamTruncatedError();
4920
6055
  } finally {
6056
+ try {
6057
+ reader.cancel().catch(() => {});
6058
+ } catch {}
4921
6059
  reader.releaseLock();
4922
6060
  }
4923
6061
  }
@@ -5420,7 +6558,7 @@ async function* abortableIterable(iterable, signal) {
5420
6558
  }
5421
6559
  }
5422
6560
  //#endregion
5423
- //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/interrupt-manager.js
6561
+ //#region node_modules/.deno/@tanstack+ai-client@0.39.2/node_modules/@tanstack/ai-client/dist/esm/interrupt-manager.js
5424
6562
  function isClientOwnedInterrupt(item) {
5425
6563
  return item.resumable;
5426
6564
  }
@@ -6362,7 +7500,7 @@ var InterruptManager = class {
6362
7500
  }
6363
7501
  };
6364
7502
  //#endregion
6365
- //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/byok/client.js
7503
+ //#region node_modules/.deno/@tanstack+ai-client@0.39.2/node_modules/@tanstack/ai-client/dist/esm/byok/client.js
6366
7504
  var registeredProviderIds = /* @__PURE__ */ new WeakMap();
6367
7505
  /**
6368
7506
  * Provider to stamp when the caller did not pass a slug.
@@ -6375,7 +7513,7 @@ function byokFallbackProviderId(byok) {
6375
7513
  if (registered.length === 1) return registered[0];
6376
7514
  }
6377
7515
  //#endregion
6378
- //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/byok/resolve.js
7516
+ //#region node_modules/.deno/@tanstack+ai-client@0.39.2/node_modules/@tanstack/ai-client/dist/esm/byok/resolve.js
6379
7517
  function resolveByokProviderId(byokProvider, ...candidates) {
6380
7518
  const fromFn = byokProvider?.();
6381
7519
  if (isProviderId(fromFn)) return fromFn;
@@ -6391,7 +7529,7 @@ async function prepareResolvedByokHeaders(byok, provider) {
6391
7529
  return byok.headers(provider);
6392
7530
  }
6393
7531
  //#endregion
6394
- //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/events.js
7532
+ //#region node_modules/.deno/@tanstack+ai-client@0.39.2/node_modules/@tanstack/ai-client/dist/esm/events.js
6395
7533
  /**
6396
7534
  * Abstract base class for ChatClient event emission
6397
7535
  */
@@ -6565,7 +7703,7 @@ var ChatClientEventEmitter = class {
6565
7703
  }
6566
7704
  };
6567
7705
  //#endregion
6568
- //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/devtools-noop.js
7706
+ //#region node_modules/.deno/@tanstack+ai-client@0.39.2/node_modules/@tanstack/ai-client/dist/esm/devtools-noop.js
6569
7707
  var NoOpChatClientEventEmitter = class extends ChatClientEventEmitter {
6570
7708
  emitEvent() {}
6571
7709
  };
@@ -6610,7 +7748,7 @@ var NoOpChatDevtoolsBridge = class {
6610
7748
  };
6611
7749
  var createNoOpChatDevtoolsBridge = (options) => new NoOpChatDevtoolsBridge(options);
6612
7750
  //#endregion
6613
- //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/client-persistor.js
7751
+ //#region node_modules/.deno/@tanstack+ai-client@0.39.2/node_modules/@tanstack/ai-client/dist/esm/client-persistor.js
6614
7752
  /** Normalize a raw `getItem` result (legacy bare array or combined record). */
6615
7753
  function normalizePersistedState(raw) {
6616
7754
  if (Array.isArray(raw)) return { messages: raw };
@@ -6709,12 +7847,13 @@ var ChatPersistor = class {
6709
7847
  }
6710
7848
  /**
6711
7849
  * Apply state from an async `getItem` once it resolves, unless the message
6712
- * list has already changed since hydration began.
7850
+ * list has already changed since hydration began. Returns a promise that
7851
+ * settles (never rejects) when it is done, or `undefined` for sync state.
6713
7852
  */
6714
7853
  hydrateAsync(persistedState) {
6715
7854
  if (!(persistedState instanceof Promise)) return;
6716
7855
  const hydrationGeneration = this.messagesGeneration;
6717
- persistedState.then((state) => {
7856
+ return persistedState.then((state) => {
6718
7857
  if (!state || this.messagesGeneration !== hydrationGeneration) return;
6719
7858
  this.lastResume = state.resume ?? null;
6720
7859
  this.lastMessages = state.messages;
@@ -6870,11 +8009,11 @@ var ChatPersistor = class {
6870
8009
  isRunlessChunkFromIgnoredRun(chunk) {
6871
8010
  if (getChunkRunId$1(chunk) || !this.currentRunlessRunId) return false;
6872
8011
  if (!this.ignoredActiveRunIds.has(this.currentRunlessRunId) && !this.clearedRunIds.has(this.currentRunlessRunId)) return false;
6873
- return chunk.type === "TEXT_MESSAGE_START" || chunk.type === "TEXT_MESSAGE_CONTENT" || chunk.type === "TOOL_CALL_START" || chunk.type === "TOOL_CALL_ARGS" || chunk.type === "TOOL_CALL_END" || chunk.type === "TOOL_CALL_RESULT" || chunk.type === "MESSAGES_SNAPSHOT" || chunk.type === "RUN_ERROR";
8012
+ return chunk.type === "TEXT_MESSAGE_START" || chunk.type === "TEXT_MESSAGE_CONTENT" || chunk.type === "TEXT_MESSAGE_CHUNK" || chunk.type === "TOOL_CALL_START" || chunk.type === "TOOL_CALL_ARGS" || chunk.type === "TOOL_CALL_END" || chunk.type === "TOOL_CALL_RESULT" || chunk.type === "TOOL_CALL_CHUNK" || chunk.type === "MESSAGES_SNAPSHOT" || chunk.type === "RUN_ERROR";
6874
8013
  }
6875
8014
  };
6876
8015
  //#endregion
6877
- //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/cleared-stream-tracker.js
8016
+ //#region node_modules/.deno/@tanstack+ai-client@0.39.2/node_modules/@tanstack/ai-client/dist/esm/cleared-stream-tracker.js
6878
8017
  function getChunkToolCallId(chunk) {
6879
8018
  return "toolCallId" in chunk && typeof chunk.toolCallId === "string" ? chunk.toolCallId : void 0;
6880
8019
  }
@@ -6963,11 +8102,11 @@ var ClearedStreamTracker = class {
6963
8102
  isRunlessChunkFromIgnoredRun(chunk) {
6964
8103
  if (getChunkRunId$1(chunk) || !this.currentRunlessRunId) return false;
6965
8104
  if (!this.ignoredActiveRunIds.has(this.currentRunlessRunId) && !this.clearedRunIds.has(this.currentRunlessRunId)) return false;
6966
- return chunk.type === "TEXT_MESSAGE_START" || chunk.type === "TEXT_MESSAGE_CONTENT" || chunk.type === "TOOL_CALL_START" || chunk.type === "TOOL_CALL_ARGS" || chunk.type === "TOOL_CALL_END" || chunk.type === "TOOL_CALL_RESULT" || chunk.type === "MESSAGES_SNAPSHOT" || chunk.type === "RUN_ERROR";
8105
+ return chunk.type === "TEXT_MESSAGE_START" || chunk.type === "TEXT_MESSAGE_CONTENT" || chunk.type === "TEXT_MESSAGE_CHUNK" || chunk.type === "TOOL_CALL_START" || chunk.type === "TOOL_CALL_ARGS" || chunk.type === "TOOL_CALL_END" || chunk.type === "TOOL_CALL_RESULT" || chunk.type === "TOOL_CALL_CHUNK" || chunk.type === "MESSAGES_SNAPSHOT" || chunk.type === "RUN_ERROR";
6967
8106
  }
6968
8107
  };
6969
8108
  //#endregion
6970
- //#region node_modules/.deno/@tanstack+ai-client@0.36.0/node_modules/@tanstack/ai-client/dist/esm/chat-client.js
8109
+ //#region node_modules/.deno/@tanstack+ai-client@0.39.2/node_modules/@tanstack/ai-client/dist/esm/chat-client.js
6971
8110
  var STREAM_PROCESSING_BUDGET_MS = 8;
6972
8111
  function yieldToHost() {
6973
8112
  const { scheduler } = globalThis;
@@ -7103,8 +8242,11 @@ var REJOIN_CONNECT_DEADLINE_MS = 2e3;
7103
8242
  var REJOIN_REBUILD_TRIGGERS = /* @__PURE__ */ new Set([
7104
8243
  "TEXT_MESSAGE_START",
7105
8244
  "TEXT_MESSAGE_CONTENT",
8245
+ "TEXT_MESSAGE_CHUNK",
7106
8246
  "REASONING_MESSAGE_CONTENT",
8247
+ "REASONING_MESSAGE_CHUNK",
7107
8248
  "TOOL_CALL_START",
8249
+ "TOOL_CALL_CHUNK",
7108
8250
  "MESSAGES_SNAPSHOT",
7109
8251
  "SUBAGENT_STARTED"
7110
8252
  ]);
@@ -7208,6 +8350,7 @@ var ChatClient = class {
7208
8350
  currentMessageId = null;
7209
8351
  postStreamActions = [];
7210
8352
  pendingToolExecutions = /* @__PURE__ */ new Map();
8353
+ handingOverPendingToolCalls = false;
7211
8354
  activeClientTools = null;
7212
8355
  activeContext = void 0;
7213
8356
  continuationPending = false;
@@ -7233,6 +8376,12 @@ var ChatClient = class {
7233
8376
  hydrationError;
7234
8377
  /** Whether a view is currently watching. See `attach` / `detach`. */
7235
8378
  tailing = false;
8379
+ /** `historyGeneration` of the mount hydration GET still in flight, if any. */
8380
+ hydrationInFlight;
8381
+ /** An async storage adapter is still reading the persisted transcript. */
8382
+ storeHydrating = false;
8383
+ /** See {@link getIsHydrating}. */
8384
+ isHydrating = false;
7236
8385
  /** Constructor inputs `attach()` needs on every re-attach, not just the first. */
7237
8386
  rejoinRunId;
7238
8387
  cachesMessages;
@@ -7287,6 +8436,7 @@ var ChatClient = class {
7287
8436
  onSubscriptionChange: options.onSubscriptionChange || (() => {}),
7288
8437
  onConnectionStatusChange: options.onConnectionStatusChange || (() => {}),
7289
8438
  onSessionGeneratingChange: options.onSessionGeneratingChange || (() => {}),
8439
+ onHydratingChange: options.onHydratingChange || (() => {}),
7290
8440
  onQueueChange: options.onQueueChange || (() => {}),
7291
8441
  onResumeStateChange: options.onResumeStateChange || (() => {}),
7292
8442
  onRunIdChange: options.onRunIdChange || (() => {}),
@@ -7368,6 +8518,10 @@ var ChatClient = class {
7368
8518
  onToolCall: (args) => {
7369
8519
  const clientTool = (this.activeClientTools ?? this.clientToolsRef.current).get(args.toolName);
7370
8520
  const executeFunc = clientTool?.execute;
8521
+ if (this.handingOverPendingToolCalls && clientTool?.needsApproval === true) {
8522
+ console.warn(`[ChatClient] Did not run tool call ${args.toolCallId}: ${args.toolName} needs approval, and the server did not ask for it`);
8523
+ return;
8524
+ }
7371
8525
  if (executeFunc) {
7372
8526
  const continuationGeneration = this.continuationGeneration;
7373
8527
  const runEventContext = this.devtoolsBridge.getCurrentRunEventContext();
@@ -7414,9 +8568,17 @@ var ChatClient = class {
7414
8568
  }
7415
8569
  });
7416
8570
  this.syncSubagentHandles();
7417
- this.persistor?.hydrateAsync(persistedState);
8571
+ const storeHydration = this.persistor?.hydrateAsync(persistedState);
8572
+ if (storeHydration) {
8573
+ this.storeHydrating = true;
8574
+ storeHydration.finally(() => {
8575
+ this.storeHydrating = false;
8576
+ this.syncIsHydrating();
8577
+ });
8578
+ }
7418
8579
  this.rejoinRunId = rejoinRunId;
7419
8580
  this.cachesMessages = cachesMessages;
8581
+ this.isHydrating = this.storeHydrating || !cachesMessages && !!this.connection.hydrate;
7420
8582
  }
7421
8583
  /**
7422
8584
  * START TAILING: re-attach to an in-flight run so its chunks arrive here.
@@ -7519,21 +8681,30 @@ var ChatClient = class {
7519
8681
  */
7520
8682
  hydrateFromServer() {
7521
8683
  const hydrate = this.connection.hydrate;
7522
- if (!hydrate) return;
7523
- if (this.isLoading || this.abortController) return;
7524
- if (this.disposed) return;
8684
+ if (!hydrate || this.isLoading || this.abortController || this.disposed) {
8685
+ this.syncIsHydrating();
8686
+ return;
8687
+ }
8688
+ if (this.hydrationInFlight === this.historyGeneration) return;
7525
8689
  const pageSize = this.historyPageSize;
7526
8690
  const hydrateOptions = pageSize === void 0 ? void 0 : { limit: pageSize };
7527
8691
  (async () => {
7528
8692
  let result;
7529
8693
  const generation = this.historyGeneration;
8694
+ this.hydrationInFlight = generation;
8695
+ this.syncIsHydrating();
8696
+ const isCurrent = () => this.hydrationInFlight === generation && generation === this.historyGeneration;
8697
+ let current;
7530
8698
  try {
7531
8699
  result = await hydrate(this.threadId, hydrateOptions);
7532
8700
  } catch (cause) {
7533
- if (generation === this.historyGeneration) this.failHydration(cause);
8701
+ if (isCurrent()) this.failHydration(cause);
7534
8702
  return;
8703
+ } finally {
8704
+ current = isCurrent();
8705
+ if (this.hydrationInFlight === generation) this.hydrationInFlight = void 0;
7535
8706
  }
7536
- if (generation !== this.historyGeneration) return;
8707
+ if (!current) return;
7537
8708
  if (this.disposed || !this.tailing) return;
7538
8709
  if (this.isLoading || this.abortController) return;
7539
8710
  if (this.hydrationError && this.error === this.hydrationError) {
@@ -7555,7 +8726,7 @@ var ChatClient = class {
7555
8726
  pendingInterrupts: result.interrupts.pending
7556
8727
  });
7557
8728
  else if (result.activeRun?.runId) this.maybeRejoinInFlight(result.activeRun.runId);
7558
- })();
8729
+ })().finally(() => this.syncIsHydrating());
7559
8730
  }
7560
8731
  /**
7561
8732
  * Surface a mount-hydration failure (`persistence: true`) on the observable
@@ -7824,6 +8995,17 @@ var ChatClient = class {
7824
8995
  this.isLoading = isLoading;
7825
8996
  this.callbacksRef.current.onLoadingChange(isLoading);
7826
8997
  this.events.loadingChanged(isLoading);
8998
+ if (isLoading) this.supersedeHydration();
8999
+ }
9000
+ /**
9001
+ * A send or a clear now owns the transcript. A load that is still pending can
9002
+ * no longer paint it (the generation guards drop its result), so it no longer
9003
+ * counts as hydrating.
9004
+ */
9005
+ supersedeHydration() {
9006
+ this.hydrationInFlight = void 0;
9007
+ this.storeHydrating = false;
9008
+ this.syncIsHydrating();
7827
9009
  }
7828
9010
  setStatus(status) {
7829
9011
  this.status = status;
@@ -7840,6 +9022,12 @@ var ChatClient = class {
7840
9022
  this.callbacksRef.current.onConnectionStatusChange(status);
7841
9023
  this.devtoolsBridge.emitSnapshot();
7842
9024
  }
9025
+ syncIsHydrating() {
9026
+ const isHydrating = this.hydrationInFlight !== void 0 || this.storeHydrating;
9027
+ if (this.isHydrating === isHydrating) return;
9028
+ this.isHydrating = isHydrating;
9029
+ this.callbacksRef.current.onHydratingChange(isHydrating);
9030
+ }
7843
9031
  setSessionGenerating(isGenerating) {
7844
9032
  if (this.sessionGenerating === isGenerating) return;
7845
9033
  this.sessionGenerating = isGenerating;
@@ -8105,7 +9293,12 @@ var ChatClient = class {
8105
9293
  this.resolveJoinedRun(chunk);
8106
9294
  return;
8107
9295
  }
8108
- this.processor.processChunk(chunk);
9296
+ this.handingOverPendingToolCalls = chunk.type === "RUN_FINISHED" && chunk.outcome?.type !== "interrupt";
9297
+ try {
9298
+ this.processor.processChunk(chunk);
9299
+ } finally {
9300
+ this.handingOverPendingToolCalls = false;
9301
+ }
8109
9302
  this.syncSubagentHandles();
8110
9303
  this.updateRunLifecycle(chunk);
8111
9304
  this.observeInterruptState(chunk);
@@ -8328,6 +9521,14 @@ var ChatClient = class {
8328
9521
  if (this.hasPendingInterrupts()) throw new Error("ChatClient: cannot append normal input while pending interrupts exist. Use resumeInterrupts() instead.");
8329
9522
  const normalizedMessage = normalizeToUIMessage(message, generateMessageId);
8330
9523
  if (normalizedMessage.role === "system") return;
9524
+ if (normalizedMessage.role === "activity") {
9525
+ const uiMessage = normalizedMessage;
9526
+ this.events.messageAppended(uiMessage);
9527
+ const messages = this.processor.getMessages();
9528
+ this.processor.setMessages([...messages, uiMessage]);
9529
+ this.devtoolsBridge.emitSnapshot();
9530
+ return;
9531
+ }
8331
9532
  const uiMessage = normalizedMessage;
8332
9533
  this.events.messageAppended(uiMessage);
8333
9534
  const messages = this.processor.getMessages();
@@ -8544,6 +9745,7 @@ var ChatClient = class {
8544
9745
  this.persistor?.beginClear();
8545
9746
  this.processor.clearMessages();
8546
9747
  this.resetHistoryPaging();
9748
+ this.supersedeHydration();
8547
9749
  this.discardPendingSends();
8548
9750
  this.persistor?.remove();
8549
9751
  this.stoppedSubagentIds.clear();
@@ -8802,16 +10004,17 @@ var ChatClient = class {
8802
10004
  this.rememberServerMessageIds(this.processor.getMessages());
8803
10005
  }
8804
10006
  messagesForSend(messages) {
8805
- if (this.historyPageSize === void 0) return messages;
10007
+ const forModel = messages.filter((message) => message.role !== "activity");
10008
+ if (this.historyPageSize === void 0) return forModel;
8806
10009
  const unknownMessages = [];
8807
- for (const message of messages) {
10010
+ for (const message of forModel) {
8808
10011
  if (this.knownServerMessageIds.has(message.id)) continue;
8809
10012
  unknownMessages.push(message);
8810
10013
  }
8811
10014
  if (unknownMessages.length > 0) return unknownMessages;
8812
- const lastUserIndex = messages.findLastIndex((message) => message.role === "user");
10015
+ const lastUserIndex = forModel.findLastIndex((message) => message.role === "user");
8813
10016
  if (lastUserIndex === -1) return [];
8814
- return messages.slice(lastUserIndex);
10017
+ return forModel.slice(lastUserIndex);
8815
10018
  }
8816
10019
  /**
8817
10020
  * True when an interrupt (or another direct send) claimed the client during
@@ -8933,6 +10136,15 @@ var ChatClient = class {
8933
10136
  return this.sessionGenerating;
8934
10137
  }
8935
10138
  /**
10139
+ * Whether the client is rebuilding the chat from persistence: the server
10140
+ * hydrate (`persistence: true`) or an async storage adapter. Turns false once
10141
+ * the transcript is applied and any in-flight run is re-joined (from then on
10142
+ * `isLoading` covers the stream), or when the load fails.
10143
+ */
10144
+ getIsHydrating() {
10145
+ return this.isHydrating;
10146
+ }
10147
+ /**
8936
10148
  * Get current error
8937
10149
  */
8938
10150
  getError() {
@@ -8983,6 +10195,7 @@ var ChatClient = class {
8983
10195
  if (options.onSubscriptionChange !== void 0) this.callbacksRef.current.onSubscriptionChange = options.onSubscriptionChange;
8984
10196
  if (options.onConnectionStatusChange !== void 0) this.callbacksRef.current.onConnectionStatusChange = options.onConnectionStatusChange;
8985
10197
  if (options.onSessionGeneratingChange !== void 0) this.callbacksRef.current.onSessionGeneratingChange = options.onSessionGeneratingChange;
10198
+ if (options.onHydratingChange !== void 0) this.callbacksRef.current.onHydratingChange = options.onHydratingChange;
8986
10199
  if (options.onQueueChange !== void 0) this.callbacksRef.current.onQueueChange = options.onQueueChange;
8987
10200
  if (options.onResumeStateChange !== void 0) this.callbacksRef.current.onResumeStateChange = options.onResumeStateChange;
8988
10201
  if (options.onRunIdChange !== void 0) this.callbacksRef.current.onRunIdChange = options.onRunIdChange;
@@ -9043,16 +10256,11 @@ function toJsonSchema(parameters) {
9043
10256
  properties: {}
9044
10257
  };
9045
10258
  if (!("~standard" in parameters)) return parameters;
9046
- try {
9047
- return convertSchemaToJsonSchema(parameters);
9048
- } catch {
9049
- return { type: "object" };
9050
- }
10259
+ return "jsonSchema" in parameters["~standard"] ? convertSchemaToJsonSchema(parameters) : { type: "object" };
9051
10260
  }
9052
10261
  async function validateParameters(parameters, input) {
9053
- if (!parameters || !("~standard" in parameters)) return input;
9054
- const result = await parameters["~standard"].validate(input);
9055
- return result.issues ? null : result.value ?? {};
10262
+ const result = await validateWithStandardSchema(parameters, input);
10263
+ return result.success ? result.data ?? {} : null;
9056
10264
  }
9057
10265
  //#endregion
9058
10266
  //#region src/core/agent-tools.ts
@@ -9314,6 +10522,7 @@ async function loadChatAuthToken(options) {
9314
10522
  } catch (cause) {
9315
10523
  signal.throwIfAborted();
9316
10524
  const error = cause instanceof Error ? cause : /* @__PURE__ */ new Error("Authentication failed");
10525
+ if (isAstralBeamApiError(error) && error.status === 401) error.message = "AstralBeam rejected the chat token after renewal. Check that apiUrl matches the deployment that issued your API key. If the key was revoked or expired, create a replacement in API Keys and update your server's secret.";
9317
10526
  session.cached = void 0;
9318
10527
  publishAuthentication(options, {
9319
10528
  status: "error",
@@ -9445,6 +10654,11 @@ function hasPendingToolRun(messages, toolNames) {
9445
10654
  return messages.some((message) => message.parts.some((part) => part.type === "tool-call" && !isSettledToolCall(part) && part.name !== "ask_questionnaire" && toolNames.has(part.name)));
9446
10655
  }
9447
10656
  //#endregion
10657
+ //#region src/core/web.ts
10658
+ function webPartMetadata(part) {
10659
+ return part.metadata ?? {};
10660
+ }
10661
+ //#endregion
9448
10662
  //#region src/core/sandbox.ts
9449
10663
  const SANDBOX_TOOLS = [
9450
10664
  SANDBOX_WRITE_FILE_TOOL,
@@ -9495,19 +10709,19 @@ function sandboxPathLabel(part) {
9495
10709
  if (relative !== void 0 && relative !== ".") return relative;
9496
10710
  return text(part.output, "path") ?? text(part.input, "path");
9497
10711
  }
9498
- /** A `sandbox_publish_artifact` call whose output carries the download ticket. */
10712
+ /** A `sandbox_publish_artifact` call whose output carries a durable file reference. */
9499
10713
  function readSandboxArtifact(part) {
9500
10714
  if (part.name !== "sandbox_publish_artifact") return void 0;
9501
10715
  const path = text(part.output, "path") ?? text(part.input, "path");
9502
- const ticket = text(part.output, "ticket");
10716
+ const fileId = text(part.output, "fileId");
9503
10717
  return {
9504
10718
  toolCallId: part.id,
9505
10719
  path: path ?? "",
9506
10720
  label: sandboxPathLabel(part) ?? path ?? "",
9507
10721
  mimeType: text(part.output, "mimeType"),
9508
10722
  size: count(part.output, "size"),
9509
- ticket,
9510
- published: ticket !== void 0 && sandboxRefusal(part) === void 0
10723
+ fileId,
10724
+ published: fileId !== void 0 && sandboxRefusal(part) === void 0
9511
10725
  };
9512
10726
  }
9513
10727
  /** A `sandbox_run_command` call. `stdout` holds combined output on providers with no stderr. */
@@ -9558,9 +10772,126 @@ function describeSandboxCommandRun(run) {
9558
10772
  return parts.join(" · ");
9559
10773
  }
9560
10774
  //#endregion
10775
+ //#region src/core/threads.ts
10776
+ function newUuid() {
10777
+ const bytes = crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16));
10778
+ bytes[6] = bytes[6] & 15 | 64;
10779
+ bytes[8] = bytes[8] & 63 | 128;
10780
+ const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
10781
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
10782
+ }
10783
+ function threadFromRecord(record, hasMessages = true) {
10784
+ return {
10785
+ id: record.id,
10786
+ title: record.title,
10787
+ agentId: record.agent_id,
10788
+ version: record.version,
10789
+ role: record.role,
10790
+ writerActive: record.writer_active,
10791
+ hasMessages,
10792
+ updatedAt: record.updated_at
10793
+ };
10794
+ }
10795
+ function savedToolCallId(messageId, partId, targetId) {
10796
+ return `saved:${messageId}:${partId}${targetId ? `:${targetId}` : ""}`;
10797
+ }
10798
+ async function loadThreadMessages(id, options, pageAfter, liveToolMessageIds = /* @__PURE__ */ new Map()) {
10799
+ const page = await listChatMessages(id, {
10800
+ page_size: 100,
10801
+ ...pageAfter ? { page_after: pageAfter } : {}
10802
+ }, options);
10803
+ const pendingInteractions = page.pending_interactions.map((pending) => ({
10804
+ sourceMessageId: pending.source_message_id,
10805
+ sourcePartId: pending.source_part_id,
10806
+ responseTargetId: pending.response_target_id,
10807
+ toolCallId: liveToolMessageIds.get(pending.tool_call_id) === pending.source_message_id ? pending.tool_call_id : savedToolCallId(pending.source_message_id, pending.source_part_id, pending.response_target_id),
10808
+ targetTenantUserId: pending.target_tenant_user_id,
10809
+ targetClientId: pending.target_client_id,
10810
+ executionLocation: pending.execution_location
10811
+ }));
10812
+ return {
10813
+ ...page,
10814
+ pendingInteractions
10815
+ };
10816
+ }
10817
+ function projectThreadMessages(records, pendingInteractions = [], liveToolMessageIds = /* @__PURE__ */ new Map()) {
10818
+ const messages = [];
10819
+ const calls = /* @__PURE__ */ new Map();
10820
+ for (const message of records) {
10821
+ if (message.role === "tool") {
10822
+ for (const part of message.parts) {
10823
+ const call = calls.get(savedToolCallId(message.source_assistant_message_id, message.source_tool_part_id, message.response_target_id));
10824
+ if (!call) continue;
10825
+ call.output = part.output ?? null;
10826
+ if (call.name === "sandbox_publish_artifact") {
10827
+ call.artifactMessageId = message.id;
10828
+ call.artifactPartId = String(part.id);
10829
+ }
10830
+ Object.assign(call, { resultOutcome: part.outcome });
10831
+ call.state = part.outcome === "succeeded" ? "complete" : "error";
10832
+ }
10833
+ continue;
10834
+ }
10835
+ const parts = message.parts.flatMap((part) => {
10836
+ const source = part.source;
10837
+ if (typeof part.type === "string" && [
10838
+ "image",
10839
+ "document",
10840
+ "audio",
10841
+ "video"
10842
+ ].includes(part.type) && source && typeof source === "object" && !Array.isArray(source) && "type" in source && source.type === "attachment") return [{
10843
+ ...part,
10844
+ source: {
10845
+ ...source,
10846
+ type: "url",
10847
+ value: ""
10848
+ },
10849
+ savedAttachmentId: part.id
10850
+ }];
10851
+ if (part.type !== "tool-call") return [part];
10852
+ if (part.executionLocation === "provider") return [part];
10853
+ const toolCallId = String(part.toolCallId);
10854
+ const targets = Array.isArray(part.targets) ? part.targets.flatMap((target) => target && typeof target === "object" && !Array.isArray(target) && "id" in target && typeof target.id === "string" ? [target.id] : []) : pendingInteractions.filter((pending) => pending.sourceMessageId === message.id && pending.sourcePartId === part.id).map((pending) => pending.responseTargetId);
10855
+ return (targets.length ? targets : [void 0]).map((responseTargetId) => {
10856
+ const savedId = savedToolCallId(message.id, String(part.id), responseTargetId);
10857
+ const call = {
10858
+ ...part,
10859
+ id: liveToolMessageIds.get(toolCallId) === message.id ? toolCallId : savedId,
10860
+ upstreamToolCallId: toolCallId,
10861
+ applicationPartId: part.id,
10862
+ sourceMessageId: message.id,
10863
+ responseTargetId
10864
+ };
10865
+ calls.set(savedId, call);
10866
+ return call;
10867
+ });
10868
+ });
10869
+ messages.push({
10870
+ id: message.id,
10871
+ role: message.role,
10872
+ parts,
10873
+ createdAt: new Date(message.created_at),
10874
+ metadata: { astralbeam: {
10875
+ state: message.state,
10876
+ authorTenantUserId: message.author_tenant_user_id
10877
+ } }
10878
+ });
10879
+ }
10880
+ return messages;
10881
+ }
10882
+ /** Cache selection within this tab. Stored IDs are reauthorized before loading history. */
10883
+ function selectedThread(key, id) {
10884
+ try {
10885
+ if (id === void 0) return globalThis.sessionStorage?.getItem(key) ?? void 0;
10886
+ if (id === null) globalThis.sessionStorage?.removeItem(key);
10887
+ else globalThis.sessionStorage?.setItem(key, id);
10888
+ } catch {}
10889
+ }
10890
+ //#endregion
9561
10891
  //#region src/core/session.ts
9562
10892
  const CORE_OPTION_KEYS = Object.keys({
9563
10893
  agentId: true,
10894
+ threadId: true,
9564
10895
  apiUrl: true,
9565
10896
  fetchAstralBeamToken: true,
9566
10897
  tools: true,
@@ -9589,10 +10920,19 @@ function createAstralBeamChat(options, deferStart = false) {
9589
10920
  capabilities: { attachments: true },
9590
10921
  agentTools: [],
9591
10922
  sandboxStatus: void 0,
10923
+ usageLimitReached: void 0,
9592
10924
  sandbox: {
9593
10925
  files: [],
9594
10926
  commands: []
9595
- }
10927
+ },
10928
+ thread: void 0,
10929
+ threadLoading: false,
10930
+ threadLoadFailed: false,
10931
+ messagesCursor: void 0,
10932
+ olderMessagesLoading: false,
10933
+ unsentMessage: void 0,
10934
+ activeToolCallIds: [],
10935
+ pendingInteractions: []
9596
10936
  };
9597
10937
  const update = (next) => {
9598
10938
  state = {
@@ -9610,24 +10950,99 @@ function createAstralBeamChat(options, deferStart = false) {
9610
10950
  let unsubscribeAuthentication;
9611
10951
  let identity;
9612
10952
  let started = false;
10953
+ let selectionGeneration = 0;
10954
+ let navigationGeneration = 0;
10955
+ let historyGeneration = 0;
10956
+ let hydration = Promise.resolve();
10957
+ let requestController = new AbortController();
10958
+ let threadKey;
10959
+ let startWithNewThread = false;
10960
+ let threadRecords = [];
10961
+ const clientId = newUuid();
10962
+ let pendingSend;
10963
+ let sending = false;
10964
+ const pendingSends = /* @__PURE__ */ new Map();
10965
+ const acceptPendingSend = () => {
10966
+ if (!pendingSend || pendingSend.accepted) return;
10967
+ pendingSend.accepted = true;
10968
+ if (state.thread) update({ thread: {
10969
+ ...state.thread,
10970
+ hasMessages: true
10971
+ } });
10972
+ pendingSend.callbacks?.onAccepted?.();
10973
+ };
10974
+ const replaceAcceptedMessage = (record) => {
10975
+ const messages = [...client.getMessages()];
10976
+ const index = messages.findLastIndex((message) => message.role === "user");
10977
+ if (index !== -1) {
10978
+ messages[index] = projectThreadMessages([record])[0];
10979
+ client.setMessagesManually(messages);
10980
+ }
10981
+ };
10982
+ const liveToolCalls = /* @__PURE__ */ new Set();
10983
+ const liveToolMessageIds = /* @__PURE__ */ new Map();
10984
+ const historicalToolIds = /* @__PURE__ */ new Map();
10985
+ let toolResults = /* @__PURE__ */ new Map();
10986
+ const threadToolResults = /* @__PURE__ */ new Map();
10987
+ const toolResultKey = (id) => {
10988
+ const messageId = liveToolMessageIds.get(id);
10989
+ return messageId ? `live:${messageId}:${id}` : id;
10990
+ };
10991
+ const retainToolResults = (history) => {
10992
+ const retained = /* @__PURE__ */ new Map();
10993
+ for (const pending of history.pending_interactions) {
10994
+ const nativeKey = `live:${pending.source_message_id}:${pending.tool_call_id}`;
10995
+ const savedKey = savedToolCallId(pending.source_message_id, pending.source_part_id, pending.response_target_id);
10996
+ const result = toolResults.get(nativeKey) ?? toolResults.get(savedKey);
10997
+ if (!result) continue;
10998
+ retained.set(savedKey, result);
10999
+ if (liveToolMessageIds.get(pending.tool_call_id) === pending.source_message_id) retained.set(nativeKey, result);
11000
+ }
11001
+ toolResults.clear();
11002
+ for (const [id, result] of retained) toolResults.set(id, result);
11003
+ };
11004
+ const reportError = (error) => {
11005
+ if (error instanceof Error && error.name === "AbortError") return;
11006
+ const failure = error instanceof Error ? error : new Error(String(error));
11007
+ update({
11008
+ error: failure,
11009
+ status: "error"
11010
+ });
11011
+ if (state.auth.status !== "error" || state.auth.error !== failure) live.streamCallbacks?.onError?.(failure);
11012
+ };
11013
+ const requestOptions = async (conversation = true) => {
11014
+ const generation = selectionGeneration;
11015
+ const authSignal = authentication.session.abortController.signal;
11016
+ const token = await getValidChatAuthToken(authentication);
11017
+ authSignal.throwIfAborted();
11018
+ if (conversation && generation !== selectionGeneration) throw new DOMException("Conversation changed", "AbortError");
11019
+ return {
11020
+ apiUrl: authentication.apiUrl,
11021
+ astralBeamToken: token,
11022
+ cache: "no-store",
11023
+ signal: conversation ? AbortSignal.any([requestController.signal, authSignal]) : authSignal,
11024
+ fetchClient: (input, init) => fetchAuthenticatedChat({
11025
+ ...authentication,
11026
+ input,
11027
+ init
11028
+ })
11029
+ };
11030
+ };
9613
11031
  let capabilitiesGeneration = 0;
9614
11032
  let capabilityIdentity;
9615
11033
  const resolveCapabilities = async () => {
9616
11034
  const generation = ++capabilitiesGeneration;
11035
+ const selection = selectionGeneration;
11036
+ if (state.thread?.agentId === null) {
11037
+ update({ capabilities: { attachments: false } });
11038
+ return;
11039
+ }
11040
+ const agentId = state.thread ? state.thread.agentId : live.agentId;
9617
11041
  try {
9618
- const token = await getValidChatAuthToken(authentication);
9619
- if (generation !== capabilitiesGeneration) return;
9620
- const body = await getChatConfig(live.agentId ? { agentId: live.agentId } : {}, {
9621
- apiUrl: authentication.apiUrl,
9622
- astralBeamToken: token,
9623
- signal: authentication.session.abortController.signal,
9624
- fetchClient: (input, init) => fetchAuthenticatedChat({
9625
- ...authentication,
9626
- input,
9627
- init
9628
- })
9629
- });
9630
- if (generation !== capabilitiesGeneration) return;
11042
+ const auth = await requestOptions();
11043
+ if (generation !== capabilitiesGeneration || selection !== selectionGeneration) return;
11044
+ const body = await getChatConfig(agentId ? { agentId } : {}, auth);
11045
+ if (generation !== capabilitiesGeneration || selection !== selectionGeneration) return;
9631
11046
  const attachments = body.capabilities?.attachments !== false;
9632
11047
  update({ capabilities: { attachments } });
9633
11048
  debug?.("mount", "agent capabilities resolved", { attachments });
@@ -9636,23 +11051,38 @@ function createAstralBeamChat(options, deferStart = false) {
9636
11051
  }
9637
11052
  };
9638
11053
  const renderCleanups = /* @__PURE__ */ new Map();
11054
+ const restoredWidgets = /* @__PURE__ */ new Set();
11055
+ const restoringWidgets = /* @__PURE__ */ new Set();
9639
11056
  const disposeRenders = () => {
9640
11057
  for (const cleanup of renderCleanups.values()) cleanup();
9641
11058
  renderCleanups.clear();
11059
+ restoredWidgets.clear();
11060
+ restoringWidgets.clear();
9642
11061
  };
9643
11062
  const renderWidget = async (input, toolCallId) => {
11063
+ const generation = selectionGeneration;
9644
11064
  const widgets = live.widgets ?? {};
9645
11065
  if (!Object.hasOwn(widgets, input.widget)) throw new Error(`Unknown widget "${input.widget}"`);
9646
11066
  const declaration = widgets[input.widget];
9647
11067
  const validated = await validateParameters(declaration?.parameters, input.props ?? {});
11068
+ if (generation !== selectionGeneration || state.thread?.role === "viewer") return {
11069
+ widget: input.widget,
11070
+ rendered: false
11071
+ };
11072
+ if (declaration !== live.widgets?.[input.widget]) return renderWidget(input, toolCallId);
9648
11073
  if (validated == null) throw new Error(`Props for widget "${input.widget}" failed validation`);
11074
+ const onRenderWidget = live.onRenderWidget;
11075
+ if (!onRenderWidget) return {
11076
+ widget: input.widget,
11077
+ rendered: false
11078
+ };
9649
11079
  renderCleanups.get(toolCallId)?.();
9650
11080
  renderCleanups.delete(toolCallId);
9651
11081
  let registered;
9652
11082
  const release = () => {
9653
11083
  if (renderCleanups.get(toolCallId) === registered) renderCleanups.delete(toolCallId);
9654
11084
  };
9655
- const cleanup = live.onRenderWidget?.({
11085
+ const cleanup = onRenderWidget({
9656
11086
  widget: input.widget,
9657
11087
  props: validated,
9658
11088
  toolCallId,
@@ -9662,12 +11092,68 @@ function createAstralBeamChat(options, deferStart = false) {
9662
11092
  registered = cleanup;
9663
11093
  renderCleanups.set(toolCallId, cleanup);
9664
11094
  }
11095
+ restoredWidgets.add(toolCallId);
9665
11096
  return {
9666
11097
  widget: input.widget,
9667
- rendered: live.onRenderWidget !== void 0
11098
+ rendered: true
9668
11099
  };
9669
11100
  };
9670
- const buildTools = () => buildAgentTools(live.widgets ?? {}, live.tools ?? {}, renderWidget, debug);
11101
+ const restoreCompletedWidgets = (messages) => {
11102
+ if (state.thread?.role === "viewer") return;
11103
+ const renderIds = /* @__PURE__ */ new Map();
11104
+ for (const message of state.messages) for (const part of message.parts) {
11105
+ if (part.type !== "tool-call" || part.name !== "render_widget") continue;
11106
+ const stored = part;
11107
+ if (!stored.widgetRenderId) continue;
11108
+ renderIds.set(savedToolCallId(message.id, part.id), stored.widgetRenderId);
11109
+ if (stored.applicationPartId) renderIds.set(savedToolCallId(message.id, stored.applicationPartId, stored.responseTargetId), stored.widgetRenderId);
11110
+ }
11111
+ for (const message of messages) for (const part of message.parts) {
11112
+ if (part.type !== "tool-call" || part.name !== "render_widget" || part.state !== "complete" || !part.input) continue;
11113
+ const stored = part;
11114
+ const upstream = stored.upstreamToolCallId;
11115
+ const renderId = stored.widgetRenderId ?? renderIds.get(savedToolCallId(message.id, part.id)) ?? (stored.applicationPartId ? renderIds.get(savedToolCallId(message.id, stored.applicationPartId, stored.responseTargetId)) : void 0) ?? (upstream && liveToolMessageIds.get(upstream) === message.id && restoredWidgets.has(upstream) ? upstream : part.id);
11116
+ stored.widgetRenderId = renderId;
11117
+ if (restoredWidgets.has(renderId) || restoringWidgets.has(renderId)) continue;
11118
+ restoringWidgets.add(renderId);
11119
+ const generation = selectionGeneration;
11120
+ const { widgets, onRenderWidget } = live;
11121
+ renderWidget(part.input, renderId).catch((error) => debug?.("error", "Saved widget could not be rendered", error)).finally(() => {
11122
+ if (generation !== selectionGeneration) return;
11123
+ restoringWidgets.delete(renderId);
11124
+ if (widgets !== live.widgets || onRenderWidget !== live.onRenderWidget) restoreCompletedWidgets(state.messages);
11125
+ });
11126
+ }
11127
+ };
11128
+ const buildTools = () => buildAgentTools(live.widgets ?? {}, live.tools ?? {}, renderWidget, debug).map((tool) => {
11129
+ if (!tool.execute) return tool;
11130
+ const execute = tool.execute;
11131
+ return {
11132
+ ...tool,
11133
+ execute: async (input, context) => {
11134
+ const toolCallId = context?.toolCallId;
11135
+ if (!toolCallId || !liveToolCalls.has(toolCallId)) throw new Error("A saved tool call cannot execute again. Send a new message to request it.");
11136
+ const results = toolResults;
11137
+ const key = toolResultKey(toolCallId);
11138
+ const resultIdentity = identity;
11139
+ const apiUrl = authentication.apiUrl;
11140
+ try {
11141
+ const output = await execute(input, context);
11142
+ if (identity === resultIdentity && authentication.apiUrl === apiUrl) results.set(key, {
11143
+ outcome: "succeeded",
11144
+ output: output ?? null
11145
+ });
11146
+ return output;
11147
+ } catch (error) {
11148
+ if (identity === resultIdentity && authentication.apiUrl === apiUrl) results.set(key, {
11149
+ outcome: "unknown",
11150
+ output: { error: error instanceof Error ? error.message : String(error) }
11151
+ });
11152
+ throw error;
11153
+ }
11154
+ }
11155
+ };
11156
+ });
9671
11157
  const declareTools = () => {
9672
11158
  const tools = buildTools();
9673
11159
  const agentTools = tools.map((tool) => {
@@ -9680,40 +11166,222 @@ function createAstralBeamChat(options, deferStart = false) {
9680
11166
  if (!sameAgentTools(state.agentTools, agentTools)) update({ agentTools });
9681
11167
  return tools;
9682
11168
  };
9683
- const forwardedProps = () => ({
9684
- ...live.agentId ? { agentId: live.agentId } : {},
9685
- ...live.debug ? { debug: true } : {}
9686
- });
11169
+ const forwardedProps = () => live.debug ? { debug: true } : {};
11170
+ const threadRequest = async (init, auth) => {
11171
+ const thread = state.thread;
11172
+ if (!thread || typeof init.body !== "string") throw new Error("Open a conversation before sending.");
11173
+ const generation = selectionGeneration;
11174
+ const body = JSON.parse(init.body);
11175
+ let path = getRunChatUrl();
11176
+ let payload;
11177
+ const submission = body.messages.at(-1)?.role === "user" ? pendingSend : void 0;
11178
+ const previouslyAttempted = submission?.tools !== void 0;
11179
+ if (submission) payload = {
11180
+ ...body,
11181
+ threadId: thread.id,
11182
+ resume: void 0,
11183
+ parentRunId: void 0,
11184
+ messages: [body.messages.at(-1)],
11185
+ tools: submission.tools ??= body.tools,
11186
+ forwardedProps: {
11187
+ ...body.forwardedProps,
11188
+ clientId
11189
+ }
11190
+ };
11191
+ else {
11192
+ scopeCompletedToolCalls();
11193
+ const history = await loadThreadMessages(thread.id, auth, void 0, liveToolMessageIds);
11194
+ if (generation !== selectionGeneration) throw new DOMException("Conversation changed", "AbortError");
11195
+ update({ thread: threadFromRecord(history.thread, history.messages.length > 0) });
11196
+ retainToolResults(history);
11197
+ const available = history.pendingInteractions.filter((pending) => toolResults.has(toolResultKey(pending.toolCallId)));
11198
+ const source = available.find((pending) => pending.toolCallId === body.messages.at(-1)?.toolCallId) ?? available[0];
11199
+ const results = history.pendingInteractions.flatMap((pending) => {
11200
+ const submitted = toolResults.get(toolResultKey(pending.toolCallId));
11201
+ return submitted && pending.sourceMessageId === source?.sourceMessageId ? [{
11202
+ source_message_id: pending.sourceMessageId,
11203
+ source_part_id: pending.sourcePartId,
11204
+ response_target_id: pending.responseTargetId,
11205
+ ...submitted
11206
+ }] : [];
11207
+ });
11208
+ if (results.length === 0) return new Response("", { headers: { "Content-Type": "text/event-stream" } });
11209
+ path = getResolveChatToolResultUrl(thread.id);
11210
+ payload = {
11211
+ client_id: clientId,
11212
+ run_id: body.runId,
11213
+ ...body.parentRunId ? { parent_run_id: body.parentRunId } : {},
11214
+ results
11215
+ };
11216
+ }
11217
+ liveToolCalls.clear();
11218
+ update({ activeToolCallIds: [] });
11219
+ const headers = new Headers(init.headers);
11220
+ if (submission) headers.set("Idempotency-Key", submission.key);
11221
+ const response = await astralBeamChatFetch(path, {
11222
+ ...auth,
11223
+ ...init,
11224
+ headers,
11225
+ body: JSON.stringify(payload)
11226
+ }).catch((error) => {
11227
+ const limit = isAstralBeamApiError(error) ? error.body?.usage_limit : void 0;
11228
+ if (limit) update({ usageLimitReached: {
11229
+ name: limit.name,
11230
+ resetsAt: limit.resets_at
11231
+ } });
11232
+ if (submission && !previouslyAttempted && isAstralBeamApiError(error) && (error.status === 400 || error.status === 413 || error.status === 429)) delete submission.tools;
11233
+ throw error;
11234
+ });
11235
+ if (generation !== selectionGeneration) throw new DOMException("Conversation changed", "AbortError");
11236
+ if (response.headers.get("Content-Type")?.includes("application/json")) {
11237
+ const receipt = await response.json();
11238
+ if (generation !== selectionGeneration) throw new DOMException("Conversation changed", "AbortError");
11239
+ update({
11240
+ thread: {
11241
+ ...state.thread,
11242
+ version: receipt.thread_version
11243
+ },
11244
+ ...receipt.usage_limit ? { usageLimitReached: {
11245
+ name: receipt.usage_limit.name,
11246
+ resetsAt: receipt.usage_limit.resets_at
11247
+ } } : {}
11248
+ });
11249
+ if (submission) {
11250
+ const history = await loadThreadMessages(thread.id, auth, void 0, liveToolMessageIds);
11251
+ if (generation !== selectionGeneration) throw new DOMException("Conversation changed", "AbortError");
11252
+ const accepted = history.messages.find((message) => message.id === receipt.accepted_message_id);
11253
+ if (accepted) replaceAcceptedMessage(accepted);
11254
+ acceptPendingSend();
11255
+ }
11256
+ return new Response("", { headers: { "Content-Type": "text/event-stream" } });
11257
+ }
11258
+ return response;
11259
+ };
9687
11260
  const connection = fetchServerSentEvents(() => resolveApiUrl(getRunChatUrl(), authentication.apiUrl), async () => {
9688
11261
  const previousIdentity = identity;
9689
11262
  const token = await getValidChatAuthToken(authentication);
9690
11263
  if (previousIdentity !== void 0 && previousIdentity !== identity) throw new DOMException("Identity changed", "AbortError");
9691
- return { fetchClient: (_input, init) => astralBeamChatFetch(getRunChatUrl(), {
9692
- ...init,
9693
- apiUrl: authentication.apiUrl,
9694
- astralBeamToken: token,
9695
- fetchClient: (input, request) => fetchAuthenticatedChat({
9696
- ...authentication,
9697
- input,
9698
- init: request
9699
- })
9700
- }) };
11264
+ return { fetchClient: (_input, init) => {
11265
+ const auth = {
11266
+ apiUrl: authentication.apiUrl,
11267
+ astralBeamToken: token,
11268
+ ...init?.signal ? { signal: init.signal } : {},
11269
+ cache: "no-store",
11270
+ fetchClient: (input, request) => fetchAuthenticatedChat({
11271
+ ...authentication,
11272
+ input,
11273
+ init: request
11274
+ })
11275
+ };
11276
+ return threadRequest(init ?? {}, auth);
11277
+ } };
9701
11278
  });
9702
11279
  const connect = connection.connect;
9703
11280
  connection.connect = async function* (...args) {
9704
11281
  try {
9705
- yield* connect(...args);
11282
+ for await (const chunk of connect(...args)) {
11283
+ if (chunk.type === EventType.TOOL_CALL_START && chunk.parentMessageId) liveToolMessageIds.set(chunk.toolCallId, chunk.parentMessageId);
11284
+ if (chunk.type !== EventType.MESSAGES_SNAPSHOT) {
11285
+ yield chunk;
11286
+ continue;
11287
+ }
11288
+ let aliases;
11289
+ yield {
11290
+ ...chunk,
11291
+ messages: chunk.messages.map((message) => {
11292
+ if (message.role === "assistant") {
11293
+ aliases = historicalToolIds.get(message.id);
11294
+ return aliases && message.toolCalls ? {
11295
+ ...message,
11296
+ toolCalls: message.toolCalls.map((call) => ({
11297
+ ...call,
11298
+ id: aliases?.get(call.id) ?? call.id
11299
+ }))
11300
+ } : message;
11301
+ }
11302
+ if (message.role === "tool") return {
11303
+ ...message,
11304
+ toolCallId: aliases?.get(message.toolCallId) ?? message.toolCallId
11305
+ };
11306
+ aliases = void 0;
11307
+ return message;
11308
+ })
11309
+ };
11310
+ }
9706
11311
  } catch (error) {
9707
11312
  throw error instanceof Error && isAstralBeamApiError(error.cause) ? error.cause : error;
9708
11313
  }
9709
11314
  };
9710
- const createClient = () => {
11315
+ const createClient = (threadId = state.thread?.id) => {
11316
+ const generation = selectionGeneration;
11317
+ const adapter = { ...connection };
11318
+ delete adapter.hydrate;
11319
+ if (threadId) adapter.hydrate = (_id, options) => {
11320
+ const readGeneration = ++historyGeneration;
11321
+ if (!options?.before) update({ threadLoading: true });
11322
+ const read = (async () => {
11323
+ let history = await loadThreadMessages(threadId, await requestOptions(), options?.before, liveToolMessageIds);
11324
+ while (options?.before && history.messages.every((message) => message.role === "tool") && history.page_after) {
11325
+ const earlier = await loadThreadMessages(threadId, await requestOptions(), history.page_after, liveToolMessageIds);
11326
+ history = {
11327
+ ...earlier,
11328
+ messages: [...earlier.messages, ...history.messages]
11329
+ };
11330
+ }
11331
+ if (generation !== selectionGeneration || readGeneration !== historyGeneration) throw new DOMException("Conversation changed", "AbortError");
11332
+ retainToolResults(history);
11333
+ threadRecords = options?.before ? [...history.messages, ...threadRecords] : history.messages;
11334
+ const pageIds = new Set(history.messages.map((message) => message.id));
11335
+ const messages = projectThreadMessages(threadRecords, history.pendingInteractions, liveToolMessageIds).filter((message) => pageIds.has(message.id));
11336
+ if (history.thread.role === "viewer") disposeRenders();
11337
+ const agentChanged = state.thread?.agentId !== history.thread.agent_id;
11338
+ update({
11339
+ thread: threadFromRecord(history.thread, history.messages.length > 0),
11340
+ messagesCursor: history.page_after ?? void 0,
11341
+ pendingInteractions: history.pendingInteractions,
11342
+ threadLoadFailed: false
11343
+ });
11344
+ restoreCompletedWidgets(messages);
11345
+ if (!options?.before && agentChanged) resolveCapabilities();
11346
+ if (!options?.before && messages.length === 0) next.setMessagesManually([]);
11347
+ return {
11348
+ messages,
11349
+ activeRun: null,
11350
+ interrupts: null,
11351
+ page: history.page_after ? {
11352
+ truncated: true,
11353
+ cursor: history.page_after
11354
+ } : { truncated: false }
11355
+ };
11356
+ })();
11357
+ if (!options?.before) {
11358
+ hydration = read;
11359
+ const settled = (failed) => queueMicrotask(() => {
11360
+ if (generation === selectionGeneration && readGeneration === historyGeneration) update({
11361
+ threadLoading: false,
11362
+ threadLoadFailed: failed
11363
+ });
11364
+ });
11365
+ read.then(() => settled(false), () => settled(true));
11366
+ }
11367
+ return read;
11368
+ };
9711
11369
  const next = new ChatClient({
9712
- connection,
11370
+ connection: adapter,
11371
+ threadId: threadId ?? clientId,
11372
+ persistence: true,
11373
+ history: { pageSize: 100 },
11374
+ queue: "drop",
9713
11375
  tools: declareTools(),
9714
11376
  forwardedProps: forwardedProps(),
9715
11377
  onMessagesChange: (messages) => {
9716
- if (client !== next) return;
11378
+ if (client !== next || generation !== selectionGeneration) return;
11379
+ restoreCompletedWidgets(messages);
11380
+ for (const message of messages) {
11381
+ if (message.role !== "user") continue;
11382
+ const saved = state.messages.find((previous) => previous.id === message.id);
11383
+ if (saved?.parts.some((part) => "savedAttachmentId" in part)) message.parts = saved.parts;
11384
+ }
9717
11385
  update({
9718
11386
  messages,
9719
11387
  sandbox: collectSandboxActivity(messages),
@@ -9721,13 +11389,45 @@ function createAstralBeamChat(options, deferStart = false) {
9721
11389
  });
9722
11390
  },
9723
11391
  onStatusChange: (status) => {
9724
- if (client === next) update({
11392
+ if (client === next && generation === selectionGeneration) update({
9725
11393
  status,
9726
- error: next.getError()
11394
+ error: next.getError(),
11395
+ ...status === "submitted" ? { usageLimitReached: void 0 } : {}
9727
11396
  });
9728
11397
  },
11398
+ onInterruptStateChange: (interrupts) => {
11399
+ if (client === next) debug?.("tool", "interrupt state changed", interrupts);
11400
+ },
9729
11401
  onCustomEvent: (eventType, data) => {
9730
11402
  if (client !== next) return;
11403
+ if (eventType === "astralbeam_thread") {
11404
+ const event = data;
11405
+ if (state.thread?.id === event.threadId) {
11406
+ if (event.acceptedMessage && pendingSend && !pendingSend.accepted) replaceAcceptedMessage(event.acceptedMessage);
11407
+ if (event.acceptedMessageId) acceptPendingSend();
11408
+ if (event.saved) for (const id of event.executableToolCallIds ?? []) {
11409
+ if (!liveToolCalls.has(id)) toolResults.delete(toolResultKey(id));
11410
+ liveToolCalls.add(id);
11411
+ }
11412
+ update({
11413
+ thread: {
11414
+ ...state.thread,
11415
+ version: event.version
11416
+ },
11417
+ activeToolCallIds: [...liveToolCalls],
11418
+ ...event.acceptedMessageId ? { unsentMessage: void 0 } : {}
11419
+ });
11420
+ }
11421
+ return;
11422
+ }
11423
+ const limit = data;
11424
+ if (eventType === "astralbeam.usage_limit.reached" && typeof limit?.name === "string" && typeof limit.resetsAt === "string") {
11425
+ update({ usageLimitReached: {
11426
+ name: limit.name,
11427
+ resetsAt: limit.resetsAt
11428
+ } });
11429
+ return;
11430
+ }
9731
11431
  const value = data?.state;
9732
11432
  if (eventType === "astralbeam.sandbox.status" && (value === "starting" || value === "ready" || value === "error")) {
9733
11433
  debug?.("sandbox", `sandbox ${value}`);
@@ -9736,72 +11436,381 @@ function createAstralBeamChat(options, deferStart = false) {
9736
11436
  }
9737
11437
  debug?.("stream", `custom event "${eventType}"`, data);
9738
11438
  },
9739
- onChunk: (chunk) => live.streamCallbacks?.onChunk?.(chunk),
9740
- onResponse: (response) => live.streamCallbacks?.onResponse?.(response),
9741
- onFinish: (message) => live.streamCallbacks?.onFinish?.(message),
9742
- onError: (error) => live.streamCallbacks?.onError?.(error)
11439
+ onChunk: (chunk) => {
11440
+ if (client !== next) return;
11441
+ live.streamCallbacks?.onChunk?.(chunk);
11442
+ },
11443
+ onResponse: (response) => {
11444
+ if (client === next) live.streamCallbacks?.onResponse?.(response);
11445
+ },
11446
+ onFinish: (message) => {
11447
+ if (client === next) live.streamCallbacks?.onFinish?.(message);
11448
+ },
11449
+ onError: (error) => {
11450
+ if (client === next && (state.auth.status !== "error" || state.auth.error !== error)) live.streamCallbacks?.onError?.(error);
11451
+ }
9743
11452
  });
9744
11453
  return next;
9745
11454
  };
9746
11455
  let client = createClient();
9747
- const replaceClient = () => {
11456
+ const replaceClient = (threadId = state.thread?.id) => {
9748
11457
  client.stop();
9749
11458
  const previous = client;
9750
- client = createClient();
11459
+ previous.detach();
11460
+ liveToolCalls.clear();
11461
+ liveToolMessageIds.clear();
11462
+ update({ activeToolCallIds: [] });
11463
+ client = createClient(threadId);
11464
+ if (started) client.attach();
9751
11465
  previous.dispose();
9752
11466
  };
9753
- const settleDanglingToolCalls = () => {
9754
- for (const message of state.messages) for (const part of message.parts) {
9755
- if (part.type !== "tool-call" || isSettledToolCall(part)) continue;
9756
- if (part.name === "ask_questionnaire") {
9757
- debug?.("questionnaire", "skipping pending questionnaire before send", { id: part.id });
9758
- client.addToolResult({
9759
- toolCallId: part.id,
9760
- tool: part.name,
9761
- output: {
9762
- answers: [],
9763
- skipped: true
9764
- }
9765
- });
9766
- } else {
9767
- debug?.("tool", `settling unimplemented tool call "${part.name}" as error`, { id: part.id });
9768
- client.addToolResult({
9769
- toolCallId: part.id,
9770
- tool: part.name,
9771
- output: null,
9772
- state: "output-error",
9773
- errorText: `The application hosting this chat has no implementation for "${part.name}"`
9774
- });
11467
+ const scopeCompletedToolCalls = () => {
11468
+ historicalToolIds.clear();
11469
+ client.setMessagesManually(state.messages.map((message) => {
11470
+ const aliases = /* @__PURE__ */ new Map();
11471
+ const parts = message.parts.map((part) => {
11472
+ if (part.type !== "tool-call" || webPartMetadata(part).providerExecuted) return part;
11473
+ const stored = part;
11474
+ const upstreamToolCallId = stored.upstreamToolCallId ?? part.id;
11475
+ if (liveToolMessageIds.get(upstreamToolCallId) === message.id && !isSettledToolCall(part)) return part;
11476
+ const id = savedToolCallId(message.id, stored.applicationPartId ?? upstreamToolCallId, stored.responseTargetId);
11477
+ aliases.set(upstreamToolCallId, id);
11478
+ return {
11479
+ ...part,
11480
+ upstreamToolCallId,
11481
+ id,
11482
+ ...part.name === "render_widget" && liveToolMessageIds.get(upstreamToolCallId) === message.id ? { widgetRenderId: stored.widgetRenderId ?? part.id } : {}
11483
+ };
11484
+ });
11485
+ if (aliases.size) historicalToolIds.set(message.id, aliases);
11486
+ return {
11487
+ ...message,
11488
+ parts
11489
+ };
11490
+ }));
11491
+ };
11492
+ const changeSelection = (threadId) => {
11493
+ const previousId = state.thread?.id;
11494
+ if (previousId) {
11495
+ if (pendingSend && !pendingSend.accepted) pendingSends.set(previousId, pendingSend);
11496
+ else pendingSends.delete(previousId);
11497
+ threadToolResults.set(previousId, toolResults);
11498
+ }
11499
+ selectionGeneration++;
11500
+ historyGeneration++;
11501
+ sending = false;
11502
+ requestController.abort();
11503
+ requestController = new AbortController();
11504
+ pendingSend = void 0;
11505
+ toolResults = threadToolResults.get(threadId ?? "") ?? /* @__PURE__ */ new Map();
11506
+ historicalToolIds.clear();
11507
+ threadRecords = [];
11508
+ disposeRenders();
11509
+ update({
11510
+ thread: void 0,
11511
+ messages: [],
11512
+ unsentMessage: void 0,
11513
+ error: void 0,
11514
+ status: "ready",
11515
+ sandbox: {
11516
+ files: [],
11517
+ commands: []
11518
+ },
11519
+ sandboxStatus: void 0,
11520
+ usageLimitReached: void 0,
11521
+ pendingInteractions: [],
11522
+ threadLoadFailed: false,
11523
+ messagesCursor: void 0,
11524
+ olderMessagesLoading: false
11525
+ });
11526
+ replaceClient(threadId);
11527
+ };
11528
+ const openThread = async (id, restoring = false) => {
11529
+ if (!restoring) navigationGeneration++;
11530
+ changeSelection(id);
11531
+ const generation = selectionGeneration;
11532
+ update({ threadLoading: true });
11533
+ try {
11534
+ pendingSend = pendingSends.get(id);
11535
+ update({ unsentMessage: pendingSend?.content });
11536
+ if (!started) client.attach();
11537
+ await hydration;
11538
+ if (generation !== selectionGeneration) return;
11539
+ if (threadKey) selectedThread(threadKey, id);
11540
+ } catch (error) {
11541
+ if (generation === selectionGeneration) {
11542
+ if (restoring && (live.threadId === void 0 || live.threadId === "auto") && isAstralBeamApiError(error) && error.status === 404) {
11543
+ newThread();
11544
+ return;
11545
+ }
11546
+ update({ threadLoadFailed: true });
11547
+ reportError(error);
11548
+ }
11549
+ } finally {
11550
+ if (generation === selectionGeneration) update({ threadLoading: false });
11551
+ }
11552
+ };
11553
+ const newThread = () => {
11554
+ navigationGeneration++;
11555
+ changeSelection();
11556
+ update({ threadLoading: false });
11557
+ if (started) resolveCapabilities();
11558
+ if (threadKey) selectedThread(threadKey, null);
11559
+ else startWithNewThread = true;
11560
+ };
11561
+ const selectConfiguredThread = (restoring = false) => {
11562
+ let selected = live.threadId;
11563
+ if (selected === void 0 || selected === "auto") {
11564
+ if (!threadKey) return;
11565
+ selected = selectedThread(threadKey);
11566
+ }
11567
+ if (selected && selected !== "new") openThread(selected, restoring);
11568
+ else if (!restoring) newThread();
11569
+ };
11570
+ const ensureThread = async () => {
11571
+ if (state.thread) return;
11572
+ const generation = selectionGeneration;
11573
+ const options = await requestOptions();
11574
+ const record = await createChatThread(live.agentId ? { agent_id: live.agentId } : {}, options);
11575
+ if (generation !== selectionGeneration) throw new DOMException("Conversation changed", "AbortError");
11576
+ const thread = threadFromRecord(record, false);
11577
+ update({ thread });
11578
+ replaceClient();
11579
+ await hydration;
11580
+ resolveCapabilities();
11581
+ if (threadKey) selectedThread(threadKey, thread.id);
11582
+ };
11583
+ const refreshThread = async (preserveError = false, resetClient = true) => {
11584
+ if (!state.thread?.id) return;
11585
+ const generation = selectionGeneration;
11586
+ const previousError = state.error;
11587
+ update({
11588
+ threadLoading: true,
11589
+ olderMessagesLoading: false
11590
+ });
11591
+ try {
11592
+ if (resetClient) replaceClient();
11593
+ else {
11594
+ client.detach();
11595
+ client.attach();
11596
+ }
11597
+ await hydration;
11598
+ if (generation !== selectionGeneration) return;
11599
+ if (preserveError && previousError) update({
11600
+ error: previousError,
11601
+ status: "error"
11602
+ });
11603
+ } catch (error) {
11604
+ if (generation === selectionGeneration) {
11605
+ if (isAstralBeamApiError(error) && error.status === 404) {
11606
+ newThread();
11607
+ return;
11608
+ }
11609
+ reportError(error);
9775
11610
  }
11611
+ } finally {
11612
+ if (generation === selectionGeneration) update({ threadLoading: false });
11613
+ }
11614
+ };
11615
+ const loadOlderMessages = async () => {
11616
+ if (!client.getHasOlderMessages() || state.olderMessagesLoading || state.threadLoading || sending) return;
11617
+ const generation = selectionGeneration;
11618
+ update({ olderMessagesLoading: true });
11619
+ try {
11620
+ await client.loadOlderMessages();
11621
+ } catch (error) {
11622
+ if (generation === selectionGeneration) reportError(error);
11623
+ } finally {
11624
+ if (generation === selectionGeneration) update({ olderMessagesLoading: false });
11625
+ }
11626
+ };
11627
+ const renameThread = async (title) => {
11628
+ const thread = state.thread;
11629
+ if (!thread || thread.role !== "manager") return false;
11630
+ const generation = selectionGeneration;
11631
+ try {
11632
+ const record = await updateChatThread(thread.id, {
11633
+ title,
11634
+ expected_version: thread.version
11635
+ }, await requestOptions());
11636
+ if (generation !== selectionGeneration) return false;
11637
+ const renamed = threadFromRecord(record, thread.hasMessages);
11638
+ update({
11639
+ thread: renamed,
11640
+ error: client.getError(),
11641
+ status: client.getStatus()
11642
+ });
11643
+ return true;
11644
+ } catch (error) {
11645
+ if (generation === selectionGeneration) reportError(error);
11646
+ return false;
11647
+ }
11648
+ };
11649
+ const deleteThread = async (thread = state.thread) => {
11650
+ if (!thread || thread.role !== "manager") return false;
11651
+ const generation = selectionGeneration;
11652
+ try {
11653
+ await deleteChatThread(thread.id, { expected_version: String(thread.version) }, await requestOptions());
11654
+ if (generation === selectionGeneration && state.thread?.id === thread.id) newThread();
11655
+ return true;
11656
+ } catch (error) {
11657
+ if (generation === selectionGeneration) reportError(error);
11658
+ return false;
11659
+ }
11660
+ };
11661
+ const addToolResult = async (result) => {
11662
+ const generation = selectionGeneration;
11663
+ if (sending || client.getIsLoading()) {
11664
+ reportError(/* @__PURE__ */ new Error("Wait for this client's current request to finish before responding."));
11665
+ return;
11666
+ }
11667
+ const current = authenticationState(authentication);
11668
+ const pending = state.pendingInteractions.find((item) => item.toolCallId === result.toolCallId);
11669
+ const closure = !("tool" in result) && pending && (state.thread?.role === "manager" || current.status === "ready" && pending.targetTenantUserId === current.currentUser.user.id);
11670
+ const questionnaire = "tool" in result && result.tool === "ask_questionnaire" && current.status === "ready" && pending?.targetTenantUserId === current.currentUser.user.id;
11671
+ if (state.thread?.role === "viewer" || !liveToolCalls.has(result.toolCallId) && !questionnaire && !closure && !toolResults.has(toolResultKey(result.toolCallId))) {
11672
+ reportError(/* @__PURE__ */ new Error("This tool call belongs to another session and cannot be executed here."));
11673
+ return;
11674
+ }
11675
+ if (closure && !toolResults.has(toolResultKey(result.toolCallId))) toolResults.set(toolResultKey(result.toolCallId), {
11676
+ outcome: "unknown",
11677
+ output: null
11678
+ });
11679
+ else if (!toolResults.has(toolResultKey(result.toolCallId)) && "tool" in result) {
11680
+ const output = result.output;
11681
+ toolResults.set(toolResultKey(result.toolCallId), {
11682
+ outcome: result.state === "output-error" ? "failed" : "succeeded",
11683
+ output: output ?? null
11684
+ });
11685
+ }
11686
+ scopeCompletedToolCalls();
11687
+ if (pending) {
11688
+ replaceClient();
11689
+ await hydration;
11690
+ if (generation !== selectionGeneration) return;
11691
+ update({ threadLoading: false });
11692
+ await client.append({
11693
+ role: "tool",
11694
+ toolCallId: result.toolCallId,
11695
+ content: JSON.stringify(toolResults.get(toolResultKey(result.toolCallId)))
11696
+ });
11697
+ } else if ("tool" in result) await client.addToolResult(result);
11698
+ if (generation === selectionGeneration) await refreshThread(client.getError() !== void 0, false);
11699
+ };
11700
+ const abandonToolCall = (toolCallId) => addToolResult({ toolCallId });
11701
+ const sendMessage = async (content, callbacks) => {
11702
+ const navigation = navigationGeneration;
11703
+ try {
11704
+ await getValidChatAuthToken(authentication);
11705
+ } catch (error) {
11706
+ if (navigation !== navigationGeneration) return;
11707
+ update({ unsentMessage: content });
11708
+ reportError(error);
11709
+ return;
11710
+ }
11711
+ if (navigation !== navigationGeneration) return;
11712
+ if (sending || state.threadLoading || state.status === "streaming") {
11713
+ reportError(/* @__PURE__ */ new Error("Wait for this client's current request to finish before sending."));
11714
+ return;
11715
+ }
11716
+ if (state.threadLoadFailed) {
11717
+ reportError(/* @__PURE__ */ new Error("Reopen this conversation or start a new one before sending."));
11718
+ return;
11719
+ }
11720
+ const samePendingContent = pendingSend !== void 0 && JSON.stringify(pendingSend.content) === JSON.stringify(content);
11721
+ const uncertainSend = pendingSend?.tools !== void 0 && !pendingSend.accepted ? pendingSend : void 0;
11722
+ const retrying = uncertainSend !== void 0 && samePendingContent;
11723
+ if (state.thread?.agentId === null && !retrying) {
11724
+ reportError(/* @__PURE__ */ new Error("This conversation’s agent is unavailable. Start a new conversation to continue."));
11725
+ return;
11726
+ }
11727
+ if (state.thread?.role === "viewer" && !retrying) {
11728
+ reportError(/* @__PURE__ */ new Error("You have read-only access to this conversation."));
11729
+ return;
11730
+ }
11731
+ if (uncertainSend && !samePendingContent) {
11732
+ update({ unsentMessage: uncertainSend.content });
11733
+ reportError(/* @__PURE__ */ new Error("The previous message’s acceptance is unconfirmed. Retry that message before sending different text, or start a new conversation."));
11734
+ return;
11735
+ }
11736
+ const generation = selectionGeneration;
11737
+ historyGeneration++;
11738
+ update({ olderMessagesLoading: false });
11739
+ sending = true;
11740
+ if (!pendingSend || pendingSend.accepted || !samePendingContent) {
11741
+ pendingSend = {
11742
+ content,
11743
+ accepted: false,
11744
+ key: newUuid(),
11745
+ callbacks
11746
+ };
11747
+ client.stop();
11748
+ liveToolCalls.clear();
11749
+ scopeCompletedToolCalls();
11750
+ liveToolMessageIds.clear();
11751
+ update({ activeToolCallIds: [] });
11752
+ }
11753
+ pendingSend.callbacks ??= callbacks;
11754
+ update({
11755
+ unsentMessage: content,
11756
+ error: void 0,
11757
+ status: "submitted"
11758
+ });
11759
+ try {
11760
+ await ensureThread();
11761
+ if (generation !== selectionGeneration) return;
11762
+ pendingSend.callbacks?.onThreadReady?.(state.thread.id);
11763
+ if (generation !== selectionGeneration) return;
11764
+ scopeCompletedToolCalls();
11765
+ await client.sendMessage(content);
11766
+ if (generation !== selectionGeneration) return;
11767
+ const failed = client.getError() !== void 0;
11768
+ if (pendingSend?.accepted) {
11769
+ pendingSend = void 0;
11770
+ update({ unsentMessage: void 0 });
11771
+ }
11772
+ await refreshThread(failed, false);
11773
+ } catch (error) {
11774
+ if (generation === selectionGeneration) reportError(error);
11775
+ } finally {
11776
+ if (generation === selectionGeneration) sending = false;
9776
11777
  }
9777
11778
  };
9778
11779
  const observeAuthentication = () => {
9779
11780
  const auth = authenticationState(authentication);
11781
+ const previous = state.auth;
9780
11782
  if (auth.status === "ready") {
9781
11783
  const nextIdentity = authenticationIdentity(auth.currentUser);
9782
11784
  if (identity !== void 0 && identity !== nextIdentity) {
9783
- replaceClient();
9784
- disposeRenders();
9785
- update({
9786
- messages: [],
9787
- sandbox: {
9788
- files: [],
9789
- commands: []
9790
- },
9791
- sandboxStatus: void 0
9792
- });
11785
+ navigationGeneration++;
11786
+ pendingSends.clear();
11787
+ threadToolResults.clear();
11788
+ toolResults.clear();
11789
+ pendingSend = void 0;
9793
11790
  }
9794
11791
  identity = nextIdentity;
11792
+ const nextThreadKey = `astralbeam:thread:${live.apiUrl ?? "https://astralbeam.ai/api"}:${nextIdentity}`;
11793
+ if (threadKey !== nextThreadKey) {
11794
+ changeSelection();
11795
+ threadKey = nextThreadKey;
11796
+ if (startWithNewThread) {
11797
+ selectedThread(nextThreadKey, null);
11798
+ startWithNewThread = false;
11799
+ } else selectConfiguredThread(true);
11800
+ }
9795
11801
  if (capabilityIdentity !== nextIdentity) {
9796
11802
  capabilityIdentity = nextIdentity;
9797
11803
  resolveCapabilities();
9798
11804
  }
9799
11805
  }
9800
11806
  update({ auth });
11807
+ if (auth.status === "error" && (previous.status !== "error" || previous.error !== auth.error)) live.streamCallbacks?.onError?.(auth.error);
9801
11808
  };
9802
11809
  const start = () => {
9803
11810
  if (started) return;
9804
11811
  started = true;
11812
+ client.attach();
11813
+ if (requestController.signal.aborted) requestController = new AbortController();
9805
11814
  unsubscribeAuthentication = subscribeAuthentication(authentication, observeAuthentication);
9806
11815
  observeAuthentication();
9807
11816
  stopAuthentication = startAuthentication(authentication);
@@ -9817,6 +11826,8 @@ function createAstralBeamChat(options, deferStart = false) {
9817
11826
  updateOptions: (next) => {
9818
11827
  const agent = live.agentId;
9819
11828
  const apiUrl = live.apiUrl;
11829
+ const selectedId = live.threadId;
11830
+ const { widgets, onRenderWidget } = live;
9820
11831
  live = {
9821
11832
  ...live,
9822
11833
  ...next
@@ -9831,13 +11842,22 @@ function createAstralBeamChat(options, deferStart = false) {
9831
11842
  tools: declareTools(),
9832
11843
  forwardedProps: forwardedProps()
9833
11844
  });
11845
+ if (live.apiUrl !== apiUrl) {
11846
+ navigationGeneration++;
11847
+ pendingSends.clear();
11848
+ threadToolResults.clear();
11849
+ toolResults.clear();
11850
+ pendingSend = void 0;
11851
+ changeSelection();
11852
+ threadKey = void 0;
11853
+ update({ threadLoading: false });
11854
+ observeAuthentication();
11855
+ } else if (live.threadId !== selectedId) selectConfiguredThread();
11856
+ if (widgets !== live.widgets || onRenderWidget !== live.onRenderWidget) restoreCompletedWidgets(state.messages);
9834
11857
  if (started && (live.agentId !== agent || live.apiUrl !== apiUrl)) resolveCapabilities();
9835
11858
  },
9836
- sendMessage: (content) => {
9837
- settleDanglingToolCalls();
9838
- return client.sendMessage(content);
9839
- },
9840
- addToolResult: (result) => client.addToolResult(result),
11859
+ sendMessage,
11860
+ addToolResult,
9841
11861
  stop: () => client.stop(),
9842
11862
  retryAuthentication: () => {
9843
11863
  getValidChatAuthToken({
@@ -9845,25 +11865,78 @@ function createAstralBeamChat(options, deferStart = false) {
9845
11865
  force: true
9846
11866
  }).catch(() => void 0);
9847
11867
  },
9848
- reload: () => client.reload(),
11868
+ reload: async () => {
11869
+ const generation = selectionGeneration;
11870
+ await refreshThread();
11871
+ if (generation !== selectionGeneration || state.error) return;
11872
+ const attempted = /* @__PURE__ */ new Set();
11873
+ while (generation === selectionGeneration) {
11874
+ const pending = state.pendingInteractions.find((item) => !attempted.has(item.sourceMessageId) && toolResults.has(toolResultKey(item.toolCallId)));
11875
+ if (!pending) return;
11876
+ attempted.add(pending.sourceMessageId);
11877
+ await addToolResult({ toolCallId: pending.toolCallId });
11878
+ if (state.error) return;
11879
+ }
11880
+ },
11881
+ searchThreads: async (q = "", cursor, signal) => {
11882
+ const options = await requestOptions();
11883
+ const page = await listChatThreads({
11884
+ q,
11885
+ page_size: 50,
11886
+ ...cursor ? { page_after: cursor } : {}
11887
+ }, {
11888
+ ...options,
11889
+ signal: AbortSignal.any([options.signal, ...signal ? [signal] : []])
11890
+ });
11891
+ return {
11892
+ items: page.items.map((record) => threadFromRecord(record)),
11893
+ page_after: page.page_after
11894
+ };
11895
+ },
11896
+ openThread,
11897
+ renameThread,
11898
+ deleteThread,
11899
+ refreshThread,
11900
+ loadOlderMessages,
11901
+ uploadAttachment: async ({ data, filename, mimeType }) => {
11902
+ const agentId = state.thread ? state.thread.agentId : live.agentId;
11903
+ const [encoded, options] = await Promise.all([data, requestOptions(false)]);
11904
+ return uploadChatFile({
11905
+ ...agentId ? { agentId } : {},
11906
+ type: mimeType.startsWith("image/") ? "image" : "document",
11907
+ source: {
11908
+ type: "data",
11909
+ value: encoded,
11910
+ mimeType
11911
+ },
11912
+ metadata: { filename }
11913
+ }, options);
11914
+ },
11915
+ getFile: async (fileId) => {
11916
+ const thread = state.thread;
11917
+ if (!thread) throw new Error("Open a conversation before downloading files.");
11918
+ return (await getChatFile({
11919
+ threadId: thread.id,
11920
+ fileId
11921
+ }, await requestOptions())).blob();
11922
+ },
11923
+ getAttachment: async (messageId, partId) => {
11924
+ const thread = state.thread;
11925
+ if (!thread) throw new Error("Open a conversation before downloading attachments.");
11926
+ return (await getChatAttachment(thread.id, messageId, partId, await requestOptions())).blob();
11927
+ },
11928
+ abandonToolCall,
9849
11929
  reset: () => {
9850
11930
  debug?.("status", "conversation reset");
9851
- replaceClient();
9852
- disposeRenders();
9853
- update({
9854
- messages: [],
9855
- status: "ready",
9856
- sandbox: {
9857
- files: [],
9858
- commands: []
9859
- },
9860
- sandboxStatus: void 0,
9861
- error: void 0
9862
- });
11931
+ newThread();
9863
11932
  },
9864
11933
  dispose: () => {
11934
+ client.detach();
9865
11935
  disposeRenders();
9866
11936
  client.stop();
11937
+ requestController.abort();
11938
+ selectionGeneration++;
11939
+ threadKey = void 0;
9867
11940
  capabilitiesGeneration++;
9868
11941
  capabilityIdentity = void 0;
9869
11942
  stopAuthentication?.();