@tanstack/ai-client 0.18.4 → 0.18.5
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/package.json +3 -3
- package/dist/esm/chat-client.d.ts +0 -264
- package/dist/esm/chat-client.js +0 -1097
- package/dist/esm/chat-client.js.map +0 -1
- package/dist/esm/client-persistor.d.ts +0 -86
- package/dist/esm/client-persistor.js +0 -243
- package/dist/esm/client-persistor.js.map +0 -1
- package/dist/esm/connection-adapters.d.ts +0 -203
- package/dist/esm/connection-adapters.js +0 -611
- package/dist/esm/connection-adapters.js.map +0 -1
- package/dist/esm/devtools-noop.d.ts +0 -63
- package/dist/esm/devtools-noop.js +0 -141
- package/dist/esm/devtools-noop.js.map +0 -1
- package/dist/esm/devtools.d.ts +0 -276
- package/dist/esm/devtools.js +0 -1178
- package/dist/esm/devtools.js.map +0 -1
- package/dist/esm/events.d.ts +0 -129
- package/dist/esm/events.js +0 -227
- package/dist/esm/events.js.map +0 -1
- package/dist/esm/generation-client.d.ts +0 -99
- package/dist/esm/generation-client.js +0 -325
- package/dist/esm/generation-client.js.map +0 -1
- package/dist/esm/generation-types.d.ts +0 -242
- package/dist/esm/generation-types.js +0 -14
- package/dist/esm/generation-types.js.map +0 -1
- package/dist/esm/index.d.ts +0 -16
- package/dist/esm/index.js +0 -44
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/realtime-client.d.ts +0 -98
- package/dist/esm/realtime-client.js +0 -403
- package/dist/esm/realtime-client.js.map +0 -1
- package/dist/esm/realtime-types.d.ts +0 -78
- package/dist/esm/response-stream.d.ts +0 -7
- package/dist/esm/response-stream.js +0 -32
- package/dist/esm/response-stream.js.map +0 -1
- package/dist/esm/sse-parser.d.ts +0 -8
- package/dist/esm/sse-parser.js +0 -52
- package/dist/esm/sse-parser.js.map +0 -1
- package/dist/esm/sse-utils.d.ts +0 -1
- package/dist/esm/sse-utils.js +0 -11
- package/dist/esm/sse-utils.js.map +0 -1
- package/dist/esm/tool-types.d.ts +0 -20
- package/dist/esm/types.d.ts +0 -419
- package/dist/esm/types.js +0 -11
- package/dist/esm/types.js.map +0 -1
- package/dist/esm/video-generation-client.d.ts +0 -110
- package/dist/esm/video-generation-client.js +0 -382
- package/dist/esm/video-generation-client.js.map +0 -1
package/dist/esm/devtools.js
DELETED
|
@@ -1,1178 +0,0 @@
|
|
|
1
|
-
import { emitAIDevtoolsEvent, aiEventClient, createAIDevtoolsEventEnvelope } from "@tanstack/ai-event-client";
|
|
2
|
-
import { convertSchemaToJsonSchema } from "@tanstack/ai/client";
|
|
3
|
-
import { DefaultChatClientEventEmitter } from "./events.js";
|
|
4
|
-
function createAIDevtoolsGenerationPreview(input) {
|
|
5
|
-
if (input.outputKind === "image") {
|
|
6
|
-
return imagePreviewFromResult(input.result);
|
|
7
|
-
}
|
|
8
|
-
if (input.outputKind === "audio") {
|
|
9
|
-
return audioPreviewFromResult(input.result);
|
|
10
|
-
}
|
|
11
|
-
if (input.outputKind === "video") {
|
|
12
|
-
return videoPreviewFromResult(input.result, input.videoStatus);
|
|
13
|
-
}
|
|
14
|
-
if (input.outputKind === "text") {
|
|
15
|
-
return textPreviewFromResult(input.result);
|
|
16
|
-
}
|
|
17
|
-
if (input.result === null || input.result === void 0) {
|
|
18
|
-
return { kind: "empty" };
|
|
19
|
-
}
|
|
20
|
-
return {
|
|
21
|
-
kind: "structured",
|
|
22
|
-
value: input.result
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
function imagePreviewFromResult(result) {
|
|
26
|
-
const record = asRecord(result);
|
|
27
|
-
const images = Array.isArray(record?.images) ? record.images : [];
|
|
28
|
-
const items = images.map((image) => mediaItemFromSource(image, "image/png")).filter(isGenerationMediaItem);
|
|
29
|
-
if (items.length === 0 && result !== null && result !== void 0) {
|
|
30
|
-
const directItem = mediaItemFromSource(result, "image/png");
|
|
31
|
-
if (directItem) {
|
|
32
|
-
items.push(directItem);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return { kind: "image", items };
|
|
36
|
-
}
|
|
37
|
-
function audioPreviewFromResult(result) {
|
|
38
|
-
const record = asRecord(result);
|
|
39
|
-
const audio = record?.audio;
|
|
40
|
-
const resultContentType = stringField(record, "contentType");
|
|
41
|
-
const format = stringField(record, "format");
|
|
42
|
-
const mimeType = resultContentType ?? mimeTypeFromAudioFormat(format);
|
|
43
|
-
const items = [];
|
|
44
|
-
const directItem = typeof audio === "string" ? base64MediaItem(audio, mimeType, {
|
|
45
|
-
format,
|
|
46
|
-
duration: numberField(record, "duration")
|
|
47
|
-
}) : mediaItemFromSource(audio, mimeType, {
|
|
48
|
-
format
|
|
49
|
-
});
|
|
50
|
-
if (directItem) {
|
|
51
|
-
items.push(directItem);
|
|
52
|
-
}
|
|
53
|
-
return { kind: "audio", items };
|
|
54
|
-
}
|
|
55
|
-
function videoPreviewFromResult(result, videoStatus) {
|
|
56
|
-
const resultRecord = asRecord(result);
|
|
57
|
-
const statusRecord = asRecord(videoStatus);
|
|
58
|
-
const item = mediaItemFromSource(result, "video/mp4") ?? mediaItemFromSource(videoStatus, "video/mp4");
|
|
59
|
-
const items = item ? [item] : [];
|
|
60
|
-
const job = videoJobFromStatus(statusRecord ?? resultRecord);
|
|
61
|
-
return {
|
|
62
|
-
kind: "video",
|
|
63
|
-
items,
|
|
64
|
-
...job ? { job } : {}
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
function textPreviewFromResult(result) {
|
|
68
|
-
const record = asRecord(result);
|
|
69
|
-
const text = stringField(record, "text") ?? stringField(record, "summary") ?? stringField(record, "content") ?? (typeof result === "string" ? result : void 0);
|
|
70
|
-
if (text !== void 0) {
|
|
71
|
-
return { kind: "text", text };
|
|
72
|
-
}
|
|
73
|
-
if (result === null || result === void 0) {
|
|
74
|
-
return { kind: "empty" };
|
|
75
|
-
}
|
|
76
|
-
return {
|
|
77
|
-
kind: "structured",
|
|
78
|
-
value: result
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
function videoJobFromStatus(record) {
|
|
82
|
-
const jobId = stringField(record, "jobId");
|
|
83
|
-
if (!jobId) return void 0;
|
|
84
|
-
return {
|
|
85
|
-
jobId,
|
|
86
|
-
...stringField(record, "status") ? { status: stringField(record, "status") } : {},
|
|
87
|
-
...numberField(record, "progress") !== void 0 ? { progress: numberField(record, "progress") } : {},
|
|
88
|
-
...stringField(record, "error") ? { error: stringField(record, "error") } : {}
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
function mediaItemFromSource(value, defaultMimeType, extras = {}) {
|
|
92
|
-
const record = asRecord(value);
|
|
93
|
-
if (!record) return void 0;
|
|
94
|
-
const explicitContentType = stringField(record, "contentType") ?? stringField(record, "mimeType");
|
|
95
|
-
const duration = numberField(record, "duration") ?? extras.duration;
|
|
96
|
-
const format = stringField(record, "format") ?? extras.format;
|
|
97
|
-
const url = stringField(record, "url");
|
|
98
|
-
if (url) {
|
|
99
|
-
return {
|
|
100
|
-
src: url,
|
|
101
|
-
sourceType: "url",
|
|
102
|
-
...explicitContentType ? { mimeType: explicitContentType } : {},
|
|
103
|
-
...format ? { format } : {},
|
|
104
|
-
...duration !== void 0 ? { duration } : {}
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
const b64Json = stringField(record, "b64Json");
|
|
108
|
-
if (!b64Json) return void 0;
|
|
109
|
-
return base64MediaItem(b64Json, explicitContentType ?? defaultMimeType, {
|
|
110
|
-
format,
|
|
111
|
-
duration
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
function base64MediaItem(value, mimeType, extras = {}) {
|
|
115
|
-
const src = value.startsWith("data:") ? value : `data:${mimeType ?? "application/octet-stream"};base64,${value}`;
|
|
116
|
-
return {
|
|
117
|
-
src,
|
|
118
|
-
sourceType: "base64",
|
|
119
|
-
...mimeType ? { mimeType } : {},
|
|
120
|
-
...extras.format ? { format: extras.format } : {},
|
|
121
|
-
...extras.duration !== void 0 ? { duration: extras.duration } : {}
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
function mimeTypeFromAudioFormat(format) {
|
|
125
|
-
if (!format) return "audio/mpeg";
|
|
126
|
-
if (format === "mp3") return "audio/mpeg";
|
|
127
|
-
return `audio/${format}`;
|
|
128
|
-
}
|
|
129
|
-
function asRecord(value) {
|
|
130
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
131
|
-
return void 0;
|
|
132
|
-
}
|
|
133
|
-
return value;
|
|
134
|
-
}
|
|
135
|
-
function stringField(record, field) {
|
|
136
|
-
const value = record?.[field];
|
|
137
|
-
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
138
|
-
}
|
|
139
|
-
function numberField(record, field) {
|
|
140
|
-
const value = record?.[field];
|
|
141
|
-
return typeof value === "number" ? value : void 0;
|
|
142
|
-
}
|
|
143
|
-
function isGenerationMediaItem(value) {
|
|
144
|
-
return Boolean(value);
|
|
145
|
-
}
|
|
146
|
-
const activeBridgeRegistryKey = /* @__PURE__ */ Symbol.for(
|
|
147
|
-
"tanstack.ai.devtools.activeBridgeByHookId"
|
|
148
|
-
);
|
|
149
|
-
function getActiveBridgeRegistry() {
|
|
150
|
-
const global = globalThis;
|
|
151
|
-
const existing = global[activeBridgeRegistryKey];
|
|
152
|
-
if (existing) return existing;
|
|
153
|
-
const registry = /* @__PURE__ */ new Map();
|
|
154
|
-
global[activeBridgeRegistryKey] = registry;
|
|
155
|
-
return registry;
|
|
156
|
-
}
|
|
157
|
-
class ClientDevtoolsBridge {
|
|
158
|
-
options;
|
|
159
|
-
bridgeId;
|
|
160
|
-
unsubscribers = [];
|
|
161
|
-
disposed = false;
|
|
162
|
-
superseded = false;
|
|
163
|
-
registered = false;
|
|
164
|
-
constructor(options) {
|
|
165
|
-
this.options = options;
|
|
166
|
-
this.bridgeId = createBridgeId(options.hookId);
|
|
167
|
-
}
|
|
168
|
-
emitRegistered() {
|
|
169
|
-
if (!this.prepareForMountEmit()) {
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
this.registered = true;
|
|
173
|
-
emitAIDevtoolsEvent("hook:registered", {
|
|
174
|
-
...this.createEnvelope("hook:registered"),
|
|
175
|
-
...this.createMetadataPayload(),
|
|
176
|
-
lifecycle: "mounted"
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
emitUpdated() {
|
|
180
|
-
if (!this.prepareForEmit()) {
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
emitAIDevtoolsEvent("hook:updated", {
|
|
184
|
-
...this.createEnvelope("hook:updated"),
|
|
185
|
-
...this.createMetadataPayload(),
|
|
186
|
-
lifecycle: "active"
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
emitSnapshot() {
|
|
190
|
-
if (!this.prepareForEmit()) {
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
emitAIDevtoolsEvent("hook:state-snapshot", {
|
|
194
|
-
...this.createEnvelope("hook:state-snapshot"),
|
|
195
|
-
...this.createMetadataPayload(),
|
|
196
|
-
// Wire envelope uses Record<string, unknown>; widen the typed snapshot
|
|
197
|
-
// here so the typed-snapshot constraint above can stay narrow.
|
|
198
|
-
// eslint-disable-next-line no-restricted-syntax -- TSnapshot extends object is structurally compatible but TS can't see the missing index signature
|
|
199
|
-
state: this.options.getSnapshot()
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
emitToolsRegistered() {
|
|
203
|
-
if (!this.prepareForEmit()) {
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
const tools = this.options.getTools ? Array.from(this.options.getTools()).map((tool) => ({
|
|
207
|
-
name: tool.name,
|
|
208
|
-
description: tool.description,
|
|
209
|
-
inputSchema: tool.inputSchema ? convertSchemaToJsonSchema(tool.inputSchema) : { type: "object" },
|
|
210
|
-
outputSchema: tool.outputSchema ? convertSchemaToJsonSchema(tool.outputSchema) : void 0,
|
|
211
|
-
needsApproval: tool.needsApproval,
|
|
212
|
-
metadata: tool.metadata
|
|
213
|
-
})) : [];
|
|
214
|
-
emitAIDevtoolsEvent("tools:registered", {
|
|
215
|
-
...this.createEnvelope("tools:registered"),
|
|
216
|
-
...this.createMetadataPayload(),
|
|
217
|
-
tools
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
emitRunLifecycle(eventType, runId, status, options = {}) {
|
|
221
|
-
if (!this.prepareForEmit()) {
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
emitAIDevtoolsEvent(eventType, {
|
|
225
|
-
...this.createEnvelope(eventType, "client-state", { runId }),
|
|
226
|
-
runId,
|
|
227
|
-
status,
|
|
228
|
-
...options.error ? { error: options.error } : {}
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
deactivate() {
|
|
232
|
-
const activeBridgeByHookId = getActiveBridgeRegistry();
|
|
233
|
-
if (activeBridgeByHookId.get(this.options.hookId) === this) {
|
|
234
|
-
activeBridgeByHookId.delete(this.options.hookId);
|
|
235
|
-
}
|
|
236
|
-
for (const unsubscribe of this.unsubscribers.splice(0)) {
|
|
237
|
-
unsubscribe();
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
supersede() {
|
|
241
|
-
if (this.disposed) {
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
this.superseded = true;
|
|
245
|
-
this.disposed = true;
|
|
246
|
-
this.deactivate();
|
|
247
|
-
}
|
|
248
|
-
dispose() {
|
|
249
|
-
if (this.disposed) {
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
this.disposed = true;
|
|
253
|
-
if (!this.registered) {
|
|
254
|
-
this.deactivate();
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
const payload = {
|
|
258
|
-
...this.createEnvelope("hook:unregistered"),
|
|
259
|
-
...this.createMetadataPayload(),
|
|
260
|
-
reason: "disposed"
|
|
261
|
-
};
|
|
262
|
-
emitAIDevtoolsEvent("hook:unregistered", payload);
|
|
263
|
-
this.deactivate();
|
|
264
|
-
}
|
|
265
|
-
prepareForEmit() {
|
|
266
|
-
if (this.disposed || this.superseded) {
|
|
267
|
-
return false;
|
|
268
|
-
}
|
|
269
|
-
this.activate();
|
|
270
|
-
return true;
|
|
271
|
-
}
|
|
272
|
-
prepareForMountEmit() {
|
|
273
|
-
if (this.superseded) {
|
|
274
|
-
return false;
|
|
275
|
-
}
|
|
276
|
-
if (this.disposed) {
|
|
277
|
-
this.disposed = false;
|
|
278
|
-
this.registered = false;
|
|
279
|
-
}
|
|
280
|
-
this.activate();
|
|
281
|
-
return true;
|
|
282
|
-
}
|
|
283
|
-
activate() {
|
|
284
|
-
if (this.disposed) {
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
const activeBridgeByHookId = getActiveBridgeRegistry();
|
|
288
|
-
const activeBridge = activeBridgeByHookId.get(this.options.hookId);
|
|
289
|
-
if (activeBridge && activeBridge !== this) {
|
|
290
|
-
if (typeof activeBridge.supersede === "function") {
|
|
291
|
-
activeBridge.supersede();
|
|
292
|
-
} else {
|
|
293
|
-
activeBridge.deactivate();
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
activeBridgeByHookId.set(this.options.hookId, this);
|
|
297
|
-
if (this.unsubscribers.length > 0) {
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
this.unsubscribers.push(
|
|
301
|
-
aiEventClient.on("devtools:request-state", (event) => {
|
|
302
|
-
this.handleRequestState(event);
|
|
303
|
-
})
|
|
304
|
-
);
|
|
305
|
-
if (this.options.applyToolFixture) {
|
|
306
|
-
this.unsubscribers.push(
|
|
307
|
-
aiEventClient.on("devtools:tool-fixture:apply", (event) => {
|
|
308
|
-
void this.handleToolFixtureApply(event);
|
|
309
|
-
})
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
handleRequestState(event) {
|
|
314
|
-
if (this.disposed || this.superseded) {
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
const targetHookId = event.payload.targetHookId;
|
|
318
|
-
if (targetHookId && targetHookId !== this.options.hookId) {
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
this.emitRegistered();
|
|
322
|
-
this.emitToolsRegistered();
|
|
323
|
-
this.emitSnapshot();
|
|
324
|
-
}
|
|
325
|
-
async handleToolFixtureApply(event) {
|
|
326
|
-
const fixture = event.payload;
|
|
327
|
-
if (!this.matchesFixtureTarget(fixture)) {
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
await this.options.applyToolFixture?.(fixture);
|
|
331
|
-
}
|
|
332
|
-
matchesFixtureTarget(fixture) {
|
|
333
|
-
if (!fixture.hookId && !fixture.threadId) {
|
|
334
|
-
return false;
|
|
335
|
-
}
|
|
336
|
-
if (fixture.hookId) {
|
|
337
|
-
return fixture.hookId === this.options.hookId;
|
|
338
|
-
}
|
|
339
|
-
if (fixture.threadId && (!this.options.threadId || fixture.threadId !== this.options.threadId)) {
|
|
340
|
-
return false;
|
|
341
|
-
}
|
|
342
|
-
return true;
|
|
343
|
-
}
|
|
344
|
-
createEnvelope(eventType, visibility = "client-state", context = {}) {
|
|
345
|
-
return createAIDevtoolsEventEnvelope({
|
|
346
|
-
eventType,
|
|
347
|
-
source: "client",
|
|
348
|
-
visibility,
|
|
349
|
-
clientId: this.options.clientId,
|
|
350
|
-
hookId: this.options.hookId,
|
|
351
|
-
correlationId: this.bridgeId,
|
|
352
|
-
...this.options.threadId ? { threadId: this.options.threadId } : {},
|
|
353
|
-
...context.runId ? { runId: context.runId } : {},
|
|
354
|
-
timestamp: Date.now()
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
createMetadataPayload() {
|
|
358
|
-
return {
|
|
359
|
-
hookId: this.options.hookId,
|
|
360
|
-
hookName: this.options.metadata.hookName,
|
|
361
|
-
...this.options.metadata.name ? { displayName: this.options.metadata.name } : {},
|
|
362
|
-
...this.options.metadata.outputKind ? { outputKind: this.options.metadata.outputKind } : {},
|
|
363
|
-
...this.options.metadata.framework ? { framework: this.options.metadata.framework } : {}
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
let bridgeIdSequence = 0;
|
|
368
|
-
function createBridgeId(hookId) {
|
|
369
|
-
const cryptoLike = globalThis.crypto;
|
|
370
|
-
if (cryptoLike?.randomUUID) {
|
|
371
|
-
return `bridge:${hookId}:${cryptoLike.randomUUID()}`;
|
|
372
|
-
}
|
|
373
|
-
bridgeIdSequence += 1;
|
|
374
|
-
return `bridge:${hookId}:${bridgeIdSequence}`;
|
|
375
|
-
}
|
|
376
|
-
class ChatDevtoolsBridge extends ClientDevtoolsBridge {
|
|
377
|
-
events;
|
|
378
|
-
chatOptions;
|
|
379
|
-
currentRunId = null;
|
|
380
|
-
currentRunThreadId = null;
|
|
381
|
-
currentStreamId = null;
|
|
382
|
-
lastStreamId = null;
|
|
383
|
-
lastRunEventContext;
|
|
384
|
-
constructor(options) {
|
|
385
|
-
super({
|
|
386
|
-
...options,
|
|
387
|
-
// Thunk defers `this.applyFixture` lookup until after `super` returns.
|
|
388
|
-
applyToolFixture: (fixture) => this.applyFixture(fixture)
|
|
389
|
-
});
|
|
390
|
-
this.chatOptions = options;
|
|
391
|
-
this.events = new ChatDevtoolsAwareEventEmitter(options.clientId, this);
|
|
392
|
-
}
|
|
393
|
-
// --- Stream / run context API -------------------------------------------
|
|
394
|
-
setCurrentStreamId(streamId) {
|
|
395
|
-
this.currentStreamId = streamId;
|
|
396
|
-
if (streamId) {
|
|
397
|
-
this.lastStreamId = streamId;
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
/**
|
|
401
|
-
* Called by the auto-attaching emitter every time it sees a non-empty
|
|
402
|
-
* streamId pass through. Lets devtools track the latest stream id
|
|
403
|
-
* without the chat client wiring it up explicitly.
|
|
404
|
-
*/
|
|
405
|
-
recordStreamId(streamId) {
|
|
406
|
-
if (streamId) this.lastStreamId = streamId;
|
|
407
|
-
}
|
|
408
|
-
mountWithTools(initialMessageCount) {
|
|
409
|
-
this.events.clientCreated(initialMessageCount);
|
|
410
|
-
this.emitRegistered();
|
|
411
|
-
this.emitToolsRegistered();
|
|
412
|
-
this.emitSnapshot();
|
|
413
|
-
}
|
|
414
|
-
notifyToolsChanged() {
|
|
415
|
-
this.emitToolsRegistered();
|
|
416
|
-
this.emitSnapshot();
|
|
417
|
-
}
|
|
418
|
-
getCurrentStreamId() {
|
|
419
|
-
return this.currentStreamId;
|
|
420
|
-
}
|
|
421
|
-
getLastStreamId() {
|
|
422
|
-
return this.lastStreamId;
|
|
423
|
-
}
|
|
424
|
-
resolveStreamId() {
|
|
425
|
-
return this.currentStreamId ?? this.lastStreamId ?? this.chatOptions.generateId("stream");
|
|
426
|
-
}
|
|
427
|
-
// Called when the chat client has just generated a runId for outbound emits;
|
|
428
|
-
// the matching RUN_STARTED chunk from the adapter lands later and
|
|
429
|
-
// observeChunk keeps the same context.
|
|
430
|
-
beginRun(runId, threadId) {
|
|
431
|
-
this.currentRunId = runId;
|
|
432
|
-
this.currentRunThreadId = threadId;
|
|
433
|
-
this.lastRunEventContext = { runId, threadId };
|
|
434
|
-
}
|
|
435
|
-
observeChunk(chunk) {
|
|
436
|
-
if (chunk.type === "RUN_STARTED") {
|
|
437
|
-
this.beginRun(chunk.runId, chunk.threadId);
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
if (chunk.type === "RUN_FINISHED" || chunk.type === "RUN_ERROR") {
|
|
441
|
-
const runId = chunk.type === "RUN_FINISHED" ? chunk.runId : chunk.runId;
|
|
442
|
-
if (!runId || runId === this.currentRunId) {
|
|
443
|
-
const context = this.getCurrentRunEventContext();
|
|
444
|
-
if (context) {
|
|
445
|
-
this.lastRunEventContext = context;
|
|
446
|
-
}
|
|
447
|
-
this.currentRunId = null;
|
|
448
|
-
this.currentRunThreadId = null;
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
getCurrentRunEventContext() {
|
|
453
|
-
if (!this.currentRunId) return void 0;
|
|
454
|
-
return {
|
|
455
|
-
threadId: this.currentRunThreadId ?? this.chatOptions.threadId ?? "",
|
|
456
|
-
runId: this.currentRunId
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
getCurrentOrLastRunEventContext() {
|
|
460
|
-
return this.getCurrentRunEventContext() ?? this.lastRunEventContext;
|
|
461
|
-
}
|
|
462
|
-
findToolCallContext(toolCallId) {
|
|
463
|
-
const base = { toolCallId };
|
|
464
|
-
const runContext = this.getCurrentRunEventContext();
|
|
465
|
-
if (runContext) {
|
|
466
|
-
return {
|
|
467
|
-
threadId: runContext.threadId,
|
|
468
|
-
runId: runContext.runId,
|
|
469
|
-
toolCallId
|
|
470
|
-
};
|
|
471
|
-
}
|
|
472
|
-
if (this.chatOptions.threadId) {
|
|
473
|
-
return { threadId: this.chatOptions.threadId, toolCallId };
|
|
474
|
-
}
|
|
475
|
-
return base;
|
|
476
|
-
}
|
|
477
|
-
// --- Fixture replay ------------------------------------------------------
|
|
478
|
-
/**
|
|
479
|
-
* Entry point invoked when the devtools panel emits
|
|
480
|
-
* `devtools:tool-fixture:apply`. The chat client never calls this
|
|
481
|
-
* directly; it is wired through the base bridge's fixture subscription.
|
|
482
|
-
*/
|
|
483
|
-
async applyFixture(fixture) {
|
|
484
|
-
const messages = this.chatOptions.getMessages();
|
|
485
|
-
const threadId = fixture.threadId ?? this.chatOptions.threadId ?? "";
|
|
486
|
-
if (fixture.execute) {
|
|
487
|
-
await this.executeFixture(fixture, messages, threadId);
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
|
-
const replay = this.createReplayMessageFromFixture(fixture, messages);
|
|
491
|
-
const { message, toolCallId } = replay;
|
|
492
|
-
const messageId = message.id;
|
|
493
|
-
this.events.messageAppended(message, void 0, {
|
|
494
|
-
threadId,
|
|
495
|
-
toolCallId,
|
|
496
|
-
...fixture.runId ? { runId: fixture.runId } : {}
|
|
497
|
-
});
|
|
498
|
-
this.chatOptions.setMessages([...messages, message]);
|
|
499
|
-
this.events.toolFixtureApplied({
|
|
500
|
-
hookId: this.chatOptions.hookId,
|
|
501
|
-
threadId,
|
|
502
|
-
...fixture.runId ? { runId: fixture.runId } : {},
|
|
503
|
-
toolName: fixture.toolName,
|
|
504
|
-
input: fixture.input,
|
|
505
|
-
output: fixture.output,
|
|
506
|
-
messageId,
|
|
507
|
-
toolCallId,
|
|
508
|
-
...fixture.execute !== void 0 ? { execute: fixture.execute } : {},
|
|
509
|
-
...fixture.message ? { message: fixture.message } : {},
|
|
510
|
-
...fixture.errorText ? { errorText: fixture.errorText } : {}
|
|
511
|
-
});
|
|
512
|
-
this.emitSnapshot();
|
|
513
|
-
}
|
|
514
|
-
async executeFixture(fixture, messages, threadId) {
|
|
515
|
-
const toolCallId = this.resolveFixtureToolCallId(
|
|
516
|
-
fixture.toolCallId,
|
|
517
|
-
messages
|
|
518
|
-
);
|
|
519
|
-
const messageId = this.resolveFixtureMessageId(fixture.messageId, messages);
|
|
520
|
-
const message = {
|
|
521
|
-
id: messageId,
|
|
522
|
-
role: "assistant",
|
|
523
|
-
parts: [
|
|
524
|
-
{
|
|
525
|
-
type: "tool-call",
|
|
526
|
-
id: toolCallId,
|
|
527
|
-
name: fixture.toolName,
|
|
528
|
-
arguments: stringifyFixtureValue(fixture.input),
|
|
529
|
-
input: fixture.input,
|
|
530
|
-
state: "input-complete"
|
|
531
|
-
}
|
|
532
|
-
],
|
|
533
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
534
|
-
};
|
|
535
|
-
this.events.messageAppended(message, void 0, {
|
|
536
|
-
threadId,
|
|
537
|
-
toolCallId,
|
|
538
|
-
...fixture.runId ? { runId: fixture.runId } : {}
|
|
539
|
-
});
|
|
540
|
-
this.chatOptions.setMessages([...messages, message]);
|
|
541
|
-
this.emitSnapshot();
|
|
542
|
-
const clientTool = this.findClientTool(fixture.toolName);
|
|
543
|
-
const executeFunc = clientTool?.execute;
|
|
544
|
-
if (!executeFunc) {
|
|
545
|
-
console.warn(
|
|
546
|
-
`[ai-devtools] tool fixture "${fixture.toolName}" requested execute=true but no client tool implementation is registered; replaying saved output instead.`
|
|
547
|
-
);
|
|
548
|
-
this.addToolResultForFixture({
|
|
549
|
-
fixture,
|
|
550
|
-
messageId,
|
|
551
|
-
toolCallId,
|
|
552
|
-
threadId,
|
|
553
|
-
output: fixture.output,
|
|
554
|
-
errorText: fixture.errorText
|
|
555
|
-
});
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
let output;
|
|
559
|
-
try {
|
|
560
|
-
output = await executeFunc(fixture.input);
|
|
561
|
-
} catch (error) {
|
|
562
|
-
console.error(
|
|
563
|
-
`[ai-devtools] tool fixture "${fixture.toolName}" execute threw`,
|
|
564
|
-
error
|
|
565
|
-
);
|
|
566
|
-
this.addToolResultForFixture({
|
|
567
|
-
fixture,
|
|
568
|
-
messageId,
|
|
569
|
-
toolCallId,
|
|
570
|
-
threadId,
|
|
571
|
-
output: null,
|
|
572
|
-
errorText: error instanceof Error ? `${error.name}: ${error.message}` : `Tool execution failed: ${String(error)}`
|
|
573
|
-
});
|
|
574
|
-
return;
|
|
575
|
-
}
|
|
576
|
-
this.addToolResultForFixture({
|
|
577
|
-
fixture,
|
|
578
|
-
messageId,
|
|
579
|
-
toolCallId,
|
|
580
|
-
threadId,
|
|
581
|
-
output
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
addToolResultForFixture(input) {
|
|
585
|
-
const state = input.errorText ? "output-error" : "output-available";
|
|
586
|
-
this.events.toolResultAdded(
|
|
587
|
-
input.toolCallId,
|
|
588
|
-
input.fixture.toolName,
|
|
589
|
-
input.output,
|
|
590
|
-
state,
|
|
591
|
-
{
|
|
592
|
-
threadId: input.threadId,
|
|
593
|
-
...input.fixture.runId ? { runId: input.fixture.runId } : {},
|
|
594
|
-
toolCallId: input.toolCallId
|
|
595
|
-
}
|
|
596
|
-
);
|
|
597
|
-
this.chatOptions.addToolResult(
|
|
598
|
-
input.toolCallId,
|
|
599
|
-
input.output,
|
|
600
|
-
input.errorText
|
|
601
|
-
);
|
|
602
|
-
this.events.toolFixtureApplied({
|
|
603
|
-
hookId: this.chatOptions.hookId,
|
|
604
|
-
threadId: input.threadId,
|
|
605
|
-
...input.fixture.runId ? { runId: input.fixture.runId } : {},
|
|
606
|
-
toolName: input.fixture.toolName,
|
|
607
|
-
input: input.fixture.input,
|
|
608
|
-
output: input.output,
|
|
609
|
-
execute: true,
|
|
610
|
-
messageId: input.messageId,
|
|
611
|
-
toolCallId: input.toolCallId,
|
|
612
|
-
...input.errorText ? { errorText: input.errorText } : {}
|
|
613
|
-
});
|
|
614
|
-
this.emitSnapshot();
|
|
615
|
-
}
|
|
616
|
-
createReplayMessageFromFixture(fixture, messages) {
|
|
617
|
-
const cloned = this.cloneFixtureSourceMessage(fixture, messages);
|
|
618
|
-
if (cloned) return cloned;
|
|
619
|
-
const toolCallId = this.resolveFixtureToolCallId(
|
|
620
|
-
fixture.toolCallId,
|
|
621
|
-
messages
|
|
622
|
-
);
|
|
623
|
-
const messageId = this.resolveFixtureMessageId(fixture.messageId, messages);
|
|
624
|
-
const state = fixture.errorText ? "error" : "complete";
|
|
625
|
-
return {
|
|
626
|
-
toolCallId,
|
|
627
|
-
message: {
|
|
628
|
-
id: messageId,
|
|
629
|
-
role: "assistant",
|
|
630
|
-
parts: [
|
|
631
|
-
{
|
|
632
|
-
type: "tool-call",
|
|
633
|
-
id: toolCallId,
|
|
634
|
-
name: fixture.toolName,
|
|
635
|
-
arguments: stringifyFixtureValue(fixture.input),
|
|
636
|
-
input: fixture.input,
|
|
637
|
-
state: "input-complete",
|
|
638
|
-
output: fixture.output
|
|
639
|
-
},
|
|
640
|
-
{
|
|
641
|
-
type: "tool-result",
|
|
642
|
-
toolCallId,
|
|
643
|
-
content: stringifyFixtureValue(fixture.output),
|
|
644
|
-
state,
|
|
645
|
-
...fixture.errorText ? { error: fixture.errorText } : {}
|
|
646
|
-
}
|
|
647
|
-
],
|
|
648
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
649
|
-
}
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
cloneFixtureSourceMessage(fixture, messages) {
|
|
653
|
-
const sourceMessage = fixture.message;
|
|
654
|
-
if (!sourceMessage || !Array.isArray(sourceMessage.parts)) {
|
|
655
|
-
return void 0;
|
|
656
|
-
}
|
|
657
|
-
const toolCallIds = this.createFixtureToolCallIdMap(
|
|
658
|
-
sourceMessage.parts,
|
|
659
|
-
messages
|
|
660
|
-
);
|
|
661
|
-
const parts = sourceMessage.parts.map((part) => cloneFixtureMessagePart(part, toolCallIds)).filter((part) => Boolean(part));
|
|
662
|
-
const mappedFixtureToolCallId = fixture.toolCallId ? toolCallIds.get(fixture.toolCallId) : void 0;
|
|
663
|
-
hydrateToolCallOutputs(parts, {
|
|
664
|
-
...mappedFixtureToolCallId ? { mappedToolCallId: mappedFixtureToolCallId } : {},
|
|
665
|
-
output: fixture.output
|
|
666
|
-
});
|
|
667
|
-
if (parts.length === 0) return void 0;
|
|
668
|
-
const toolCallId = (fixture.toolCallId ? toolCallIds.get(fixture.toolCallId) : void 0) ?? firstToolCallId(parts);
|
|
669
|
-
if (!toolCallId) return void 0;
|
|
670
|
-
return {
|
|
671
|
-
toolCallId,
|
|
672
|
-
message: {
|
|
673
|
-
id: this.resolveFixtureMessageId(sourceMessage.id, messages),
|
|
674
|
-
role: sourceMessage.role,
|
|
675
|
-
parts,
|
|
676
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
677
|
-
}
|
|
678
|
-
};
|
|
679
|
-
}
|
|
680
|
-
createFixtureToolCallIdMap(parts, messages) {
|
|
681
|
-
const ids = /* @__PURE__ */ new Map();
|
|
682
|
-
for (const part of parts) {
|
|
683
|
-
if (!isRecord(part) || part.type !== "tool-call") continue;
|
|
684
|
-
if (typeof part.id !== "string") continue;
|
|
685
|
-
ids.set(part.id, this.resolveFixtureToolCallId(part.id, messages));
|
|
686
|
-
}
|
|
687
|
-
return ids;
|
|
688
|
-
}
|
|
689
|
-
resolveFixtureMessageId(messageId, messages) {
|
|
690
|
-
if (messageId && !messages.some((message) => message.id === messageId)) {
|
|
691
|
-
return messageId;
|
|
692
|
-
}
|
|
693
|
-
return this.chatOptions.generateId("fixture-msg");
|
|
694
|
-
}
|
|
695
|
-
resolveFixtureToolCallId(toolCallId, messages) {
|
|
696
|
-
if (toolCallId && !hasToolCallId(messages, toolCallId)) {
|
|
697
|
-
return toolCallId;
|
|
698
|
-
}
|
|
699
|
-
return this.chatOptions.generateId("fixture-tool-call");
|
|
700
|
-
}
|
|
701
|
-
findClientTool(name) {
|
|
702
|
-
const tools = this.chatOptions.getTools?.();
|
|
703
|
-
if (!tools) return void 0;
|
|
704
|
-
for (const tool of tools) {
|
|
705
|
-
if (tool.name === name) return tool;
|
|
706
|
-
}
|
|
707
|
-
return void 0;
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
function isRecord(value) {
|
|
711
|
-
return typeof value === "object" && value !== null;
|
|
712
|
-
}
|
|
713
|
-
function stringifyFixtureValue(value) {
|
|
714
|
-
if (typeof value === "string") return value;
|
|
715
|
-
if (value === void 0 || typeof value === "function" || typeof value === "symbol") {
|
|
716
|
-
return String(value);
|
|
717
|
-
}
|
|
718
|
-
try {
|
|
719
|
-
return JSON.stringify(value);
|
|
720
|
-
} catch (error) {
|
|
721
|
-
console.error(
|
|
722
|
-
"[ai-devtools] failed to JSON.stringify fixture value; falling back to String(). Tool call arguments may be malformed.",
|
|
723
|
-
{ error, value }
|
|
724
|
-
);
|
|
725
|
-
return String(value);
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
function parseFixtureResultContent(content) {
|
|
729
|
-
try {
|
|
730
|
-
return JSON.parse(content);
|
|
731
|
-
} catch (error) {
|
|
732
|
-
console.error(
|
|
733
|
-
"[ai-devtools] failed to JSON.parse fixture result content; replaying as raw string. Fixture payload may be corrupted.",
|
|
734
|
-
{ error, content }
|
|
735
|
-
);
|
|
736
|
-
return content;
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
function cloneFixtureMessagePart(part, toolCallIds) {
|
|
740
|
-
if (!isRecord(part) || typeof part.type !== "string") return void 0;
|
|
741
|
-
const cloned = { ...part };
|
|
742
|
-
if (part.type === "tool-call" && typeof part.id === "string") {
|
|
743
|
-
cloned.id = toolCallIds.get(part.id) ?? part.id;
|
|
744
|
-
}
|
|
745
|
-
if (part.type === "tool-result" && typeof part.toolCallId === "string") {
|
|
746
|
-
cloned.toolCallId = toolCallIds.get(part.toolCallId) ?? part.toolCallId;
|
|
747
|
-
}
|
|
748
|
-
return cloned;
|
|
749
|
-
}
|
|
750
|
-
function firstToolCallId(parts) {
|
|
751
|
-
const toolCall = parts.find((part) => part.type === "tool-call");
|
|
752
|
-
return toolCall?.type === "tool-call" ? toolCall.id : void 0;
|
|
753
|
-
}
|
|
754
|
-
function hydrateToolCallOutputs(parts, fixtureOutput) {
|
|
755
|
-
for (const part of parts) {
|
|
756
|
-
if (part.type !== "tool-result") continue;
|
|
757
|
-
const toolCall = parts.find(
|
|
758
|
-
(candidate) => candidate.type === "tool-call" && candidate.id === part.toolCallId && candidate.output === void 0
|
|
759
|
-
);
|
|
760
|
-
if (toolCall) {
|
|
761
|
-
toolCall.output = Array.isArray(part.content) ? part.content : parseFixtureResultContent(part.content);
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
if (fixtureOutput.mappedToolCallId && fixtureOutput.output !== void 0) {
|
|
765
|
-
const toolCall = parts.find(
|
|
766
|
-
(candidate) => candidate.type === "tool-call" && candidate.id === fixtureOutput.mappedToolCallId && candidate.output === void 0
|
|
767
|
-
);
|
|
768
|
-
if (toolCall) {
|
|
769
|
-
toolCall.output = fixtureOutput.output;
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
function hasToolCallId(messages, toolCallId) {
|
|
774
|
-
return messages.some(
|
|
775
|
-
(message) => message.parts.some((part) => {
|
|
776
|
-
if (part.type === "tool-call") return part.id === toolCallId;
|
|
777
|
-
if (part.type === "tool-result") return part.toolCallId === toolCallId;
|
|
778
|
-
return false;
|
|
779
|
-
})
|
|
780
|
-
);
|
|
781
|
-
}
|
|
782
|
-
class GenerationDevtoolsBridge extends ClientDevtoolsBridge {
|
|
783
|
-
activeRunId = null;
|
|
784
|
-
activeRunStarted = false;
|
|
785
|
-
devtoolsRuns = [];
|
|
786
|
-
maxRuns;
|
|
787
|
-
getCoreState;
|
|
788
|
-
constructor(options) {
|
|
789
|
-
super({
|
|
790
|
-
...options,
|
|
791
|
-
getSnapshot: () => this.buildSnapshot()
|
|
792
|
-
});
|
|
793
|
-
this.maxRuns = options.maxRuns ?? 20;
|
|
794
|
-
this.getCoreState = options.getCoreState;
|
|
795
|
-
}
|
|
796
|
-
// --- Run lifecycle (called by GenerationClient) -----------------------
|
|
797
|
-
beginRun(input) {
|
|
798
|
-
const runId = this.generateRunId();
|
|
799
|
-
this.activeRunId = runId;
|
|
800
|
-
this.activeRunStarted = false;
|
|
801
|
-
this.upsertRun(runId, {
|
|
802
|
-
input,
|
|
803
|
-
result: null,
|
|
804
|
-
preview: this.createPreview(null),
|
|
805
|
-
progress: null,
|
|
806
|
-
status: "generating",
|
|
807
|
-
isLoading: true,
|
|
808
|
-
clearError: true
|
|
809
|
-
});
|
|
810
|
-
return runId;
|
|
811
|
-
}
|
|
812
|
-
ensureRunStarted(runId) {
|
|
813
|
-
if (this.activeRunStarted && this.activeRunId === runId) return;
|
|
814
|
-
if (!this.activeRunStarted && this.activeRunId && this.activeRunId !== runId) {
|
|
815
|
-
this.renameRun(this.activeRunId, runId);
|
|
816
|
-
}
|
|
817
|
-
this.activeRunId = runId;
|
|
818
|
-
this.activeRunStarted = true;
|
|
819
|
-
this.upsertRun(runId, {
|
|
820
|
-
status: "generating",
|
|
821
|
-
isLoading: true,
|
|
822
|
-
clearError: true
|
|
823
|
-
});
|
|
824
|
-
this.emitRunLifecycle("run:started", runId, "started");
|
|
825
|
-
this.emitState();
|
|
826
|
-
}
|
|
827
|
-
finishRun(runId, eventType, status, error) {
|
|
828
|
-
this.ensureRunStarted(runId);
|
|
829
|
-
const completedAt = Date.now();
|
|
830
|
-
const completedProgress = status === "completed" ? this.completeProgress() : this.getProgress();
|
|
831
|
-
const runStatus = status === "completed" ? "success" : status === "errored" ? "error" : "cancelled";
|
|
832
|
-
this.upsertRun(runId, {
|
|
833
|
-
status: runStatus,
|
|
834
|
-
isLoading: false,
|
|
835
|
-
progress: completedProgress,
|
|
836
|
-
completedAt,
|
|
837
|
-
...error ? { error } : { clearError: true }
|
|
838
|
-
});
|
|
839
|
-
if (this.activeRunId === runId) {
|
|
840
|
-
this.activeRunId = null;
|
|
841
|
-
}
|
|
842
|
-
this.activeRunStarted = false;
|
|
843
|
-
this.emitRunLifecycle(eventType, runId, status, {
|
|
844
|
-
...error ? { error } : {}
|
|
845
|
-
});
|
|
846
|
-
this.emitState();
|
|
847
|
-
}
|
|
848
|
-
getActiveRunId() {
|
|
849
|
-
return this.activeRunId;
|
|
850
|
-
}
|
|
851
|
-
/** Clear all per-run history. Called when the client `reset()`s. */
|
|
852
|
-
resetRuns() {
|
|
853
|
-
this.activeRunId = null;
|
|
854
|
-
this.activeRunStarted = false;
|
|
855
|
-
this.devtoolsRuns = [];
|
|
856
|
-
}
|
|
857
|
-
/** Record state changes from the client and emit the matching snapshot. */
|
|
858
|
-
recordResultChange() {
|
|
859
|
-
this.updateActiveRun({
|
|
860
|
-
result: this.getCoreState().result,
|
|
861
|
-
preview: this.createPreview(this.getCoreState().result),
|
|
862
|
-
clearError: true
|
|
863
|
-
});
|
|
864
|
-
this.emitState();
|
|
865
|
-
}
|
|
866
|
-
recordLoadingChange() {
|
|
867
|
-
this.updateActiveRun({ isLoading: this.getCoreState().isLoading });
|
|
868
|
-
this.emitState();
|
|
869
|
-
}
|
|
870
|
-
recordErrorChange(error) {
|
|
871
|
-
this.updateActiveRun(
|
|
872
|
-
error ? { error: error.message } : { clearError: true }
|
|
873
|
-
);
|
|
874
|
-
this.emitState();
|
|
875
|
-
}
|
|
876
|
-
recordStatusChange(status) {
|
|
877
|
-
this.updateActiveRun({ status });
|
|
878
|
-
this.emitState();
|
|
879
|
-
}
|
|
880
|
-
recordProgressChange() {
|
|
881
|
-
this.updateActiveRun({ progress: this.getCoreState().progress });
|
|
882
|
-
this.emitState();
|
|
883
|
-
}
|
|
884
|
-
/** Emit the latest snapshot without touching run state. */
|
|
885
|
-
emitState() {
|
|
886
|
-
this.emitUpdated();
|
|
887
|
-
this.emitSnapshot();
|
|
888
|
-
}
|
|
889
|
-
// --- Internal ---------------------------------------------------------
|
|
890
|
-
buildSnapshot() {
|
|
891
|
-
const core = this.getCoreState();
|
|
892
|
-
return {
|
|
893
|
-
input: core.input,
|
|
894
|
-
result: core.result,
|
|
895
|
-
preview: this.createPreview(core.result),
|
|
896
|
-
progress: core.progress,
|
|
897
|
-
status: core.status,
|
|
898
|
-
isLoading: core.isLoading,
|
|
899
|
-
activeRunId: this.activeRunId,
|
|
900
|
-
runs: this.devtoolsRuns,
|
|
901
|
-
...core.error ? { error: core.error } : {}
|
|
902
|
-
};
|
|
903
|
-
}
|
|
904
|
-
updateActiveRun(patch) {
|
|
905
|
-
if (!this.activeRunId) return;
|
|
906
|
-
this.upsertRun(this.activeRunId, patch);
|
|
907
|
-
}
|
|
908
|
-
upsertRun(runId, patch) {
|
|
909
|
-
const now = Date.now();
|
|
910
|
-
const index = this.devtoolsRuns.findIndex((run) => run.id === runId);
|
|
911
|
-
const existing = index >= 0 ? this.devtoolsRuns[index] : void 0;
|
|
912
|
-
const next = existing ? { ...existing } : {
|
|
913
|
-
id: runId,
|
|
914
|
-
input: this.getCoreState().input,
|
|
915
|
-
result: null,
|
|
916
|
-
preview: this.createPreview(null),
|
|
917
|
-
progress: null,
|
|
918
|
-
status: "idle",
|
|
919
|
-
isLoading: false,
|
|
920
|
-
startedAt: now,
|
|
921
|
-
updatedAt: now
|
|
922
|
-
};
|
|
923
|
-
if ("input" in patch) next.input = patch.input ?? null;
|
|
924
|
-
if ("result" in patch) next.result = patch.result ?? null;
|
|
925
|
-
if (patch.preview) next.preview = patch.preview;
|
|
926
|
-
if ("progress" in patch) next.progress = patch.progress ?? null;
|
|
927
|
-
if (patch.status) next.status = patch.status;
|
|
928
|
-
if ("isLoading" in patch) next.isLoading = patch.isLoading === true;
|
|
929
|
-
if (patch.completedAt !== void 0) next.completedAt = patch.completedAt;
|
|
930
|
-
if (patch.clearError) delete next.error;
|
|
931
|
-
if (patch.error !== void 0) next.error = patch.error;
|
|
932
|
-
next.updatedAt = now;
|
|
933
|
-
if (index >= 0) {
|
|
934
|
-
this.devtoolsRuns = this.devtoolsRuns.map(
|
|
935
|
-
(run) => run.id === runId ? next : run
|
|
936
|
-
);
|
|
937
|
-
} else {
|
|
938
|
-
this.devtoolsRuns = [...this.devtoolsRuns, next];
|
|
939
|
-
}
|
|
940
|
-
if (this.devtoolsRuns.length > this.maxRuns) {
|
|
941
|
-
this.devtoolsRuns = this.devtoolsRuns.slice(-this.maxRuns);
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
renameRun(previousRunId, nextRunId) {
|
|
945
|
-
if (previousRunId === nextRunId) return;
|
|
946
|
-
const existing = this.devtoolsRuns.find((run) => run.id === previousRunId);
|
|
947
|
-
if (!existing) return;
|
|
948
|
-
const renamed = { ...existing, id: nextRunId, updatedAt: Date.now() };
|
|
949
|
-
this.devtoolsRuns = this.devtoolsRuns.filter((run) => run.id !== nextRunId).map((run) => run.id === previousRunId ? renamed : run);
|
|
950
|
-
}
|
|
951
|
-
getProgress() {
|
|
952
|
-
return this.getCoreState().progress;
|
|
953
|
-
}
|
|
954
|
-
completeProgress() {
|
|
955
|
-
const progress = this.getCoreState().progress;
|
|
956
|
-
if (!progress) return null;
|
|
957
|
-
return {
|
|
958
|
-
value: 100,
|
|
959
|
-
...progress.message ? { message: progress.message } : {}
|
|
960
|
-
};
|
|
961
|
-
}
|
|
962
|
-
createPreview(result) {
|
|
963
|
-
return createAIDevtoolsGenerationPreview({
|
|
964
|
-
outputKind: this.options.metadata.outputKind,
|
|
965
|
-
result
|
|
966
|
-
});
|
|
967
|
-
}
|
|
968
|
-
generateRunId() {
|
|
969
|
-
return `run-${Date.now()}-${Math.random().toString(36).substring(7)}`;
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
class VideoDevtoolsBridge extends GenerationDevtoolsBridge {
|
|
973
|
-
constructor(options) {
|
|
974
|
-
super(options);
|
|
975
|
-
}
|
|
976
|
-
recordJobIdChange() {
|
|
977
|
-
this.updateActiveRun({
|
|
978
|
-
jobId: this.getCoreState().jobId
|
|
979
|
-
});
|
|
980
|
-
this.emitState();
|
|
981
|
-
}
|
|
982
|
-
recordVideoStatusChange() {
|
|
983
|
-
const core = this.getCoreState();
|
|
984
|
-
this.updateActiveRun({
|
|
985
|
-
videoStatus: core.videoStatus,
|
|
986
|
-
preview: this.createVideoPreview(core.result, core.videoStatus)
|
|
987
|
-
});
|
|
988
|
-
this.emitState();
|
|
989
|
-
}
|
|
990
|
-
buildSnapshot() {
|
|
991
|
-
const core = this.getCoreState();
|
|
992
|
-
return {
|
|
993
|
-
input: core.input,
|
|
994
|
-
result: core.result,
|
|
995
|
-
preview: this.createVideoPreview(core.result, core.videoStatus),
|
|
996
|
-
progress: core.progress,
|
|
997
|
-
status: core.status,
|
|
998
|
-
isLoading: core.isLoading,
|
|
999
|
-
activeRunId: this.activeRunId,
|
|
1000
|
-
runs: this.devtoolsRuns,
|
|
1001
|
-
jobId: core.jobId,
|
|
1002
|
-
videoStatus: core.videoStatus,
|
|
1003
|
-
...core.error ? { error: core.error } : {}
|
|
1004
|
-
};
|
|
1005
|
-
}
|
|
1006
|
-
upsertRun(runId, patch) {
|
|
1007
|
-
super.upsertRun(runId, patch);
|
|
1008
|
-
if (!("jobId" in patch || "videoStatus" in patch)) return;
|
|
1009
|
-
const index = this.devtoolsRuns.findIndex((run) => run.id === runId);
|
|
1010
|
-
if (index < 0) return;
|
|
1011
|
-
const target = this.devtoolsRuns[index];
|
|
1012
|
-
if (!target) return;
|
|
1013
|
-
const merged = { ...target };
|
|
1014
|
-
if ("jobId" in patch) merged.jobId = patch.jobId ?? null;
|
|
1015
|
-
if ("videoStatus" in patch) merged.videoStatus = patch.videoStatus ?? null;
|
|
1016
|
-
this.devtoolsRuns = this.devtoolsRuns.map(
|
|
1017
|
-
(run) => run.id === runId ? merged : run
|
|
1018
|
-
);
|
|
1019
|
-
}
|
|
1020
|
-
// Override so record* methods inherited from GenerationDevtoolsBridge
|
|
1021
|
-
// (e.g. recordResultChange) thread the latest videoStatus into the preview.
|
|
1022
|
-
createPreview(result) {
|
|
1023
|
-
const core = this.getCoreState();
|
|
1024
|
-
return this.createVideoPreview(result, core.videoStatus);
|
|
1025
|
-
}
|
|
1026
|
-
createVideoPreview(result, videoStatus) {
|
|
1027
|
-
return createAIDevtoolsGenerationPreview({
|
|
1028
|
-
outputKind: this.options.metadata.outputKind,
|
|
1029
|
-
result,
|
|
1030
|
-
videoStatus
|
|
1031
|
-
});
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
class ChatDevtoolsAwareEventEmitter extends DefaultChatClientEventEmitter {
|
|
1035
|
-
constructor(clientId, helper) {
|
|
1036
|
-
super(clientId);
|
|
1037
|
-
this.helper = helper;
|
|
1038
|
-
}
|
|
1039
|
-
helper;
|
|
1040
|
-
afterEmit(streamId) {
|
|
1041
|
-
if (streamId) this.helper.recordStreamId(streamId);
|
|
1042
|
-
this.helper.emitSnapshot();
|
|
1043
|
-
}
|
|
1044
|
-
// -- methods with run context --------------------------------------------
|
|
1045
|
-
textUpdated(streamId, messageId, content, context) {
|
|
1046
|
-
super.textUpdated(
|
|
1047
|
-
streamId,
|
|
1048
|
-
messageId,
|
|
1049
|
-
content,
|
|
1050
|
-
context ?? this.helper.getCurrentRunEventContext()
|
|
1051
|
-
);
|
|
1052
|
-
this.afterEmit(streamId);
|
|
1053
|
-
}
|
|
1054
|
-
thinkingUpdated(streamId, messageId, content, delta, context) {
|
|
1055
|
-
super.thinkingUpdated(
|
|
1056
|
-
streamId,
|
|
1057
|
-
messageId,
|
|
1058
|
-
content,
|
|
1059
|
-
delta,
|
|
1060
|
-
context ?? this.helper.getCurrentRunEventContext()
|
|
1061
|
-
);
|
|
1062
|
-
this.afterEmit(streamId);
|
|
1063
|
-
}
|
|
1064
|
-
messageAppended(uiMessage, streamId, context) {
|
|
1065
|
-
super.messageAppended(
|
|
1066
|
-
uiMessage,
|
|
1067
|
-
streamId,
|
|
1068
|
-
context ?? this.helper.getCurrentRunEventContext()
|
|
1069
|
-
);
|
|
1070
|
-
this.afterEmit(streamId);
|
|
1071
|
-
}
|
|
1072
|
-
toolCallStateChanged(streamId, messageId, toolCallId, toolName, state, args, context) {
|
|
1073
|
-
super.toolCallStateChanged(
|
|
1074
|
-
streamId,
|
|
1075
|
-
messageId,
|
|
1076
|
-
toolCallId,
|
|
1077
|
-
toolName,
|
|
1078
|
-
state,
|
|
1079
|
-
args,
|
|
1080
|
-
context ?? this.helper.getCurrentRunEventContext()
|
|
1081
|
-
);
|
|
1082
|
-
this.afterEmit(streamId);
|
|
1083
|
-
}
|
|
1084
|
-
structuredOutputChanged(eventName, streamId, messageId, output, context) {
|
|
1085
|
-
super.structuredOutputChanged(
|
|
1086
|
-
eventName,
|
|
1087
|
-
streamId,
|
|
1088
|
-
messageId,
|
|
1089
|
-
output,
|
|
1090
|
-
context ?? this.helper.getCurrentOrLastRunEventContext()
|
|
1091
|
-
);
|
|
1092
|
-
this.afterEmit(streamId);
|
|
1093
|
-
}
|
|
1094
|
-
approvalRequested(streamId, messageId, toolCallId, toolName, input, approvalId, context) {
|
|
1095
|
-
super.approvalRequested(
|
|
1096
|
-
streamId,
|
|
1097
|
-
messageId,
|
|
1098
|
-
toolCallId,
|
|
1099
|
-
toolName,
|
|
1100
|
-
input,
|
|
1101
|
-
approvalId,
|
|
1102
|
-
context ?? this.helper.getCurrentOrLastRunEventContext()
|
|
1103
|
-
);
|
|
1104
|
-
this.afterEmit(streamId);
|
|
1105
|
-
}
|
|
1106
|
-
toolResultAdded(toolCallId, toolName, output, state, context) {
|
|
1107
|
-
super.toolResultAdded(
|
|
1108
|
-
toolCallId,
|
|
1109
|
-
toolName,
|
|
1110
|
-
output,
|
|
1111
|
-
state,
|
|
1112
|
-
context ?? this.helper.getCurrentRunEventContext()
|
|
1113
|
-
);
|
|
1114
|
-
this.afterEmit();
|
|
1115
|
-
}
|
|
1116
|
-
toolApprovalResponded(approvalId, toolCallId, approved, context) {
|
|
1117
|
-
super.toolApprovalResponded(
|
|
1118
|
-
approvalId,
|
|
1119
|
-
toolCallId,
|
|
1120
|
-
approved,
|
|
1121
|
-
context ?? this.helper.getCurrentRunEventContext()
|
|
1122
|
-
);
|
|
1123
|
-
this.afterEmit();
|
|
1124
|
-
}
|
|
1125
|
-
// -- methods without context (just auto-emit snapshot) -------------------
|
|
1126
|
-
clientCreated(initialMessageCount) {
|
|
1127
|
-
super.clientCreated(initialMessageCount);
|
|
1128
|
-
this.afterEmit();
|
|
1129
|
-
}
|
|
1130
|
-
loadingChanged(isLoading) {
|
|
1131
|
-
super.loadingChanged(isLoading);
|
|
1132
|
-
this.afterEmit();
|
|
1133
|
-
}
|
|
1134
|
-
errorChanged(error) {
|
|
1135
|
-
super.errorChanged(error);
|
|
1136
|
-
this.afterEmit();
|
|
1137
|
-
}
|
|
1138
|
-
reloaded(fromMessageIndex) {
|
|
1139
|
-
super.reloaded(fromMessageIndex);
|
|
1140
|
-
this.afterEmit();
|
|
1141
|
-
}
|
|
1142
|
-
stopped() {
|
|
1143
|
-
super.stopped();
|
|
1144
|
-
this.afterEmit();
|
|
1145
|
-
}
|
|
1146
|
-
messagesCleared() {
|
|
1147
|
-
super.messagesCleared();
|
|
1148
|
-
this.afterEmit();
|
|
1149
|
-
}
|
|
1150
|
-
messageSent(messageId, content) {
|
|
1151
|
-
super.messageSent(messageId, content);
|
|
1152
|
-
this.afterEmit();
|
|
1153
|
-
}
|
|
1154
|
-
toolFixtureApplied(fixture) {
|
|
1155
|
-
super.toolFixtureApplied(fixture);
|
|
1156
|
-
this.afterEmit();
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
function createChatDevtoolsBridge(options) {
|
|
1160
|
-
return new ChatDevtoolsBridge(options);
|
|
1161
|
-
}
|
|
1162
|
-
function createGenerationDevtoolsBridge(options) {
|
|
1163
|
-
return new GenerationDevtoolsBridge(options);
|
|
1164
|
-
}
|
|
1165
|
-
function createVideoDevtoolsBridge(options) {
|
|
1166
|
-
return new VideoDevtoolsBridge(options);
|
|
1167
|
-
}
|
|
1168
|
-
export {
|
|
1169
|
-
ChatDevtoolsBridge,
|
|
1170
|
-
ClientDevtoolsBridge,
|
|
1171
|
-
GenerationDevtoolsBridge,
|
|
1172
|
-
VideoDevtoolsBridge,
|
|
1173
|
-
createAIDevtoolsGenerationPreview,
|
|
1174
|
-
createChatDevtoolsBridge,
|
|
1175
|
-
createGenerationDevtoolsBridge,
|
|
1176
|
-
createVideoDevtoolsBridge
|
|
1177
|
-
};
|
|
1178
|
-
//# sourceMappingURL=devtools.js.map
|