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