@tanstack/ai-client 0.29.0 → 0.29.2
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/dist/esm/chat-client.d.ts +6 -0
- package/dist/esm/chat-client.js +28 -4
- package/dist/esm/chat-client.js.map +1 -1
- package/dist/esm/connection-adapters.js +15 -0
- package/dist/esm/connection-adapters.js.map +1 -1
- package/dist/esm/devtools-noop.d.ts +3 -0
- package/dist/esm/devtools-noop.js +3 -0
- package/dist/esm/devtools-noop.js.map +1 -1
- package/dist/esm/devtools.d.ts +15 -1
- package/dist/esm/devtools.js +149 -10
- package/dist/esm/devtools.js.map +1 -1
- package/package.json +3 -3
- package/src/chat-client.ts +50 -8
- package/src/connection-adapters.ts +23 -0
- package/src/devtools-noop.ts +3 -0
- package/src/devtools.ts +280 -12
package/dist/esm/devtools.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devtools.js","names":[],"sources":["../../src/devtools.ts"],"sourcesContent":["import {\n aiEventClient,\n createAIDevtoolsEventEnvelope,\n emitAIDevtoolsEvent,\n} from '@tanstack/ai-event-client'\nimport { convertSchemaToJsonSchema } from '@tanstack/ai/client'\nimport { DefaultChatClientEventEmitter } from './events'\nimport type { AnyClientTool, StreamChunk } from '@tanstack/ai/client'\nimport type {\n AIDevtoolsEventVisibility,\n MemoryScopeLite,\n} from '@tanstack/ai-event-client'\nimport type {\n ChatClientEventContext,\n ChatClientEventEmitter,\n ChatClientRunEventContext,\n} from './events'\nimport type {\n ChatClientState,\n ConnectionStatus,\n MessagePart,\n QueuedMessage,\n ToolCallPart,\n UIMessage,\n} from './types'\n\nexport interface AIDevtoolsDisplayOptions {\n name?: string\n}\n\n/**\n * Structural mirror of `MemoryStateEventValue` from `@tanstack/ai-memory` — the\n * payload of the `memory:state` CUSTOM chunk. Kept local so `ai-client` doesn't\n * depend on `ai-memory`; the memory middleware is the producer.\n */\ninterface MemoryStateEventValue {\n scope: MemoryScopeLite\n adapter: string\n query?: string\n recall?: {\n fragmentCount?: number\n hasTools?: boolean\n systemPromptChars?: number\n durationMs?: number\n }\n snapshot?: {\n takenAt: string\n data: unknown\n facts?: Array<{\n id: string\n text: string\n source?: string\n createdAt?: string\n }>\n }\n}\n\nexport interface AIDevtoolsClientMetadata extends AIDevtoolsDisplayOptions {\n framework?: string\n hookName: string\n outputKind?: 'chat' | 'text' | 'structured' | 'image' | 'video' | 'audio'\n}\n\nexport interface AIDevtoolsGenerationProgress {\n value: number\n message?: string\n}\n\nexport interface AIDevtoolsGenerationMediaItem {\n src: string\n sourceType: 'url' | 'base64'\n mimeType?: string\n format?: string\n duration?: number\n}\n\nexport interface AIDevtoolsGenerationVideoJob {\n jobId: string\n status?: string\n progress?: number\n error?: string\n}\n\nexport type AIDevtoolsGenerationPreview =\n | {\n kind: 'image'\n items: Array<AIDevtoolsGenerationMediaItem>\n }\n | {\n kind: 'audio'\n items: Array<AIDevtoolsGenerationMediaItem>\n }\n | {\n kind: 'video'\n items: Array<AIDevtoolsGenerationMediaItem>\n job?: AIDevtoolsGenerationVideoJob\n }\n | {\n kind: 'text'\n text: string\n }\n | {\n kind: 'structured'\n value: unknown\n }\n | {\n kind: 'empty'\n }\n\nexport type AIDevtoolsGenerationRunStatus =\n | 'idle'\n | 'generating'\n | 'success'\n | 'error'\n | 'cancelled'\n\nexport interface AIDevtoolsGenerationRunSnapshot<TOutput = unknown> {\n id: string\n input: unknown\n result: TOutput | null\n preview: AIDevtoolsGenerationPreview\n progress: AIDevtoolsGenerationProgress | null\n status: AIDevtoolsGenerationRunStatus\n isLoading: boolean\n startedAt: number\n updatedAt: number\n completedAt?: number\n error?: string\n jobId?: string | null\n videoStatus?: unknown\n}\n\nexport interface AIDevtoolsGenerationPreviewInput {\n outputKind?: AIDevtoolsClientMetadata['outputKind']\n result: unknown\n videoStatus?: unknown\n}\n\nexport interface AIDevtoolsChatSnapshot {\n [key: string]: unknown\n messages: Array<UIMessage>\n status: ChatClientState\n isLoading: boolean\n isSubscribed: boolean\n connectionStatus: ConnectionStatus\n sessionGenerating: boolean\n activeRunIds: Array<string>\n queue?: Array<QueuedMessage>\n error?: string\n}\n\nexport function createAIDevtoolsGenerationPreview(\n input: AIDevtoolsGenerationPreviewInput,\n): AIDevtoolsGenerationPreview {\n if (input.outputKind === 'image') {\n return imagePreviewFromResult(input.result)\n }\n\n if (input.outputKind === 'audio') {\n return audioPreviewFromResult(input.result)\n }\n\n if (input.outputKind === 'video') {\n return videoPreviewFromResult(input.result, input.videoStatus)\n }\n\n if (input.outputKind === 'text') {\n return textPreviewFromResult(input.result)\n }\n\n if (input.result === null || input.result === undefined) {\n return { kind: 'empty' }\n }\n\n return {\n kind: 'structured',\n value: input.result,\n }\n}\n\ntype UnknownRecord = { [key: string]: unknown }\n\nfunction imagePreviewFromResult(result: unknown): AIDevtoolsGenerationPreview {\n const record = asRecord(result)\n const images = Array.isArray(record?.images) ? record.images : []\n const items = images\n .map((image) => mediaItemFromSource(image, 'image/png'))\n .filter(isGenerationMediaItem)\n\n if (items.length === 0 && result !== null && result !== undefined) {\n const directItem = mediaItemFromSource(result, 'image/png')\n if (directItem) {\n items.push(directItem)\n }\n }\n\n return { kind: 'image', items }\n}\n\nfunction audioPreviewFromResult(result: unknown): AIDevtoolsGenerationPreview {\n const record = asRecord(result)\n const audio = record?.audio\n const resultContentType = stringField(record, 'contentType')\n const format = stringField(record, 'format')\n const mimeType = resultContentType ?? mimeTypeFromAudioFormat(format)\n\n const items: Array<AIDevtoolsGenerationMediaItem> = []\n const directItem =\n typeof audio === 'string'\n ? base64MediaItem(audio, mimeType, {\n format,\n duration: numberField(record, 'duration'),\n })\n : mediaItemFromSource(audio, mimeType, {\n format,\n })\n\n if (directItem) {\n items.push(directItem)\n }\n\n return { kind: 'audio', items }\n}\n\nfunction videoPreviewFromResult(\n result: unknown,\n videoStatus: unknown,\n): AIDevtoolsGenerationPreview {\n const resultRecord = asRecord(result)\n const statusRecord = asRecord(videoStatus)\n const item =\n mediaItemFromSource(result, 'video/mp4') ??\n mediaItemFromSource(videoStatus, 'video/mp4')\n const items = item ? [item] : []\n const job = videoJobFromStatus(statusRecord ?? resultRecord)\n\n return {\n kind: 'video',\n items,\n ...(job ? { job } : {}),\n }\n}\n\nfunction textPreviewFromResult(result: unknown): AIDevtoolsGenerationPreview {\n const record = asRecord(result)\n const text =\n stringField(record, 'text') ??\n stringField(record, 'summary') ??\n stringField(record, 'content') ??\n (typeof result === 'string' ? result : undefined)\n\n if (text !== undefined) {\n return { kind: 'text', text }\n }\n\n if (result === null || result === undefined) {\n return { kind: 'empty' }\n }\n\n return {\n kind: 'structured',\n value: result,\n }\n}\n\nfunction videoJobFromStatus(\n record: UnknownRecord | undefined,\n): AIDevtoolsGenerationVideoJob | undefined {\n const jobId = stringField(record, 'jobId')\n if (!jobId) return undefined\n\n return {\n jobId,\n ...(stringField(record, 'status')\n ? { status: stringField(record, 'status') }\n : {}),\n ...(numberField(record, 'progress') !== undefined\n ? { progress: numberField(record, 'progress') }\n : {}),\n ...(stringField(record, 'error')\n ? { error: stringField(record, 'error') }\n : {}),\n }\n}\n\nfunction mediaItemFromSource(\n value: unknown,\n defaultMimeType: string,\n extras: {\n format?: string\n duration?: number\n } = {},\n): AIDevtoolsGenerationMediaItem | undefined {\n const record = asRecord(value)\n if (!record) return undefined\n\n const explicitContentType =\n stringField(record, 'contentType') ?? stringField(record, 'mimeType')\n const duration = numberField(record, 'duration') ?? extras.duration\n const format = stringField(record, 'format') ?? extras.format\n const url = stringField(record, 'url')\n if (url) {\n return {\n src: url,\n sourceType: 'url',\n ...(explicitContentType ? { mimeType: explicitContentType } : {}),\n ...(format ? { format } : {}),\n ...(duration !== undefined ? { duration } : {}),\n }\n }\n\n const b64Json = stringField(record, 'b64Json')\n if (!b64Json) return undefined\n\n return base64MediaItem(b64Json, explicitContentType ?? defaultMimeType, {\n format,\n duration,\n })\n}\n\nfunction base64MediaItem(\n value: string,\n mimeType: string | undefined,\n extras: {\n format?: string\n duration?: number\n } = {},\n): AIDevtoolsGenerationMediaItem {\n const src = value.startsWith('data:')\n ? value\n : `data:${mimeType ?? 'application/octet-stream'};base64,${value}`\n\n return {\n src,\n sourceType: 'base64',\n ...(mimeType ? { mimeType } : {}),\n ...(extras.format ? { format: extras.format } : {}),\n ...(extras.duration !== undefined ? { duration: extras.duration } : {}),\n }\n}\n\nfunction mimeTypeFromAudioFormat(format: string | undefined): string {\n if (!format) return 'audio/mpeg'\n if (format === 'mp3') return 'audio/mpeg'\n return `audio/${format}`\n}\n\nfunction asRecord(value: unknown): UnknownRecord | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return undefined\n }\n return value as UnknownRecord\n}\n\nfunction stringField(\n record: UnknownRecord | undefined,\n field: string,\n): string | undefined {\n const value = record?.[field]\n return typeof value === 'string' && value.length > 0 ? value : undefined\n}\n\nfunction numberField(\n record: UnknownRecord | undefined,\n field: string,\n): number | undefined {\n const value = record?.[field]\n return typeof value === 'number' ? value : undefined\n}\n\nfunction isGenerationMediaItem(\n value: AIDevtoolsGenerationMediaItem | undefined,\n): value is AIDevtoolsGenerationMediaItem {\n return Boolean(value)\n}\n\nexport interface AIDevtoolsToolFixture {\n fixtureId?: string\n hookId?: string\n threadId?: string\n runId?: string\n toolName: string\n input: unknown\n output: unknown\n execute?: boolean\n message?: {\n id: string\n role: UIMessage['role']\n parts: Array<unknown>\n createdAt?: number | string\n }\n toolCallId?: string\n messageId?: string\n errorText?: string\n}\n\ntype AIDevtoolsRunEventType =\n | 'run:created'\n | 'run:started'\n | 'run:updated'\n | 'run:completed'\n | 'run:errored'\n | 'run:cancelled'\n\ntype AIDevtoolsRunStatus =\n | 'created'\n | 'started'\n | 'updated'\n | 'completed'\n | 'errored'\n | 'cancelled'\n\nexport interface AIDevtoolsBridgeOptions<TSnapshot extends object> {\n hookId: string\n threadId?: string\n clientId: string\n metadata: AIDevtoolsClientMetadata\n getSnapshot: () => TSnapshot\n getTools?: () => Iterable<AnyClientTool>\n applyToolFixture?: (fixture: AIDevtoolsToolFixture) => void | Promise<void>\n}\n\ntype Unsubscribe = () => void\n\ninterface AIDevtoolsEvent<TPayload> {\n payload: TPayload\n}\n\ninterface ActiveDevtoolsBridge {\n deactivate: () => void\n dispose: () => void\n supersede?: () => void\n}\n\nconst activeBridgeRegistryKey = Symbol.for(\n 'tanstack.ai.devtools.activeBridgeByHookId',\n)\n\nfunction getActiveBridgeRegistry(): Map<string, ActiveDevtoolsBridge> {\n const global = globalThis as typeof globalThis & {\n [activeBridgeRegistryKey]?: Map<string, ActiveDevtoolsBridge>\n }\n const existing = global[activeBridgeRegistryKey]\n if (existing) return existing\n\n const registry = new Map<string, ActiveDevtoolsBridge>()\n global[activeBridgeRegistryKey] = registry\n return registry\n}\n\nexport class ClientDevtoolsBridge<TSnapshot extends object> {\n protected readonly options: AIDevtoolsBridgeOptions<TSnapshot>\n private readonly unsubscribers: Array<Unsubscribe> = []\n private disposed = false\n private superseded = false\n private registered = false\n\n constructor(options: AIDevtoolsBridgeOptions<TSnapshot>) {\n this.options = options\n }\n\n emitRegistered(): void {\n if (!this.prepareForMountEmit()) {\n return\n }\n this.registered = true\n emitAIDevtoolsEvent('hook:registered', {\n ...this.createEnvelope('hook:registered'),\n ...this.createMetadataPayload(),\n lifecycle: 'mounted',\n })\n }\n\n emitUpdated(): void {\n if (!this.prepareForEmit()) {\n return\n }\n emitAIDevtoolsEvent('hook:updated', {\n ...this.createEnvelope('hook:updated'),\n ...this.createMetadataPayload(),\n lifecycle: 'active',\n })\n }\n\n emitSnapshot(): void {\n if (!this.prepareForEmit()) {\n return\n }\n emitAIDevtoolsEvent('hook:state-snapshot', {\n ...this.createEnvelope('hook:state-snapshot'),\n ...this.createMetadataPayload(),\n // Wire envelope uses Record<string, unknown>; widen the typed snapshot\n // here so the typed-snapshot constraint above can stay narrow.\n // oxlint-disable-next-line eslint-js/no-restricted-syntax -- TSnapshot extends object is structurally compatible but TS can't see the missing index signature\n state: this.options.getSnapshot() as unknown as Record<string, unknown>,\n })\n }\n\n emitToolsRegistered(): void {\n if (!this.prepareForEmit()) {\n return\n }\n const tools = this.options.getTools\n ? Array.from(this.options.getTools()).map((tool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema\n ? convertSchemaToJsonSchema(tool.inputSchema)\n : { type: 'object' },\n outputSchema: tool.outputSchema\n ? convertSchemaToJsonSchema(tool.outputSchema)\n : undefined,\n needsApproval: tool.needsApproval,\n metadata: tool.metadata,\n }))\n : []\n\n emitAIDevtoolsEvent('tools:registered', {\n ...this.createEnvelope('tools:registered'),\n ...this.createMetadataPayload(),\n tools,\n })\n }\n\n emitRunLifecycle(\n eventType: AIDevtoolsRunEventType,\n runId: string,\n status: AIDevtoolsRunStatus,\n options: { error?: string } = {},\n ): void {\n if (!this.prepareForEmit()) {\n return\n }\n emitAIDevtoolsEvent(eventType, {\n ...this.createEnvelope(eventType, 'client-state', { runId }),\n runId,\n status,\n ...(options.error ? { error: options.error } : {}),\n })\n }\n\n deactivate(): void {\n const activeBridgeByHookId = getActiveBridgeRegistry()\n if (activeBridgeByHookId.get(this.options.hookId) === this) {\n activeBridgeByHookId.delete(this.options.hookId)\n }\n\n for (const unsubscribe of this.unsubscribers.splice(0)) {\n unsubscribe()\n }\n }\n\n supersede(): void {\n if (this.disposed) {\n return\n }\n\n this.superseded = true\n this.disposed = true\n this.deactivate()\n }\n\n dispose(): void {\n if (this.disposed) {\n return\n }\n\n this.disposed = true\n if (!this.registered) {\n this.deactivate()\n return\n }\n\n const live = getActiveBridgeRegistry().get(this.options.hookId)\n if (live !== this) {\n this.deactivate()\n return\n }\n\n const payload = {\n ...this.createEnvelope('hook:unregistered'),\n ...this.createMetadataPayload(),\n reason: 'disposed',\n } as const\n\n emitAIDevtoolsEvent('hook:unregistered', payload)\n\n this.deactivate()\n }\n\n private prepareForEmit(): boolean {\n if (this.disposed || this.superseded) {\n return false\n }\n this.activate()\n return true\n }\n\n private prepareForMountEmit(): boolean {\n if (this.superseded) {\n return false\n }\n\n if (this.disposed) {\n this.disposed = false\n this.registered = false\n }\n\n this.activate()\n return true\n }\n\n private activate(): void {\n if (this.disposed) {\n return\n }\n\n const activeBridgeByHookId = getActiveBridgeRegistry()\n const activeBridge = activeBridgeByHookId.get(this.options.hookId)\n if (activeBridge && activeBridge !== this) {\n if (typeof activeBridge.supersede === 'function') {\n activeBridge.supersede()\n } else {\n activeBridge.deactivate()\n }\n }\n activeBridgeByHookId.set(this.options.hookId, this)\n\n if (this.unsubscribers.length > 0) {\n return\n }\n\n this.unsubscribers.push(\n aiEventClient.on('devtools:request-state', (event) => {\n this.handleRequestState(event)\n }),\n )\n\n if (this.options.applyToolFixture) {\n this.unsubscribers.push(\n aiEventClient.on('devtools:tool-fixture:apply', (event) => {\n void this.handleToolFixtureApply(event)\n }),\n )\n }\n }\n\n private handleRequestState(\n event: AIDevtoolsEvent<{ targetHookId?: string }>,\n ): void {\n if (this.disposed || this.superseded) {\n return\n }\n\n const targetHookId = event.payload.targetHookId\n if (targetHookId && targetHookId !== this.options.hookId) {\n return\n }\n\n this.emitRegistered()\n this.emitToolsRegistered()\n this.emitSnapshot()\n this.onReplayState()\n }\n\n /**\n * Extension hook for subclasses to replay any additional cached state on a\n * `devtools:request-state` (i.e. when a panel opens). Called only after the\n * base guards (disposed/superseded/targetHookId) pass. No-op by default.\n */\n protected onReplayState(): void {}\n\n private async handleToolFixtureApply(\n event: AIDevtoolsEvent<AIDevtoolsToolFixture>,\n ): Promise<void> {\n const fixture = event.payload\n if (!this.matchesFixtureTarget(fixture)) {\n return\n }\n\n await this.options.applyToolFixture?.(fixture)\n }\n\n private matchesFixtureTarget(fixture: AIDevtoolsToolFixture): boolean {\n if (!fixture.hookId && !fixture.threadId) {\n return false\n }\n\n // Fixture routing: `hookId` wins when present (latest bridge for that\n // registry key). `threadId` is the fallback for fixtures scoped only to a\n // conversation / generation slot without a hook id.\n if (fixture.hookId) {\n return fixture.hookId === this.options.hookId\n }\n\n if (\n fixture.threadId &&\n (!this.options.threadId || fixture.threadId !== this.options.threadId)\n ) {\n return false\n }\n return true\n }\n\n protected createEnvelope(\n eventType:\n | 'hook:registered'\n | 'hook:updated'\n | 'hook:unregistered'\n | 'hook:state-snapshot'\n | 'tools:registered'\n | 'memory:retrieve:started'\n | 'memory:retrieve:completed'\n | 'memory:snapshot'\n | AIDevtoolsRunEventType,\n visibility: AIDevtoolsEventVisibility = 'client-state',\n context: { runId?: string } = {},\n ) {\n return createAIDevtoolsEventEnvelope({\n eventType,\n source: 'client',\n visibility,\n clientId: this.options.clientId,\n hookId: this.options.hookId,\n ...(this.options.threadId ? { threadId: this.options.threadId } : {}),\n ...(context.runId ? { runId: context.runId } : {}),\n timestamp: Date.now(),\n })\n }\n\n private createMetadataPayload() {\n return {\n hookId: this.options.hookId,\n hookName: this.options.metadata.hookName,\n ...(this.options.metadata.name\n ? { displayName: this.options.metadata.name }\n : {}),\n ...(this.options.metadata.outputKind\n ? { outputKind: this.options.metadata.outputKind }\n : {}),\n ...(this.options.metadata.framework\n ? { framework: this.options.metadata.framework }\n : {}),\n }\n }\n}\n\n// Owns the chat-client devtools surface so the chat client itself stays a\n// pure transport. Fixture replay, per-run / per-stream event context, and\n// snapshot emission all live here; a no-op bridge can drop in for prod.\n\nexport interface ChatDevtoolsBridgeOptions extends AIDevtoolsBridgeOptions<AIDevtoolsChatSnapshot> {\n getMessages: () => Array<UIMessage>\n setMessages: (messages: Array<UIMessage>) => void\n addToolResult: (\n toolCallId: string,\n output: unknown,\n errorText?: string,\n ) => void\n generateId: (prefix: string) => string\n}\n\nexport class ChatDevtoolsBridge extends ClientDevtoolsBridge<AIDevtoolsChatSnapshot> {\n readonly events: ChatClientEventEmitter\n private readonly chatOptions: ChatDevtoolsBridgeOptions\n private currentRunId: string | null = null\n private currentRunThreadId: string | null = null\n private currentStreamId: string | null = null\n private lastStreamId: string | null = null\n private lastRunEventContext: ChatClientRunEventContext | undefined\n /** Last transported `memory:state` value, replayed when a panel opens. */\n private lastMemoryStateValue: unknown = null\n\n constructor(options: ChatDevtoolsBridgeOptions) {\n super({\n ...options,\n // Thunk defers `this.applyFixture` lookup until after `super` returns.\n applyToolFixture: (fixture) => this.applyFixture(fixture),\n })\n this.chatOptions = options\n // Auto-attaches run/thread context and auto-emits a snapshot after each\n // event so callers can keep using `this.events.X(...)` with no context arg.\n this.events = new ChatDevtoolsAwareEventEmitter(options.clientId, this)\n }\n\n // --- Stream / run context API -------------------------------------------\n\n setCurrentStreamId(streamId: string | null): void {\n this.currentStreamId = streamId\n if (streamId) {\n this.lastStreamId = streamId\n }\n }\n\n /**\n * Called by the auto-attaching emitter every time it sees a non-empty\n * streamId pass through. Lets devtools track the latest stream id\n * without the chat client wiring it up explicitly.\n */\n recordStreamId(streamId: string): void {\n if (streamId) this.lastStreamId = streamId\n }\n\n mountWithTools(initialMessageCount: number): void {\n this.events.clientCreated(initialMessageCount)\n this.emitRegistered()\n this.emitToolsRegistered()\n this.emitSnapshot()\n }\n\n notifyToolsChanged(): void {\n this.emitToolsRegistered()\n this.emitSnapshot()\n }\n\n getCurrentStreamId(): string | null {\n return this.currentStreamId\n }\n\n getLastStreamId(): string | null {\n return this.lastStreamId\n }\n\n resolveStreamId(): string {\n return (\n this.currentStreamId ??\n this.lastStreamId ??\n this.chatOptions.generateId('stream')\n )\n }\n\n // Called when the chat client has just generated a runId for outbound emits;\n // the matching RUN_STARTED chunk from the adapter lands later and\n // observeChunk keeps the same context.\n beginRun(runId: string, threadId: string): void {\n this.currentRunId = runId\n this.currentRunThreadId = threadId\n this.lastRunEventContext = { runId, threadId }\n }\n\n observeChunk(chunk: StreamChunk): void {\n if (chunk.type === 'RUN_STARTED') {\n this.beginRun(chunk.runId, chunk.threadId)\n return\n }\n\n if (chunk.type === 'RUN_FINISHED' || chunk.type === 'RUN_ERROR') {\n const runId =\n chunk.type === 'RUN_FINISHED'\n ? chunk.runId\n : (chunk as { runId?: string }).runId\n if (!runId || runId === this.currentRunId) {\n const context = this.getCurrentRunEventContext()\n if (context) {\n this.lastRunEventContext = context\n }\n this.currentRunId = null\n this.currentRunThreadId = null\n }\n }\n }\n\n /**\n * Record a transported `memory:state` value (the server memory middleware's\n * per-turn recall metrics + store snapshot). Called from the chat client's\n * `onCustomEvent` handler — the designated path for CUSTOM stream events —\n * NOT from `observeChunk`. Re-emits the browser `memory:*` events the devtools\n * store consumes, and caches the value so a panel opened later can replay it\n * (see {@link onReplayState}). Symmetric with the generation bridge's\n * `recordResultChange` / `recordProgressChange`.\n */\n recordMemoryState(rawValue: unknown): void {\n this.lastMemoryStateValue = rawValue\n this.emitMemoryState(rawValue)\n }\n\n /**\n * Re-emit the browser-side `memory:*` events from a transported\n * `memory:state` value. The devtools store consumes these to render the\n * Memory tab (operations timeline + live contents). The recall pair mirrors\n * the server middleware's own emits; the snapshot is included only when the\n * adapter supports introspection.\n */\n private emitMemoryState(rawValue: unknown): void {\n if (!rawValue || typeof rawValue !== 'object') return\n const value = rawValue as MemoryStateEventValue\n const scope = value.scope\n const adapter = value.adapter\n if (!scope || typeof adapter !== 'string') return\n const runContext = this.currentRunId ? { runId: this.currentRunId } : {}\n\n emitAIDevtoolsEvent('memory:retrieve:started', {\n ...this.createEnvelope(\n 'memory:retrieve:started',\n 'client-state',\n runContext,\n ),\n scope,\n adapter,\n query: value.query ?? '',\n })\n if (value.recall) {\n emitAIDevtoolsEvent('memory:retrieve:completed', {\n ...this.createEnvelope(\n 'memory:retrieve:completed',\n 'client-state',\n runContext,\n ),\n scope,\n adapter,\n fragmentCount: value.recall.fragmentCount ?? 0,\n hasTools: Boolean(value.recall.hasTools),\n systemPromptChars: value.recall.systemPromptChars ?? 0,\n durationMs: value.recall.durationMs ?? 0,\n })\n }\n if (value.snapshot) {\n emitAIDevtoolsEvent('memory:snapshot', {\n ...this.createEnvelope('memory:snapshot', 'client-state', runContext),\n scope,\n adapter,\n takenAt: value.snapshot.takenAt,\n data: value.snapshot.data,\n facts: value.snapshot.facts ?? [],\n })\n }\n }\n\n protected override onReplayState(): void {\n if (this.lastMemoryStateValue != null) {\n this.emitMemoryState(this.lastMemoryStateValue)\n }\n }\n\n getCurrentRunEventContext(): ChatClientRunEventContext | undefined {\n if (!this.currentRunId) return undefined\n return {\n threadId: this.currentRunThreadId ?? this.chatOptions.threadId ?? '',\n runId: this.currentRunId,\n }\n }\n\n getCurrentOrLastRunEventContext(): ChatClientRunEventContext | undefined {\n return this.getCurrentRunEventContext() ?? this.lastRunEventContext\n }\n\n findToolCallContext(toolCallId: string): ChatClientEventContext {\n const base: ChatClientEventContext = { toolCallId }\n const runContext = this.getCurrentRunEventContext()\n if (runContext) {\n return {\n threadId: runContext.threadId,\n runId: runContext.runId,\n toolCallId,\n }\n }\n if (this.chatOptions.threadId) {\n return { threadId: this.chatOptions.threadId, toolCallId }\n }\n return base\n }\n\n // --- Fixture replay ------------------------------------------------------\n\n /**\n * Entry point invoked when the devtools panel emits\n * `devtools:tool-fixture:apply`. The chat client never calls this\n * directly; it is wired through the base bridge's fixture subscription.\n */\n async applyFixture(fixture: AIDevtoolsToolFixture): Promise<void> {\n const messages = this.chatOptions.getMessages()\n const threadId = fixture.threadId ?? this.chatOptions.threadId ?? ''\n if (fixture.execute) {\n await this.executeFixture(fixture, messages, threadId)\n return\n }\n\n const replay = this.createReplayMessageFromFixture(fixture, messages)\n const { message, toolCallId } = replay\n const messageId = message.id\n\n this.events.messageAppended(message, undefined, {\n threadId,\n toolCallId,\n ...(fixture.runId ? { runId: fixture.runId } : {}),\n })\n this.chatOptions.setMessages([...messages, message])\n this.events.toolFixtureApplied({\n hookId: this.chatOptions.hookId,\n threadId,\n ...(fixture.runId ? { runId: fixture.runId } : {}),\n toolName: fixture.toolName,\n input: fixture.input,\n output: fixture.output,\n messageId,\n toolCallId,\n ...(fixture.execute !== undefined ? { execute: fixture.execute } : {}),\n ...(fixture.message ? { message: fixture.message } : {}),\n ...(fixture.errorText ? { errorText: fixture.errorText } : {}),\n })\n this.emitSnapshot()\n }\n\n private async executeFixture(\n fixture: AIDevtoolsToolFixture,\n messages: Array<UIMessage>,\n threadId: string,\n ): Promise<void> {\n const toolCallId = this.resolveFixtureToolCallId(\n fixture.toolCallId,\n messages,\n )\n const messageId = this.resolveFixtureMessageId(fixture.messageId, messages)\n const message: UIMessage = {\n id: messageId,\n role: 'assistant',\n parts: [\n {\n type: 'tool-call',\n id: toolCallId,\n name: fixture.toolName,\n arguments: stringifyFixtureValue(fixture.input),\n input: fixture.input,\n state: 'input-complete',\n },\n ],\n createdAt: new Date(),\n }\n\n this.events.messageAppended(message, undefined, {\n threadId,\n toolCallId,\n ...(fixture.runId ? { runId: fixture.runId } : {}),\n })\n this.chatOptions.setMessages([...messages, message])\n this.emitSnapshot()\n\n const clientTool = this.findClientTool(fixture.toolName)\n const executeFunc = clientTool?.execute\n if (!executeFunc) {\n console.warn(\n `[ai-devtools] tool fixture \"${fixture.toolName}\" requested execute=true but no client tool implementation is registered; replaying saved output instead.`,\n )\n this.addToolResultForFixture({\n fixture,\n messageId,\n toolCallId,\n threadId,\n output: fixture.output,\n errorText: fixture.errorText,\n })\n return\n }\n\n let output: unknown\n try {\n output = await executeFunc(fixture.input)\n } catch (error) {\n console.error(\n `[ai-devtools] tool fixture \"${fixture.toolName}\" execute threw`,\n error,\n )\n this.addToolResultForFixture({\n fixture,\n messageId,\n toolCallId,\n threadId,\n output: null,\n errorText:\n error instanceof Error\n ? `${error.name}: ${error.message}`\n : `Tool execution failed: ${String(error)}`,\n })\n return\n }\n this.addToolResultForFixture({\n fixture,\n messageId,\n toolCallId,\n threadId,\n output,\n })\n }\n\n private addToolResultForFixture(input: {\n fixture: AIDevtoolsToolFixture\n messageId: string\n toolCallId: string\n threadId: string\n output: unknown\n errorText?: string\n }): void {\n const state = input.errorText ? 'output-error' : 'output-available'\n this.events.toolResultAdded(\n input.toolCallId,\n input.fixture.toolName,\n input.output,\n state,\n {\n threadId: input.threadId,\n ...(input.fixture.runId ? { runId: input.fixture.runId } : {}),\n toolCallId: input.toolCallId,\n },\n )\n this.chatOptions.addToolResult(\n input.toolCallId,\n input.output,\n input.errorText,\n )\n this.events.toolFixtureApplied({\n hookId: this.chatOptions.hookId,\n threadId: input.threadId,\n ...(input.fixture.runId ? { runId: input.fixture.runId } : {}),\n toolName: input.fixture.toolName,\n input: input.fixture.input,\n output: input.output,\n execute: true,\n messageId: input.messageId,\n toolCallId: input.toolCallId,\n ...(input.errorText ? { errorText: input.errorText } : {}),\n })\n this.emitSnapshot()\n }\n\n private createReplayMessageFromFixture(\n fixture: AIDevtoolsToolFixture,\n messages: Array<UIMessage>,\n ): { message: UIMessage; toolCallId: string } {\n const cloned = this.cloneFixtureSourceMessage(fixture, messages)\n if (cloned) return cloned\n\n const toolCallId = this.resolveFixtureToolCallId(\n fixture.toolCallId,\n messages,\n )\n const messageId = this.resolveFixtureMessageId(fixture.messageId, messages)\n const state = fixture.errorText ? 'error' : 'complete'\n\n return {\n toolCallId,\n message: {\n id: messageId,\n role: 'assistant',\n parts: [\n {\n type: 'tool-call',\n id: toolCallId,\n name: fixture.toolName,\n arguments: stringifyFixtureValue(fixture.input),\n input: fixture.input,\n state: 'input-complete',\n output: fixture.output,\n },\n {\n type: 'tool-result',\n toolCallId,\n content: stringifyFixtureValue(fixture.output),\n state,\n ...(fixture.errorText ? { error: fixture.errorText } : {}),\n },\n ],\n createdAt: new Date(),\n },\n }\n }\n\n private cloneFixtureSourceMessage(\n fixture: AIDevtoolsToolFixture,\n messages: Array<UIMessage>,\n ): { message: UIMessage; toolCallId: string } | undefined {\n const sourceMessage = fixture.message\n if (!sourceMessage || !Array.isArray(sourceMessage.parts)) {\n return undefined\n }\n\n const toolCallIds = this.createFixtureToolCallIdMap(\n sourceMessage.parts,\n messages,\n )\n const parts = sourceMessage.parts\n .map((part) => cloneFixtureMessagePart(part, toolCallIds))\n .filter((part): part is MessagePart => Boolean(part))\n const mappedFixtureToolCallId = fixture.toolCallId\n ? toolCallIds.get(fixture.toolCallId)\n : undefined\n hydrateToolCallOutputs(parts, {\n ...(mappedFixtureToolCallId\n ? { mappedToolCallId: mappedFixtureToolCallId }\n : {}),\n output: fixture.output,\n })\n\n if (parts.length === 0) return undefined\n\n const toolCallId =\n (fixture.toolCallId ? toolCallIds.get(fixture.toolCallId) : undefined) ??\n firstToolCallId(parts)\n if (!toolCallId) return undefined\n\n return {\n toolCallId,\n message: {\n id: this.resolveFixtureMessageId(sourceMessage.id, messages),\n role: sourceMessage.role,\n parts,\n createdAt: new Date(),\n },\n }\n }\n\n private createFixtureToolCallIdMap(\n parts: Array<unknown>,\n messages: Array<UIMessage>,\n ): Map<string, string> {\n const ids = new Map<string, string>()\n for (const part of parts) {\n if (!isRecord(part) || part.type !== 'tool-call') continue\n if (typeof part.id !== 'string') continue\n ids.set(part.id, this.resolveFixtureToolCallId(part.id, messages))\n }\n return ids\n }\n\n private resolveFixtureMessageId(\n messageId: string | undefined,\n messages: Array<UIMessage>,\n ): string {\n if (messageId && !messages.some((message) => message.id === messageId)) {\n return messageId\n }\n return this.chatOptions.generateId('fixture-msg')\n }\n\n private resolveFixtureToolCallId(\n toolCallId: string | undefined,\n messages: Array<UIMessage>,\n ): string {\n if (toolCallId && !hasToolCallId(messages, toolCallId)) {\n return toolCallId\n }\n return this.chatOptions.generateId('fixture-tool-call')\n }\n\n private findClientTool(name: string): AnyClientTool | undefined {\n const tools = this.chatOptions.getTools?.()\n if (!tools) return undefined\n for (const tool of tools) {\n if (tool.name === name) return tool\n }\n return undefined\n }\n}\n\n// ---- Module-level fixture helpers (pure; share no state) -------------------\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\nfunction stringifyFixtureValue(value: unknown): string {\n if (typeof value === 'string') return value\n if (\n value === undefined ||\n typeof value === 'function' ||\n typeof value === 'symbol'\n ) {\n return String(value)\n }\n try {\n return JSON.stringify(value)\n } catch (error) {\n console.error(\n '[ai-devtools] failed to JSON.stringify fixture value; falling back to String(). Tool call arguments may be malformed.',\n { error, value },\n )\n return String(value)\n }\n}\n\nfunction parseFixtureResultContent(content: string): unknown {\n try {\n return JSON.parse(content)\n } catch (error) {\n console.error(\n '[ai-devtools] failed to JSON.parse fixture result content; replaying as raw string. Fixture payload may be corrupted.',\n { error, content },\n )\n return content\n }\n}\n\nfunction cloneFixtureMessagePart(\n part: unknown,\n toolCallIds: Map<string, string>,\n): MessagePart | undefined {\n if (!isRecord(part) || typeof part.type !== 'string') return undefined\n const cloned: Record<string, unknown> = { ...part }\n\n if (part.type === 'tool-call' && typeof part.id === 'string') {\n cloned.id = toolCallIds.get(part.id) ?? part.id\n }\n if (part.type === 'tool-result' && typeof part.toolCallId === 'string') {\n cloned.toolCallId = toolCallIds.get(part.toolCallId) ?? part.toolCallId\n }\n return cloned as MessagePart\n}\n\nfunction firstToolCallId(parts: Array<MessagePart>): string | undefined {\n const toolCall = parts.find((part) => part.type === 'tool-call')\n return toolCall?.type === 'tool-call' ? toolCall.id : undefined\n}\n\nfunction hydrateToolCallOutputs(\n parts: Array<MessagePart>,\n fixtureOutput: { mappedToolCallId?: string; output: unknown },\n): void {\n for (const part of parts) {\n if (part.type !== 'tool-result') continue\n const toolCall = parts.find(\n (candidate): candidate is ToolCallPart =>\n candidate.type === 'tool-call' &&\n candidate.id === part.toolCallId &&\n candidate.output === undefined,\n )\n if (toolCall) {\n toolCall.output = Array.isArray(part.content)\n ? part.content\n : parseFixtureResultContent(part.content)\n }\n }\n\n if (fixtureOutput.mappedToolCallId && fixtureOutput.output !== undefined) {\n const toolCall = parts.find(\n (candidate): candidate is ToolCallPart =>\n candidate.type === 'tool-call' &&\n candidate.id === fixtureOutput.mappedToolCallId &&\n candidate.output === undefined,\n )\n if (toolCall) {\n toolCall.output = fixtureOutput.output\n }\n }\n}\n\nfunction hasToolCallId(\n messages: Array<UIMessage>,\n toolCallId: string,\n): boolean {\n return messages.some((message) =>\n message.parts.some((part) => {\n if (part.type === 'tool-call') return part.id === toolCallId\n if (part.type === 'tool-result') return part.toolCallId === toolCallId\n return false\n }),\n )\n}\n\n// Devtools surface for GenerationClient / VideoGenerationClient. Owns per-run\n// history, active-run lifecycle, and snapshot emission; the generation client\n// pushes its core state in via the record* methods.\n\nexport interface AIDevtoolsGenerationSnapshotBase<TOutput> {\n input: unknown\n result: TOutput | null\n preview: AIDevtoolsGenerationPreview\n progress: AIDevtoolsGenerationProgress | null\n status: AIDevtoolsGenerationRunStatus\n isLoading: boolean\n activeRunId: string | null\n runs: Array<AIDevtoolsGenerationRunSnapshot<TOutput>>\n error?: string\n}\n\nexport interface GenerationDevtoolsBridgeOptions<TOutput> extends Omit<\n AIDevtoolsBridgeOptions<AIDevtoolsGenerationSnapshotBase<TOutput>>,\n 'getSnapshot'\n> {\n getCoreState: () => GenerationDevtoolsCoreState<TOutput>\n maxRuns?: number\n}\n\nexport interface GenerationDevtoolsCoreState<TOutput> {\n input: unknown\n result: TOutput | null\n progress: AIDevtoolsGenerationProgress | null\n status: AIDevtoolsGenerationRunStatus\n isLoading: boolean\n error?: string\n}\n\nexport interface GenerationRunPatch<TOutput> {\n input?: unknown\n result?: TOutput | null\n preview?: AIDevtoolsGenerationPreview\n progress?: AIDevtoolsGenerationProgress | null\n status?: AIDevtoolsGenerationRunStatus\n isLoading?: boolean\n completedAt?: number\n error?: string\n clearError?: boolean\n}\n\nexport class GenerationDevtoolsBridge<TOutput> extends ClientDevtoolsBridge<\n AIDevtoolsGenerationSnapshotBase<TOutput>\n> {\n protected activeRunId: string | null = null\n protected activeRunStarted = false\n protected devtoolsRuns: Array<AIDevtoolsGenerationRunSnapshot<TOutput>> = []\n protected readonly maxRuns: number\n protected readonly getCoreState: () => GenerationDevtoolsCoreState<TOutput>\n\n constructor(options: GenerationDevtoolsBridgeOptions<TOutput>) {\n super({\n ...options,\n getSnapshot: () => this.buildSnapshot(),\n })\n this.maxRuns = options.maxRuns ?? 20\n this.getCoreState = options.getCoreState\n }\n\n // --- Run lifecycle (called by GenerationClient) -----------------------\n\n beginRun(input: unknown): string {\n const runId = this.generateRunId()\n this.activeRunId = runId\n this.activeRunStarted = false\n this.upsertRun(runId, {\n input,\n result: null,\n preview: this.createPreview(null),\n progress: null,\n status: 'generating',\n isLoading: true,\n clearError: true,\n })\n return runId\n }\n\n ensureRunStarted(runId: string): void {\n if (this.activeRunStarted && this.activeRunId === runId) return\n\n if (\n !this.activeRunStarted &&\n this.activeRunId &&\n this.activeRunId !== runId\n ) {\n this.renameRun(this.activeRunId, runId)\n }\n\n this.activeRunId = runId\n this.activeRunStarted = true\n this.upsertRun(runId, {\n status: 'generating',\n isLoading: true,\n clearError: true,\n })\n this.emitRunLifecycle('run:started', runId, 'started')\n this.emitState()\n }\n\n finishRun(\n runId: string,\n eventType: 'run:completed' | 'run:errored' | 'run:cancelled',\n status: 'completed' | 'errored' | 'cancelled',\n error?: string,\n ): void {\n this.ensureRunStarted(runId)\n const completedAt = Date.now()\n const completedProgress =\n status === 'completed' ? this.completeProgress() : this.getProgress()\n const runStatus =\n status === 'completed'\n ? 'success'\n : status === 'errored'\n ? 'error'\n : 'cancelled'\n\n this.upsertRun(runId, {\n status: runStatus,\n isLoading: false,\n progress: completedProgress,\n completedAt,\n ...(error ? { error } : { clearError: true }),\n })\n\n if (this.activeRunId === runId) {\n this.activeRunId = null\n }\n this.activeRunStarted = false\n this.emitRunLifecycle(eventType, runId, status, {\n ...(error ? { error } : {}),\n })\n this.emitState()\n }\n\n getActiveRunId(): string | null {\n return this.activeRunId\n }\n\n /** Clear all per-run history. Called when the client `reset()`s. */\n resetRuns(): void {\n this.activeRunId = null\n this.activeRunStarted = false\n this.devtoolsRuns = []\n }\n\n /** Record state changes from the client and emit the matching snapshot. */\n recordResultChange(): void {\n this.updateActiveRun({\n result: this.getCoreState().result,\n preview: this.createPreview(this.getCoreState().result),\n clearError: true,\n })\n this.emitState()\n }\n\n recordLoadingChange(): void {\n this.updateActiveRun({ isLoading: this.getCoreState().isLoading })\n this.emitState()\n }\n\n recordErrorChange(error: Error | undefined): void {\n this.updateActiveRun(\n error ? { error: error.message } : { clearError: true },\n )\n this.emitState()\n }\n\n recordStatusChange(status: AIDevtoolsGenerationRunStatus): void {\n this.updateActiveRun({ status })\n this.emitState()\n }\n\n recordProgressChange(): void {\n this.updateActiveRun({ progress: this.getCoreState().progress })\n this.emitState()\n }\n\n /** Emit the latest snapshot without touching run state. */\n emitState(): void {\n this.emitUpdated()\n this.emitSnapshot()\n }\n\n // --- Internal ---------------------------------------------------------\n\n protected buildSnapshot(): AIDevtoolsGenerationSnapshotBase<TOutput> {\n const core = this.getCoreState()\n return {\n input: core.input,\n result: core.result,\n preview: this.createPreview(core.result),\n progress: core.progress,\n status: core.status,\n isLoading: core.isLoading,\n activeRunId: this.activeRunId,\n runs: this.devtoolsRuns,\n ...(core.error ? { error: core.error } : {}),\n }\n }\n\n protected updateActiveRun(patch: GenerationRunPatch<TOutput>): void {\n if (!this.activeRunId) return\n this.upsertRun(this.activeRunId, patch)\n }\n\n protected upsertRun(runId: string, patch: GenerationRunPatch<TOutput>): void {\n const now = Date.now()\n const index = this.devtoolsRuns.findIndex((run) => run.id === runId)\n const existing = index >= 0 ? this.devtoolsRuns[index] : undefined\n const next: AIDevtoolsGenerationRunSnapshot<TOutput> = existing\n ? { ...existing }\n : {\n id: runId,\n input: this.getCoreState().input,\n result: null,\n preview: this.createPreview(null),\n progress: null,\n status: 'idle',\n isLoading: false,\n startedAt: now,\n updatedAt: now,\n }\n\n if ('input' in patch) next.input = patch.input ?? null\n if ('result' in patch) next.result = patch.result ?? null\n if (patch.preview) next.preview = patch.preview\n if ('progress' in patch) next.progress = patch.progress ?? null\n if (patch.status) next.status = patch.status\n if ('isLoading' in patch) next.isLoading = patch.isLoading === true\n if (patch.completedAt !== undefined) next.completedAt = patch.completedAt\n if (patch.clearError) delete next.error\n if (patch.error !== undefined) next.error = patch.error\n next.updatedAt = now\n\n if (index >= 0) {\n this.devtoolsRuns = this.devtoolsRuns.map((run) =>\n run.id === runId ? next : run,\n )\n } else {\n this.devtoolsRuns = [...this.devtoolsRuns, next]\n }\n\n if (this.devtoolsRuns.length > this.maxRuns) {\n this.devtoolsRuns = this.devtoolsRuns.slice(-this.maxRuns)\n }\n }\n\n protected renameRun(previousRunId: string, nextRunId: string): void {\n if (previousRunId === nextRunId) return\n\n const existing = this.devtoolsRuns.find((run) => run.id === previousRunId)\n if (!existing) return\n\n const renamed = { ...existing, id: nextRunId, updatedAt: Date.now() }\n this.devtoolsRuns = this.devtoolsRuns\n .filter((run) => run.id !== nextRunId)\n .map((run) => (run.id === previousRunId ? renamed : run))\n }\n\n protected getProgress(): AIDevtoolsGenerationProgress | null {\n return this.getCoreState().progress\n }\n\n protected completeProgress(): AIDevtoolsGenerationProgress | null {\n const progress = this.getCoreState().progress\n if (!progress) return null\n return {\n value: 100,\n ...(progress.message ? { message: progress.message } : {}),\n }\n }\n\n protected createPreview(result: TOutput | null): AIDevtoolsGenerationPreview {\n return createAIDevtoolsGenerationPreview({\n outputKind: this.options.metadata.outputKind,\n result,\n })\n }\n\n protected generateRunId(): string {\n return `run-${Date.now()}-${Math.random().toString(36).substring(7)}`\n }\n}\n\n// Video-job specialization: snapshots also carry the job id and the latest\n// provider-reported video status so the panel can show streaming progress\n// before the final URL lands.\n\nexport interface AIDevtoolsVideoSnapshotBase<\n TOutput,\n> extends AIDevtoolsGenerationSnapshotBase<TOutput> {\n jobId: string | null\n videoStatus: unknown\n}\n\nexport interface VideoDevtoolsCoreState<\n TOutput,\n> extends GenerationDevtoolsCoreState<TOutput> {\n jobId: string | null\n videoStatus: unknown\n}\n\nexport interface VideoDevtoolsBridgeOptions<TOutput> extends Omit<\n GenerationDevtoolsBridgeOptions<TOutput>,\n 'getCoreState'\n> {\n getCoreState: () => VideoDevtoolsCoreState<TOutput>\n}\n\nexport interface VideoRunPatch<TOutput> extends GenerationRunPatch<TOutput> {\n jobId?: string | null\n videoStatus?: unknown\n}\n\nexport class VideoDevtoolsBridge<\n TOutput,\n> extends GenerationDevtoolsBridge<TOutput> {\n constructor(options: VideoDevtoolsBridgeOptions<TOutput>) {\n super(options)\n }\n\n recordJobIdChange(): void {\n this.updateActiveRun({\n jobId: (this.getCoreState() as VideoDevtoolsCoreState<TOutput>).jobId,\n } as VideoRunPatch<TOutput>)\n this.emitState()\n }\n\n recordVideoStatusChange(): void {\n const core = this.getCoreState() as VideoDevtoolsCoreState<TOutput>\n this.updateActiveRun({\n videoStatus: core.videoStatus,\n preview: this.createVideoPreview(core.result, core.videoStatus),\n } as VideoRunPatch<TOutput>)\n this.emitState()\n }\n\n protected override buildSnapshot(): AIDevtoolsVideoSnapshotBase<TOutput> {\n const core = this.getCoreState() as VideoDevtoolsCoreState<TOutput>\n return {\n input: core.input,\n result: core.result,\n preview: this.createVideoPreview(core.result, core.videoStatus),\n progress: core.progress,\n status: core.status,\n isLoading: core.isLoading,\n activeRunId: this.activeRunId,\n runs: this.devtoolsRuns,\n jobId: core.jobId,\n videoStatus: core.videoStatus,\n ...(core.error ? { error: core.error } : {}),\n }\n }\n\n protected override upsertRun(\n runId: string,\n patch: VideoRunPatch<TOutput>,\n ): void {\n super.upsertRun(runId, patch)\n if (!('jobId' in patch || 'videoStatus' in patch)) return\n\n const index = this.devtoolsRuns.findIndex((run) => run.id === runId)\n if (index < 0) return\n const target = this.devtoolsRuns[index]\n if (!target) return\n const merged: AIDevtoolsGenerationRunSnapshot<TOutput> = { ...target }\n if ('jobId' in patch) merged.jobId = patch.jobId ?? null\n if ('videoStatus' in patch) merged.videoStatus = patch.videoStatus ?? null\n this.devtoolsRuns = this.devtoolsRuns.map((run) =>\n run.id === runId ? merged : run,\n )\n }\n\n // Override so record* methods inherited from GenerationDevtoolsBridge\n // (e.g. recordResultChange) thread the latest videoStatus into the preview.\n protected override createPreview(\n result: TOutput | null,\n ): AIDevtoolsGenerationPreview {\n const core = this.getCoreState() as VideoDevtoolsCoreState<TOutput>\n return this.createVideoPreview(result, core.videoStatus)\n }\n\n private createVideoPreview(\n result: TOutput | null,\n videoStatus: unknown,\n ): AIDevtoolsGenerationPreview {\n return createAIDevtoolsGenerationPreview({\n outputKind: this.options.metadata.outputKind,\n result,\n videoStatus,\n })\n }\n}\n\n// Wraps the plain emitter so callers can do `this.events.X(...)` and get:\n// auto-attached run/thread context on every event that accepts one,\n// an auto-emitted snapshot after each event, and passive streamId tracking\n// so resolveStreamId() works without the chat client telling it.\nclass ChatDevtoolsAwareEventEmitter extends DefaultChatClientEventEmitter {\n constructor(\n clientId: string,\n private readonly helper: ChatDevtoolsBridge,\n ) {\n super(clientId)\n }\n\n private afterEmit(streamId?: string): void {\n if (streamId) this.helper.recordStreamId(streamId)\n this.helper.emitSnapshot()\n }\n\n // -- methods with run context --------------------------------------------\n\n override textUpdated(\n streamId: string,\n messageId: string,\n content: string,\n context?: ChatClientRunEventContext,\n ): void {\n super.textUpdated(\n streamId,\n messageId,\n content,\n context ?? this.helper.getCurrentRunEventContext(),\n )\n this.afterEmit(streamId)\n }\n\n override thinkingUpdated(\n streamId: string,\n messageId: string,\n content: string,\n delta?: string,\n context?: ChatClientRunEventContext,\n ): void {\n super.thinkingUpdated(\n streamId,\n messageId,\n content,\n delta,\n context ?? this.helper.getCurrentRunEventContext(),\n )\n this.afterEmit(streamId)\n }\n\n override messageAppended(\n uiMessage: Parameters<DefaultChatClientEventEmitter['messageAppended']>[0],\n streamId?: string,\n context?: ChatClientEventContext,\n ): void {\n super.messageAppended(\n uiMessage,\n streamId,\n context ?? this.helper.getCurrentRunEventContext(),\n )\n this.afterEmit(streamId)\n }\n\n override toolCallStateChanged(\n streamId: string,\n messageId: string,\n toolCallId: string,\n toolName: string,\n state: string,\n args: string,\n context?: ChatClientRunEventContext,\n ): void {\n super.toolCallStateChanged(\n streamId,\n messageId,\n toolCallId,\n toolName,\n state,\n args,\n context ?? this.helper.getCurrentRunEventContext(),\n )\n this.afterEmit(streamId)\n }\n\n override structuredOutputChanged(\n eventName: Parameters<\n DefaultChatClientEventEmitter['structuredOutputChanged']\n >[0],\n streamId: string,\n messageId: string,\n output: Parameters<\n DefaultChatClientEventEmitter['structuredOutputChanged']\n >[3],\n context?: ChatClientRunEventContext,\n ): void {\n super.structuredOutputChanged(\n eventName,\n streamId,\n messageId,\n output,\n context ?? this.helper.getCurrentOrLastRunEventContext(),\n )\n this.afterEmit(streamId)\n }\n\n override approvalRequested(\n streamId: string,\n messageId: string,\n toolCallId: string,\n toolName: string,\n input: unknown,\n approvalId: string,\n context?: ChatClientRunEventContext,\n ): void {\n super.approvalRequested(\n streamId,\n messageId,\n toolCallId,\n toolName,\n input,\n approvalId,\n context ?? this.helper.getCurrentOrLastRunEventContext(),\n )\n this.afterEmit(streamId)\n }\n\n override toolResultAdded(\n toolCallId: string,\n toolName: string,\n output: unknown,\n state: string,\n context?: ChatClientEventContext,\n ): void {\n super.toolResultAdded(\n toolCallId,\n toolName,\n output,\n state,\n context ?? this.helper.getCurrentRunEventContext(),\n )\n this.afterEmit()\n }\n\n override toolApprovalResponded(\n approvalId: string,\n toolCallId: string,\n approved: boolean,\n context?: ChatClientRunEventContext,\n ): void {\n super.toolApprovalResponded(\n approvalId,\n toolCallId,\n approved,\n context ?? this.helper.getCurrentRunEventContext(),\n )\n this.afterEmit()\n }\n\n // -- methods without context (just auto-emit snapshot) -------------------\n\n override clientCreated(initialMessageCount: number): void {\n super.clientCreated(initialMessageCount)\n this.afterEmit()\n }\n override loadingChanged(isLoading: boolean): void {\n super.loadingChanged(isLoading)\n this.afterEmit()\n }\n override errorChanged(error: string | null): void {\n super.errorChanged(error)\n this.afterEmit()\n }\n override reloaded(fromMessageIndex: number): void {\n super.reloaded(fromMessageIndex)\n this.afterEmit()\n }\n override stopped(): void {\n super.stopped()\n this.afterEmit()\n }\n override messagesCleared(): void {\n super.messagesCleared()\n this.afterEmit()\n }\n override messageSent(\n messageId: string,\n content: Parameters<DefaultChatClientEventEmitter['messageSent']>[1],\n ): void {\n super.messageSent(messageId, content)\n this.afterEmit()\n }\n override toolFixtureApplied(\n fixture: Parameters<DefaultChatClientEventEmitter['toolFixtureApplied']>[0],\n ): void {\n super.toolFixtureApplied(fixture)\n this.afterEmit()\n }\n}\n\nexport function createChatDevtoolsBridge(\n options: ChatDevtoolsBridgeOptions,\n): ChatDevtoolsBridge {\n return new ChatDevtoolsBridge(options)\n}\n\nexport function createGenerationDevtoolsBridge<TOutput>(\n options: GenerationDevtoolsBridgeOptions<TOutput>,\n): GenerationDevtoolsBridge<TOutput> {\n return new GenerationDevtoolsBridge<TOutput>(options)\n}\n\nexport function createVideoDevtoolsBridge<TOutput>(\n options: VideoDevtoolsBridgeOptions<TOutput>,\n): VideoDevtoolsBridge<TOutput> {\n return new VideoDevtoolsBridge<TOutput>(options)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"devtools.js","names":[],"sources":["../../src/devtools.ts"],"sourcesContent":["import {\n aiEventClient,\n createAIDevtoolsEventEnvelope,\n emitAIDevtoolsEvent,\n} from '@tanstack/ai-event-client'\nimport { convertSchemaToJsonSchema } from '@tanstack/ai/client'\nimport { DefaultChatClientEventEmitter } from './events'\nimport type { AnyClientTool, StreamChunk } from '@tanstack/ai/client'\nimport type {\n AIDevtoolsEventVisibility,\n CompactionMessagePreview,\n MemoryScopeLite,\n} from '@tanstack/ai-event-client'\nimport type {\n ChatClientEventContext,\n ChatClientEventEmitter,\n ChatClientRunEventContext,\n} from './events'\nimport type {\n ChatClientState,\n ConnectionStatus,\n MessagePart,\n QueuedMessage,\n ToolCallPart,\n UIMessage,\n} from './types'\n\nexport interface AIDevtoolsDisplayOptions {\n name?: string\n}\n\n/**\n * Structural mirror of `MemoryStateEventValue` from `@tanstack/ai-memory` — the\n * payload of the `memory:state` CUSTOM chunk. Kept local so `ai-client` doesn't\n * depend on `ai-memory`; the memory middleware is the producer.\n */\ninterface SkillsStateEventValue {\n catalog?: Array<{ name: string; description: string }>\n activated?: Array<string>\n}\n\ninterface MemoryStateEventValue {\n scope: MemoryScopeLite\n adapter: string\n query?: string\n recall?: {\n fragmentCount?: number\n hasTools?: boolean\n systemPromptChars?: number\n durationMs?: number\n }\n snapshot?: {\n takenAt: string\n data: unknown\n facts?: Array<{\n id: string\n text: string\n source?: string\n createdAt?: string\n }>\n }\n}\n\nfunction readCompactionBoundaryValue(rawValue: unknown): {\n before?: number\n after?: number\n messagesBefore?: number\n messagesAfter?: number\n reusedCheckpoint?: boolean\n maxTokens?: number\n strategyKey?: string\n durationMs?: number\n} {\n if (!rawValue || typeof rawValue !== 'object') return {}\n return {\n ...('before' in rawValue && typeof rawValue.before === 'number'\n ? { before: rawValue.before }\n : {}),\n ...('after' in rawValue && typeof rawValue.after === 'number'\n ? { after: rawValue.after }\n : {}),\n ...('messagesBefore' in rawValue &&\n typeof rawValue.messagesBefore === 'number'\n ? { messagesBefore: rawValue.messagesBefore }\n : {}),\n ...('messagesAfter' in rawValue &&\n typeof rawValue.messagesAfter === 'number'\n ? { messagesAfter: rawValue.messagesAfter }\n : {}),\n ...('reusedCheckpoint' in rawValue &&\n typeof rawValue.reusedCheckpoint === 'boolean'\n ? { reusedCheckpoint: rawValue.reusedCheckpoint }\n : {}),\n ...('maxTokens' in rawValue && typeof rawValue.maxTokens === 'number'\n ? { maxTokens: rawValue.maxTokens }\n : {}),\n ...('strategyKey' in rawValue && typeof rawValue.strategyKey === 'string'\n ? { strategyKey: rawValue.strategyKey }\n : {}),\n ...('durationMs' in rawValue && typeof rawValue.durationMs === 'number'\n ? { durationMs: rawValue.durationMs }\n : {}),\n }\n}\n\nfunction readPreviewList(\n value: unknown,\n): Array<CompactionMessagePreview> | undefined {\n if (!Array.isArray(value)) return undefined\n const previews: Array<CompactionMessagePreview> = []\n for (const item of value) {\n if (!item || typeof item !== 'object') continue\n if (!('role' in item) || !('tokens' in item) || !('text' in item)) continue\n if (\n typeof item.role !== 'string' ||\n typeof item.tokens !== 'number' ||\n typeof item.text !== 'string'\n ) {\n continue\n }\n previews.push({\n role: item.role,\n tokens: item.tokens,\n text: item.text,\n })\n }\n return previews\n}\n\nfunction readCompactionStateValue(rawValue: unknown): {\n before: number\n after: number\n messagesBefore: number\n messagesAfter: number\n reusedCheckpoint: boolean\n maxTokens?: number\n strategyKey?: string\n dropped?: Array<CompactionMessagePreview>\n result?: Array<CompactionMessagePreview>\n} | null {\n if (!rawValue || typeof rawValue !== 'object') return null\n if (\n !('before' in rawValue) ||\n !('after' in rawValue) ||\n !('messagesBefore' in rawValue) ||\n !('messagesAfter' in rawValue)\n ) {\n return null\n }\n if (\n typeof rawValue.before !== 'number' ||\n typeof rawValue.after !== 'number' ||\n typeof rawValue.messagesBefore !== 'number' ||\n typeof rawValue.messagesAfter !== 'number'\n ) {\n return null\n }\n const reusedCheckpoint =\n 'reusedCheckpoint' in rawValue && rawValue.reusedCheckpoint === true\n const maxTokens =\n 'maxTokens' in rawValue && typeof rawValue.maxTokens === 'number'\n ? rawValue.maxTokens\n : undefined\n const strategyKey =\n 'strategyKey' in rawValue && typeof rawValue.strategyKey === 'string'\n ? rawValue.strategyKey\n : undefined\n const dropped =\n 'dropped' in rawValue ? readPreviewList(rawValue.dropped) : undefined\n const result =\n 'result' in rawValue ? readPreviewList(rawValue.result) : undefined\n return {\n before: rawValue.before,\n after: rawValue.after,\n messagesBefore: rawValue.messagesBefore,\n messagesAfter: rawValue.messagesAfter,\n reusedCheckpoint,\n ...(maxTokens !== undefined ? { maxTokens } : {}),\n ...(strategyKey ? { strategyKey } : {}),\n ...(dropped ? { dropped } : {}),\n ...(result ? { result } : {}),\n }\n}\n\nexport interface AIDevtoolsClientMetadata extends AIDevtoolsDisplayOptions {\n framework?: string\n hookName: string\n outputKind?: 'chat' | 'text' | 'structured' | 'image' | 'video' | 'audio'\n}\n\nexport interface AIDevtoolsGenerationProgress {\n value: number\n message?: string\n}\n\nexport interface AIDevtoolsGenerationMediaItem {\n src: string\n sourceType: 'url' | 'base64'\n mimeType?: string\n format?: string\n duration?: number\n}\n\nexport interface AIDevtoolsGenerationVideoJob {\n jobId: string\n status?: string\n progress?: number\n error?: string\n}\n\nexport type AIDevtoolsGenerationPreview =\n | {\n kind: 'image'\n items: Array<AIDevtoolsGenerationMediaItem>\n }\n | {\n kind: 'audio'\n items: Array<AIDevtoolsGenerationMediaItem>\n }\n | {\n kind: 'video'\n items: Array<AIDevtoolsGenerationMediaItem>\n job?: AIDevtoolsGenerationVideoJob\n }\n | {\n kind: 'text'\n text: string\n }\n | {\n kind: 'structured'\n value: unknown\n }\n | {\n kind: 'empty'\n }\n\nexport type AIDevtoolsGenerationRunStatus =\n | 'idle'\n | 'generating'\n | 'success'\n | 'error'\n | 'cancelled'\n\nexport interface AIDevtoolsGenerationRunSnapshot<TOutput = unknown> {\n id: string\n input: unknown\n result: TOutput | null\n preview: AIDevtoolsGenerationPreview\n progress: AIDevtoolsGenerationProgress | null\n status: AIDevtoolsGenerationRunStatus\n isLoading: boolean\n startedAt: number\n updatedAt: number\n completedAt?: number\n error?: string\n jobId?: string | null\n videoStatus?: unknown\n}\n\nexport interface AIDevtoolsGenerationPreviewInput {\n outputKind?: AIDevtoolsClientMetadata['outputKind']\n result: unknown\n videoStatus?: unknown\n}\n\nexport interface AIDevtoolsChatSnapshot {\n [key: string]: unknown\n messages: Array<UIMessage>\n status: ChatClientState\n isLoading: boolean\n isSubscribed: boolean\n connectionStatus: ConnectionStatus\n sessionGenerating: boolean\n activeRunIds: Array<string>\n queue?: Array<QueuedMessage>\n error?: string\n}\n\nexport function createAIDevtoolsGenerationPreview(\n input: AIDevtoolsGenerationPreviewInput,\n): AIDevtoolsGenerationPreview {\n if (input.outputKind === 'image') {\n return imagePreviewFromResult(input.result)\n }\n\n if (input.outputKind === 'audio') {\n return audioPreviewFromResult(input.result)\n }\n\n if (input.outputKind === 'video') {\n return videoPreviewFromResult(input.result, input.videoStatus)\n }\n\n if (input.outputKind === 'text') {\n return textPreviewFromResult(input.result)\n }\n\n if (input.result === null || input.result === undefined) {\n return { kind: 'empty' }\n }\n\n return {\n kind: 'structured',\n value: input.result,\n }\n}\n\ntype UnknownRecord = { [key: string]: unknown }\n\nfunction imagePreviewFromResult(result: unknown): AIDevtoolsGenerationPreview {\n const record = asRecord(result)\n const images = Array.isArray(record?.images) ? record.images : []\n const items = images\n .map((image) => mediaItemFromSource(image, 'image/png'))\n .filter(isGenerationMediaItem)\n\n if (items.length === 0 && result !== null && result !== undefined) {\n const directItem = mediaItemFromSource(result, 'image/png')\n if (directItem) {\n items.push(directItem)\n }\n }\n\n return { kind: 'image', items }\n}\n\nfunction audioPreviewFromResult(result: unknown): AIDevtoolsGenerationPreview {\n const record = asRecord(result)\n const audio = record?.audio\n const resultContentType = stringField(record, 'contentType')\n const format = stringField(record, 'format')\n const mimeType = resultContentType ?? mimeTypeFromAudioFormat(format)\n\n const items: Array<AIDevtoolsGenerationMediaItem> = []\n const directItem =\n typeof audio === 'string'\n ? base64MediaItem(audio, mimeType, {\n format,\n duration: numberField(record, 'duration'),\n })\n : mediaItemFromSource(audio, mimeType, {\n format,\n })\n\n if (directItem) {\n items.push(directItem)\n }\n\n return { kind: 'audio', items }\n}\n\nfunction videoPreviewFromResult(\n result: unknown,\n videoStatus: unknown,\n): AIDevtoolsGenerationPreview {\n const resultRecord = asRecord(result)\n const statusRecord = asRecord(videoStatus)\n const item =\n mediaItemFromSource(result, 'video/mp4') ??\n mediaItemFromSource(videoStatus, 'video/mp4')\n const items = item ? [item] : []\n const job = videoJobFromStatus(statusRecord ?? resultRecord)\n\n return {\n kind: 'video',\n items,\n ...(job ? { job } : {}),\n }\n}\n\nfunction textPreviewFromResult(result: unknown): AIDevtoolsGenerationPreview {\n const record = asRecord(result)\n const text =\n stringField(record, 'text') ??\n stringField(record, 'summary') ??\n stringField(record, 'content') ??\n (typeof result === 'string' ? result : undefined)\n\n if (text !== undefined) {\n return { kind: 'text', text }\n }\n\n if (result === null || result === undefined) {\n return { kind: 'empty' }\n }\n\n return {\n kind: 'structured',\n value: result,\n }\n}\n\nfunction videoJobFromStatus(\n record: UnknownRecord | undefined,\n): AIDevtoolsGenerationVideoJob | undefined {\n const jobId = stringField(record, 'jobId')\n if (!jobId) return undefined\n\n return {\n jobId,\n ...(stringField(record, 'status')\n ? { status: stringField(record, 'status') }\n : {}),\n ...(numberField(record, 'progress') !== undefined\n ? { progress: numberField(record, 'progress') }\n : {}),\n ...(stringField(record, 'error')\n ? { error: stringField(record, 'error') }\n : {}),\n }\n}\n\nfunction mediaItemFromSource(\n value: unknown,\n defaultMimeType: string,\n extras: {\n format?: string\n duration?: number\n } = {},\n): AIDevtoolsGenerationMediaItem | undefined {\n const record = asRecord(value)\n if (!record) return undefined\n\n const explicitContentType =\n stringField(record, 'contentType') ?? stringField(record, 'mimeType')\n const duration = numberField(record, 'duration') ?? extras.duration\n const format = stringField(record, 'format') ?? extras.format\n const url = stringField(record, 'url')\n if (url) {\n return {\n src: url,\n sourceType: 'url',\n ...(explicitContentType ? { mimeType: explicitContentType } : {}),\n ...(format ? { format } : {}),\n ...(duration !== undefined ? { duration } : {}),\n }\n }\n\n const b64Json = stringField(record, 'b64Json')\n if (!b64Json) return undefined\n\n return base64MediaItem(b64Json, explicitContentType ?? defaultMimeType, {\n format,\n duration,\n })\n}\n\nfunction base64MediaItem(\n value: string,\n mimeType: string | undefined,\n extras: {\n format?: string\n duration?: number\n } = {},\n): AIDevtoolsGenerationMediaItem {\n const src = value.startsWith('data:')\n ? value\n : `data:${mimeType ?? 'application/octet-stream'};base64,${value}`\n\n return {\n src,\n sourceType: 'base64',\n ...(mimeType ? { mimeType } : {}),\n ...(extras.format ? { format: extras.format } : {}),\n ...(extras.duration !== undefined ? { duration: extras.duration } : {}),\n }\n}\n\nfunction mimeTypeFromAudioFormat(format: string | undefined): string {\n if (!format) return 'audio/mpeg'\n if (format === 'mp3') return 'audio/mpeg'\n return `audio/${format}`\n}\n\nfunction asRecord(value: unknown): UnknownRecord | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n return undefined\n }\n return value as UnknownRecord\n}\n\nfunction stringField(\n record: UnknownRecord | undefined,\n field: string,\n): string | undefined {\n const value = record?.[field]\n return typeof value === 'string' && value.length > 0 ? value : undefined\n}\n\nfunction numberField(\n record: UnknownRecord | undefined,\n field: string,\n): number | undefined {\n const value = record?.[field]\n return typeof value === 'number' ? value : undefined\n}\n\nfunction isGenerationMediaItem(\n value: AIDevtoolsGenerationMediaItem | undefined,\n): value is AIDevtoolsGenerationMediaItem {\n return Boolean(value)\n}\n\nexport interface AIDevtoolsToolFixture {\n fixtureId?: string\n hookId?: string\n threadId?: string\n runId?: string\n toolName: string\n input: unknown\n output: unknown\n execute?: boolean\n message?: {\n id: string\n role: UIMessage['role']\n parts: Array<unknown>\n createdAt?: number | string\n }\n toolCallId?: string\n messageId?: string\n errorText?: string\n}\n\ntype AIDevtoolsRunEventType =\n | 'run:created'\n | 'run:started'\n | 'run:updated'\n | 'run:completed'\n | 'run:errored'\n | 'run:cancelled'\n\ntype AIDevtoolsRunStatus =\n | 'created'\n | 'started'\n | 'updated'\n | 'completed'\n | 'errored'\n | 'cancelled'\n\nexport interface AIDevtoolsBridgeOptions<TSnapshot extends object> {\n hookId: string\n threadId?: string\n clientId: string\n metadata: AIDevtoolsClientMetadata\n getSnapshot: () => TSnapshot\n getTools?: () => Iterable<AnyClientTool>\n applyToolFixture?: (fixture: AIDevtoolsToolFixture) => void | Promise<void>\n}\n\ntype Unsubscribe = () => void\n\ninterface AIDevtoolsEvent<TPayload> {\n payload: TPayload\n}\n\ninterface ActiveDevtoolsBridge {\n deactivate: () => void\n dispose: () => void\n supersede?: () => void\n}\n\nconst activeBridgeRegistryKey = Symbol.for(\n 'tanstack.ai.devtools.activeBridgeByHookId',\n)\n\nfunction getActiveBridgeRegistry(): Map<string, ActiveDevtoolsBridge> {\n const global = globalThis as typeof globalThis & {\n [activeBridgeRegistryKey]?: Map<string, ActiveDevtoolsBridge>\n }\n const existing = global[activeBridgeRegistryKey]\n if (existing) return existing\n\n const registry = new Map<string, ActiveDevtoolsBridge>()\n global[activeBridgeRegistryKey] = registry\n return registry\n}\n\n/**\n * `{...options}` turns `get hookId()` into a data property. Chat/generation\n * clients mint `threadId` after construct (`ensureThreadId` on mount), so a\n * spread would freeze `hookId: ''` and DevTools could never select the hook.\n */\nfunction withLiveClientIdentity<TSnapshot extends object>(\n identity: Pick<\n AIDevtoolsBridgeOptions<TSnapshot>,\n | 'hookId'\n | 'clientId'\n | 'threadId'\n | 'metadata'\n | 'getTools'\n | 'applyToolFixture'\n >,\n rest: Pick<AIDevtoolsBridgeOptions<TSnapshot>, 'getSnapshot'> &\n Partial<\n Pick<AIDevtoolsBridgeOptions<TSnapshot>, 'getTools' | 'applyToolFixture'>\n >,\n): AIDevtoolsBridgeOptions<TSnapshot> {\n return {\n get hookId() {\n return identity.hookId\n },\n get clientId() {\n return identity.clientId\n },\n get threadId() {\n return identity.threadId\n },\n metadata: identity.metadata,\n getSnapshot: rest.getSnapshot,\n ...(rest.getTools || identity.getTools\n ? { getTools: rest.getTools ?? identity.getTools }\n : {}),\n ...(rest.applyToolFixture || identity.applyToolFixture\n ? {\n applyToolFixture: rest.applyToolFixture ?? identity.applyToolFixture,\n }\n : {}),\n }\n}\n\nexport class ClientDevtoolsBridge<TSnapshot extends object> {\n protected readonly options: AIDevtoolsBridgeOptions<TSnapshot>\n private readonly unsubscribers: Array<Unsubscribe> = []\n private disposed = false\n private superseded = false\n private registered = false\n\n constructor(options: AIDevtoolsBridgeOptions<TSnapshot>) {\n this.options = options\n }\n\n emitRegistered(): void {\n if (!this.prepareForMountEmit()) {\n return\n }\n this.registered = true\n emitAIDevtoolsEvent('hook:registered', {\n ...this.createEnvelope('hook:registered'),\n ...this.createMetadataPayload(),\n lifecycle: 'mounted',\n })\n }\n\n emitUpdated(): void {\n if (!this.prepareForEmit()) {\n return\n }\n emitAIDevtoolsEvent('hook:updated', {\n ...this.createEnvelope('hook:updated'),\n ...this.createMetadataPayload(),\n lifecycle: 'active',\n })\n }\n\n emitSnapshot(): void {\n if (!this.prepareForEmit()) {\n return\n }\n emitAIDevtoolsEvent('hook:state-snapshot', {\n ...this.createEnvelope('hook:state-snapshot'),\n ...this.createMetadataPayload(),\n // Wire envelope uses Record<string, unknown>; widen the typed snapshot\n // here so the typed-snapshot constraint above can stay narrow.\n // oxlint-disable-next-line eslint-js/no-restricted-syntax -- TSnapshot extends object is structurally compatible but TS can't see the missing index signature\n state: this.options.getSnapshot() as unknown as Record<string, unknown>,\n })\n }\n\n emitToolsRegistered(): void {\n if (!this.prepareForEmit()) {\n return\n }\n const tools = this.options.getTools\n ? Array.from(this.options.getTools()).map((tool) => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema\n ? convertSchemaToJsonSchema(tool.inputSchema)\n : { type: 'object' },\n outputSchema: tool.outputSchema\n ? convertSchemaToJsonSchema(tool.outputSchema)\n : undefined,\n needsApproval: tool.needsApproval,\n metadata: tool.metadata,\n }))\n : []\n\n emitAIDevtoolsEvent('tools:registered', {\n ...this.createEnvelope('tools:registered'),\n ...this.createMetadataPayload(),\n tools,\n })\n }\n\n emitRunLifecycle(\n eventType: AIDevtoolsRunEventType,\n runId: string,\n status: AIDevtoolsRunStatus,\n options: { error?: string } = {},\n ): void {\n if (!this.prepareForEmit()) {\n return\n }\n emitAIDevtoolsEvent(eventType, {\n ...this.createEnvelope(eventType, 'client-state', { runId }),\n runId,\n status,\n ...(options.error ? { error: options.error } : {}),\n })\n }\n\n deactivate(): void {\n const activeBridgeByHookId = getActiveBridgeRegistry()\n if (activeBridgeByHookId.get(this.options.hookId) === this) {\n activeBridgeByHookId.delete(this.options.hookId)\n }\n\n for (const unsubscribe of this.unsubscribers.splice(0)) {\n unsubscribe()\n }\n }\n\n supersede(): void {\n if (this.disposed) {\n return\n }\n\n this.superseded = true\n this.disposed = true\n this.deactivate()\n }\n\n dispose(): void {\n if (this.disposed) {\n return\n }\n\n this.disposed = true\n if (!this.registered) {\n this.deactivate()\n return\n }\n\n const live = getActiveBridgeRegistry().get(this.options.hookId)\n if (live !== this) {\n this.deactivate()\n return\n }\n\n const payload = {\n ...this.createEnvelope('hook:unregistered'),\n ...this.createMetadataPayload(),\n reason: 'disposed',\n } as const\n\n emitAIDevtoolsEvent('hook:unregistered', payload)\n\n this.deactivate()\n }\n\n private prepareForEmit(): boolean {\n if (this.disposed || this.superseded) {\n return false\n }\n this.activate()\n return true\n }\n\n private prepareForMountEmit(): boolean {\n if (this.superseded) {\n return false\n }\n\n if (this.disposed) {\n this.disposed = false\n this.registered = false\n }\n\n this.activate()\n return true\n }\n\n private activate(): void {\n if (this.disposed) {\n return\n }\n\n const activeBridgeByHookId = getActiveBridgeRegistry()\n const activeBridge = activeBridgeByHookId.get(this.options.hookId)\n if (activeBridge && activeBridge !== this) {\n if (typeof activeBridge.supersede === 'function') {\n activeBridge.supersede()\n } else {\n activeBridge.deactivate()\n }\n }\n activeBridgeByHookId.set(this.options.hookId, this)\n\n if (this.unsubscribers.length > 0) {\n return\n }\n\n this.unsubscribers.push(\n aiEventClient.on('devtools:request-state', (event) => {\n this.handleRequestState(event)\n }),\n )\n\n if (this.options.applyToolFixture) {\n this.unsubscribers.push(\n aiEventClient.on('devtools:tool-fixture:apply', (event) => {\n void this.handleToolFixtureApply(event)\n }),\n )\n }\n }\n\n private handleRequestState(\n event: AIDevtoolsEvent<{ targetHookId?: string }>,\n ): void {\n if (this.disposed || this.superseded) {\n return\n }\n\n const targetHookId = event.payload.targetHookId\n if (targetHookId && targetHookId !== this.options.hookId) {\n return\n }\n\n this.emitRegistered()\n this.emitToolsRegistered()\n this.emitSnapshot()\n this.onReplayState()\n }\n\n /**\n * Extension hook for subclasses to replay any additional cached state on a\n * `devtools:request-state` (i.e. when a panel opens). Called only after the\n * base guards (disposed/superseded/targetHookId) pass. No-op by default.\n */\n protected onReplayState(): void {}\n\n private async handleToolFixtureApply(\n event: AIDevtoolsEvent<AIDevtoolsToolFixture>,\n ): Promise<void> {\n const fixture = event.payload\n if (!this.matchesFixtureTarget(fixture)) {\n return\n }\n\n await this.options.applyToolFixture?.(fixture)\n }\n\n private matchesFixtureTarget(fixture: AIDevtoolsToolFixture): boolean {\n if (!fixture.hookId && !fixture.threadId) {\n return false\n }\n\n // Fixture routing: `hookId` wins when present (latest bridge for that\n // registry key). `threadId` is the fallback for fixtures scoped only to a\n // conversation / generation slot without a hook id.\n if (fixture.hookId) {\n return fixture.hookId === this.options.hookId\n }\n\n if (\n fixture.threadId &&\n (!this.options.threadId || fixture.threadId !== this.options.threadId)\n ) {\n return false\n }\n return true\n }\n\n protected createEnvelope(\n eventType:\n | 'hook:registered'\n | 'hook:updated'\n | 'hook:unregistered'\n | 'hook:state-snapshot'\n | 'tools:registered'\n | 'memory:retrieve:started'\n | 'memory:retrieve:completed'\n | 'memory:snapshot'\n | 'compaction:started'\n | 'compaction:state'\n | 'compaction:ended'\n | 'skills:snapshot'\n | AIDevtoolsRunEventType,\n visibility: AIDevtoolsEventVisibility = 'client-state',\n context: { runId?: string } = {},\n ) {\n return createAIDevtoolsEventEnvelope({\n eventType,\n source: 'client',\n visibility,\n clientId: this.options.clientId,\n hookId: this.options.hookId,\n ...(this.options.threadId ? { threadId: this.options.threadId } : {}),\n ...(context.runId ? { runId: context.runId } : {}),\n timestamp: Date.now(),\n })\n }\n\n private createMetadataPayload() {\n return {\n hookId: this.options.hookId,\n hookName: this.options.metadata.hookName,\n ...(this.options.metadata.name\n ? { displayName: this.options.metadata.name }\n : {}),\n ...(this.options.metadata.outputKind\n ? { outputKind: this.options.metadata.outputKind }\n : {}),\n ...(this.options.metadata.framework\n ? { framework: this.options.metadata.framework }\n : {}),\n }\n }\n}\n\n// Owns the chat-client devtools surface so the chat client itself stays a\n// pure transport. Fixture replay, per-run / per-stream event context, and\n// snapshot emission all live here; a no-op bridge can drop in for prod.\n\nexport interface ChatDevtoolsBridgeOptions extends AIDevtoolsBridgeOptions<AIDevtoolsChatSnapshot> {\n getMessages: () => Array<UIMessage>\n setMessages: (messages: Array<UIMessage>) => void\n addToolResult: (\n toolCallId: string,\n output: unknown,\n errorText?: string,\n ) => void\n generateId: (prefix: string) => string\n}\n\nexport class ChatDevtoolsBridge extends ClientDevtoolsBridge<AIDevtoolsChatSnapshot> {\n readonly events: ChatClientEventEmitter\n private readonly chatOptions: ChatDevtoolsBridgeOptions\n private currentRunId: string | null = null\n private currentRunThreadId: string | null = null\n private currentStreamId: string | null = null\n private lastStreamId: string | null = null\n private lastRunEventContext: ChatClientRunEventContext | undefined\n /** Last transported `memory:state` value, replayed when a panel opens. */\n private lastMemoryStateValue: unknown = null\n /** Transported compaction CUSTOM events, replayed when a panel opens. */\n private readonly lastCompactionEvents: Array<{\n eventType: string\n value: unknown\n }> = []\n /** Last transported `skills:state` value, replayed when a panel opens. */\n private lastSkillsStateValue: unknown = null\n\n constructor(options: ChatDevtoolsBridgeOptions) {\n super(\n withLiveClientIdentity(options, {\n getSnapshot: options.getSnapshot,\n // Thunk defers `this.applyFixture` lookup until after `super` returns.\n applyToolFixture: (fixture) => this.applyFixture(fixture),\n }),\n )\n this.chatOptions = options\n // Auto-attaches run/thread context and auto-emits a snapshot after each\n // event so callers can keep using `this.events.X(...)` with no context arg.\n this.events = new ChatDevtoolsAwareEventEmitter(\n () => options.clientId,\n this,\n )\n }\n\n // --- Stream / run context API -------------------------------------------\n\n setCurrentStreamId(streamId: string | null): void {\n this.currentStreamId = streamId\n if (streamId) {\n this.lastStreamId = streamId\n }\n }\n\n /**\n * Called by the auto-attaching emitter every time it sees a non-empty\n * streamId pass through. Lets devtools track the latest stream id\n * without the chat client wiring it up explicitly.\n */\n recordStreamId(streamId: string): void {\n if (streamId) this.lastStreamId = streamId\n }\n\n mountWithTools(initialMessageCount: number): void {\n this.events.clientCreated(initialMessageCount)\n this.emitRegistered()\n this.emitToolsRegistered()\n this.emitSnapshot()\n }\n\n notifyToolsChanged(): void {\n this.emitToolsRegistered()\n this.emitSnapshot()\n }\n\n getCurrentStreamId(): string | null {\n return this.currentStreamId\n }\n\n getLastStreamId(): string | null {\n return this.lastStreamId\n }\n\n resolveStreamId(): string {\n return (\n this.currentStreamId ??\n this.lastStreamId ??\n this.chatOptions.generateId('stream')\n )\n }\n\n // Called when the chat client has just generated a runId for outbound emits;\n // the matching RUN_STARTED chunk from the adapter lands later and\n // observeChunk keeps the same context.\n beginRun(runId: string, threadId: string): void {\n this.currentRunId = runId\n this.currentRunThreadId = threadId\n this.lastRunEventContext = { runId, threadId }\n }\n\n observeChunk(chunk: StreamChunk): void {\n if (chunk.type === 'RUN_STARTED') {\n this.beginRun(chunk.runId, chunk.threadId)\n return\n }\n\n if (chunk.type === 'RUN_FINISHED' || chunk.type === 'RUN_ERROR') {\n const runId =\n chunk.type === 'RUN_FINISHED'\n ? chunk.runId\n : (chunk as { runId?: string }).runId\n if (!runId || runId === this.currentRunId) {\n const context = this.getCurrentRunEventContext()\n if (context) {\n this.lastRunEventContext = context\n }\n this.currentRunId = null\n this.currentRunThreadId = null\n }\n }\n }\n\n /**\n * Record a transported `memory:state` value (the server memory middleware's\n * per-turn recall metrics + store snapshot). Called from the chat client's\n * `onCustomEvent` handler — the designated path for CUSTOM stream events —\n * NOT from `observeChunk`. Re-emits the browser `memory:*` events the devtools\n * store consumes, and caches the value so a panel opened later can replay it\n * (see {@link onReplayState}). Symmetric with the generation bridge's\n * `recordResultChange` / `recordProgressChange`.\n */\n recordMemoryState(rawValue: unknown): void {\n this.lastMemoryStateValue = rawValue\n this.emitMemoryState(rawValue)\n }\n\n recordSkillsState(rawValue: unknown): void {\n this.lastSkillsStateValue = rawValue\n this.emitSkillsState(rawValue)\n }\n\n /**\n * Re-emit the browser-side `memory:*` events from a transported\n * `memory:state` value. The devtools store consumes these to render the\n * Memory tab (operations timeline + live contents). The recall pair mirrors\n * the server middleware's own emits; the snapshot is included only when the\n * adapter supports introspection.\n */\n private emitMemoryState(rawValue: unknown): void {\n if (!rawValue || typeof rawValue !== 'object') return\n const value = rawValue as MemoryStateEventValue\n const scope = value.scope\n const adapter = value.adapter\n if (!scope || typeof adapter !== 'string') return\n const runContext = this.currentRunId ? { runId: this.currentRunId } : {}\n\n emitAIDevtoolsEvent('memory:retrieve:started', {\n ...this.createEnvelope(\n 'memory:retrieve:started',\n 'client-state',\n runContext,\n ),\n scope,\n adapter,\n query: value.query ?? '',\n })\n if (value.recall) {\n emitAIDevtoolsEvent('memory:retrieve:completed', {\n ...this.createEnvelope(\n 'memory:retrieve:completed',\n 'client-state',\n runContext,\n ),\n scope,\n adapter,\n fragmentCount: value.recall.fragmentCount ?? 0,\n hasTools: Boolean(value.recall.hasTools),\n systemPromptChars: value.recall.systemPromptChars ?? 0,\n durationMs: value.recall.durationMs ?? 0,\n })\n }\n if (value.snapshot) {\n emitAIDevtoolsEvent('memory:snapshot', {\n ...this.createEnvelope('memory:snapshot', 'client-state', runContext),\n scope,\n adapter,\n takenAt: value.snapshot.takenAt,\n data: value.snapshot.data,\n facts: value.snapshot.facts ?? [],\n })\n }\n }\n\n /**\n * Record a transported compaction CUSTOM event. Called from the chat\n * client's `onCustomEvent` handler so server-side compaction reaches the\n * browser DevTools panel.\n */\n recordCompactionEvent(eventType: string, rawValue: unknown): void {\n this.lastCompactionEvents.push({ eventType, value: rawValue })\n if (this.lastCompactionEvents.length > 60) {\n this.lastCompactionEvents.splice(0, this.lastCompactionEvents.length - 60)\n }\n this.emitCompactionEvent(eventType, rawValue)\n }\n\n recordCompactionState(rawValue: unknown): void {\n this.recordCompactionEvent('compaction:state', rawValue)\n }\n\n private emitCompactionEvent(eventType: string, rawValue: unknown): void {\n const runContext = this.currentRunId ? { runId: this.currentRunId } : {}\n if (eventType === 'compaction:started') {\n const value = readCompactionBoundaryValue(rawValue)\n emitAIDevtoolsEvent('compaction:started', {\n ...this.createEnvelope(\n 'compaction:started',\n 'client-state',\n runContext,\n ),\n ...value,\n })\n return\n }\n if (eventType === 'compaction:ended') {\n const value = readCompactionBoundaryValue(rawValue)\n emitAIDevtoolsEvent('compaction:ended', {\n ...this.createEnvelope('compaction:ended', 'client-state', runContext),\n ...value,\n })\n return\n }\n if (eventType === 'compaction:state') {\n const value = readCompactionStateValue(rawValue)\n if (!value) return\n emitAIDevtoolsEvent('compaction:state', {\n ...this.createEnvelope('compaction:state', 'client-state', runContext),\n ...value,\n })\n }\n }\n\n private emitSkillsState(rawValue: unknown): void {\n if (!rawValue || typeof rawValue !== 'object') return\n const value = rawValue as SkillsStateEventValue\n const catalog = Array.isArray(value.catalog) ? value.catalog : []\n const activated = Array.isArray(value.activated) ? value.activated : []\n const runContext = this.currentRunId ? { runId: this.currentRunId } : {}\n emitAIDevtoolsEvent('skills:snapshot', {\n ...this.createEnvelope('skills:snapshot', 'client-state', runContext),\n catalog,\n activated,\n })\n }\n\n protected override onReplayState(): void {\n if (this.lastMemoryStateValue != null) {\n this.emitMemoryState(this.lastMemoryStateValue)\n }\n for (const event of this.lastCompactionEvents) {\n this.emitCompactionEvent(event.eventType, event.value)\n }\n if (this.lastSkillsStateValue != null) {\n this.emitSkillsState(this.lastSkillsStateValue)\n }\n }\n\n getCurrentRunEventContext(): ChatClientRunEventContext | undefined {\n if (!this.currentRunId) return undefined\n return {\n threadId: this.currentRunThreadId ?? this.chatOptions.threadId ?? '',\n runId: this.currentRunId,\n }\n }\n\n getCurrentOrLastRunEventContext(): ChatClientRunEventContext | undefined {\n return this.getCurrentRunEventContext() ?? this.lastRunEventContext\n }\n\n findToolCallContext(toolCallId: string): ChatClientEventContext {\n const base: ChatClientEventContext = { toolCallId }\n const runContext = this.getCurrentRunEventContext()\n if (runContext) {\n return {\n threadId: runContext.threadId,\n runId: runContext.runId,\n toolCallId,\n }\n }\n if (this.chatOptions.threadId) {\n return { threadId: this.chatOptions.threadId, toolCallId }\n }\n return base\n }\n\n // --- Fixture replay ------------------------------------------------------\n\n /**\n * Entry point invoked when the devtools panel emits\n * `devtools:tool-fixture:apply`. The chat client never calls this\n * directly; it is wired through the base bridge's fixture subscription.\n */\n async applyFixture(fixture: AIDevtoolsToolFixture): Promise<void> {\n const messages = this.chatOptions.getMessages()\n const threadId = fixture.threadId ?? this.chatOptions.threadId ?? ''\n if (fixture.execute) {\n await this.executeFixture(fixture, messages, threadId)\n return\n }\n\n const replay = this.createReplayMessageFromFixture(fixture, messages)\n const { message, toolCallId } = replay\n const messageId = message.id\n\n this.events.messageAppended(message, undefined, {\n threadId,\n toolCallId,\n ...(fixture.runId ? { runId: fixture.runId } : {}),\n })\n this.chatOptions.setMessages([...messages, message])\n this.events.toolFixtureApplied({\n hookId: this.chatOptions.hookId,\n threadId,\n ...(fixture.runId ? { runId: fixture.runId } : {}),\n toolName: fixture.toolName,\n input: fixture.input,\n output: fixture.output,\n messageId,\n toolCallId,\n ...(fixture.execute !== undefined ? { execute: fixture.execute } : {}),\n ...(fixture.message ? { message: fixture.message } : {}),\n ...(fixture.errorText ? { errorText: fixture.errorText } : {}),\n })\n this.emitSnapshot()\n }\n\n private async executeFixture(\n fixture: AIDevtoolsToolFixture,\n messages: Array<UIMessage>,\n threadId: string,\n ): Promise<void> {\n const toolCallId = this.resolveFixtureToolCallId(\n fixture.toolCallId,\n messages,\n )\n const messageId = this.resolveFixtureMessageId(fixture.messageId, messages)\n const message: UIMessage = {\n id: messageId,\n role: 'assistant',\n parts: [\n {\n type: 'tool-call',\n id: toolCallId,\n name: fixture.toolName,\n arguments: stringifyFixtureValue(fixture.input),\n input: fixture.input,\n state: 'input-complete',\n },\n ],\n createdAt: new Date(),\n }\n\n this.events.messageAppended(message, undefined, {\n threadId,\n toolCallId,\n ...(fixture.runId ? { runId: fixture.runId } : {}),\n })\n this.chatOptions.setMessages([...messages, message])\n this.emitSnapshot()\n\n const clientTool = this.findClientTool(fixture.toolName)\n const executeFunc = clientTool?.execute\n if (!executeFunc) {\n console.warn(\n `[ai-devtools] tool fixture \"${fixture.toolName}\" requested execute=true but no client tool implementation is registered; replaying saved output instead.`,\n )\n this.addToolResultForFixture({\n fixture,\n messageId,\n toolCallId,\n threadId,\n output: fixture.output,\n errorText: fixture.errorText,\n })\n return\n }\n\n let output: unknown\n try {\n output = await executeFunc(fixture.input)\n } catch (error) {\n console.error(\n `[ai-devtools] tool fixture \"${fixture.toolName}\" execute threw`,\n error,\n )\n this.addToolResultForFixture({\n fixture,\n messageId,\n toolCallId,\n threadId,\n output: null,\n errorText:\n error instanceof Error\n ? `${error.name}: ${error.message}`\n : `Tool execution failed: ${String(error)}`,\n })\n return\n }\n this.addToolResultForFixture({\n fixture,\n messageId,\n toolCallId,\n threadId,\n output,\n })\n }\n\n private addToolResultForFixture(input: {\n fixture: AIDevtoolsToolFixture\n messageId: string\n toolCallId: string\n threadId: string\n output: unknown\n errorText?: string\n }): void {\n const state = input.errorText ? 'output-error' : 'output-available'\n this.events.toolResultAdded(\n input.toolCallId,\n input.fixture.toolName,\n input.output,\n state,\n {\n threadId: input.threadId,\n ...(input.fixture.runId ? { runId: input.fixture.runId } : {}),\n toolCallId: input.toolCallId,\n },\n )\n this.chatOptions.addToolResult(\n input.toolCallId,\n input.output,\n input.errorText,\n )\n this.events.toolFixtureApplied({\n hookId: this.chatOptions.hookId,\n threadId: input.threadId,\n ...(input.fixture.runId ? { runId: input.fixture.runId } : {}),\n toolName: input.fixture.toolName,\n input: input.fixture.input,\n output: input.output,\n execute: true,\n messageId: input.messageId,\n toolCallId: input.toolCallId,\n ...(input.errorText ? { errorText: input.errorText } : {}),\n })\n this.emitSnapshot()\n }\n\n private createReplayMessageFromFixture(\n fixture: AIDevtoolsToolFixture,\n messages: Array<UIMessage>,\n ): { message: UIMessage; toolCallId: string } {\n const cloned = this.cloneFixtureSourceMessage(fixture, messages)\n if (cloned) return cloned\n\n const toolCallId = this.resolveFixtureToolCallId(\n fixture.toolCallId,\n messages,\n )\n const messageId = this.resolveFixtureMessageId(fixture.messageId, messages)\n const state = fixture.errorText ? 'error' : 'complete'\n\n return {\n toolCallId,\n message: {\n id: messageId,\n role: 'assistant',\n parts: [\n {\n type: 'tool-call',\n id: toolCallId,\n name: fixture.toolName,\n arguments: stringifyFixtureValue(fixture.input),\n input: fixture.input,\n state: 'input-complete',\n output: fixture.output,\n },\n {\n type: 'tool-result',\n toolCallId,\n content: stringifyFixtureValue(fixture.output),\n state,\n ...(fixture.errorText ? { error: fixture.errorText } : {}),\n },\n ],\n createdAt: new Date(),\n },\n }\n }\n\n private cloneFixtureSourceMessage(\n fixture: AIDevtoolsToolFixture,\n messages: Array<UIMessage>,\n ): { message: UIMessage; toolCallId: string } | undefined {\n const sourceMessage = fixture.message\n if (!sourceMessage || !Array.isArray(sourceMessage.parts)) {\n return undefined\n }\n\n const toolCallIds = this.createFixtureToolCallIdMap(\n sourceMessage.parts,\n messages,\n )\n const parts = sourceMessage.parts\n .map((part) => cloneFixtureMessagePart(part, toolCallIds))\n .filter((part): part is MessagePart => Boolean(part))\n const mappedFixtureToolCallId = fixture.toolCallId\n ? toolCallIds.get(fixture.toolCallId)\n : undefined\n hydrateToolCallOutputs(parts, {\n ...(mappedFixtureToolCallId\n ? { mappedToolCallId: mappedFixtureToolCallId }\n : {}),\n output: fixture.output,\n })\n\n if (parts.length === 0) return undefined\n\n const toolCallId =\n (fixture.toolCallId ? toolCallIds.get(fixture.toolCallId) : undefined) ??\n firstToolCallId(parts)\n if (!toolCallId) return undefined\n\n return {\n toolCallId,\n message: {\n id: this.resolveFixtureMessageId(sourceMessage.id, messages),\n role: sourceMessage.role,\n parts,\n createdAt: new Date(),\n },\n }\n }\n\n private createFixtureToolCallIdMap(\n parts: Array<unknown>,\n messages: Array<UIMessage>,\n ): Map<string, string> {\n const ids = new Map<string, string>()\n for (const part of parts) {\n if (!isRecord(part) || part.type !== 'tool-call') continue\n if (typeof part.id !== 'string') continue\n ids.set(part.id, this.resolveFixtureToolCallId(part.id, messages))\n }\n return ids\n }\n\n private resolveFixtureMessageId(\n messageId: string | undefined,\n messages: Array<UIMessage>,\n ): string {\n if (messageId && !messages.some((message) => message.id === messageId)) {\n return messageId\n }\n return this.chatOptions.generateId('fixture-msg')\n }\n\n private resolveFixtureToolCallId(\n toolCallId: string | undefined,\n messages: Array<UIMessage>,\n ): string {\n if (toolCallId && !hasToolCallId(messages, toolCallId)) {\n return toolCallId\n }\n return this.chatOptions.generateId('fixture-tool-call')\n }\n\n private findClientTool(name: string): AnyClientTool | undefined {\n const tools = this.chatOptions.getTools?.()\n if (!tools) return undefined\n for (const tool of tools) {\n if (tool.name === name) return tool\n }\n return undefined\n }\n}\n\n// ---- Module-level fixture helpers (pure; share no state) -------------------\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null\n}\n\nfunction stringifyFixtureValue(value: unknown): string {\n if (typeof value === 'string') return value\n if (\n value === undefined ||\n typeof value === 'function' ||\n typeof value === 'symbol'\n ) {\n return String(value)\n }\n try {\n return JSON.stringify(value)\n } catch (error) {\n console.error(\n '[ai-devtools] failed to JSON.stringify fixture value; falling back to String(). Tool call arguments may be malformed.',\n { error, value },\n )\n return String(value)\n }\n}\n\nfunction parseFixtureResultContent(content: string): unknown {\n try {\n return JSON.parse(content)\n } catch (error) {\n console.error(\n '[ai-devtools] failed to JSON.parse fixture result content; replaying as raw string. Fixture payload may be corrupted.',\n { error, content },\n )\n return content\n }\n}\n\nfunction cloneFixtureMessagePart(\n part: unknown,\n toolCallIds: Map<string, string>,\n): MessagePart | undefined {\n if (!isRecord(part) || typeof part.type !== 'string') return undefined\n const cloned: Record<string, unknown> = { ...part }\n\n if (part.type === 'tool-call' && typeof part.id === 'string') {\n cloned.id = toolCallIds.get(part.id) ?? part.id\n }\n if (part.type === 'tool-result' && typeof part.toolCallId === 'string') {\n cloned.toolCallId = toolCallIds.get(part.toolCallId) ?? part.toolCallId\n }\n return cloned as MessagePart\n}\n\nfunction firstToolCallId(parts: Array<MessagePart>): string | undefined {\n const toolCall = parts.find((part) => part.type === 'tool-call')\n return toolCall?.type === 'tool-call' ? toolCall.id : undefined\n}\n\nfunction hydrateToolCallOutputs(\n parts: Array<MessagePart>,\n fixtureOutput: { mappedToolCallId?: string; output: unknown },\n): void {\n for (const part of parts) {\n if (part.type !== 'tool-result') continue\n const toolCall = parts.find(\n (candidate): candidate is ToolCallPart =>\n candidate.type === 'tool-call' &&\n candidate.id === part.toolCallId &&\n candidate.output === undefined,\n )\n if (toolCall) {\n toolCall.output = Array.isArray(part.content)\n ? part.content\n : parseFixtureResultContent(part.content)\n }\n }\n\n if (fixtureOutput.mappedToolCallId && fixtureOutput.output !== undefined) {\n const toolCall = parts.find(\n (candidate): candidate is ToolCallPart =>\n candidate.type === 'tool-call' &&\n candidate.id === fixtureOutput.mappedToolCallId &&\n candidate.output === undefined,\n )\n if (toolCall) {\n toolCall.output = fixtureOutput.output\n }\n }\n}\n\nfunction hasToolCallId(\n messages: Array<UIMessage>,\n toolCallId: string,\n): boolean {\n return messages.some((message) =>\n message.parts.some((part) => {\n if (part.type === 'tool-call') return part.id === toolCallId\n if (part.type === 'tool-result') return part.toolCallId === toolCallId\n return false\n }),\n )\n}\n\n// Devtools surface for GenerationClient / VideoGenerationClient. Owns per-run\n// history, active-run lifecycle, and snapshot emission; the generation client\n// pushes its core state in via the record* methods.\n\nexport interface AIDevtoolsGenerationSnapshotBase<TOutput> {\n input: unknown\n result: TOutput | null\n preview: AIDevtoolsGenerationPreview\n progress: AIDevtoolsGenerationProgress | null\n status: AIDevtoolsGenerationRunStatus\n isLoading: boolean\n activeRunId: string | null\n runs: Array<AIDevtoolsGenerationRunSnapshot<TOutput>>\n error?: string\n}\n\nexport interface GenerationDevtoolsBridgeOptions<TOutput> extends Omit<\n AIDevtoolsBridgeOptions<AIDevtoolsGenerationSnapshotBase<TOutput>>,\n 'getSnapshot'\n> {\n getCoreState: () => GenerationDevtoolsCoreState<TOutput>\n maxRuns?: number\n}\n\nexport interface GenerationDevtoolsCoreState<TOutput> {\n input: unknown\n result: TOutput | null\n progress: AIDevtoolsGenerationProgress | null\n status: AIDevtoolsGenerationRunStatus\n isLoading: boolean\n error?: string\n}\n\nexport interface GenerationRunPatch<TOutput> {\n input?: unknown\n result?: TOutput | null\n preview?: AIDevtoolsGenerationPreview\n progress?: AIDevtoolsGenerationProgress | null\n status?: AIDevtoolsGenerationRunStatus\n isLoading?: boolean\n completedAt?: number\n error?: string\n clearError?: boolean\n}\n\nexport class GenerationDevtoolsBridge<TOutput> extends ClientDevtoolsBridge<\n AIDevtoolsGenerationSnapshotBase<TOutput>\n> {\n protected activeRunId: string | null = null\n protected activeRunStarted = false\n protected devtoolsRuns: Array<AIDevtoolsGenerationRunSnapshot<TOutput>> = []\n protected readonly maxRuns: number\n protected readonly getCoreState: () => GenerationDevtoolsCoreState<TOutput>\n\n constructor(options: GenerationDevtoolsBridgeOptions<TOutput>) {\n super(\n withLiveClientIdentity(options, {\n getSnapshot: () => this.buildSnapshot(),\n }),\n )\n this.maxRuns = options.maxRuns ?? 20\n this.getCoreState = options.getCoreState\n }\n\n // --- Run lifecycle (called by GenerationClient) -----------------------\n\n beginRun(input: unknown): string {\n const runId = this.generateRunId()\n this.activeRunId = runId\n this.activeRunStarted = false\n this.upsertRun(runId, {\n input,\n result: null,\n preview: this.createPreview(null),\n progress: null,\n status: 'generating',\n isLoading: true,\n clearError: true,\n })\n return runId\n }\n\n ensureRunStarted(runId: string): void {\n if (this.activeRunStarted && this.activeRunId === runId) return\n\n if (\n !this.activeRunStarted &&\n this.activeRunId &&\n this.activeRunId !== runId\n ) {\n this.renameRun(this.activeRunId, runId)\n }\n\n this.activeRunId = runId\n this.activeRunStarted = true\n this.upsertRun(runId, {\n status: 'generating',\n isLoading: true,\n clearError: true,\n })\n this.emitRunLifecycle('run:started', runId, 'started')\n this.emitState()\n }\n\n finishRun(\n runId: string,\n eventType: 'run:completed' | 'run:errored' | 'run:cancelled',\n status: 'completed' | 'errored' | 'cancelled',\n error?: string,\n ): void {\n this.ensureRunStarted(runId)\n const completedAt = Date.now()\n const completedProgress =\n status === 'completed' ? this.completeProgress() : this.getProgress()\n const runStatus =\n status === 'completed'\n ? 'success'\n : status === 'errored'\n ? 'error'\n : 'cancelled'\n\n this.upsertRun(runId, {\n status: runStatus,\n isLoading: false,\n progress: completedProgress,\n completedAt,\n ...(error ? { error } : { clearError: true }),\n })\n\n if (this.activeRunId === runId) {\n this.activeRunId = null\n }\n this.activeRunStarted = false\n this.emitRunLifecycle(eventType, runId, status, {\n ...(error ? { error } : {}),\n })\n this.emitState()\n }\n\n getActiveRunId(): string | null {\n return this.activeRunId\n }\n\n /** Clear all per-run history. Called when the client `reset()`s. */\n resetRuns(): void {\n this.activeRunId = null\n this.activeRunStarted = false\n this.devtoolsRuns = []\n }\n\n /** Record state changes from the client and emit the matching snapshot. */\n recordResultChange(): void {\n this.updateActiveRun({\n result: this.getCoreState().result,\n preview: this.createPreview(this.getCoreState().result),\n clearError: true,\n })\n this.emitState()\n }\n\n recordLoadingChange(): void {\n this.updateActiveRun({ isLoading: this.getCoreState().isLoading })\n this.emitState()\n }\n\n recordErrorChange(error: Error | undefined): void {\n this.updateActiveRun(\n error ? { error: error.message } : { clearError: true },\n )\n this.emitState()\n }\n\n recordStatusChange(status: AIDevtoolsGenerationRunStatus): void {\n this.updateActiveRun({ status })\n this.emitState()\n }\n\n recordProgressChange(): void {\n this.updateActiveRun({ progress: this.getCoreState().progress })\n this.emitState()\n }\n\n /** Emit the latest snapshot without touching run state. */\n emitState(): void {\n this.emitUpdated()\n this.emitSnapshot()\n }\n\n // --- Internal ---------------------------------------------------------\n\n protected buildSnapshot(): AIDevtoolsGenerationSnapshotBase<TOutput> {\n const core = this.getCoreState()\n return {\n input: core.input,\n result: core.result,\n preview: this.createPreview(core.result),\n progress: core.progress,\n status: core.status,\n isLoading: core.isLoading,\n activeRunId: this.activeRunId,\n runs: this.devtoolsRuns,\n ...(core.error ? { error: core.error } : {}),\n }\n }\n\n protected updateActiveRun(patch: GenerationRunPatch<TOutput>): void {\n if (!this.activeRunId) return\n this.upsertRun(this.activeRunId, patch)\n }\n\n protected upsertRun(runId: string, patch: GenerationRunPatch<TOutput>): void {\n const now = Date.now()\n const index = this.devtoolsRuns.findIndex((run) => run.id === runId)\n const existing = index >= 0 ? this.devtoolsRuns[index] : undefined\n const next: AIDevtoolsGenerationRunSnapshot<TOutput> = existing\n ? { ...existing }\n : {\n id: runId,\n input: this.getCoreState().input,\n result: null,\n preview: this.createPreview(null),\n progress: null,\n status: 'idle',\n isLoading: false,\n startedAt: now,\n updatedAt: now,\n }\n\n if ('input' in patch) next.input = patch.input ?? null\n if ('result' in patch) next.result = patch.result ?? null\n if (patch.preview) next.preview = patch.preview\n if ('progress' in patch) next.progress = patch.progress ?? null\n if (patch.status) next.status = patch.status\n if ('isLoading' in patch) next.isLoading = patch.isLoading === true\n if (patch.completedAt !== undefined) next.completedAt = patch.completedAt\n if (patch.clearError) delete next.error\n if (patch.error !== undefined) next.error = patch.error\n next.updatedAt = now\n\n if (index >= 0) {\n this.devtoolsRuns = this.devtoolsRuns.map((run) =>\n run.id === runId ? next : run,\n )\n } else {\n this.devtoolsRuns = [...this.devtoolsRuns, next]\n }\n\n if (this.devtoolsRuns.length > this.maxRuns) {\n this.devtoolsRuns = this.devtoolsRuns.slice(-this.maxRuns)\n }\n }\n\n protected renameRun(previousRunId: string, nextRunId: string): void {\n if (previousRunId === nextRunId) return\n\n const existing = this.devtoolsRuns.find((run) => run.id === previousRunId)\n if (!existing) return\n\n const renamed = { ...existing, id: nextRunId, updatedAt: Date.now() }\n this.devtoolsRuns = this.devtoolsRuns\n .filter((run) => run.id !== nextRunId)\n .map((run) => (run.id === previousRunId ? renamed : run))\n }\n\n protected getProgress(): AIDevtoolsGenerationProgress | null {\n return this.getCoreState().progress\n }\n\n protected completeProgress(): AIDevtoolsGenerationProgress | null {\n const progress = this.getCoreState().progress\n if (!progress) return null\n return {\n value: 100,\n ...(progress.message ? { message: progress.message } : {}),\n }\n }\n\n protected createPreview(result: TOutput | null): AIDevtoolsGenerationPreview {\n return createAIDevtoolsGenerationPreview({\n outputKind: this.options.metadata.outputKind,\n result,\n })\n }\n\n protected generateRunId(): string {\n return `run-${Date.now()}-${Math.random().toString(36).substring(7)}`\n }\n}\n\n// Video-job specialization: snapshots also carry the job id and the latest\n// provider-reported video status so the panel can show streaming progress\n// before the final URL lands.\n\nexport interface AIDevtoolsVideoSnapshotBase<\n TOutput,\n> extends AIDevtoolsGenerationSnapshotBase<TOutput> {\n jobId: string | null\n videoStatus: unknown\n}\n\nexport interface VideoDevtoolsCoreState<\n TOutput,\n> extends GenerationDevtoolsCoreState<TOutput> {\n jobId: string | null\n videoStatus: unknown\n}\n\nexport interface VideoDevtoolsBridgeOptions<TOutput> extends Omit<\n GenerationDevtoolsBridgeOptions<TOutput>,\n 'getCoreState'\n> {\n getCoreState: () => VideoDevtoolsCoreState<TOutput>\n}\n\nexport interface VideoRunPatch<TOutput> extends GenerationRunPatch<TOutput> {\n jobId?: string | null\n videoStatus?: unknown\n}\n\nexport class VideoDevtoolsBridge<\n TOutput,\n> extends GenerationDevtoolsBridge<TOutput> {\n constructor(options: VideoDevtoolsBridgeOptions<TOutput>) {\n super(options)\n }\n\n recordJobIdChange(): void {\n this.updateActiveRun({\n jobId: (this.getCoreState() as VideoDevtoolsCoreState<TOutput>).jobId,\n } as VideoRunPatch<TOutput>)\n this.emitState()\n }\n\n recordVideoStatusChange(): void {\n const core = this.getCoreState() as VideoDevtoolsCoreState<TOutput>\n this.updateActiveRun({\n videoStatus: core.videoStatus,\n preview: this.createVideoPreview(core.result, core.videoStatus),\n } as VideoRunPatch<TOutput>)\n this.emitState()\n }\n\n protected override buildSnapshot(): AIDevtoolsVideoSnapshotBase<TOutput> {\n const core = this.getCoreState() as VideoDevtoolsCoreState<TOutput>\n return {\n input: core.input,\n result: core.result,\n preview: this.createVideoPreview(core.result, core.videoStatus),\n progress: core.progress,\n status: core.status,\n isLoading: core.isLoading,\n activeRunId: this.activeRunId,\n runs: this.devtoolsRuns,\n jobId: core.jobId,\n videoStatus: core.videoStatus,\n ...(core.error ? { error: core.error } : {}),\n }\n }\n\n protected override upsertRun(\n runId: string,\n patch: VideoRunPatch<TOutput>,\n ): void {\n super.upsertRun(runId, patch)\n if (!('jobId' in patch || 'videoStatus' in patch)) return\n\n const index = this.devtoolsRuns.findIndex((run) => run.id === runId)\n if (index < 0) return\n const target = this.devtoolsRuns[index]\n if (!target) return\n const merged: AIDevtoolsGenerationRunSnapshot<TOutput> = { ...target }\n if ('jobId' in patch) merged.jobId = patch.jobId ?? null\n if ('videoStatus' in patch) merged.videoStatus = patch.videoStatus ?? null\n this.devtoolsRuns = this.devtoolsRuns.map((run) =>\n run.id === runId ? merged : run,\n )\n }\n\n // Override so record* methods inherited from GenerationDevtoolsBridge\n // (e.g. recordResultChange) thread the latest videoStatus into the preview.\n protected override createPreview(\n result: TOutput | null,\n ): AIDevtoolsGenerationPreview {\n const core = this.getCoreState() as VideoDevtoolsCoreState<TOutput>\n return this.createVideoPreview(result, core.videoStatus)\n }\n\n private createVideoPreview(\n result: TOutput | null,\n videoStatus: unknown,\n ): AIDevtoolsGenerationPreview {\n return createAIDevtoolsGenerationPreview({\n outputKind: this.options.metadata.outputKind,\n result,\n videoStatus,\n })\n }\n}\n\n// Wraps the plain emitter so callers can do `this.events.X(...)` and get:\n// auto-attached run/thread context on every event that accepts one,\n// an auto-emitted snapshot after each event, and passive streamId tracking\n// so resolveStreamId() works without the chat client telling it.\nclass ChatDevtoolsAwareEventEmitter extends DefaultChatClientEventEmitter {\n constructor(\n private readonly getClientId: () => string,\n private readonly helper: ChatDevtoolsBridge,\n ) {\n super(getClientId())\n }\n\n protected override emitEvent(\n eventName: string,\n data?: Record<string, any>,\n ): void {\n this.clientId = this.getClientId()\n super.emitEvent(eventName, data)\n }\n\n private afterEmit(streamId?: string): void {\n if (streamId) this.helper.recordStreamId(streamId)\n this.helper.emitSnapshot()\n }\n\n // -- methods with run context --------------------------------------------\n\n override textUpdated(\n streamId: string,\n messageId: string,\n content: string,\n context?: ChatClientRunEventContext,\n ): void {\n super.textUpdated(\n streamId,\n messageId,\n content,\n context ?? this.helper.getCurrentRunEventContext(),\n )\n this.afterEmit(streamId)\n }\n\n override thinkingUpdated(\n streamId: string,\n messageId: string,\n content: string,\n delta?: string,\n context?: ChatClientRunEventContext,\n ): void {\n super.thinkingUpdated(\n streamId,\n messageId,\n content,\n delta,\n context ?? this.helper.getCurrentRunEventContext(),\n )\n this.afterEmit(streamId)\n }\n\n override messageAppended(\n uiMessage: Parameters<DefaultChatClientEventEmitter['messageAppended']>[0],\n streamId?: string,\n context?: ChatClientEventContext,\n ): void {\n super.messageAppended(\n uiMessage,\n streamId,\n context ?? this.helper.getCurrentRunEventContext(),\n )\n this.afterEmit(streamId)\n }\n\n override toolCallStateChanged(\n streamId: string,\n messageId: string,\n toolCallId: string,\n toolName: string,\n state: string,\n args: string,\n context?: ChatClientRunEventContext,\n ): void {\n super.toolCallStateChanged(\n streamId,\n messageId,\n toolCallId,\n toolName,\n state,\n args,\n context ?? this.helper.getCurrentRunEventContext(),\n )\n this.afterEmit(streamId)\n }\n\n override structuredOutputChanged(\n eventName: Parameters<\n DefaultChatClientEventEmitter['structuredOutputChanged']\n >[0],\n streamId: string,\n messageId: string,\n output: Parameters<\n DefaultChatClientEventEmitter['structuredOutputChanged']\n >[3],\n context?: ChatClientRunEventContext,\n ): void {\n super.structuredOutputChanged(\n eventName,\n streamId,\n messageId,\n output,\n context ?? this.helper.getCurrentOrLastRunEventContext(),\n )\n this.afterEmit(streamId)\n }\n\n override approvalRequested(\n streamId: string,\n messageId: string,\n toolCallId: string,\n toolName: string,\n input: unknown,\n approvalId: string,\n context?: ChatClientRunEventContext,\n ): void {\n super.approvalRequested(\n streamId,\n messageId,\n toolCallId,\n toolName,\n input,\n approvalId,\n context ?? this.helper.getCurrentOrLastRunEventContext(),\n )\n this.afterEmit(streamId)\n }\n\n override toolResultAdded(\n toolCallId: string,\n toolName: string,\n output: unknown,\n state: string,\n context?: ChatClientEventContext,\n ): void {\n super.toolResultAdded(\n toolCallId,\n toolName,\n output,\n state,\n context ?? this.helper.getCurrentRunEventContext(),\n )\n this.afterEmit()\n }\n\n override toolApprovalResponded(\n approvalId: string,\n toolCallId: string,\n approved: boolean,\n context?: ChatClientRunEventContext,\n ): void {\n super.toolApprovalResponded(\n approvalId,\n toolCallId,\n approved,\n context ?? this.helper.getCurrentRunEventContext(),\n )\n this.afterEmit()\n }\n\n // -- methods without context (just auto-emit snapshot) -------------------\n\n override clientCreated(initialMessageCount: number): void {\n super.clientCreated(initialMessageCount)\n this.afterEmit()\n }\n override loadingChanged(isLoading: boolean): void {\n super.loadingChanged(isLoading)\n this.afterEmit()\n }\n override errorChanged(error: string | null): void {\n super.errorChanged(error)\n this.afterEmit()\n }\n override reloaded(fromMessageIndex: number): void {\n super.reloaded(fromMessageIndex)\n this.afterEmit()\n }\n override stopped(): void {\n super.stopped()\n this.afterEmit()\n }\n override messagesCleared(): void {\n super.messagesCleared()\n this.afterEmit()\n }\n override messageSent(\n messageId: string,\n content: Parameters<DefaultChatClientEventEmitter['messageSent']>[1],\n ): void {\n super.messageSent(messageId, content)\n this.afterEmit()\n }\n override toolFixtureApplied(\n fixture: Parameters<DefaultChatClientEventEmitter['toolFixtureApplied']>[0],\n ): void {\n super.toolFixtureApplied(fixture)\n this.afterEmit()\n }\n}\n\nexport function createChatDevtoolsBridge(\n options: ChatDevtoolsBridgeOptions,\n): ChatDevtoolsBridge {\n return new ChatDevtoolsBridge(options)\n}\n\nexport function createGenerationDevtoolsBridge<TOutput>(\n options: GenerationDevtoolsBridgeOptions<TOutput>,\n): GenerationDevtoolsBridge<TOutput> {\n return new GenerationDevtoolsBridge<TOutput>(options)\n}\n\nexport function createVideoDevtoolsBridge<TOutput>(\n options: VideoDevtoolsBridgeOptions<TOutput>,\n): VideoDevtoolsBridge<TOutput> {\n return new VideoDevtoolsBridge<TOutput>(options)\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-client",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.2",
|
|
4
4
|
"description": "Framework-agnostic headless client for TanStack AI chat, realtime sessions, streaming transports, and media generations.",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
"src"
|
|
54
54
|
],
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@tanstack/ai": "^0.
|
|
57
|
-
"@tanstack/ai-event-client": "^0.11.
|
|
56
|
+
"@tanstack/ai": "^0.52.0",
|
|
57
|
+
"@tanstack/ai-event-client": "^0.11.2",
|
|
58
58
|
"@tanstack/ai-utils": "^0.4.0"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
package/src/chat-client.ts
CHANGED
|
@@ -164,6 +164,20 @@ function resolveTransport(transport: {
|
|
|
164
164
|
throw new Error('ChatClient: either `connection` or `fetcher` is required.')
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
function connectionDrainsOnSend(connection: ConnectionAdapter): boolean {
|
|
168
|
+
return 'connect' in connection
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function isIntermediateToolTurn(chunk: StreamChunk): boolean {
|
|
172
|
+
if (chunk.type !== 'RUN_FINISHED') return false
|
|
173
|
+
if (chunk.outcome?.type === 'interrupt') return false
|
|
174
|
+
const extra = chunk as StreamChunk & { finishReason?: unknown }
|
|
175
|
+
if (extra.finishReason !== undefined) {
|
|
176
|
+
return extra.finishReason === 'tool_calls'
|
|
177
|
+
}
|
|
178
|
+
return tanstackMetadata(chunk)?.finishReason === 'tool_calls'
|
|
179
|
+
}
|
|
180
|
+
|
|
167
181
|
export interface NormalizedQueueConfig {
|
|
168
182
|
whenBusy: WhenBusy
|
|
169
183
|
drain: 'fifo' | 'batch'
|
|
@@ -416,6 +430,12 @@ export class ChatClient<
|
|
|
416
430
|
private continuationPending = false
|
|
417
431
|
private subscriptionAbortController: AbortController | null = null
|
|
418
432
|
private processingResolve: (() => void) | null = null
|
|
433
|
+
/**
|
|
434
|
+
* `connect()` adapters push the full HTTP body into the subscribe queue, then
|
|
435
|
+
* wait until that queue is idle. After `send()` returns, every chunk from this
|
|
436
|
+
* request has been processed. Subscribe/send sockets do not drain that way.
|
|
437
|
+
*/
|
|
438
|
+
private connectionDrainsOnSend = false
|
|
419
439
|
private errorReportedGeneration: number | null = null
|
|
420
440
|
private streamGeneration = 0
|
|
421
441
|
private continuationGeneration = 0
|
|
@@ -518,7 +538,9 @@ export class ChatClient<
|
|
|
518
538
|
this.byokProvider = options.byokProvider
|
|
519
539
|
this.context = options.context
|
|
520
540
|
this.queueConfig = normalizeQueueOption(options.queue)
|
|
521
|
-
|
|
541
|
+
const transport = resolveTransport(options)
|
|
542
|
+
this.connectionDrainsOnSend = connectionDrainsOnSend(transport)
|
|
543
|
+
this.connection = normalizeConnectionAdapter(transport)
|
|
522
544
|
|
|
523
545
|
// Build client tools map
|
|
524
546
|
this.clientToolsRef = { current: new Map() }
|
|
@@ -829,6 +851,16 @@ export class ChatClient<
|
|
|
829
851
|
if (eventType === 'memory:state') {
|
|
830
852
|
this.devtoolsBridge.recordMemoryState(data)
|
|
831
853
|
}
|
|
854
|
+
if (
|
|
855
|
+
eventType === 'compaction:started' ||
|
|
856
|
+
eventType === 'compaction:state' ||
|
|
857
|
+
eventType === 'compaction:ended'
|
|
858
|
+
) {
|
|
859
|
+
this.devtoolsBridge.recordCompactionEvent(eventType, data)
|
|
860
|
+
}
|
|
861
|
+
if (eventType === 'skills:state') {
|
|
862
|
+
this.devtoolsBridge.recordSkillsState(data)
|
|
863
|
+
}
|
|
832
864
|
this.callbacksRef.current.onCustomEvent(eventType, data, context)
|
|
833
865
|
},
|
|
834
866
|
},
|
|
@@ -1140,7 +1172,9 @@ export class ChatClient<
|
|
|
1140
1172
|
this.clearedStreamTracker.onSessionRunError()
|
|
1141
1173
|
}
|
|
1142
1174
|
this.setSessionGenerating(this.activeRunIds.size > 0)
|
|
1143
|
-
|
|
1175
|
+
const skipProcessingResolve =
|
|
1176
|
+
chunk.type === 'RUN_FINISHED' && isIntermediateToolTurn(chunk)
|
|
1177
|
+
if (options?.resolveProcessing !== false && !skipProcessingResolve) {
|
|
1144
1178
|
this.resolveProcessing()
|
|
1145
1179
|
}
|
|
1146
1180
|
}
|
|
@@ -2344,6 +2378,14 @@ export class ChatClient<
|
|
|
2344
2378
|
return false
|
|
2345
2379
|
}
|
|
2346
2380
|
|
|
2381
|
+
// connect() send() already waited until the subscribe queue was idle.
|
|
2382
|
+
// Kick the processing wait so a stream that ends on tool_calls (no
|
|
2383
|
+
// interrupt / stop) cannot hang. Subscribe/send sockets still wait for
|
|
2384
|
+
// a request-ending terminal below.
|
|
2385
|
+
if (this.connectionDrainsOnSend) {
|
|
2386
|
+
this.resolveProcessing()
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2347
2389
|
// Wait for subscription loop to finish processing all chunks
|
|
2348
2390
|
await processingComplete
|
|
2349
2391
|
|
|
@@ -3045,12 +3087,12 @@ export class ChatClient<
|
|
|
3045
3087
|
this.resetSessionGenerating()
|
|
3046
3088
|
this.setIsSubscribed(false)
|
|
3047
3089
|
this.setConnectionStatus('disconnected')
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
)
|
|
3090
|
+
const transport = resolveTransport({
|
|
3091
|
+
connection: options.connection,
|
|
3092
|
+
fetcher: options.fetcher,
|
|
3093
|
+
})
|
|
3094
|
+
this.connectionDrainsOnSend = connectionDrainsOnSend(transport)
|
|
3095
|
+
this.connection = normalizeConnectionAdapter(transport)
|
|
3054
3096
|
|
|
3055
3097
|
if (wasSubscribed) {
|
|
3056
3098
|
this.subscribe()
|
|
@@ -1055,6 +1055,28 @@ export function normalizeConnectionAdapter(
|
|
|
1055
1055
|
}
|
|
1056
1056
|
}
|
|
1057
1057
|
|
|
1058
|
+
async function waitUntilSubscriberIdle(
|
|
1059
|
+
abortSignal?: AbortSignal,
|
|
1060
|
+
): Promise<void> {
|
|
1061
|
+
// Idle means the subscriber is waiting for the next chunk, so the
|
|
1062
|
+
// previous chunk has left processIncomingChunk. Empty waiters with an
|
|
1063
|
+
// empty buffer is in-flight delivery, not idle.
|
|
1064
|
+
const idle = () =>
|
|
1065
|
+
activeBuffer.length === 0 &&
|
|
1066
|
+
(activeWaiters.length > 0 || abortSignal?.aborted)
|
|
1067
|
+
for (let i = 0; i < 16 && !abortSignal?.aborted; i++) {
|
|
1068
|
+
if (idle()) return
|
|
1069
|
+
await Promise.resolve()
|
|
1070
|
+
}
|
|
1071
|
+
let macrotaskWaits = 0
|
|
1072
|
+
while (!abortSignal?.aborted) {
|
|
1073
|
+
if (idle()) return
|
|
1074
|
+
await new Promise<void>((resolve) => setTimeout(resolve, 0))
|
|
1075
|
+
macrotaskWaits++
|
|
1076
|
+
if (activeWaiters.length === 0 && macrotaskWaits >= 32) return
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1058
1080
|
return {
|
|
1059
1081
|
subscribe(abortSignal?: AbortSignal): AsyncIterable<StreamChunk> {
|
|
1060
1082
|
// Transfer ownership to the latest subscriber so only one active
|
|
@@ -1162,6 +1184,7 @@ export function normalizeConnectionAdapter(
|
|
|
1162
1184
|
}
|
|
1163
1185
|
throw err
|
|
1164
1186
|
}
|
|
1187
|
+
await waitUntilSubscriberIdle(abortSignal)
|
|
1165
1188
|
},
|
|
1166
1189
|
// Expose joinRun only when the underlying connection is resumable. Require
|
|
1167
1190
|
// a real function — `'joinRun' in connection` is true for
|
package/src/devtools-noop.ts
CHANGED
|
@@ -87,6 +87,9 @@ export class NoOpChatDevtoolsBridge {
|
|
|
87
87
|
}
|
|
88
88
|
observeChunk(_chunk: StreamChunk): void {}
|
|
89
89
|
recordMemoryState(_value: unknown): void {}
|
|
90
|
+
recordCompactionEvent(_eventType: string, _value: unknown): void {}
|
|
91
|
+
recordCompactionState(_value: unknown): void {}
|
|
92
|
+
recordSkillsState(_value: unknown): void {}
|
|
90
93
|
beginRun(_runId: string, _threadId: string): void {}
|
|
91
94
|
getCurrentRunEventContext(): ChatClientRunEventContext | undefined {
|
|
92
95
|
return undefined
|