@assistant-ui/core 0.2.7 → 0.2.8
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/adapters/attachment.d.ts.map +1 -1
- package/dist/adapters/speech.d.ts.map +1 -1
- package/dist/adapters/speech.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/duplicate-detection.d.ts.map +1 -1
- package/dist/internal.d.ts +2 -2
- package/dist/internal.js +2 -2
- package/dist/model-context/frame/host.d.ts.map +1 -1
- package/dist/model-context/frame/host.js.map +1 -1
- package/dist/model-context/frame/provider.d.ts.map +1 -1
- package/dist/model-context/frame/provider.js.map +1 -1
- package/dist/model-context/registry.d.ts.map +1 -1
- package/dist/model-context/tool.d.ts.map +1 -1
- package/dist/react/client/Interactables.js.map +1 -1
- package/dist/react/client/Tools.d.ts.map +1 -1
- package/dist/react/client/Tools.js +26 -15
- package/dist/react/client/Tools.js.map +1 -1
- package/dist/react/index.d.ts +4 -3
- package/dist/react/index.js +2 -1
- package/dist/react/model-context/toolbox.d.ts +29 -2
- package/dist/react/model-context/toolbox.d.ts.map +1 -1
- package/dist/react/model-context/toolbox.js +18 -0
- package/dist/react/model-context/toolbox.js.map +1 -0
- package/dist/react/model-context/useAssistantTool.d.ts.map +1 -1
- package/dist/react/model-context/useAssistantTool.js +6 -3
- package/dist/react/model-context/useAssistantTool.js.map +1 -1
- package/dist/react/model-context/useAssistantToolUI.d.ts +6 -0
- package/dist/react/model-context/useAssistantToolUI.d.ts.map +1 -1
- package/dist/react/model-context/useAssistantToolUI.js +4 -2
- package/dist/react/model-context/useAssistantToolUI.js.map +1 -1
- package/dist/react/model-context/useInlineRender.js.map +1 -1
- package/dist/react/primitives/message/MessageGroupedParts.d.ts +49 -7
- package/dist/react/primitives/message/MessageGroupedParts.d.ts.map +1 -1
- package/dist/react/primitives/message/MessageGroupedParts.js +28 -3
- package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
- package/dist/react/primitives/message/MessageParts.d.ts.map +1 -1
- package/dist/react/primitives/message/MessageParts.js +2 -7
- package/dist/react/primitives/message/MessageParts.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
- package/dist/react/runtimes/RuntimeAdapterProvider.d.ts.map +1 -1
- package/dist/react/runtimes/RuntimeAdapterProvider.js +6 -5
- package/dist/react/runtimes/RuntimeAdapterProvider.js.map +1 -1
- package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
- package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
- package/dist/react/runtimes/external-message-converter.js +1 -0
- package/dist/react/runtimes/external-message-converter.js.map +1 -1
- package/dist/react/runtimes/useExternalStoreSharedOptions.d.ts +7 -0
- package/dist/react/runtimes/useExternalStoreSharedOptions.d.ts.map +1 -0
- package/dist/react/runtimes/useExternalStoreSharedOptions.js +21 -0
- package/dist/react/runtimes/useExternalStoreSharedOptions.js.map +1 -0
- package/dist/react/runtimes/useLocalRuntime.d.ts.map +1 -1
- package/dist/react/runtimes/useLocalRuntime.js.map +1 -1
- package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
- package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
- package/dist/react/types/scopes/tools.d.ts +19 -2
- package/dist/react/types/scopes/tools.d.ts.map +1 -1
- package/dist/react/utils/groupParts.d.ts +32 -11
- package/dist/react/utils/groupParts.d.ts.map +1 -1
- package/dist/react/utils/groupParts.js +13 -6
- package/dist/react/utils/groupParts.js.map +1 -1
- package/dist/runtime/api/assistant-runtime.d.ts.map +1 -1
- package/dist/runtime/api/attachment-runtime.d.ts.map +1 -1
- package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
- package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
- package/dist/runtime/api/message-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
- package/dist/runtime/base/base-assistant-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/default-edit-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/utils/message-repository.d.ts +9 -1
- package/dist/runtime/utils/message-repository.d.ts.map +1 -1
- package/dist/runtime/utils/message-repository.js +34 -14
- package/dist/runtime/utils/message-repository.js.map +1 -1
- package/dist/runtime/utils/thread-message-like.d.ts +1 -0
- package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
- package/dist/runtime/utils/thread-message-like.js +2 -1
- package/dist/runtime/utils/thread-message-like.js.map +1 -1
- package/dist/runtimes/external-store/external-store-shared-options.d.ts +8 -0
- package/dist/runtimes/external-store/external-store-shared-options.d.ts.map +1 -0
- package/dist/runtimes/external-store/external-store-shared-options.js +11 -0
- package/dist/runtimes/external-store/external-store-shared-options.js.map +1 -0
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +0 -2
- package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js +12 -23
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/external-store/thread-message-converter.d.ts.map +1 -1
- package/dist/runtimes/local/local-thread-list-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/adapter/in-memory.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/optimistic-state.d.ts.map +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
- package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
- package/dist/subscribable/subscribable.d.ts.map +1 -1
- package/dist/tests/remote-thread-list-test-helpers.d.ts.map +1 -1
- package/dist/types/message.d.ts +6 -0
- package/dist/types/message.d.ts.map +1 -1
- package/dist/types/message.js.map +1 -1
- package/dist/utils/composite-context-provider.d.ts.map +1 -1
- package/dist/utils/id.d.ts +1 -3
- package/dist/utils/id.d.ts.map +1 -1
- package/dist/utils/id.js +1 -4
- package/dist/utils/id.js.map +1 -1
- package/package.json +10 -10
- package/src/adapters/speech.ts +0 -1
- package/src/index.ts +2 -0
- package/src/internal.ts +0 -2
- package/src/model-context/frame/host.ts +0 -1
- package/src/model-context/frame/provider.ts +0 -1
- package/src/react/client/Interactables.ts +0 -1
- package/src/react/client/Tools.ts +50 -25
- package/src/react/index.ts +8 -2
- package/src/react/model-context/toolbox.ts +46 -1
- package/src/react/model-context/useAssistantTool.ts +8 -3
- package/src/react/model-context/useAssistantToolUI.ts +9 -2
- package/src/react/model-context/useInlineRender.ts +0 -1
- package/src/react/primitives/message/MessageGroupedParts.tsx +101 -12
- package/src/react/primitives/message/MessageParts.tsx +4 -7
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +0 -3
- package/src/react/runtimes/RuntimeAdapterProvider.tsx +12 -7
- package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +0 -3
- package/src/react/runtimes/external-message-converter.ts +4 -0
- package/src/react/runtimes/useExternalStoreSharedOptions.ts +23 -0
- package/src/react/runtimes/useLocalRuntime.ts +0 -10
- package/src/react/runtimes/useRemoteThreadListRuntime.ts +0 -6
- package/src/react/types/scopes/tools.ts +20 -1
- package/src/react/utils/groupParts.ts +49 -18
- package/src/runtime/utils/message-repository.ts +57 -16
- package/src/runtime/utils/thread-message-like.ts +2 -0
- package/src/runtimes/external-store/external-store-shared-options.ts +18 -0
- package/src/runtimes/external-store/external-store-thread-runtime-core.ts +18 -33
- package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +0 -1
- package/src/tests/MessageRepository.test.ts +83 -52
- package/src/tests/OptimisticState-list-race.test.ts +0 -4
- package/src/tests/external-store-thread-runtime-core.test.ts +105 -73
- package/src/tests/groupParts.test.ts +70 -0
- package/src/tests/remote-thread-list-isLoading.test.ts +0 -5
- package/src/types/message.ts +6 -0
- package/src/utils/id.ts +0 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolInvocationTracker.js","names":[],"sources":["../../../src/runtimes/tool-invocations/ToolInvocationTracker.ts"],"sourcesContent":["declare const process: { env: { NODE_ENV?: string } };\n\nimport {\n createAssistantStreamController,\n type ToolCallStreamController,\n ToolResponse,\n unstable_toolResultStream,\n type Tool,\n type ToolModelContentPart,\n} from \"assistant-stream\";\nimport {\n AssistantMetaTransformStream,\n type ReadonlyJSONValue,\n} from \"assistant-stream/utils\";\nimport { isJSONValueEqual } from \"../../utils/json/is-json-equal\";\nimport type { ThreadMessage } from \"../../types/message\";\n\n/**\n * Streaming execution state for a frontend tool.\n */\nexport type ToolExecutionStatus =\n | { type: \"executing\" }\n | {\n type: \"interrupt\";\n payload: { type: \"human\"; payload: unknown };\n };\n\nexport type AddToolResultCommand = {\n readonly type: \"add-tool-result\";\n readonly toolCallId: string;\n readonly toolName: string;\n readonly result: ReadonlyJSONValue;\n readonly isError: boolean;\n readonly artifact?: ReadonlyJSONValue;\n readonly modelContent?: readonly ToolModelContentPart[];\n};\n\nexport type ToolInvocationTrackerSnapshot = {\n readonly messages: readonly ThreadMessage[];\n /** Whether the producing runtime is currently streaming new output. */\n readonly isRunning: boolean;\n /**\n * Whether the producing runtime is still loading historical state.\n * When `true`, every snapshot is treated as historical (no `streamCall` /\n * `execute` fires). When `false`, processing resumes as live.\n */\n readonly isLoading?: boolean;\n};\n\nexport type ToolInvocationTrackerCallbacks = {\n /**\n * Invoked when a client-side `execute()` returns a result and the runtime\n * needs to feed it back into the conversation.\n */\n onResult: (command: AddToolResultCommand) => void;\n /**\n * Invoked whenever the per-tool-call status map changes (executing /\n * interrupt / cleared). The callback receives a fresh map; mutating the\n * argument is not supported.\n */\n onStatusesChange: (\n statuses: ReadonlyMap<string, ToolExecutionStatus>,\n ) => void;\n};\n\ntype ToolCallEntry = {\n toolName: string;\n argsText: string;\n hasResult: boolean;\n} & (\n | {\n /** Restored phase — observed during a history-load snapshot. */\n controller?: undefined;\n argsComplete?: undefined;\n }\n | {\n /** Active phase — chunks are flowing through `controller`. */\n controller: ToolCallStreamController;\n argsComplete: boolean;\n }\n);\n\nconst isArgsTextComplete = (argsText: string) => {\n try {\n JSON.parse(argsText);\n return true;\n } catch {\n return false;\n }\n};\n\nconst parseArgsText = (argsText: string) => {\n try {\n return JSON.parse(argsText);\n } catch {\n return undefined;\n }\n};\n\nconst isEquivalentCompleteArgsText = (previous: string, next: string) => {\n const previousValue = parseArgsText(previous);\n const nextValue = parseArgsText(next);\n if (previousValue === undefined || nextValue === undefined) return false;\n return isJSONValueEqual(previousValue, nextValue);\n};\n\n/**\n * Plain-class port of the former `useToolInvocations` React hook. Owns the\n * assistant-stream pipeline that drives client-side `streamCall` / `execute`\n * for tool-call parts surfaced by a thread runtime, plus the per-tool-call\n * status map that consumers render against.\n *\n * **Contract**: `streamCall` (and `execute`) fires exactly once per logical\n * `toolCallId`. Args mutations after first completion, result replacement,\n * and result clearing are *not* surfaced through additional `streamCall`\n * invocations — by design — so hosts cannot observe spurious re-fires of\n * side effects. The follow-up `reader.events()` API will expose those\n * post-completion transitions to consumers that opt in.\n *\n * State-transition safety: every public method that observes runtime state\n * (`setState`, `reset`, `abort`, `resume`) wraps its work in try/catch and\n * logs to `console.error` rather than throwing. The tracker is built into\n * the hot message-processing path, so a malformed snapshot must never crash\n * the host runtime. See ./EDGE_CASES.md for the known non-trivial state\n * transitions and what each does today.\n */\nexport class ToolInvocationTracker {\n private readonly _getTools: () => Record<string, Tool> | undefined;\n private readonly _callbacks: ToolInvocationTrackerCallbacks;\n\n private readonly _entries = new Map<string, ToolCallEntry>();\n /**\n * Tool call ids whose `execute` should be short-circuited in the wrapper.\n * Populated when an entry is created with a result already attached\n * (history reload, mid-run resume, etc.) — `execute` is suppressed so\n * client-side side effects don't double-run. Membership outlives the\n * entry: `reset()` deliberately does *not* clear this so post-abort\n * cancellation `result` chunks for pre-resolved entries can still be\n * recognized and dropped. Growth is bounded by the number of pre-resolved\n * tool calls observed in the session.\n */\n private readonly _skipExecuteStreamIds = new Set<string>();\n private readonly _humanInput = new Map<\n string,\n {\n resolve: (payload: unknown) => void;\n reject: (reason: unknown) => void;\n }\n >();\n /** In-flight `execute` invocations keyed by tool call id. */\n private readonly _executing = new Set<string>();\n private readonly _settledResolvers: Array<() => void> = [];\n\n private _statuses = new Map<string, ToolExecutionStatus>();\n\n private _ac: AbortController = new AbortController();\n private _pendingRestore = true;\n\n /** Cached last snapshot, used to skip processing on identical re-renders. */\n private _lastSnapshot: ToolInvocationTrackerSnapshot | null = null;\n private _isRunning = false;\n\n private _controller!: ReturnType<typeof createAssistantStreamController>[1];\n\n /**\n * Set when the assistant-stream pipeline has died (errored out via\n * `.pipeTo(...).catch(...)`). The next `setState` re-initializes the\n * pipeline and demotes all active entries to restored so they survive\n * across the restart without re-firing `streamCall` (preserves the\n * \"exactly once\" contract). Capped at a single auto-restart per session\n * — repeated failures keep the tracker dead with a more visible error.\n */\n private _pipelineDead = false;\n private _pipelineRestartUsed = false;\n\n constructor(\n getTools: () => Record<string, Tool> | undefined,\n callbacks: ToolInvocationTrackerCallbacks,\n ) {\n this._getTools = getTools;\n this._callbacks = callbacks;\n\n this._initPipeline();\n }\n\n /**\n * Build the assistant-stream pipeline. Called once from the constructor\n * and at most once again if `_pipelineDead` is set (see F.4 in\n * EDGE_CASES.md).\n */\n private _initPipeline(): void {\n const [stream, controller] = createAssistantStreamController();\n this._controller = controller;\n\n const transform = unstable_toolResultStream(\n () => this._getWrappedTools(),\n () => this._ac.signal,\n (toolCallId, payload) => this._onHumanInput(toolCallId, payload),\n {\n onExecutionStart: (id) => this._onExecutionStart(id),\n onExecutionEnd: (id) => this._onExecutionEnd(id),\n },\n );\n\n stream\n .pipeThrough(transform)\n .pipeThrough(new AssistantMetaTransformStream())\n .pipeTo(\n new WritableStream({\n write: (chunk) => {\n try {\n if (chunk.type !== \"result\") return;\n this._handleResultChunk(chunk);\n } catch (err) {\n console.error(\n \"[ToolInvocationTracker] result chunk handling failed\",\n err,\n );\n }\n },\n }),\n )\n .catch((err) => {\n console.error(\n \"[ToolInvocationTracker] stream pipeline failed; will attempt single restart on next setState\",\n err,\n );\n this._pipelineDead = true;\n });\n }\n\n // ───────────────────────── public API ─────────────────────────\n\n /**\n * Feed the next observed snapshot into the tracker. Called from the host\n * runtime whenever its message list / running state changes.\n */\n public setState(snapshot: ToolInvocationTrackerSnapshot): void {\n try {\n // Recover from a dead pipeline before processing anything. We demote\n // all active entries to \"restored\" so the rebuilt pipeline does not\n // re-fire `streamCall` for tool calls that already fired pre-death;\n // preserves the \"exactly once per toolCallId\" contract.\n if (this._pipelineDead) {\n if (this._pipelineRestartUsed) {\n // Already retried once and failed again. Stay dead.\n return;\n }\n this._pipelineRestartUsed = true;\n this._pipelineDead = false;\n this._demoteEntriesToRestored();\n this._executing.clear();\n this._ac = new AbortController();\n this._initPipeline();\n // Fall through and process the snapshot against the fresh pipeline.\n }\n\n // Identical snapshot — skip processing entirely. Note: external-store\n // runtimes rebuild the messages array on every adapter update, so this\n // fast-path rarely triggers there; it's primarily for the React-hook\n // shim where state references are stable.\n if (\n this._lastSnapshot &&\n this._lastSnapshot.messages === snapshot.messages &&\n this._lastSnapshot.isRunning === snapshot.isRunning &&\n this._lastSnapshot.isLoading === snapshot.isLoading\n ) {\n return;\n }\n\n // While the host is still loading initial state, treat every snapshot\n // as historical: tool calls are recorded so the next live snapshot can\n // diff against them, but `streamCall` / `execute` do not fire.\n const restoreFromLoading = snapshot.isLoading === true;\n if (restoreFromLoading) {\n this._pendingRestore = true;\n }\n\n // E.4 / AF3 — only mark `_lastSnapshot`/`_isRunning` as observed after\n // processing succeeds. If `_processMessages` throws, the next snapshot\n // (even if identical) gets re-processed against the recovered state.\n const previousIsRunning = this._isRunning;\n this._isRunning = snapshot.isRunning;\n try {\n this._processMessages(snapshot.messages);\n } catch (err) {\n this._isRunning = previousIsRunning;\n throw err;\n }\n this._lastSnapshot = snapshot;\n this._pendingRestore = false;\n } catch (err) {\n console.error(\n \"[ToolInvocationTracker] setState failed; snapshot dropped\",\n err,\n );\n }\n }\n\n /**\n * Reset the tracker so the next observed snapshot is treated as historical.\n * Clears entries and aborts any in-flight executions. Used by callers like\n * `importExternalState` to mark a freshly loaded state as restored.\n */\n public reset(): void {\n try {\n this._pendingRestore = true;\n this._entries.clear();\n this._lastSnapshot = null;\n // `_skipExecuteStreamIds` is intentionally not cleared — see field doc.\n void this.abort().finally(() => {\n this._executing.clear();\n });\n } catch (err) {\n console.error(\"[ToolInvocationTracker] reset failed\", err);\n }\n }\n\n /**\n * Abort any in-flight `execute()` invocations. Resolves once all of them\n * have settled (or immediately if none are running).\n */\n public abort(): Promise<void> {\n try {\n this._humanInput.forEach(({ reject }) => {\n try {\n reject(new Error(\"Tool execution aborted\"));\n } catch {\n // host rejection handler threw — already in the abort path,\n // swallow so we continue cleaning up.\n }\n });\n this._humanInput.clear();\n\n this._ac.abort();\n this._ac = new AbortController();\n\n if (this._executing.size === 0) {\n return Promise.resolve();\n }\n return new Promise<void>((resolve) => {\n this._settledResolvers.push(resolve);\n });\n } catch (err) {\n console.error(\"[ToolInvocationTracker] abort failed\", err);\n return Promise.resolve();\n }\n }\n\n /**\n * Resolve a pending human-input request for the given tool call. Returns\n * `true` if a pending request was resumed, `false` if the tracker has no\n * outstanding request for that id (the caller should fall back to its own\n * dispatch path).\n */\n public resume(toolCallId: string, payload: unknown): boolean {\n try {\n const handlers = this._humanInput.get(toolCallId);\n if (!handlers) return false;\n this._humanInput.delete(toolCallId);\n this._setStatus(toolCallId, { type: \"executing\" });\n handlers.resolve(payload);\n return true;\n } catch (err) {\n console.error(\"[ToolInvocationTracker] resume failed\", err);\n return false;\n }\n }\n\n /**\n * Returns the current tool execution status map. The returned `Map` is\n * the tracker's internal store — do not mutate it. Treat the reference\n * as a snapshot that may be replaced wholesale on the next status\n * transition.\n */\n public getStatuses(): ReadonlyMap<string, ToolExecutionStatus> {\n return this._statuses;\n }\n\n // ───────────────────── internal: tool wrapping ─────────────────────\n\n private _getWrappedTools(): Record<string, Tool> | undefined {\n const tools = this._getTools();\n if (!tools) return undefined;\n\n return Object.fromEntries(\n Object.entries(tools).map(([name, tool]) => {\n const execute = tool.execute;\n if (execute === undefined) return [name, tool];\n\n const wrappedTool = {\n ...tool,\n execute: (\n ...[args, context]: Parameters<NonNullable<typeof execute>>\n ) => {\n if (this._skipExecuteStreamIds.has(context.toolCallId)) {\n // Pre-resolved tool call: never invoke the host's execute.\n // Returning a never-settling Promise keeps the executor's\n // pending entry alive but enqueues nothing.\n return new Promise(() => {}) as never;\n }\n return execute(args, context);\n },\n } as Tool;\n return [name, wrappedTool];\n }),\n ) as Record<string, Tool>;\n }\n\n // ──────────────── internal: execution lifecycle callbacks ────────────────\n\n private _onHumanInput(\n toolCallId: string,\n payload: unknown,\n ): Promise<unknown> {\n return new Promise<unknown>((resolve, reject) => {\n const previous = this._humanInput.get(toolCallId);\n if (previous) {\n try {\n previous.reject(\n new Error(\"Human input request was superseded by a new request\"),\n );\n } catch {\n // host rejection handler threw; ignore and proceed\n }\n }\n this._humanInput.set(toolCallId, { resolve, reject });\n this._setStatus(toolCallId, {\n type: \"interrupt\",\n payload: { type: \"human\", payload },\n });\n });\n }\n\n private _onExecutionStart(toolCallId: string): void {\n if (this._skipExecuteStreamIds.has(toolCallId)) return;\n\n this._executing.add(toolCallId);\n this._setStatus(toolCallId, { type: \"executing\" });\n }\n\n private _onExecutionEnd(toolCallId: string): void {\n if (!this._executing.delete(toolCallId)) return;\n\n this._deleteStatus(toolCallId);\n\n if (this._executing.size === 0) {\n const resolvers = this._settledResolvers.splice(0);\n // biome-ignore lint/suspicious/useIterableCallbackReturn: forEach callback intentionally has no return\n resolvers.forEach((resolve) => {\n try {\n resolve();\n } catch {\n // ignore — settled-resolver consumer threw\n }\n });\n }\n }\n\n private _handleResultChunk(chunk: {\n type: \"result\";\n result: ReadonlyJSONValue;\n isError: boolean;\n artifact?: ReadonlyJSONValue;\n modelContent?: readonly ToolModelContentPart[];\n meta: { toolCallId: string; toolName: string };\n }): void {\n const toolCallId = chunk.meta.toolCallId;\n const entry = this._entries.get(toolCallId);\n\n // Pre-resolved tool call whose entry has been cleared by `reset()`.\n // The post-abort cancellation chunk lands here after the entry is\n // gone; suppress via the long-lived skip-execute marker.\n if (!entry && this._skipExecuteStreamIds.has(toolCallId)) {\n return;\n }\n\n // The host already set the result (via the live snapshot's\n // `setResponse` path). Suppress the executor's redundant emit.\n if (entry?.hasResult) return;\n\n this._invokeOnResult({\n type: \"add-tool-result\",\n toolCallId,\n toolName: chunk.meta.toolName,\n result: chunk.result,\n isError: chunk.isError,\n ...(chunk.artifact !== undefined && { artifact: chunk.artifact }),\n ...(chunk.modelContent !== undefined && {\n modelContent: chunk.modelContent,\n }),\n });\n }\n\n // ──────────────── internal: callback invocation (AF1/AF2) ────────────────\n\n private _invokeOnResult(command: AddToolResultCommand): void {\n try {\n this._callbacks.onResult(command);\n } catch (err) {\n console.error(\n \"[ToolInvocationTracker] onResult callback threw; result dropped\",\n err,\n );\n }\n }\n\n private _invokeOnStatusesChange(): void {\n try {\n this._callbacks.onStatusesChange(this._statuses);\n } catch (err) {\n console.error(\n \"[ToolInvocationTracker] onStatusesChange callback threw; status change not propagated\",\n err,\n );\n }\n }\n\n // ──────────────── internal: status map mutations ────────────────\n\n private _setStatus(toolCallId: string, status: ToolExecutionStatus): void {\n const next = new Map(this._statuses);\n next.set(toolCallId, status);\n this._statuses = next;\n this._invokeOnStatusesChange();\n }\n\n private _deleteStatus(toolCallId: string): void {\n if (!this._statuses.has(toolCallId)) return;\n const next = new Map(this._statuses);\n next.delete(toolCallId);\n this._statuses = next;\n this._invokeOnStatusesChange();\n }\n\n // ──────────────── internal: snapshot processing ────────────────\n\n private _hasExecutableTool(toolName: string): boolean {\n const tool = this._getTools()?.[toolName];\n return tool?.execute !== undefined || tool?.streamCall !== undefined;\n }\n\n private _shouldCloseArgsStream({\n toolName,\n argsText,\n hasResult,\n }: {\n toolName: string;\n argsText: string;\n hasResult: boolean;\n }): boolean {\n if (hasResult) return true;\n if (!this._hasExecutableTool(toolName)) {\n return !this._isRunning && isArgsTextComplete(argsText);\n }\n return isArgsTextComplete(argsText);\n }\n\n private _startActiveEntry(\n toolCallId: string,\n toolName: string,\n skipExecute: boolean,\n ): ToolCallEntry {\n const toolCallController = this._controller.addToolCallPart({\n toolName,\n toolCallId,\n });\n if (skipExecute) {\n this._skipExecuteStreamIds.add(toolCallId);\n }\n const entry: ToolCallEntry = {\n toolName,\n controller: toolCallController,\n argsText: \"\",\n hasResult: false,\n argsComplete: false,\n };\n this._entries.set(toolCallId, entry);\n return entry;\n }\n\n /**\n * Demote every active entry back to the restored phase. Used by the\n * pipeline-restart path so that, after a fresh pipeline is built, the\n * next observed snapshot does not re-fire `streamCall` for tool calls\n * that already fired pre-death. Args / hasResult tracking is preserved\n * so signature comparisons still work.\n */\n private _demoteEntriesToRestored(): void {\n for (const [toolCallId, entry] of this._entries) {\n if (!entry.controller) continue;\n this._entries.set(toolCallId, {\n toolName: entry.toolName,\n argsText: entry.argsText,\n hasResult: entry.hasResult,\n });\n }\n }\n\n private _processArgsText(\n entry: ToolCallEntry,\n content: {\n toolCallId: string;\n toolName: string;\n argsText: string;\n result?: unknown;\n },\n ): void {\n if (!entry.controller) return;\n const hasResult = content.result !== undefined;\n\n if (content.argsText !== entry.argsText) {\n let shouldWriteArgsText = true;\n\n if (entry.argsComplete) {\n if (isEquivalentCompleteArgsText(entry.argsText, content.argsText)) {\n // A.3 — key reorder. Track new text, no re-fire needed.\n entry.argsText = content.argsText;\n shouldWriteArgsText = false;\n } else {\n // A.4 — args changed after first completion. Under the\n // \"exactly once per toolCallId\" contract we do not restart the\n // stream. The host's existing `streamCall` keeps its original\n // args view; the snapshot's new text is recorded for diffing\n // but not surfaced. Events API in a follow-up will expose this\n // to consumers that opt in.\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n \"[ToolInvocationTracker] argsText changed after first completion; not re-firing streamCall (see EDGE_CASES.md A.4)\",\n {\n previous: entry.argsText,\n next: content.argsText,\n toolCallId: content.toolCallId,\n },\n );\n }\n shouldWriteArgsText = false;\n }\n } else if (!content.argsText.startsWith(entry.argsText)) {\n if (\n isArgsTextComplete(entry.argsText) &&\n isArgsTextComplete(content.argsText) &&\n isEquivalentCompleteArgsText(entry.argsText, content.argsText)\n ) {\n const shouldClose = this._shouldCloseArgsStream({\n toolName: content.toolName,\n argsText: content.argsText,\n hasResult,\n });\n if (shouldClose) entry.controller.argsText.close();\n entry.argsText = content.argsText;\n entry.argsComplete = shouldClose;\n shouldWriteArgsText = false;\n } else {\n // A.2 — args regressed mid-stream. Under the \"exactly once\"\n // contract we do not restart. The controller keeps whatever\n // prefix we already streamed; subsequent prefix-respecting\n // updates can still flow against it. Snapshots that never\n // re-converge to a prefix will leave the controller's args\n // view stale relative to the snapshot. Events API in a\n // follow-up will expose this to consumers that opt in.\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n \"[ToolInvocationTracker] argsText regressed mid-stream; not restarting (see EDGE_CASES.md A.2)\",\n {\n previous: entry.argsText,\n next: content.argsText,\n toolCallId: content.toolCallId,\n },\n );\n }\n shouldWriteArgsText = false;\n }\n }\n\n if (shouldWriteArgsText && entry.controller) {\n const delta = content.argsText.slice(entry.argsText.length);\n entry.controller.argsText.append(delta);\n const shouldClose = this._shouldCloseArgsStream({\n toolName: content.toolName,\n argsText: content.argsText,\n hasResult,\n });\n if (shouldClose) entry.controller.argsText.close();\n entry.argsText = content.argsText;\n entry.argsComplete = shouldClose;\n }\n }\n\n if (!entry.argsComplete && entry.controller) {\n const shouldClose = this._shouldCloseArgsStream({\n toolName: content.toolName,\n argsText: content.argsText,\n hasResult,\n });\n if (shouldClose) {\n entry.controller.argsText.close();\n entry.argsText = content.argsText;\n entry.argsComplete = true;\n }\n }\n }\n\n private _processMessages(messages: readonly ThreadMessage[]): void {\n const isRestore = this._pendingRestore;\n\n for (const message of messages) {\n if (!message || !Array.isArray((message as ThreadMessage).content)) {\n continue;\n }\n for (const content of message.content as readonly ThreadMessage[\"content\"][number][]) {\n if (!content || content.type !== \"tool-call\") continue;\n\n const existing = this._entries.get(content.toolCallId);\n\n if (isRestore) {\n // Don't overwrite an already-active entry (e.g. live tool-call\n // observed before this restore snapshot landed). Restore can\n // only seed entries the runtime has never seen.\n if (!existing?.controller) {\n this._entries.set(content.toolCallId, {\n toolName: content.toolName,\n argsText: content.argsText,\n hasResult: content.result !== undefined,\n });\n }\n if (content.messages) this._processMessages(content.messages);\n continue;\n }\n\n // Live snapshot.\n let entry = existing;\n\n if (entry && !entry.controller) {\n // Restored entry observed in a live snapshot. Promote if its\n // signature has changed; otherwise treat as still-historical.\n const signatureChanged =\n content.argsText !== entry.argsText ||\n (content.result !== undefined) !== entry.hasResult;\n if (!signatureChanged) {\n if (content.messages) this._processMessages(content.messages);\n continue;\n }\n this._entries.delete(content.toolCallId);\n entry = undefined;\n }\n\n if (!entry) {\n entry = this._startActiveEntry(\n content.toolCallId,\n content.toolName,\n content.result !== undefined,\n );\n }\n\n this._processArgsText(entry, content);\n\n if (content.result !== undefined && !entry.hasResult) {\n // `entry` is in active phase from this point — either just\n // created by `_startActiveEntry`, or pre-existing with a live\n // controller. Narrow once instead of asserting at every use.\n const { controller: activeController } = entry;\n if (!activeController) continue;\n entry.hasResult = true;\n entry.argsComplete = true;\n activeController.setResponse(\n new ToolResponse({\n result: content.result as ReadonlyJSONValue,\n artifact: content.artifact as ReadonlyJSONValue | undefined,\n isError: content.isError,\n ...(content.modelContent !== undefined\n ? { modelContent: content.modelContent }\n : {}),\n }),\n );\n activeController.close();\n }\n\n if (content.messages) this._processMessages(content.messages);\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ToolInvocationTracker.js","names":[],"sources":["../../../src/runtimes/tool-invocations/ToolInvocationTracker.ts"],"sourcesContent":["declare const process: { env: { NODE_ENV?: string } };\n\nimport {\n createAssistantStreamController,\n type ToolCallStreamController,\n ToolResponse,\n unstable_toolResultStream,\n type Tool,\n type ToolModelContentPart,\n} from \"assistant-stream\";\nimport {\n AssistantMetaTransformStream,\n type ReadonlyJSONValue,\n} from \"assistant-stream/utils\";\nimport { isJSONValueEqual } from \"../../utils/json/is-json-equal\";\nimport type { ThreadMessage } from \"../../types/message\";\n\n/**\n * Streaming execution state for a frontend tool.\n */\nexport type ToolExecutionStatus =\n | { type: \"executing\" }\n | {\n type: \"interrupt\";\n payload: { type: \"human\"; payload: unknown };\n };\n\nexport type AddToolResultCommand = {\n readonly type: \"add-tool-result\";\n readonly toolCallId: string;\n readonly toolName: string;\n readonly result: ReadonlyJSONValue;\n readonly isError: boolean;\n readonly artifact?: ReadonlyJSONValue;\n readonly modelContent?: readonly ToolModelContentPart[];\n};\n\nexport type ToolInvocationTrackerSnapshot = {\n readonly messages: readonly ThreadMessage[];\n /** Whether the producing runtime is currently streaming new output. */\n readonly isRunning: boolean;\n /**\n * Whether the producing runtime is still loading historical state.\n * When `true`, every snapshot is treated as historical (no `streamCall` /\n * `execute` fires). When `false`, processing resumes as live.\n */\n readonly isLoading?: boolean;\n};\n\nexport type ToolInvocationTrackerCallbacks = {\n /**\n * Invoked when a client-side `execute()` returns a result and the runtime\n * needs to feed it back into the conversation.\n */\n onResult: (command: AddToolResultCommand) => void;\n /**\n * Invoked whenever the per-tool-call status map changes (executing /\n * interrupt / cleared). The callback receives a fresh map; mutating the\n * argument is not supported.\n */\n onStatusesChange: (\n statuses: ReadonlyMap<string, ToolExecutionStatus>,\n ) => void;\n};\n\ntype ToolCallEntry = {\n toolName: string;\n argsText: string;\n hasResult: boolean;\n} & (\n | {\n /** Restored phase — observed during a history-load snapshot. */\n controller?: undefined;\n argsComplete?: undefined;\n }\n | {\n /** Active phase — chunks are flowing through `controller`. */\n controller: ToolCallStreamController;\n argsComplete: boolean;\n }\n);\n\nconst isArgsTextComplete = (argsText: string) => {\n try {\n JSON.parse(argsText);\n return true;\n } catch {\n return false;\n }\n};\n\nconst parseArgsText = (argsText: string) => {\n try {\n return JSON.parse(argsText);\n } catch {\n return undefined;\n }\n};\n\nconst isEquivalentCompleteArgsText = (previous: string, next: string) => {\n const previousValue = parseArgsText(previous);\n const nextValue = parseArgsText(next);\n if (previousValue === undefined || nextValue === undefined) return false;\n return isJSONValueEqual(previousValue, nextValue);\n};\n\n/**\n * Plain-class port of the former `useToolInvocations` React hook. Owns the\n * assistant-stream pipeline that drives client-side `streamCall` / `execute`\n * for tool-call parts surfaced by a thread runtime, plus the per-tool-call\n * status map that consumers render against.\n *\n * **Contract**: `streamCall` (and `execute`) fires exactly once per logical\n * `toolCallId`. Args mutations after first completion, result replacement,\n * and result clearing are *not* surfaced through additional `streamCall`\n * invocations — by design — so hosts cannot observe spurious re-fires of\n * side effects. The follow-up `reader.events()` API will expose those\n * post-completion transitions to consumers that opt in.\n *\n * State-transition safety: every public method that observes runtime state\n * (`setState`, `reset`, `abort`, `resume`) wraps its work in try/catch and\n * logs to `console.error` rather than throwing. The tracker is built into\n * the hot message-processing path, so a malformed snapshot must never crash\n * the host runtime. See ./EDGE_CASES.md for the known non-trivial state\n * transitions and what each does today.\n */\nexport class ToolInvocationTracker {\n private readonly _getTools: () => Record<string, Tool> | undefined;\n private readonly _callbacks: ToolInvocationTrackerCallbacks;\n\n private readonly _entries = new Map<string, ToolCallEntry>();\n /**\n * Tool call ids whose `execute` should be short-circuited in the wrapper.\n * Populated when an entry is created with a result already attached\n * (history reload, mid-run resume, etc.) — `execute` is suppressed so\n * client-side side effects don't double-run. Membership outlives the\n * entry: `reset()` deliberately does *not* clear this so post-abort\n * cancellation `result` chunks for pre-resolved entries can still be\n * recognized and dropped. Growth is bounded by the number of pre-resolved\n * tool calls observed in the session.\n */\n private readonly _skipExecuteStreamIds = new Set<string>();\n private readonly _humanInput = new Map<\n string,\n {\n resolve: (payload: unknown) => void;\n reject: (reason: unknown) => void;\n }\n >();\n /** In-flight `execute` invocations keyed by tool call id. */\n private readonly _executing = new Set<string>();\n private readonly _settledResolvers: Array<() => void> = [];\n\n private _statuses = new Map<string, ToolExecutionStatus>();\n\n private _ac: AbortController = new AbortController();\n private _pendingRestore = true;\n\n /** Cached last snapshot, used to skip processing on identical re-renders. */\n private _lastSnapshot: ToolInvocationTrackerSnapshot | null = null;\n private _isRunning = false;\n\n private _controller!: ReturnType<typeof createAssistantStreamController>[1];\n\n /**\n * Set when the assistant-stream pipeline has died (errored out via\n * `.pipeTo(...).catch(...)`). The next `setState` re-initializes the\n * pipeline and demotes all active entries to restored so they survive\n * across the restart without re-firing `streamCall` (preserves the\n * \"exactly once\" contract). Capped at a single auto-restart per session\n * — repeated failures keep the tracker dead with a more visible error.\n */\n private _pipelineDead = false;\n private _pipelineRestartUsed = false;\n\n constructor(\n getTools: () => Record<string, Tool> | undefined,\n callbacks: ToolInvocationTrackerCallbacks,\n ) {\n this._getTools = getTools;\n this._callbacks = callbacks;\n\n this._initPipeline();\n }\n\n /**\n * Build the assistant-stream pipeline. Called once from the constructor\n * and at most once again if `_pipelineDead` is set (see F.4 in\n * EDGE_CASES.md).\n */\n private _initPipeline(): void {\n const [stream, controller] = createAssistantStreamController();\n this._controller = controller;\n\n const transform = unstable_toolResultStream(\n () => this._getWrappedTools(),\n () => this._ac.signal,\n (toolCallId, payload) => this._onHumanInput(toolCallId, payload),\n {\n onExecutionStart: (id) => this._onExecutionStart(id),\n onExecutionEnd: (id) => this._onExecutionEnd(id),\n },\n );\n\n stream\n .pipeThrough(transform)\n .pipeThrough(new AssistantMetaTransformStream())\n .pipeTo(\n new WritableStream({\n write: (chunk) => {\n try {\n if (chunk.type !== \"result\") return;\n this._handleResultChunk(chunk);\n } catch (err) {\n console.error(\n \"[ToolInvocationTracker] result chunk handling failed\",\n err,\n );\n }\n },\n }),\n )\n .catch((err) => {\n console.error(\n \"[ToolInvocationTracker] stream pipeline failed; will attempt single restart on next setState\",\n err,\n );\n this._pipelineDead = true;\n });\n }\n\n // ───────────────────────── public API ─────────────────────────\n\n /**\n * Feed the next observed snapshot into the tracker. Called from the host\n * runtime whenever its message list / running state changes.\n */\n public setState(snapshot: ToolInvocationTrackerSnapshot): void {\n try {\n // Recover from a dead pipeline before processing anything. We demote\n // all active entries to \"restored\" so the rebuilt pipeline does not\n // re-fire `streamCall` for tool calls that already fired pre-death;\n // preserves the \"exactly once per toolCallId\" contract.\n if (this._pipelineDead) {\n if (this._pipelineRestartUsed) {\n // Already retried once and failed again. Stay dead.\n return;\n }\n this._pipelineRestartUsed = true;\n this._pipelineDead = false;\n this._demoteEntriesToRestored();\n this._executing.clear();\n this._ac = new AbortController();\n this._initPipeline();\n // Fall through and process the snapshot against the fresh pipeline.\n }\n\n // Identical snapshot — skip processing entirely. Note: external-store\n // runtimes rebuild the messages array on every adapter update, so this\n // fast-path rarely triggers there; it's primarily for the React-hook\n // shim where state references are stable.\n if (\n this._lastSnapshot &&\n this._lastSnapshot.messages === snapshot.messages &&\n this._lastSnapshot.isRunning === snapshot.isRunning &&\n this._lastSnapshot.isLoading === snapshot.isLoading\n ) {\n return;\n }\n\n // While the host is still loading initial state, treat every snapshot\n // as historical: tool calls are recorded so the next live snapshot can\n // diff against them, but `streamCall` / `execute` do not fire.\n const restoreFromLoading = snapshot.isLoading === true;\n if (restoreFromLoading) {\n this._pendingRestore = true;\n }\n\n // E.4 / AF3 — only mark `_lastSnapshot`/`_isRunning` as observed after\n // processing succeeds. If `_processMessages` throws, the next snapshot\n // (even if identical) gets re-processed against the recovered state.\n const previousIsRunning = this._isRunning;\n this._isRunning = snapshot.isRunning;\n try {\n this._processMessages(snapshot.messages);\n } catch (err) {\n this._isRunning = previousIsRunning;\n throw err;\n }\n this._lastSnapshot = snapshot;\n this._pendingRestore = false;\n } catch (err) {\n console.error(\n \"[ToolInvocationTracker] setState failed; snapshot dropped\",\n err,\n );\n }\n }\n\n /**\n * Reset the tracker so the next observed snapshot is treated as historical.\n * Clears entries and aborts any in-flight executions. Used by callers like\n * `importExternalState` to mark a freshly loaded state as restored.\n */\n public reset(): void {\n try {\n this._pendingRestore = true;\n this._entries.clear();\n this._lastSnapshot = null;\n // `_skipExecuteStreamIds` is intentionally not cleared — see field doc.\n void this.abort().finally(() => {\n this._executing.clear();\n });\n } catch (err) {\n console.error(\"[ToolInvocationTracker] reset failed\", err);\n }\n }\n\n /**\n * Abort any in-flight `execute()` invocations. Resolves once all of them\n * have settled (or immediately if none are running).\n */\n public abort(): Promise<void> {\n try {\n this._humanInput.forEach(({ reject }) => {\n try {\n reject(new Error(\"Tool execution aborted\"));\n } catch {\n // host rejection handler threw — already in the abort path,\n // swallow so we continue cleaning up.\n }\n });\n this._humanInput.clear();\n\n this._ac.abort();\n this._ac = new AbortController();\n\n if (this._executing.size === 0) {\n return Promise.resolve();\n }\n return new Promise<void>((resolve) => {\n this._settledResolvers.push(resolve);\n });\n } catch (err) {\n console.error(\"[ToolInvocationTracker] abort failed\", err);\n return Promise.resolve();\n }\n }\n\n /**\n * Resolve a pending human-input request for the given tool call. Returns\n * `true` if a pending request was resumed, `false` if the tracker has no\n * outstanding request for that id (the caller should fall back to its own\n * dispatch path).\n */\n public resume(toolCallId: string, payload: unknown): boolean {\n try {\n const handlers = this._humanInput.get(toolCallId);\n if (!handlers) return false;\n this._humanInput.delete(toolCallId);\n this._setStatus(toolCallId, { type: \"executing\" });\n handlers.resolve(payload);\n return true;\n } catch (err) {\n console.error(\"[ToolInvocationTracker] resume failed\", err);\n return false;\n }\n }\n\n /**\n * Returns the current tool execution status map. The returned `Map` is\n * the tracker's internal store — do not mutate it. Treat the reference\n * as a snapshot that may be replaced wholesale on the next status\n * transition.\n */\n public getStatuses(): ReadonlyMap<string, ToolExecutionStatus> {\n return this._statuses;\n }\n\n // ───────────────────── internal: tool wrapping ─────────────────────\n\n private _getWrappedTools(): Record<string, Tool> | undefined {\n const tools = this._getTools();\n if (!tools) return undefined;\n\n return Object.fromEntries(\n Object.entries(tools).map(([name, tool]) => {\n const execute = tool.execute;\n if (execute === undefined) return [name, tool];\n\n const wrappedTool = {\n ...tool,\n execute: (\n ...[args, context]: Parameters<NonNullable<typeof execute>>\n ) => {\n if (this._skipExecuteStreamIds.has(context.toolCallId)) {\n // Pre-resolved tool call: never invoke the host's execute.\n // Returning a never-settling Promise keeps the executor's\n // pending entry alive but enqueues nothing.\n return new Promise(() => {}) as never;\n }\n return execute(args, context);\n },\n } as Tool;\n return [name, wrappedTool];\n }),\n ) as Record<string, Tool>;\n }\n\n // ──────────────── internal: execution lifecycle callbacks ────────────────\n\n private _onHumanInput(\n toolCallId: string,\n payload: unknown,\n ): Promise<unknown> {\n return new Promise<unknown>((resolve, reject) => {\n const previous = this._humanInput.get(toolCallId);\n if (previous) {\n try {\n previous.reject(\n new Error(\"Human input request was superseded by a new request\"),\n );\n } catch {\n // host rejection handler threw; ignore and proceed\n }\n }\n this._humanInput.set(toolCallId, { resolve, reject });\n this._setStatus(toolCallId, {\n type: \"interrupt\",\n payload: { type: \"human\", payload },\n });\n });\n }\n\n private _onExecutionStart(toolCallId: string): void {\n if (this._skipExecuteStreamIds.has(toolCallId)) return;\n\n this._executing.add(toolCallId);\n this._setStatus(toolCallId, { type: \"executing\" });\n }\n\n private _onExecutionEnd(toolCallId: string): void {\n if (!this._executing.delete(toolCallId)) return;\n\n this._deleteStatus(toolCallId);\n\n if (this._executing.size === 0) {\n const resolvers = this._settledResolvers.splice(0);\n resolvers.forEach((resolve) => {\n try {\n resolve();\n } catch {\n // ignore — settled-resolver consumer threw\n }\n });\n }\n }\n\n private _handleResultChunk(chunk: {\n type: \"result\";\n result: ReadonlyJSONValue;\n isError: boolean;\n artifact?: ReadonlyJSONValue;\n modelContent?: readonly ToolModelContentPart[];\n meta: { toolCallId: string; toolName: string };\n }): void {\n const toolCallId = chunk.meta.toolCallId;\n const entry = this._entries.get(toolCallId);\n\n // Pre-resolved tool call whose entry has been cleared by `reset()`.\n // The post-abort cancellation chunk lands here after the entry is\n // gone; suppress via the long-lived skip-execute marker.\n if (!entry && this._skipExecuteStreamIds.has(toolCallId)) {\n return;\n }\n\n // The host already set the result (via the live snapshot's\n // `setResponse` path). Suppress the executor's redundant emit.\n if (entry?.hasResult) return;\n\n this._invokeOnResult({\n type: \"add-tool-result\",\n toolCallId,\n toolName: chunk.meta.toolName,\n result: chunk.result,\n isError: chunk.isError,\n ...(chunk.artifact !== undefined && { artifact: chunk.artifact }),\n ...(chunk.modelContent !== undefined && {\n modelContent: chunk.modelContent,\n }),\n });\n }\n\n // ──────────────── internal: callback invocation (AF1/AF2) ────────────────\n\n private _invokeOnResult(command: AddToolResultCommand): void {\n try {\n this._callbacks.onResult(command);\n } catch (err) {\n console.error(\n \"[ToolInvocationTracker] onResult callback threw; result dropped\",\n err,\n );\n }\n }\n\n private _invokeOnStatusesChange(): void {\n try {\n this._callbacks.onStatusesChange(this._statuses);\n } catch (err) {\n console.error(\n \"[ToolInvocationTracker] onStatusesChange callback threw; status change not propagated\",\n err,\n );\n }\n }\n\n // ──────────────── internal: status map mutations ────────────────\n\n private _setStatus(toolCallId: string, status: ToolExecutionStatus): void {\n const next = new Map(this._statuses);\n next.set(toolCallId, status);\n this._statuses = next;\n this._invokeOnStatusesChange();\n }\n\n private _deleteStatus(toolCallId: string): void {\n if (!this._statuses.has(toolCallId)) return;\n const next = new Map(this._statuses);\n next.delete(toolCallId);\n this._statuses = next;\n this._invokeOnStatusesChange();\n }\n\n // ──────────────── internal: snapshot processing ────────────────\n\n private _hasExecutableTool(toolName: string): boolean {\n const tool = this._getTools()?.[toolName];\n return tool?.execute !== undefined || tool?.streamCall !== undefined;\n }\n\n private _shouldCloseArgsStream({\n toolName,\n argsText,\n hasResult,\n }: {\n toolName: string;\n argsText: string;\n hasResult: boolean;\n }): boolean {\n if (hasResult) return true;\n if (!this._hasExecutableTool(toolName)) {\n return !this._isRunning && isArgsTextComplete(argsText);\n }\n return isArgsTextComplete(argsText);\n }\n\n private _startActiveEntry(\n toolCallId: string,\n toolName: string,\n skipExecute: boolean,\n ): ToolCallEntry {\n const toolCallController = this._controller.addToolCallPart({\n toolName,\n toolCallId,\n });\n if (skipExecute) {\n this._skipExecuteStreamIds.add(toolCallId);\n }\n const entry: ToolCallEntry = {\n toolName,\n controller: toolCallController,\n argsText: \"\",\n hasResult: false,\n argsComplete: false,\n };\n this._entries.set(toolCallId, entry);\n return entry;\n }\n\n /**\n * Demote every active entry back to the restored phase. Used by the\n * pipeline-restart path so that, after a fresh pipeline is built, the\n * next observed snapshot does not re-fire `streamCall` for tool calls\n * that already fired pre-death. Args / hasResult tracking is preserved\n * so signature comparisons still work.\n */\n private _demoteEntriesToRestored(): void {\n for (const [toolCallId, entry] of this._entries) {\n if (!entry.controller) continue;\n this._entries.set(toolCallId, {\n toolName: entry.toolName,\n argsText: entry.argsText,\n hasResult: entry.hasResult,\n });\n }\n }\n\n private _processArgsText(\n entry: ToolCallEntry,\n content: {\n toolCallId: string;\n toolName: string;\n argsText: string;\n result?: unknown;\n },\n ): void {\n if (!entry.controller) return;\n const hasResult = content.result !== undefined;\n\n if (content.argsText !== entry.argsText) {\n let shouldWriteArgsText = true;\n\n if (entry.argsComplete) {\n if (isEquivalentCompleteArgsText(entry.argsText, content.argsText)) {\n // A.3 — key reorder. Track new text, no re-fire needed.\n entry.argsText = content.argsText;\n shouldWriteArgsText = false;\n } else {\n // A.4 — args changed after first completion. Under the\n // \"exactly once per toolCallId\" contract we do not restart the\n // stream. The host's existing `streamCall` keeps its original\n // args view; the snapshot's new text is recorded for diffing\n // but not surfaced. Events API in a follow-up will expose this\n // to consumers that opt in.\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n \"[ToolInvocationTracker] argsText changed after first completion; not re-firing streamCall (see EDGE_CASES.md A.4)\",\n {\n previous: entry.argsText,\n next: content.argsText,\n toolCallId: content.toolCallId,\n },\n );\n }\n shouldWriteArgsText = false;\n }\n } else if (!content.argsText.startsWith(entry.argsText)) {\n if (\n isArgsTextComplete(entry.argsText) &&\n isArgsTextComplete(content.argsText) &&\n isEquivalentCompleteArgsText(entry.argsText, content.argsText)\n ) {\n const shouldClose = this._shouldCloseArgsStream({\n toolName: content.toolName,\n argsText: content.argsText,\n hasResult,\n });\n if (shouldClose) entry.controller.argsText.close();\n entry.argsText = content.argsText;\n entry.argsComplete = shouldClose;\n shouldWriteArgsText = false;\n } else {\n // A.2 — args regressed mid-stream. Under the \"exactly once\"\n // contract we do not restart. The controller keeps whatever\n // prefix we already streamed; subsequent prefix-respecting\n // updates can still flow against it. Snapshots that never\n // re-converge to a prefix will leave the controller's args\n // view stale relative to the snapshot. Events API in a\n // follow-up will expose this to consumers that opt in.\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n \"[ToolInvocationTracker] argsText regressed mid-stream; not restarting (see EDGE_CASES.md A.2)\",\n {\n previous: entry.argsText,\n next: content.argsText,\n toolCallId: content.toolCallId,\n },\n );\n }\n shouldWriteArgsText = false;\n }\n }\n\n if (shouldWriteArgsText && entry.controller) {\n const delta = content.argsText.slice(entry.argsText.length);\n entry.controller.argsText.append(delta);\n const shouldClose = this._shouldCloseArgsStream({\n toolName: content.toolName,\n argsText: content.argsText,\n hasResult,\n });\n if (shouldClose) entry.controller.argsText.close();\n entry.argsText = content.argsText;\n entry.argsComplete = shouldClose;\n }\n }\n\n if (!entry.argsComplete && entry.controller) {\n const shouldClose = this._shouldCloseArgsStream({\n toolName: content.toolName,\n argsText: content.argsText,\n hasResult,\n });\n if (shouldClose) {\n entry.controller.argsText.close();\n entry.argsText = content.argsText;\n entry.argsComplete = true;\n }\n }\n }\n\n private _processMessages(messages: readonly ThreadMessage[]): void {\n const isRestore = this._pendingRestore;\n\n for (const message of messages) {\n if (!message || !Array.isArray((message as ThreadMessage).content)) {\n continue;\n }\n for (const content of message.content as readonly ThreadMessage[\"content\"][number][]) {\n if (!content || content.type !== \"tool-call\") continue;\n\n const existing = this._entries.get(content.toolCallId);\n\n if (isRestore) {\n // Don't overwrite an already-active entry (e.g. live tool-call\n // observed before this restore snapshot landed). Restore can\n // only seed entries the runtime has never seen.\n if (!existing?.controller) {\n this._entries.set(content.toolCallId, {\n toolName: content.toolName,\n argsText: content.argsText,\n hasResult: content.result !== undefined,\n });\n }\n if (content.messages) this._processMessages(content.messages);\n continue;\n }\n\n // Live snapshot.\n let entry = existing;\n\n if (entry && !entry.controller) {\n // Restored entry observed in a live snapshot. Promote if its\n // signature has changed; otherwise treat as still-historical.\n const signatureChanged =\n content.argsText !== entry.argsText ||\n (content.result !== undefined) !== entry.hasResult;\n if (!signatureChanged) {\n if (content.messages) this._processMessages(content.messages);\n continue;\n }\n this._entries.delete(content.toolCallId);\n entry = undefined;\n }\n\n if (!entry) {\n entry = this._startActiveEntry(\n content.toolCallId,\n content.toolName,\n content.result !== undefined,\n );\n }\n\n this._processArgsText(entry, content);\n\n if (content.result !== undefined && !entry.hasResult) {\n // `entry` is in active phase from this point — either just\n // created by `_startActiveEntry`, or pre-existing with a live\n // controller. Narrow once instead of asserting at every use.\n const { controller: activeController } = entry;\n if (!activeController) continue;\n entry.hasResult = true;\n entry.argsComplete = true;\n activeController.setResponse(\n new ToolResponse({\n result: content.result as ReadonlyJSONValue,\n artifact: content.artifact as ReadonlyJSONValue | undefined,\n isError: content.isError,\n ...(content.modelContent !== undefined\n ? { modelContent: content.modelContent }\n : {}),\n }),\n );\n activeController.close();\n }\n\n if (content.messages) this._processMessages(content.messages);\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscribable.d.ts","names":[],"sources":["../../src/subscribable/subscribable.ts"],"mappings":";;;cAEa,WAAA;AAAA,KACD,WAAA,UAAqB,WAAW;AAAA,KAEhC,YAAA;EACV,SAAA,GAAY,QAAA,iBAAyB,WAAW;AAAA;AAAA,KAGtC,qBAAA,kBAAuC,YAAA;EACjD,IAAA,EAAM,KAAA;EACN,QAAA,QAAgB,MAAA;AAAA;AAAA,KAGN,kBAAA,gBACK,YAAA,uBAEb,qBAAA,CAAsB,MAAA,EAAQ,KAAA;AAAA,KAEtB,iBAAA;EACV,KAAA,EAAO,MAAA;EACP,OAAA,EAAS,qBAAA;IAEH,WAAA,GACE,KAAA,EAAO,MAAA,EACP,QAAA,GAAW,OAAA,wBACR,WAAA;EAAA;AAAA;AAAA,cAwBA,gBAAA;EAAA,QACH,YAAA;EAED,SAAA,CAAU,QAAA,eAAuB,WAAA;EAKjC,aAAA,
|
|
1
|
+
{"version":3,"file":"subscribable.d.ts","names":[],"sources":["../../src/subscribable/subscribable.ts"],"mappings":";;;cAEa,WAAA;AAAA,KACD,WAAA,UAAqB,WAAW;AAAA,KAEhC,YAAA;EACV,SAAA,GAAY,QAAA,iBAAyB,WAAW;AAAA;AAAA,KAGtC,qBAAA,kBAAuC,YAAA;EACjD,IAAA,EAAM,KAAA;EACN,QAAA,QAAgB,MAAA;AAAA;AAAA,KAGN,kBAAA,gBACK,YAAA,uBAEb,qBAAA,CAAsB,MAAA,EAAQ,KAAA;AAAA,KAEtB,iBAAA;EACV,KAAA,EAAO,MAAA;EACP,OAAA,EAAS,qBAAA;IAEH,WAAA,GACE,KAAA,EAAO,MAAA,EACP,QAAA,GAAW,OAAA,wBACR,WAAA;EAAA;AAAA;AAAA,cAwBA,gBAAA;EAAA,QACH,YAAA;EAED,SAAA,CAAU,QAAA,eAAuB,WAAA;EAKjC,aAAA,IAAa,OAAA;EAAA,UASV,kBAAA;AAAA;AAAA,uBAwBU,WAAA;EAAA,QACZ,cAAA;EAAA,QACA,WAAA;EAAA,cAEM,WAAA;EAAA,mBAIK,QAAA,IAAY,WAAW;EAAA,UAEhC,iBAAA,CAAkB,OAAA;EAAA,QAIpB,iBAAA;EAUD,SAAA,CAAU,QAAA,GAAW,OAAA;AAAA;AAAA,cAWjB,qBAAA,uCACH,WAAA,YACG,qBAAA,CAAsB,MAAA,EAAQ,KAAA;EAAA,QAO/B,OAAA;EAAA,IALC,IAAA,IAAI,KAAA;cAKL,OAAA,EAAS,qBAAA,CAAsB,MAAA,GAAS,WAAA,EAAa,KAAA;EAAA,QASvD,cAAA;EACD,QAAA,QAAQ,MAAA;EAAA,QAKP,UAAA;EAAA,UAQE,QAAA,IAAQ,WAAA;AAAA;AAAA,cAWP,kBAAA,uCACH,WAAA,YACG,qBAAA,CAAsB,MAAA,EAAQ,KAAA;EAAA,QAO/B,OAAA;EAAA,IALC,IAAA,IAAI,KAAA;cAKL,OAAA,EAAS,qBAAA,CAAsB,MAAA,GAAS,WAAA,EAAa,KAAA;EAAA,QAKvD,mBAAA;EAAA,QACA,cAAA;EACD,QAAA,QAAQ,MAAA;EAAA,UAaL,QAAA,IAAQ,WAAA;AAAA;AAAA,cAUP,yBAAA,gBACI,YAAA,6BAGP,WAAA,YAEN,qBAAA,CAAsB,MAAA,EAAQ,KAAA,GAC9B,kBAAA,CAAmB,MAAA,EAAQ,KAAA;EAAA,QAMT,OAAA;EAAA,IAJT,IAAA,IAAI,KAAA;cAIK,OAAA,EAAS,kBAAA,CAAmB,MAAA,EAAQ,KAAA;EAIjD,QAAA,IAAQ,MAAA;EAIR,cAAA,CAAe,QAAA,eAAoB,WAAA;EAAA,UAIhC,QAAA,IAAY,WAAA;AAAA;AAAA,cA0BX,wBAAA,gCAEH,WAAA;EAAA,QACY,MAAA;cAAA,MAAA,EAAQ,iBAAA,CAAkB,MAAA;EAIvC,QAAA;4CAjPY,OAAA,wBACR,WAAA;EAAA;EAoPJ,cAAA,CAAe,QAAA,eAAoB,WAAA;EAAA,UAIhC,QAAA,IAAY,WAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-thread-list-test-helpers.d.ts","names":[],"sources":["../../src/tests/remote-thread-list-test-helpers.ts"],"mappings":";;;;;iBAKgB,QAAA
|
|
1
|
+
{"version":3,"file":"remote-thread-list-test-helpers.d.ts","names":[],"sources":["../../src/tests/remote-thread-list-test-helpers.ts"],"mappings":";;;;;iBAKgB,QAAA;;mBACQ,CAAA;;;cASX,eAAA,EAAiB,oBAG7B;AAAA,iBAEe,WAAA,CACd,SAAA,GAAW,OAAA,CAAQ,uBAAA,IAClB,uBAAA;AAAA,iBA6Ba,UAAA,CACd,OAAA,EAAS,uBAAA,EACT,QAAA,YACC,qCAAqC"}
|
package/dist/types/message.d.ts
CHANGED
|
@@ -225,6 +225,11 @@ type ThreadAssistantMessage = MessageCommonProps & {
|
|
|
225
225
|
readonly type: "positive" | "negative";
|
|
226
226
|
};
|
|
227
227
|
readonly timing?: MessageTiming;
|
|
228
|
+
/**
|
|
229
|
+
* Marks a client-side optimistic placeholder. Such messages are evicted
|
|
230
|
+
* once off the head branch and are never persisted.
|
|
231
|
+
*/
|
|
232
|
+
readonly isOptimistic?: boolean;
|
|
228
233
|
readonly custom: Record<string, unknown>;
|
|
229
234
|
};
|
|
230
235
|
};
|
|
@@ -239,6 +244,7 @@ type BaseThreadMessage = {
|
|
|
239
244
|
readonly type: "positive" | "negative";
|
|
240
245
|
};
|
|
241
246
|
readonly timing?: MessageTiming;
|
|
247
|
+
readonly isOptimistic?: boolean;
|
|
242
248
|
readonly custom: Record<string, unknown>;
|
|
243
249
|
};
|
|
244
250
|
readonly attachments?: ThreadUserMessage["attachments"];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.d.ts","names":[],"sources":["../../src/types/message.ts"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"message.d.ts","names":[],"sources":["../../src/types/message.ts"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.js","names":[],"sources":["../../src/types/message.ts"],"sourcesContent":["import type {\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"assistant-stream/utils\";\nimport type { ToolModelContentPart } from \"assistant-stream\";\nimport type { CompleteAttachment } from \"./attachment\";\n\nexport type { ToolModelContentPart };\n\nexport type TextMessagePart = {\n readonly type: \"text\";\n readonly text: string;\n readonly parentId?: string;\n};\n\nexport type ReasoningMessagePart = {\n readonly type: \"reasoning\";\n readonly text: string;\n readonly parentId?: string;\n};\n\nexport type SourceProviderMetadata = {\n readonly [providerName: string]: ReadonlyJSONObject;\n};\n\nexport type SourceMessagePart =\n | {\n readonly type: \"source\";\n readonly sourceType: \"url\";\n readonly id: string;\n readonly url: string;\n readonly title?: string;\n readonly providerMetadata?: SourceProviderMetadata;\n readonly parentId?: string;\n }\n | {\n readonly type: \"source\";\n readonly sourceType: \"document\";\n readonly id: string;\n readonly url?: undefined;\n readonly title: string;\n readonly mediaType: string;\n readonly filename?: string;\n readonly providerMetadata?: SourceProviderMetadata;\n readonly parentId?: string;\n };\n\nexport type ImageMessagePart = {\n readonly type: \"image\";\n readonly image: string;\n readonly filename?: string;\n};\n\nexport type FileMessagePart = {\n readonly type: \"file\";\n readonly filename?: string;\n readonly data: string;\n readonly mimeType: string;\n readonly parentId?: string;\n};\n\nexport type Unstable_AudioMessagePart = {\n readonly type: \"audio\";\n readonly audio: {\n readonly data: string;\n readonly format: \"mp3\" | \"wav\";\n };\n};\n\nexport type DataMessagePart<T = any> = {\n readonly type: \"data\";\n readonly name: string;\n readonly data: T;\n};\n\n/**\n * A JSON spec describing a tree of UI components to render.\n *\n * The agent emits a {@link GenerativeUIMessagePart} containing this spec, and\n * the consumer-provided component allowlist is used to resolve `component`\n * names. Any component referenced that is not present in the allowlist is\n * rejected with a typed error — the allowlist is the security boundary in the\n * default same-realm rendering path.\n */\nexport type GenerativeUINode =\n | string\n | {\n /** Allowlisted component name (resolved against the consumer registry). */\n readonly component: string;\n /** Props passed to the resolved component (must be JSON-serializable). */\n readonly props?: Record<string, unknown>;\n /** Optional children — strings render as text, objects recurse. */\n readonly children?: readonly GenerativeUINode[];\n /** Optional stable key for React reconciliation. */\n readonly key?: string;\n };\n\n/**\n * The root spec for a generative UI tree.\n */\nexport type GenerativeUISpec = {\n /** Root node(s) to render. */\n readonly root: GenerativeUINode | readonly GenerativeUINode[];\n};\n\n/**\n * A message part that carries a JSON spec describing UI to render.\n *\n * Render with `<MessagePrimitive.GenerativeUI components={...} />`. The\n * primitive resolves component names against the consumer-provided allowlist\n * — any unknown name throws a typed error rather than rendering. Stream-\n * friendly: a partially-streamed spec renders progressively.\n */\nexport type GenerativeUIMessagePart = {\n readonly type: \"generative-ui\";\n /** The JSON spec describing the UI tree. */\n readonly spec: GenerativeUISpec;\n /** Optional id (useful for replays / stable keys). */\n readonly id?: string;\n readonly parentId?: string;\n};\n\nexport type McpAppMetadata = {\n readonly resourceUri: string;\n readonly mimeType?: string;\n readonly visibility?: readonly (\"model\" | \"app\")[];\n};\n\nexport const MCP_APP_URI_SCHEME = \"ui://\";\n\nexport const isMcpAppUri = (uri: string | undefined): boolean =>\n !!uri?.startsWith(MCP_APP_URI_SCHEME);\n\nexport type ToolCallMessagePartMcpMetadata = {\n readonly app?: McpAppMetadata;\n};\n\nexport type ToolCallMessagePart<\n TArgs = ReadonlyJSONObject,\n TResult = unknown,\n> = {\n /** Identifies this part as a tool call. */\n readonly type: \"tool-call\";\n /** Stable identifier for this invocation of the tool. */\n readonly toolCallId: string;\n /** Name of the tool requested by the model. */\n readonly toolName: string;\n /**\n * Arguments supplied by the model. During streaming this is a partial parse:\n * fields may be missing or incomplete. From a tool-call renderer, use\n * `useToolArgsStatus` to detect which fields are still arriving.\n */\n readonly args: TArgs;\n /** Result returned by the tool, if it has completed. */\n readonly result?: TResult | undefined;\n /** Whether the result represents a tool execution error. */\n readonly isError?: boolean | undefined;\n /** Raw JSON argument text streamed by the model. */\n readonly argsText: string;\n /** UI-only artifact associated with the tool result. */\n readonly artifact?: unknown;\n /** MCP app metadata associated with this tool call, when present. */\n readonly mcp?: ToolCallMessagePartMcpMetadata;\n /** Content returned to the model for this tool result. */\n readonly modelContent?: readonly ToolModelContentPart[] | undefined;\n /** Human-input request that must be resolved before the run can continue. */\n readonly interrupt?: { type: \"human\"; payload: unknown };\n /** Server-side approval gate. `approved === undefined` is the only state in which `respondToApproval` may be called. */\n readonly approval?: {\n readonly id: string;\n readonly approved?: boolean;\n readonly reason?: string;\n readonly isAutomatic?: boolean;\n };\n /** Parent message-part ID when this part belongs to a nested structure. */\n readonly parentId?: string;\n /**\n * Nested thread messages produced by this tool call, for example a sub-agent\n * conversation.\n */\n readonly messages?: readonly ThreadMessage[];\n};\n\nexport type ThreadUserMessagePart =\n | TextMessagePart\n | ImageMessagePart\n | FileMessagePart\n | DataMessagePart\n | Unstable_AudioMessagePart;\n\nexport type ThreadAssistantMessagePart =\n | TextMessagePart\n | ReasoningMessagePart\n | ToolCallMessagePart\n | SourceMessagePart\n | FileMessagePart\n | ImageMessagePart\n | DataMessagePart\n | GenerativeUIMessagePart;\n\nexport type MessagePartStatus =\n | {\n readonly type: \"running\";\n }\n | {\n readonly type: \"complete\";\n }\n | {\n readonly type: \"incomplete\";\n readonly reason:\n | \"cancelled\"\n | \"length\"\n | \"content-filter\"\n | \"other\"\n | \"error\";\n readonly error?: unknown;\n };\n\nexport type ToolCallMessagePartStatus =\n | {\n /** The tool call is waiting for UI or human input before continuing. */\n readonly type: \"requires-action\";\n /** Reason the tool call requires action. */\n readonly reason: \"interrupt\";\n }\n | MessagePartStatus;\n\nexport type MessageStatus =\n | {\n readonly type: \"running\";\n }\n | {\n readonly type: \"requires-action\";\n readonly reason: \"tool-calls\" | \"interrupt\";\n }\n | {\n readonly type: \"complete\";\n readonly reason: \"stop\" | \"unknown\";\n }\n | {\n readonly type: \"incomplete\";\n readonly reason:\n | \"cancelled\"\n | \"tool-calls\"\n | \"length\"\n | \"content-filter\"\n | \"other\"\n | \"error\";\n readonly error?: ReadonlyJSONValue;\n };\n\nexport type MessageTiming = {\n readonly streamStartTime: number;\n readonly firstTokenTime?: number;\n readonly totalStreamTime?: number;\n readonly tokenCount?: number;\n readonly tokensPerSecond?: number;\n readonly totalChunks: number;\n readonly toolCallCount: number;\n};\n\nexport type ThreadStep = {\n readonly messageId?: string;\n readonly usage?:\n | {\n readonly inputTokens: number;\n readonly outputTokens: number;\n }\n | undefined;\n};\n\ntype MessageCommonProps = {\n readonly id: string;\n readonly createdAt: Date;\n};\n\nexport type ThreadSystemMessage = MessageCommonProps & {\n readonly role: \"system\";\n readonly content: readonly [TextMessagePart];\n readonly metadata: {\n readonly unstable_state?: undefined;\n readonly unstable_annotations?: undefined;\n readonly unstable_data?: undefined;\n readonly steps?: undefined;\n readonly submittedFeedback?: undefined;\n readonly timing?: undefined;\n readonly custom: Record<string, unknown>;\n };\n};\n\nexport type ThreadUserMessage = MessageCommonProps & {\n readonly role: \"user\";\n readonly content: readonly ThreadUserMessagePart[];\n readonly attachments: readonly CompleteAttachment[];\n readonly metadata: {\n readonly unstable_state?: undefined;\n readonly unstable_annotations?: undefined;\n readonly unstable_data?: undefined;\n readonly steps?: undefined;\n readonly submittedFeedback?: undefined;\n readonly timing?: undefined;\n readonly custom: Record<string, unknown>;\n };\n};\n\nexport type ThreadAssistantMessage = MessageCommonProps & {\n readonly role: \"assistant\";\n readonly content: readonly ThreadAssistantMessagePart[];\n readonly status: MessageStatus;\n readonly metadata: {\n readonly unstable_state: ReadonlyJSONValue;\n readonly unstable_annotations: readonly ReadonlyJSONValue[];\n readonly unstable_data: readonly ReadonlyJSONValue[];\n readonly steps: readonly ThreadStep[];\n readonly submittedFeedback?: { readonly type: \"positive\" | \"negative\" };\n readonly timing?: MessageTiming;\n readonly custom: Record<string, unknown>;\n };\n};\n\ntype BaseThreadMessage = {\n readonly status?: ThreadAssistantMessage[\"status\"];\n readonly metadata: {\n readonly unstable_state?: ReadonlyJSONValue;\n readonly unstable_annotations?: readonly ReadonlyJSONValue[];\n readonly unstable_data?: readonly ReadonlyJSONValue[];\n readonly steps?: readonly ThreadStep[];\n readonly submittedFeedback?: { readonly type: \"positive\" | \"negative\" };\n readonly timing?: MessageTiming;\n readonly custom: Record<string, unknown>;\n };\n readonly attachments?: ThreadUserMessage[\"attachments\"];\n};\n\nexport type ThreadMessage = BaseThreadMessage &\n (ThreadSystemMessage | ThreadUserMessage | ThreadAssistantMessage);\n\nexport type MessageRole = ThreadMessage[\"role\"];\n\nexport type RunConfig = {\n readonly custom?: Record<string, unknown>;\n};\n\nexport type AppendMessage = Omit<ThreadMessage, \"id\"> & {\n parentId: string | null;\n\n /** The ID of the message that was edited or undefined. */\n sourceId: string | null;\n runConfig: RunConfig | undefined;\n startRun?: boolean | undefined;\n};\n"],"mappings":";AAgIA,MAAa,qBAAqB;AAElC,MAAa,eAAe,QAC1B,CAAC,CAAC,KAAK,WAAW,kBAAkB"}
|
|
1
|
+
{"version":3,"file":"message.js","names":[],"sources":["../../src/types/message.ts"],"sourcesContent":["import type {\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"assistant-stream/utils\";\nimport type { ToolModelContentPart } from \"assistant-stream\";\nimport type { CompleteAttachment } from \"./attachment\";\n\nexport type { ToolModelContentPart };\n\nexport type TextMessagePart = {\n readonly type: \"text\";\n readonly text: string;\n readonly parentId?: string;\n};\n\nexport type ReasoningMessagePart = {\n readonly type: \"reasoning\";\n readonly text: string;\n readonly parentId?: string;\n};\n\nexport type SourceProviderMetadata = {\n readonly [providerName: string]: ReadonlyJSONObject;\n};\n\nexport type SourceMessagePart =\n | {\n readonly type: \"source\";\n readonly sourceType: \"url\";\n readonly id: string;\n readonly url: string;\n readonly title?: string;\n readonly providerMetadata?: SourceProviderMetadata;\n readonly parentId?: string;\n }\n | {\n readonly type: \"source\";\n readonly sourceType: \"document\";\n readonly id: string;\n readonly url?: undefined;\n readonly title: string;\n readonly mediaType: string;\n readonly filename?: string;\n readonly providerMetadata?: SourceProviderMetadata;\n readonly parentId?: string;\n };\n\nexport type ImageMessagePart = {\n readonly type: \"image\";\n readonly image: string;\n readonly filename?: string;\n};\n\nexport type FileMessagePart = {\n readonly type: \"file\";\n readonly filename?: string;\n readonly data: string;\n readonly mimeType: string;\n readonly parentId?: string;\n};\n\nexport type Unstable_AudioMessagePart = {\n readonly type: \"audio\";\n readonly audio: {\n readonly data: string;\n readonly format: \"mp3\" | \"wav\";\n };\n};\n\nexport type DataMessagePart<T = any> = {\n readonly type: \"data\";\n readonly name: string;\n readonly data: T;\n};\n\n/**\n * A JSON spec describing a tree of UI components to render.\n *\n * The agent emits a {@link GenerativeUIMessagePart} containing this spec, and\n * the consumer-provided component allowlist is used to resolve `component`\n * names. Any component referenced that is not present in the allowlist is\n * rejected with a typed error — the allowlist is the security boundary in the\n * default same-realm rendering path.\n */\nexport type GenerativeUINode =\n | string\n | {\n /** Allowlisted component name (resolved against the consumer registry). */\n readonly component: string;\n /** Props passed to the resolved component (must be JSON-serializable). */\n readonly props?: Record<string, unknown>;\n /** Optional children — strings render as text, objects recurse. */\n readonly children?: readonly GenerativeUINode[];\n /** Optional stable key for React reconciliation. */\n readonly key?: string;\n };\n\n/**\n * The root spec for a generative UI tree.\n */\nexport type GenerativeUISpec = {\n /** Root node(s) to render. */\n readonly root: GenerativeUINode | readonly GenerativeUINode[];\n};\n\n/**\n * A message part that carries a JSON spec describing UI to render.\n *\n * Render with `<MessagePrimitive.GenerativeUI components={...} />`. The\n * primitive resolves component names against the consumer-provided allowlist\n * — any unknown name throws a typed error rather than rendering. Stream-\n * friendly: a partially-streamed spec renders progressively.\n */\nexport type GenerativeUIMessagePart = {\n readonly type: \"generative-ui\";\n /** The JSON spec describing the UI tree. */\n readonly spec: GenerativeUISpec;\n /** Optional id (useful for replays / stable keys). */\n readonly id?: string;\n readonly parentId?: string;\n};\n\nexport type McpAppMetadata = {\n readonly resourceUri: string;\n readonly mimeType?: string;\n readonly visibility?: readonly (\"model\" | \"app\")[];\n};\n\nexport const MCP_APP_URI_SCHEME = \"ui://\";\n\nexport const isMcpAppUri = (uri: string | undefined): boolean =>\n !!uri?.startsWith(MCP_APP_URI_SCHEME);\n\nexport type ToolCallMessagePartMcpMetadata = {\n readonly app?: McpAppMetadata;\n};\n\nexport type ToolCallMessagePart<\n TArgs = ReadonlyJSONObject,\n TResult = unknown,\n> = {\n /** Identifies this part as a tool call. */\n readonly type: \"tool-call\";\n /** Stable identifier for this invocation of the tool. */\n readonly toolCallId: string;\n /** Name of the tool requested by the model. */\n readonly toolName: string;\n /**\n * Arguments supplied by the model. During streaming this is a partial parse:\n * fields may be missing or incomplete. From a tool-call renderer, use\n * `useToolArgsStatus` to detect which fields are still arriving.\n */\n readonly args: TArgs;\n /** Result returned by the tool, if it has completed. */\n readonly result?: TResult | undefined;\n /** Whether the result represents a tool execution error. */\n readonly isError?: boolean | undefined;\n /** Raw JSON argument text streamed by the model. */\n readonly argsText: string;\n /** UI-only artifact associated with the tool result. */\n readonly artifact?: unknown;\n /** MCP app metadata associated with this tool call, when present. */\n readonly mcp?: ToolCallMessagePartMcpMetadata;\n /** Content returned to the model for this tool result. */\n readonly modelContent?: readonly ToolModelContentPart[] | undefined;\n /** Human-input request that must be resolved before the run can continue. */\n readonly interrupt?: { type: \"human\"; payload: unknown };\n /** Server-side approval gate. `approved === undefined` is the only state in which `respondToApproval` may be called. */\n readonly approval?: {\n readonly id: string;\n readonly approved?: boolean;\n readonly reason?: string;\n readonly isAutomatic?: boolean;\n };\n /** Parent message-part ID when this part belongs to a nested structure. */\n readonly parentId?: string;\n /**\n * Nested thread messages produced by this tool call, for example a sub-agent\n * conversation.\n */\n readonly messages?: readonly ThreadMessage[];\n};\n\nexport type ThreadUserMessagePart =\n | TextMessagePart\n | ImageMessagePart\n | FileMessagePart\n | DataMessagePart\n | Unstable_AudioMessagePart;\n\nexport type ThreadAssistantMessagePart =\n | TextMessagePart\n | ReasoningMessagePart\n | ToolCallMessagePart\n | SourceMessagePart\n | FileMessagePart\n | ImageMessagePart\n | DataMessagePart\n | GenerativeUIMessagePart;\n\nexport type MessagePartStatus =\n | {\n readonly type: \"running\";\n }\n | {\n readonly type: \"complete\";\n }\n | {\n readonly type: \"incomplete\";\n readonly reason:\n | \"cancelled\"\n | \"length\"\n | \"content-filter\"\n | \"other\"\n | \"error\";\n readonly error?: unknown;\n };\n\nexport type ToolCallMessagePartStatus =\n | {\n /** The tool call is waiting for UI or human input before continuing. */\n readonly type: \"requires-action\";\n /** Reason the tool call requires action. */\n readonly reason: \"interrupt\";\n }\n | MessagePartStatus;\n\nexport type MessageStatus =\n | {\n readonly type: \"running\";\n }\n | {\n readonly type: \"requires-action\";\n readonly reason: \"tool-calls\" | \"interrupt\";\n }\n | {\n readonly type: \"complete\";\n readonly reason: \"stop\" | \"unknown\";\n }\n | {\n readonly type: \"incomplete\";\n readonly reason:\n | \"cancelled\"\n | \"tool-calls\"\n | \"length\"\n | \"content-filter\"\n | \"other\"\n | \"error\";\n readonly error?: ReadonlyJSONValue;\n };\n\nexport type MessageTiming = {\n readonly streamStartTime: number;\n readonly firstTokenTime?: number;\n readonly totalStreamTime?: number;\n readonly tokenCount?: number;\n readonly tokensPerSecond?: number;\n readonly totalChunks: number;\n readonly toolCallCount: number;\n};\n\nexport type ThreadStep = {\n readonly messageId?: string;\n readonly usage?:\n | {\n readonly inputTokens: number;\n readonly outputTokens: number;\n }\n | undefined;\n};\n\ntype MessageCommonProps = {\n readonly id: string;\n readonly createdAt: Date;\n};\n\nexport type ThreadSystemMessage = MessageCommonProps & {\n readonly role: \"system\";\n readonly content: readonly [TextMessagePart];\n readonly metadata: {\n readonly unstable_state?: undefined;\n readonly unstable_annotations?: undefined;\n readonly unstable_data?: undefined;\n readonly steps?: undefined;\n readonly submittedFeedback?: undefined;\n readonly timing?: undefined;\n readonly custom: Record<string, unknown>;\n };\n};\n\nexport type ThreadUserMessage = MessageCommonProps & {\n readonly role: \"user\";\n readonly content: readonly ThreadUserMessagePart[];\n readonly attachments: readonly CompleteAttachment[];\n readonly metadata: {\n readonly unstable_state?: undefined;\n readonly unstable_annotations?: undefined;\n readonly unstable_data?: undefined;\n readonly steps?: undefined;\n readonly submittedFeedback?: undefined;\n readonly timing?: undefined;\n readonly custom: Record<string, unknown>;\n };\n};\n\nexport type ThreadAssistantMessage = MessageCommonProps & {\n readonly role: \"assistant\";\n readonly content: readonly ThreadAssistantMessagePart[];\n readonly status: MessageStatus;\n readonly metadata: {\n readonly unstable_state: ReadonlyJSONValue;\n readonly unstable_annotations: readonly ReadonlyJSONValue[];\n readonly unstable_data: readonly ReadonlyJSONValue[];\n readonly steps: readonly ThreadStep[];\n readonly submittedFeedback?: { readonly type: \"positive\" | \"negative\" };\n readonly timing?: MessageTiming;\n /**\n * Marks a client-side optimistic placeholder. Such messages are evicted\n * once off the head branch and are never persisted.\n */\n readonly isOptimistic?: boolean;\n readonly custom: Record<string, unknown>;\n };\n};\n\ntype BaseThreadMessage = {\n readonly status?: ThreadAssistantMessage[\"status\"];\n readonly metadata: {\n readonly unstable_state?: ReadonlyJSONValue;\n readonly unstable_annotations?: readonly ReadonlyJSONValue[];\n readonly unstable_data?: readonly ReadonlyJSONValue[];\n readonly steps?: readonly ThreadStep[];\n readonly submittedFeedback?: { readonly type: \"positive\" | \"negative\" };\n readonly timing?: MessageTiming;\n readonly isOptimistic?: boolean;\n readonly custom: Record<string, unknown>;\n };\n readonly attachments?: ThreadUserMessage[\"attachments\"];\n};\n\nexport type ThreadMessage = BaseThreadMessage &\n (ThreadSystemMessage | ThreadUserMessage | ThreadAssistantMessage);\n\nexport type MessageRole = ThreadMessage[\"role\"];\n\nexport type RunConfig = {\n readonly custom?: Record<string, unknown>;\n};\n\nexport type AppendMessage = Omit<ThreadMessage, \"id\"> & {\n parentId: string | null;\n\n /** The ID of the message that was edited or undefined. */\n sourceId: string | null;\n runConfig: RunConfig | undefined;\n startRun?: boolean | undefined;\n};\n"],"mappings":";AAgIA,MAAa,qBAAqB;AAElC,MAAa,eAAe,QAC1B,CAAC,CAAC,KAAK,WAAW,kBAAkB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composite-context-provider.d.ts","names":[],"sources":["../../src/utils/composite-context-provider.ts"],"mappings":";;cAKa,wBAAA,YAAoC,oBAAA;EAAA,QACvC,UAAA;EAER,eAAA,
|
|
1
|
+
{"version":3,"file":"composite-context-provider.d.ts","names":[],"sources":["../../src/utils/composite-context-provider.ts"],"mappings":";;cAKa,wBAAA,YAAoC,oBAAA;EAAA,QACvC,UAAA;EAER,eAAA,IAHoC,YAAA;EAOpC,4BAAA,CAA6B,QAAA,EAAU,oBAAA;EAAA,QAa/B,YAAA;EAER,iBAAA;EAIA,SAAA,CAAU,QAAA;AAAA"}
|
package/dist/utils/id.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
//#region src/utils/id.d.ts
|
|
2
2
|
declare const generateId: (size?: number) => string;
|
|
3
|
-
declare const generateOptimisticId: () => string;
|
|
4
|
-
declare const isOptimisticId: (id: string) => boolean;
|
|
5
3
|
declare const generateErrorMessageId: () => string;
|
|
6
4
|
declare const isErrorMessageId: (id: string) => boolean;
|
|
7
5
|
//#endregion
|
|
8
|
-
export { generateErrorMessageId, generateId,
|
|
6
|
+
export { generateErrorMessageId, generateId, isErrorMessageId };
|
|
9
7
|
//# sourceMappingURL=id.d.ts.map
|
package/dist/utils/id.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"id.d.ts","names":[],"sources":["../../src/utils/id.ts"],"mappings":";cAEa,UAAA,GAAU,IAAA;AAAA,cAMV,
|
|
1
|
+
{"version":3,"file":"id.d.ts","names":[],"sources":["../../src/utils/id.ts"],"mappings":";cAEa,UAAA,GAAU,IAAA;AAAA,cAMV,sBAAA;AAAA,cACA,gBAAA,GAAoB,EAAU"}
|
package/dist/utils/id.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { customAlphabet } from "nanoid/non-secure";
|
|
2
2
|
//#region src/utils/id.ts
|
|
3
3
|
const generateId = customAlphabet("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 7);
|
|
4
|
-
const optimisticPrefix = "__optimistic__";
|
|
5
|
-
const generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
|
|
6
|
-
const isOptimisticId = (id) => id.startsWith(optimisticPrefix);
|
|
7
4
|
const errorPrefix = "__error__";
|
|
8
5
|
const generateErrorMessageId = () => `${errorPrefix}${generateId()}`;
|
|
9
6
|
const isErrorMessageId = (id) => id.startsWith(errorPrefix);
|
|
10
7
|
//#endregion
|
|
11
|
-
export { generateErrorMessageId, generateId,
|
|
8
|
+
export { generateErrorMessageId, generateId, isErrorMessageId };
|
|
12
9
|
|
|
13
10
|
//# sourceMappingURL=id.js.map
|
package/dist/utils/id.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"id.js","names":[],"sources":["../../src/utils/id.ts"],"sourcesContent":["import { customAlphabet } from \"nanoid/non-secure\";\n\nexport const generateId = customAlphabet(\n \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\",\n 7,\n);\n\nconst
|
|
1
|
+
{"version":3,"file":"id.js","names":[],"sources":["../../src/utils/id.ts"],"sourcesContent":["import { customAlphabet } from \"nanoid/non-secure\";\n\nexport const generateId = customAlphabet(\n \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\",\n 7,\n);\n\nconst errorPrefix = \"__error__\";\nexport const generateErrorMessageId = () => `${errorPrefix}${generateId()}`;\nexport const isErrorMessageId = (id: string) => id.startsWith(errorPrefix);\n"],"mappings":";;AAEA,MAAa,aAAa,eACxB,kEACA,CACF;AAEA,MAAM,cAAc;AACpB,MAAa,+BAA+B,GAAG,cAAc,WAAW;AACxE,MAAa,oBAAoB,OAAe,GAAG,WAAW,WAAW"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/core",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "Framework-agnostic core runtime for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"assistant",
|
|
@@ -59,15 +59,15 @@
|
|
|
59
59
|
],
|
|
60
60
|
"sideEffects": false,
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"assistant-stream": "^0.3.
|
|
62
|
+
"assistant-stream": "^0.3.18",
|
|
63
63
|
"nanoid": "^5.1.11"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
|
-
"@assistant-ui/store": "^0.2.
|
|
67
|
-
"@assistant-ui/tap": "^0.5.
|
|
66
|
+
"@assistant-ui/store": "^0.2.13",
|
|
67
|
+
"@assistant-ui/tap": "^0.5.14",
|
|
68
68
|
"@types/react": "*",
|
|
69
69
|
"react": "^18 || ^19",
|
|
70
|
-
"assistant-cloud": "^0.1.
|
|
70
|
+
"assistant-cloud": "^0.1.30",
|
|
71
71
|
"zustand": "^5.0.11"
|
|
72
72
|
},
|
|
73
73
|
"peerDependenciesMeta": {
|
|
@@ -88,11 +88,11 @@
|
|
|
88
88
|
"@types/react": "^19.2.15",
|
|
89
89
|
"react": "^19.2.6",
|
|
90
90
|
"vitest": "^4.1.7",
|
|
91
|
-
"zustand": "^5.0.
|
|
92
|
-
"@assistant-ui/store": "0.2.
|
|
93
|
-
"@assistant-ui/tap": "0.5.
|
|
94
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
95
|
-
"assistant-cloud": "0.1.
|
|
91
|
+
"zustand": "^5.0.14",
|
|
92
|
+
"@assistant-ui/store": "0.2.13",
|
|
93
|
+
"@assistant-ui/tap": "0.5.14",
|
|
94
|
+
"@assistant-ui/x-buildutils": "0.0.10",
|
|
95
|
+
"assistant-cloud": "0.1.30"
|
|
96
96
|
},
|
|
97
97
|
"publishConfig": {
|
|
98
98
|
"access": "public",
|
package/src/adapters/speech.ts
CHANGED
|
@@ -64,7 +64,6 @@ export class WebSpeechSynthesisAdapter implements SpeechSynthesisAdapter {
|
|
|
64
64
|
if (res.status.type === "ended") return;
|
|
65
65
|
|
|
66
66
|
res.status = { type: "ended", reason, error };
|
|
67
|
-
// biome-ignore lint/suspicious/useIterableCallbackReturn: forEach callback intentionally has no return
|
|
68
67
|
subscribers.forEach((handler) => handler());
|
|
69
68
|
};
|
|
70
69
|
|
package/src/index.ts
CHANGED
|
@@ -282,6 +282,8 @@ export type {
|
|
|
282
282
|
ExternalStoreThreadListAdapter,
|
|
283
283
|
ExternalStoreThreadData,
|
|
284
284
|
} from "./runtimes/external-store/external-store-adapter";
|
|
285
|
+
export type { ExternalStoreSharedOptions } from "./runtimes/external-store/external-store-shared-options";
|
|
286
|
+
export { pickExternalStoreSharedOptions } from "./runtimes/external-store/external-store-shared-options";
|
|
285
287
|
|
|
286
288
|
// Remote Thread List (user-facing)
|
|
287
289
|
export type {
|
package/src/internal.ts
CHANGED
|
@@ -175,7 +175,6 @@ export class AssistantFrameHost implements ModelContextProvider {
|
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
private notifySubscribers() {
|
|
178
|
-
// biome-ignore lint/suspicious/useIterableCallbackReturn: forEach callback intentionally has no return
|
|
179
178
|
this._subscribers.forEach((callback) => callback());
|
|
180
179
|
}
|
|
181
180
|
|
|
@@ -179,7 +179,6 @@ export class AssistantFrameProvider {
|
|
|
179
179
|
const instance = AssistantFrameProvider._instance;
|
|
180
180
|
window.removeEventListener("message", instance.handleMessage);
|
|
181
181
|
|
|
182
|
-
// biome-ignore lint/suspicious/useIterableCallbackReturn: forEach callback intentionally has no return
|
|
183
182
|
instance._providerUnsubscribes.forEach((unsubscribe) => unsubscribe?.());
|
|
184
183
|
instance._providerUnsubscribes.clear();
|
|
185
184
|
instance._providers.clear();
|
|
@@ -253,7 +253,6 @@ export const Interactables = resource((): ClientOutput<"interactables"> => {
|
|
|
253
253
|
[setDefState],
|
|
254
254
|
);
|
|
255
255
|
|
|
256
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: state dep triggers notification
|
|
257
256
|
tapEffect(() => {
|
|
258
257
|
for (const cb of subscribersRef.current) cb();
|
|
259
258
|
}, [state]);
|
|
@@ -15,7 +15,10 @@ import {
|
|
|
15
15
|
} from "@assistant-ui/store";
|
|
16
16
|
import type { McpAppResourceOutput, ToolsState } from "../types/scopes/tools";
|
|
17
17
|
import type { Tool } from "assistant-stream";
|
|
18
|
-
import
|
|
18
|
+
import {
|
|
19
|
+
isStandaloneToolDisplay,
|
|
20
|
+
type Toolkit,
|
|
21
|
+
} from "../model-context/toolbox";
|
|
19
22
|
import type { ToolCallMessagePartComponent } from "../types/MessagePartComponentTypes";
|
|
20
23
|
import { ModelContext } from "../../store";
|
|
21
24
|
|
|
@@ -45,36 +48,53 @@ export const Tools = resource(
|
|
|
45
48
|
);
|
|
46
49
|
const mcpAppOutput = mcpAppOutputs[0];
|
|
47
50
|
|
|
48
|
-
const [
|
|
49
|
-
tools: ToolsState["tools"];
|
|
50
|
-
}>(() => ({
|
|
51
|
-
tools: {},
|
|
52
|
-
}));
|
|
51
|
+
const [toolUIs, setToolUIs] = tapState<ToolsState["toolUIs"]>(() => ({}));
|
|
53
52
|
|
|
54
53
|
const state = tapMemo(
|
|
55
|
-
(): ToolsState => ({
|
|
56
|
-
|
|
54
|
+
(): ToolsState => ({
|
|
55
|
+
toolUIs,
|
|
56
|
+
mcpApp: mcpAppOutput,
|
|
57
|
+
// Deprecated component-only view, derived from `toolUIs`. Removed in v0.15.
|
|
58
|
+
tools: Object.fromEntries(
|
|
59
|
+
Object.entries(toolUIs).map(([name, regs]) => [
|
|
60
|
+
name,
|
|
61
|
+
regs.map((r) => r.render),
|
|
62
|
+
]),
|
|
63
|
+
),
|
|
64
|
+
}),
|
|
65
|
+
[toolUIs, mcpAppOutput],
|
|
57
66
|
);
|
|
58
67
|
|
|
59
68
|
const clientRef = tapAssistantClientRef();
|
|
60
69
|
|
|
61
70
|
const setToolUI = tapCallback(
|
|
62
|
-
(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
71
|
+
(
|
|
72
|
+
toolName: string,
|
|
73
|
+
render: ToolCallMessagePartComponent,
|
|
74
|
+
options?: { standalone?: boolean },
|
|
75
|
+
) => {
|
|
76
|
+
// One registration object per call; identity is the removal key, so
|
|
77
|
+
// the per-name list stays correctly ref-counted across re-registers.
|
|
78
|
+
const registration = {
|
|
79
|
+
render,
|
|
80
|
+
standalone: options?.standalone ?? false,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
setToolUIs((prev) => ({
|
|
84
|
+
...prev,
|
|
85
|
+
[toolName]: [...(prev[toolName] ?? []), registration],
|
|
68
86
|
}));
|
|
69
87
|
|
|
70
88
|
return () => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
89
|
+
setToolUIs((prev) => {
|
|
90
|
+
const next =
|
|
91
|
+
prev[toolName]?.filter((r) => r !== registration) ?? [];
|
|
92
|
+
if (next.length > 0) return { ...prev, [toolName]: next };
|
|
93
|
+
// Drop the key entirely so repeatedly mounted/unmounted tools
|
|
94
|
+
// don't leave empty arrays accumulating across a long session.
|
|
95
|
+
const { [toolName]: _removed, ...rest } = prev;
|
|
96
|
+
return rest;
|
|
97
|
+
});
|
|
78
98
|
};
|
|
79
99
|
},
|
|
80
100
|
[],
|
|
@@ -87,14 +107,20 @@ export const Tools = resource(
|
|
|
87
107
|
// Register tool UIs (exclude symbols)
|
|
88
108
|
for (const [toolName, tool] of Object.entries(toolkit)) {
|
|
89
109
|
if (tool.render) {
|
|
90
|
-
unsubscribes.push(
|
|
110
|
+
unsubscribes.push(
|
|
111
|
+
setToolUI(toolName, tool.render, {
|
|
112
|
+
standalone: isStandaloneToolDisplay(tool),
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
91
115
|
}
|
|
92
116
|
}
|
|
93
117
|
|
|
94
|
-
// Register tools with model context (exclude symbols)
|
|
118
|
+
// Register tools with model context (exclude symbols). `render` and
|
|
119
|
+
// `display` are client-only presentation concerns and never reach the
|
|
120
|
+
// model.
|
|
95
121
|
const toolsWithoutRender = Object.entries(toolkit).reduce(
|
|
96
122
|
(acc, [name, tool]) => {
|
|
97
|
-
const { render, ...rest } = tool;
|
|
123
|
+
const { render, display, ...rest } = tool;
|
|
98
124
|
acc[name] = rest;
|
|
99
125
|
return acc;
|
|
100
126
|
},
|
|
@@ -112,7 +138,6 @@ export const Tools = resource(
|
|
|
112
138
|
);
|
|
113
139
|
|
|
114
140
|
return () => {
|
|
115
|
-
// biome-ignore lint/suspicious/useIterableCallbackReturn: forEach callback intentionally has no return
|
|
116
141
|
unsubscribes.forEach((fn) => fn());
|
|
117
142
|
};
|
|
118
143
|
}, [toolkit, setToolUI, clientRef]);
|
package/src/react/index.ts
CHANGED
|
@@ -32,7 +32,12 @@ export {
|
|
|
32
32
|
type AssistantDataUIProps,
|
|
33
33
|
} from "./model-context/useAssistantDataUI";
|
|
34
34
|
export { useInlineRender } from "./model-context/useInlineRender";
|
|
35
|
-
export
|
|
35
|
+
export {
|
|
36
|
+
type Toolkit,
|
|
37
|
+
type ToolDefinition,
|
|
38
|
+
type ToolkitDeclaration,
|
|
39
|
+
type ToolkitDeclarationDefinition,
|
|
40
|
+
} from "./model-context/toolbox";
|
|
36
41
|
export {
|
|
37
42
|
useAssistantInteractable,
|
|
38
43
|
type AssistantInteractableProps,
|
|
@@ -131,6 +136,7 @@ export {
|
|
|
131
136
|
type RuntimeAdapters,
|
|
132
137
|
} from "./runtimes/RuntimeAdapterProvider";
|
|
133
138
|
export { useExternalStoreRuntime } from "./runtimes/useExternalStoreRuntime";
|
|
139
|
+
export { useExternalStoreSharedOptions } from "./runtimes/useExternalStoreSharedOptions";
|
|
134
140
|
export {
|
|
135
141
|
useExternalMessageConverter,
|
|
136
142
|
convertExternalMessages,
|
|
@@ -173,7 +179,7 @@ export {
|
|
|
173
179
|
type PartState,
|
|
174
180
|
} from "./primitives/message/MessageParts";
|
|
175
181
|
export { MessagePrimitiveGroupedParts } from "./primitives/message/MessageGroupedParts";
|
|
176
|
-
export { groupPartByType } from "./utils/groupParts";
|
|
182
|
+
export { groupPartByType, type GroupByContext } from "./utils/groupParts";
|
|
177
183
|
export {
|
|
178
184
|
MessagePrimitiveGenerativeUI,
|
|
179
185
|
GenerativeUIRender,
|