@assistant-ui/react 0.14.9 → 0.14.11
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/internal.d.ts +3 -4
- package/dist/internal.js +1 -3
- package/dist/internal.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts +3 -2
- package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +4 -10
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.d.ts +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js.map +1 -1
- package/dist/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/mcp-apps/app-frame.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalRoot.js +5 -1
- package/dist/primitives/assistantModal/AssistantModalRoot.js.map +1 -1
- package/dist/utils/useToolArgsFieldStatus.d.ts +2 -2
- package/dist/utils/useToolArgsFieldStatus.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/internal.ts +1 -4
- package/src/legacy-runtime/runtime/ThreadListRuntime.ts +1 -4
- package/src/legacy-runtime/runtime-cores/assistant-transport/types.ts +3 -1
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts +7 -15
- package/src/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.ts +1 -1
- package/src/mcp-apps/McpAppRenderer.tsx +1 -1
- package/src/mcp-apps/app-frame.tsx +2 -2
- package/src/primitives/assistantModal/AssistantModalRoot.tsx +1 -1
- package/src/primitives/composer/ComposerAttachmentDropzone.test.tsx +1 -1
- package/src/primitives/composer/ComposerInput.test.tsx +1 -1
- package/src/primitives/composer/trigger/TriggerPopoverRootContext.test.tsx +1 -1
- package/src/primitives/threadListItem/ThreadListItemTitle.test.tsx +1 -1
- package/src/tests/BaseComposerRuntimeCore.test.ts +2 -2
- package/src/tests/MessageParts.loading.test.tsx +1 -1
- package/src/tests/RemoteThreadListRuntime.adapterProvider.test.tsx +2 -4
- package/src/tests/auiV0Encode.test.ts +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.d.ts +0 -2
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.js +0 -2
- package/src/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.test.ts +0 -892
- package/src/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.ts +0 -4
package/dist/internal.d.ts
CHANGED
|
@@ -2,14 +2,13 @@ import { splitLocalRuntimeOptions } from "./legacy-runtime/runtime-cores/local/L
|
|
|
2
2
|
import { useSmooth } from "./utils/smooth/useSmooth.js";
|
|
3
3
|
import { useSmoothStatus, withSmoothContextProvider } from "./utils/smooth/SmoothContext.js";
|
|
4
4
|
import { useComposerInputPluginRegistryOptional } from "./primitives/composer/ComposerInputPluginContext.js";
|
|
5
|
-
import {
|
|
6
|
-
import { ThreadListRuntimeCore, ThreadRuntimeCore } from "@assistant-ui/core";
|
|
5
|
+
import { ThreadListRuntimeCore, ThreadRuntimeCore, ToolExecutionStatus } from "@assistant-ui/core";
|
|
7
6
|
import { AssistantRuntimeImpl, BaseAssistantRuntimeCore, CompositeContextProvider, DefaultThreadComposerRuntimeCore, MessageRepository, ThreadListItemRuntimeBinding, ThreadRuntimeCoreBinding, ThreadRuntimeImpl, fromThreadMessageLike, generateId, getAutoStatus } from "@assistant-ui/core/internal";
|
|
8
7
|
|
|
9
8
|
//#region src/internal.d.ts
|
|
10
9
|
declare namespace internal_d_exports {
|
|
11
|
-
export { AssistantRuntimeImpl, BaseAssistantRuntimeCore, CompositeContextProvider, DefaultThreadComposerRuntimeCore, MessageRepository, ThreadListItemRuntimeBinding, ThreadListRuntimeCore, ThreadRuntimeCore, ThreadRuntimeCoreBinding, ThreadRuntimeImpl, ToolExecutionStatus, fromThreadMessageLike, generateId, getAutoStatus, splitLocalRuntimeOptions, useComposerInputPluginRegistryOptional, useSmooth, useSmoothStatus,
|
|
10
|
+
export { AssistantRuntimeImpl, BaseAssistantRuntimeCore, CompositeContextProvider, DefaultThreadComposerRuntimeCore, MessageRepository, ThreadListItemRuntimeBinding, ThreadListRuntimeCore, ThreadRuntimeCore, ThreadRuntimeCoreBinding, ThreadRuntimeImpl, ToolExecutionStatus, fromThreadMessageLike, generateId, getAutoStatus, splitLocalRuntimeOptions, useComposerInputPluginRegistryOptional, useSmooth, useSmoothStatus, withSmoothContextProvider };
|
|
12
11
|
}
|
|
13
12
|
//#endregion
|
|
14
|
-
export { AssistantRuntimeImpl, BaseAssistantRuntimeCore, CompositeContextProvider, DefaultThreadComposerRuntimeCore, MessageRepository, type ThreadListItemRuntimeBinding, type ThreadListRuntimeCore, type ThreadRuntimeCore, type ThreadRuntimeCoreBinding, ThreadRuntimeImpl, type ToolExecutionStatus, fromThreadMessageLike, generateId, getAutoStatus, internal_d_exports, splitLocalRuntimeOptions, useComposerInputPluginRegistryOptional, useSmooth, useSmoothStatus,
|
|
13
|
+
export { AssistantRuntimeImpl, BaseAssistantRuntimeCore, CompositeContextProvider, DefaultThreadComposerRuntimeCore, MessageRepository, type ThreadListItemRuntimeBinding, type ThreadListRuntimeCore, type ThreadRuntimeCore, type ThreadRuntimeCoreBinding, ThreadRuntimeImpl, type ToolExecutionStatus, fromThreadMessageLike, generateId, getAutoStatus, internal_d_exports, splitLocalRuntimeOptions, useComposerInputPluginRegistryOptional, useSmooth, useSmoothStatus, withSmoothContextProvider };
|
|
15
14
|
//# sourceMappingURL=internal.d.ts.map
|
package/dist/internal.js
CHANGED
|
@@ -3,7 +3,6 @@ import { useComposerInputPluginRegistryOptional } from "./primitives/composer/Co
|
|
|
3
3
|
import { useSmoothStatus, withSmoothContextProvider } from "./utils/smooth/SmoothContext.js";
|
|
4
4
|
import { useSmooth } from "./utils/smooth/useSmooth.js";
|
|
5
5
|
import { splitLocalRuntimeOptions } from "./legacy-runtime/runtime-cores/local/LocalRuntimeOptions.js";
|
|
6
|
-
import { useToolInvocations } from "@assistant-ui/core/react";
|
|
7
6
|
import { AssistantRuntimeImpl, BaseAssistantRuntimeCore, CompositeContextProvider, DefaultThreadComposerRuntimeCore, MessageRepository, ThreadRuntimeImpl, fromThreadMessageLike, generateId, getAutoStatus } from "@assistant-ui/core/internal";
|
|
8
7
|
//#region src/internal.ts
|
|
9
8
|
var internal_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -20,10 +19,9 @@ var internal_exports = /* @__PURE__ */ __exportAll({
|
|
|
20
19
|
useComposerInputPluginRegistryOptional: () => useComposerInputPluginRegistryOptional,
|
|
21
20
|
useSmooth: () => useSmooth,
|
|
22
21
|
useSmoothStatus: () => useSmoothStatus,
|
|
23
|
-
useToolInvocations: () => useToolInvocations,
|
|
24
22
|
withSmoothContextProvider: () => withSmoothContextProvider
|
|
25
23
|
});
|
|
26
24
|
//#endregion
|
|
27
|
-
export { AssistantRuntimeImpl, BaseAssistantRuntimeCore, CompositeContextProvider, DefaultThreadComposerRuntimeCore, MessageRepository, ThreadRuntimeImpl, fromThreadMessageLike, generateId, getAutoStatus, internal_exports, splitLocalRuntimeOptions, useComposerInputPluginRegistryOptional, useSmooth, useSmoothStatus,
|
|
25
|
+
export { AssistantRuntimeImpl, BaseAssistantRuntimeCore, CompositeContextProvider, DefaultThreadComposerRuntimeCore, MessageRepository, ThreadRuntimeImpl, fromThreadMessageLike, generateId, getAutoStatus, internal_exports, splitLocalRuntimeOptions, useComposerInputPluginRegistryOptional, useSmooth, useSmoothStatus, withSmoothContextProvider };
|
|
28
26
|
|
|
29
27
|
//# sourceMappingURL=internal.js.map
|
package/dist/internal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal.js","names":[],"sources":["../src/internal.ts"],"sourcesContent":["// Re-export from @assistant-ui/core\nexport type {\n ThreadRuntimeCore,\n ThreadListRuntimeCore,\n} from \"@assistant-ui/core\";\n\n// Re-export from @assistant-ui/core/internal\nexport {\n DefaultThreadComposerRuntimeCore,\n CompositeContextProvider,\n MessageRepository,\n BaseAssistantRuntimeCore,\n generateId,\n AssistantRuntimeImpl,\n ThreadRuntimeImpl,\n fromThreadMessageLike,\n getAutoStatus,\n} from \"@assistant-ui/core/internal\";\nexport type {\n ThreadRuntimeCoreBinding,\n ThreadListItemRuntimeBinding,\n} from \"@assistant-ui/core/internal\";\n\n// React-specific (stay in react)\nexport { splitLocalRuntimeOptions } from \"./legacy-runtime/runtime-cores/local/LocalRuntimeOptions\";\nexport
|
|
1
|
+
{"version":3,"file":"internal.js","names":[],"sources":["../src/internal.ts"],"sourcesContent":["// Re-export from @assistant-ui/core\nexport type {\n ThreadRuntimeCore,\n ThreadListRuntimeCore,\n} from \"@assistant-ui/core\";\n\n// Re-export from @assistant-ui/core/internal\nexport {\n DefaultThreadComposerRuntimeCore,\n CompositeContextProvider,\n MessageRepository,\n BaseAssistantRuntimeCore,\n generateId,\n AssistantRuntimeImpl,\n ThreadRuntimeImpl,\n fromThreadMessageLike,\n getAutoStatus,\n} from \"@assistant-ui/core/internal\";\nexport type {\n ThreadRuntimeCoreBinding,\n ThreadListItemRuntimeBinding,\n} from \"@assistant-ui/core/internal\";\n\n// React-specific (stay in react)\nexport { splitLocalRuntimeOptions } from \"./legacy-runtime/runtime-cores/local/LocalRuntimeOptions\";\nexport type { ToolExecutionStatus } from \"@assistant-ui/core\";\n\nexport { useSmooth } from \"./utils/smooth/useSmooth\";\nexport {\n useSmoothStatus,\n withSmoothContextProvider,\n} from \"./utils/smooth/SmoothContext\";\n\n// ComposerInput plugin registry (used by react-lexical)\nexport { useComposerInputPluginRegistryOptional } from \"./primitives/composer/ComposerInputPluginContext\";\n"],"mappings":""}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { UserCommands } from "../../../augmentations.js";
|
|
2
|
-
import { ToolExecutionStatus } from "
|
|
3
|
-
import { AttachmentAdapter, LanguageModelConfig, LanguageModelV1CallSettings, ThreadHistoryAdapter, ThreadMessage } from "@assistant-ui/core";
|
|
2
|
+
import { AttachmentAdapter, LanguageModelConfig, LanguageModelV1CallSettings, ThreadHistoryAdapter, ThreadMessage, ToolExecutionStatus } from "@assistant-ui/core";
|
|
4
3
|
import { ReadonlyJSONValue } from "assistant-stream/utils";
|
|
4
|
+
import { ToolModelContentPart } from "assistant-stream";
|
|
5
5
|
|
|
6
6
|
//#region src/legacy-runtime/runtime-cores/assistant-transport/types.d.ts
|
|
7
7
|
type TextPart = {
|
|
@@ -34,6 +34,7 @@ type AddToolResultCommand = {
|
|
|
34
34
|
readonly result: ReadonlyJSONValue;
|
|
35
35
|
readonly isError: boolean;
|
|
36
36
|
readonly artifact?: ReadonlyJSONValue;
|
|
37
|
+
readonly modelContent?: readonly ToolModelContentPart[];
|
|
37
38
|
};
|
|
38
39
|
type AssistantTransportCommand = AddMessageCommand | AddToolResultCommand | UserCommands;
|
|
39
40
|
type AssistantTransportState = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../../src/legacy-runtime/runtime-cores/assistant-transport/types.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../../src/legacy-runtime/runtime-cores/assistant-transport/types.ts"],"mappings":";;;;;;KAaY,QAAA;EAAA,SACD,IAAA;EAAA,SACA,IAAI;AAAA;AAAA,KAGH,SAAA;EAAA,SACD,IAAA;EAAA,SACA,KAAK;AAAA;AAAA,KAGJ,eAAA,GAAkB,QAAA,GAAW,SAAS;AAAA,KAEtC,WAAA;EAAA,SACD,IAAA;EAAA,SACA,KAAA,WAAgB,eAAe;AAAA;AAAA,KAG9B,gBAAA;EAAA,SACD,IAAA;EAAA,SACA,KAAA,WAAgB,QAAQ;AAAA;AAAA,KAIvB,iBAAA;EAAA,SACD,IAAA;EAAA,SACA,OAAA,EAAS,WAAA,GAAc,gBAAgB;EAAA,SACvC,QAAA;EAAA,SACA,QAAA;AAAA;AAAA,KAGC,oBAAA;EAAA,SACD,IAAA;EAAA,SACA,UAAA;EAAA,SACA,QAAA;EAAA,SACA,MAAA,EAAQ,iBAAA;EAAA,SACR,OAAA;EAAA,SACA,QAAA,GAAW,iBAAA;EAAA,SACX,YAAA,YAAwB,oBAAA;AAAA;AAAA,KAGvB,yBAAA,GACR,iBAAA,GACA,oBAAA,GACA,YAAA;AAAA,KAGQ,uBAAA;EAAA,SACD,QAAA,WAAmB,aAAA;EAAA,SACnB,KAAA,GAAQ,iBAAiB;EAAA,SACzB,SAAA;AAAA;AAAA,KAGC,oCAAA;EACV,eAAA,EAAiB,yBAAA;EACjB,SAAA;EACA,YAAA,EAAc,MAAA,SAAe,mBAAA;AAAA;AAAA,KAGnB,gCAAA,OACV,KAAA,EAAO,CAAA,EACP,kBAAA,EAAoB,oCAAA,KACjB,uBAAA;AAAA,KAGO,iBAAA;EACV,MAAA,EAAQ,yBAAA;EACR,SAAA,EAAW,yBAAyB;AAAA;AAAA,KAI1B,aAAA,GAAgB,yBAAyB;AAAA,KAKzC,YAAA,GAAe,MAAA,mBAAyB,OAAO;AAAA,KAE/C,0BAAA;AAAA,KAEA,uBAAA;EACV,QAAA,EAAU,aAAA;EACV,KAAA;EACA,MAAA;EACA,KAAA,EAAO,MAAA;EACP,YAAA,EAAc,2BAAA;EACd,MAAA,EAAQ,mBAAA;EACR,QAAA;EACA,QAAA;EAAA,CAIC,GAAA;AAAA;AAAA,KAGS,yBAAA;EACV,YAAA,EAAc,CAAA;EACd,GAAA;EACA,SAAA;EACA,QAAA,GAAW,0BAAA;EACX,SAAA,EAAW,gCAAA,CAAiC,CAAA;EAC5C,OAAA,EAAS,YAAA,UAAsB,OAAA,CAAQ,YAAA;EACvC,IAAA,mBAAuB,OAAA;EA1DrB;;;;;;;AAAY;AAGhB;;;;EAoEE,0BAAA,IACE,IAAA,EAAM,uBAAA,KACH,MAAA,oBAA0B,OAAA,CAAQ,MAAA;EACvC,UAAA,IAAc,QAAA,EAAU,QAAA;EACxB,QAAA;EACA,OAAA,IACE,KAAA,EAAO,KAAA,EACP,MAAA;IACE,QAAA,EAAU,yBAAA;IACV,WAAA,GAAc,OAAA,GAAU,KAAA,EAAO,CAAA,KAAM,CAAA;EAAA,aAE7B,OAAA;EAzEF;;;;;;EAgFV,QAAA,IAAY,MAAA;IACV,QAAA,EAAU,yBAAA;IACV,WAAA,GAAc,OAAA,GAAU,KAAA,EAAO,CAAA,KAAM,CAAA;IACrC,KAAA,GAAQ,KAAA;EAAA;EAEV,YAAA;IACE,IAAA;EAAA;EAEF,QAAA;IACE,WAAA,GAAc,iBAAA;IACd,OAAA,GAAU,oBAAA;EAAA;AAAA"}
|
package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAssistantTransportRuntime.d.ts","names":[],"sources":["../../../../src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"useAssistantTransportRuntime.d.ts","names":[],"sources":["../../../../src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts"],"mappings":";;;;;cA0Fa,gCAAA,SAGH,OAAkC,EAAzB,yBAAyB;AAAA,iBAO5B,0BAAA,CAAA,GAA8B,iBAAiB;AAAA,iBAC/C,0BAAA,GAAA,CACd,QAAA,GAAW,KAAA,EAAO,iBAAA,KAAsB,CAAA,GACvC,CAAA;;;;cAmQU,4BAAA,MACX,OAAA,EAAS,yBAAA,CAA0B,CAAA,MAClC,gBAAA"}
|
package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js
CHANGED
|
@@ -4,7 +4,6 @@ import { useExternalStoreRuntime } from "../external-store/useExternalStoreRunti
|
|
|
4
4
|
import { useCommandQueue } from "./commandQueue.js";
|
|
5
5
|
import { useRunManager } from "./runManager.js";
|
|
6
6
|
import { useConvertedState } from "./useConvertedState.js";
|
|
7
|
-
import { useToolInvocations } from "./useToolInvocations.js";
|
|
8
7
|
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
9
8
|
import { InMemoryThreadListAdapter, createRequestHeaders } from "@assistant-ui/core";
|
|
10
9
|
import { asAsyncIterableStream } from "assistant-stream/utils";
|
|
@@ -152,6 +151,8 @@ const useAssistantTransportThreadRuntime = (options) => {
|
|
|
152
151
|
state: converted.state,
|
|
153
152
|
isRunning: converted.isRunning,
|
|
154
153
|
adapters: options.adapters,
|
|
154
|
+
unstable_enableToolInvocations: true,
|
|
155
|
+
setToolStatuses,
|
|
155
156
|
extras: {
|
|
156
157
|
[symbolAssistantTransportExtras]: true,
|
|
157
158
|
sendCommand: (command) => {
|
|
@@ -171,7 +172,6 @@ const useAssistantTransportThreadRuntime = (options) => {
|
|
|
171
172
|
} },
|
|
172
173
|
onCancel: async () => {
|
|
173
174
|
runManager.cancel();
|
|
174
|
-
await toolInvocations.abort();
|
|
175
175
|
},
|
|
176
176
|
onResume: async () => {
|
|
177
177
|
if (!options.resumeApi) throw new Error("Must pass resumeApi to options to resume runs");
|
|
@@ -185,22 +185,16 @@ const useAssistantTransportThreadRuntime = (options) => {
|
|
|
185
185
|
result: toolOptions.result,
|
|
186
186
|
toolName: toolOptions.toolName,
|
|
187
187
|
isError: toolOptions.isError,
|
|
188
|
-
...toolOptions.artifact && { artifact: toolOptions.artifact }
|
|
188
|
+
...toolOptions.artifact && { artifact: toolOptions.artifact },
|
|
189
|
+
...toolOptions.modelContent !== void 0 && { modelContent: toolOptions.modelContent }
|
|
189
190
|
};
|
|
190
191
|
commandQueue.enqueue(command);
|
|
191
192
|
},
|
|
192
193
|
onLoadExternalState: async (state) => {
|
|
193
194
|
agentStateRef.current = state;
|
|
194
|
-
toolInvocations.reset();
|
|
195
195
|
rerender((prev) => prev + 1);
|
|
196
196
|
}
|
|
197
197
|
});
|
|
198
|
-
const toolInvocations = useToolInvocations({
|
|
199
|
-
state: converted,
|
|
200
|
-
getTools: () => runtime.thread.getModelContext().tools,
|
|
201
|
-
onResult: commandQueue.enqueue,
|
|
202
|
-
setToolStatuses
|
|
203
|
-
});
|
|
204
198
|
return runtime;
|
|
205
199
|
};
|
|
206
200
|
/**
|
package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAssistantTransportRuntime.js","names":["createInitialMessage"],"sources":["../../../../src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts"],"sourcesContent":["\"use client\";\n\nimport type { AppendMessage } from \"@assistant-ui/core\";\nimport {\n type ReadonlyJSONObject,\n type ReadonlyJSONValue,\n asAsyncIterableStream,\n} from \"assistant-stream/utils\";\nimport { useExternalStoreRuntime } from \"../external-store/useExternalStoreRuntime\";\nimport type { AssistantRuntime } from \"../../runtime/AssistantRuntime\";\nimport type { AddToolResultOptions } from \"@assistant-ui/core\";\nimport { useState, useRef, useMemo } from \"react\";\nimport {\n AssistantMessageAccumulator,\n DataStreamDecoder,\n AssistantTransportDecoder,\n unstable_createInitialMessage as createInitialMessage,\n toToolsJSONSchema,\n} from \"assistant-stream\";\nimport type {\n AssistantTransportOptions,\n AddMessageCommand,\n AddToolResultCommand,\n UserMessagePart,\n QueuedCommand,\n AssistantTransportCommand,\n SendCommandsRequestBody,\n} from \"./types\";\nimport { useCommandQueue } from \"./commandQueue\";\nimport { useRunManager } from \"./runManager\";\nimport { useConvertedState } from \"./useConvertedState\";\nimport {\n type ToolExecutionStatus,\n useToolInvocations,\n} from \"./useToolInvocations\";\nimport { createRequestHeaders } from \"@assistant-ui/core\";\nimport { useRemoteThreadListRuntime } from \"../remote-thread-list/useRemoteThreadListRuntime\";\nimport { InMemoryThreadListAdapter } from \"@assistant-ui/core\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport type { UserExternalState } from \"../../../augmentations\";\n\nconst convertAppendMessageToCommand = (\n message: AppendMessage,\n): AddMessageCommand => {\n if (message.role !== \"user\")\n throw new Error(\"Only user messages are supported\");\n\n const parts: UserMessagePart[] = [];\n const content = [\n ...message.content,\n ...(message.attachments?.flatMap((a) => a.content) ?? []),\n ];\n for (const contentPart of content) {\n if (contentPart.type === \"text\") {\n parts.push({ type: \"text\", text: contentPart.text });\n } else if (contentPart.type === \"image\") {\n parts.push({ type: \"image\", image: contentPart.image });\n }\n }\n\n return {\n type: \"add-message\",\n message: {\n role: \"user\",\n parts,\n },\n parentId: message.parentId,\n sourceId: message.sourceId,\n };\n};\n\nconst symbolAssistantTransportExtras = Symbol(\"assistant-transport-extras\");\ntype AssistantTransportExtras = {\n [symbolAssistantTransportExtras]: true;\n sendCommand: (command: AssistantTransportCommand) => void;\n state: UserExternalState;\n};\n\nconst asAssistantTransportExtras = (\n extras: unknown,\n): AssistantTransportExtras => {\n if (\n typeof extras !== \"object\" ||\n extras == null ||\n !(symbolAssistantTransportExtras in extras)\n )\n throw new Error(\n \"This method can only be called when you are using useAssistantTransportRuntime\",\n );\n\n return extras as AssistantTransportExtras;\n};\n\nexport const useAssistantTransportSendCommand = () => {\n const aui = useAui();\n\n return (command: AssistantTransportCommand) => {\n const extras = aui.thread().getState().extras;\n const transportExtras = asAssistantTransportExtras(extras);\n transportExtras.sendCommand(command);\n };\n};\n\nexport function useAssistantTransportState(): UserExternalState;\nexport function useAssistantTransportState<T>(\n selector: (state: UserExternalState) => T,\n): T;\nexport function useAssistantTransportState<T>(\n selector: (state: UserExternalState) => T = (t) => t as T,\n): T | UserExternalState {\n return useAuiState((s) =>\n selector(asAssistantTransportExtras(s.thread.extras).state),\n );\n}\n\nconst useAssistantTransportThreadRuntime = <T>(\n options: AssistantTransportOptions<T>,\n): AssistantRuntime => {\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const agentStateRef = useRef(options.initialState);\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const [, rerender] = useState(0);\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const resumeFlagRef = useRef(false);\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const parentIdRef = useRef<string | null | undefined>(undefined);\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const commandQueue = useCommandQueue({\n onQueue: () => runManager.schedule(),\n });\n\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const threadId = useAuiState((s) => s.threadListItem.remoteId);\n\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const runManager = useRunManager({\n onRun: async (signal: AbortSignal) => {\n const isResume = resumeFlagRef.current;\n resumeFlagRef.current = false;\n const commands: QueuedCommand[] = isResume ? [] : commandQueue.flush();\n if (commands.length === 0 && !isResume)\n throw new Error(\"No commands to send\");\n\n const headers = await createRequestHeaders(options.headers);\n const bodyValue =\n typeof options.body === \"function\"\n ? await options.body()\n : options.body;\n const context = runtime.thread.getModelContext();\n\n let requestBody: Record<string, unknown> = {\n commands,\n state: agentStateRef.current,\n system: context.system,\n tools: context.tools ? toToolsJSONSchema(context.tools) : undefined,\n threadId,\n ...(parentIdRef.current !== undefined && {\n parentId: parentIdRef.current,\n }),\n // nested (new format, aligned with AssistantChatTransport)\n callSettings: context.callSettings,\n config: context.config,\n // @deprecated spread at top level — use nested `callSettings`/`config` instead. Will be removed in a future version.\n ...context.callSettings,\n ...context.config,\n ...(bodyValue ?? {}),\n };\n\n if (options.prepareSendCommandsRequest) {\n requestBody = await options.prepareSendCommandsRequest(\n requestBody as SendCommandsRequestBody,\n );\n }\n\n const response = await fetch(\n isResume ? options.resumeApi! : options.api,\n {\n method: \"POST\",\n headers,\n body: JSON.stringify(requestBody),\n signal,\n },\n );\n\n options.onResponse?.(response);\n\n if (!response.ok) {\n throw new Error(`Status ${response.status}: ${await response.text()}`);\n }\n\n if (!response.body) {\n throw new Error(\"Response body is null\");\n }\n\n // Select decoder based on protocol option\n const protocol = options.protocol ?? \"data-stream\";\n const decoder =\n protocol === \"assistant-transport\"\n ? new AssistantTransportDecoder()\n : new DataStreamDecoder();\n\n let err: string | undefined;\n const stream = response.body.pipeThrough(decoder).pipeThrough(\n new AssistantMessageAccumulator({\n initialMessage: createInitialMessage({\n unstable_state:\n (agentStateRef.current as ReadonlyJSONValue) ?? null,\n }),\n throttle: isResume,\n onError: (error) => {\n err = error;\n },\n }),\n );\n\n let markedDelivered = false;\n\n for await (const chunk of asAsyncIterableStream(stream)) {\n if (chunk.metadata.unstable_state === agentStateRef.current) continue;\n\n if (!markedDelivered) {\n commandQueue.markDelivered();\n markedDelivered = true;\n }\n\n agentStateRef.current = chunk.metadata.unstable_state as T;\n rerender((prev) => prev + 1);\n }\n\n if (err) {\n throw new Error(err);\n }\n },\n onFinish: options.onFinish,\n onCancel: () => {\n const cmds = [\n ...commandQueue.state.inTransit,\n ...commandQueue.state.queued,\n ];\n\n commandQueue.reset();\n\n options.onCancel?.({\n commands: cmds,\n updateState: (updater) => {\n agentStateRef.current = updater(agentStateRef.current);\n rerender((prev) => prev + 1);\n },\n });\n },\n onError: async (error) => {\n const inTransitCmds = [...commandQueue.state.inTransit];\n const queuedCmds = [...commandQueue.state.queued];\n\n commandQueue.reset();\n\n try {\n await options.onError?.(error as Error, {\n commands: inTransitCmds,\n updateState: (updater) => {\n agentStateRef.current = updater(agentStateRef.current);\n rerender((prev) => prev + 1);\n },\n });\n } finally {\n options.onCancel?.({\n commands: queuedCmds,\n updateState: (updater) => {\n agentStateRef.current = updater(agentStateRef.current);\n rerender((prev) => prev + 1);\n },\n error: error as Error,\n });\n }\n },\n });\n\n // Tool execution status state\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const [toolStatuses, setToolStatuses] = useState<\n Record<string, ToolExecutionStatus>\n >({});\n\n // Reactive conversion of agent state + connection metadata → UI state\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const pendingCommands = useMemo(\n () => [...commandQueue.state.inTransit, ...commandQueue.state.queued],\n [commandQueue.state],\n );\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const converted = useConvertedState(\n options.converter,\n agentStateRef.current,\n pendingCommands,\n runManager.isRunning,\n toolStatuses,\n );\n\n // Create runtime\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const runtime = useExternalStoreRuntime({\n messages: converted.messages,\n state: converted.state,\n isRunning: converted.isRunning,\n adapters: options.adapters,\n extras: {\n [symbolAssistantTransportExtras]: true,\n sendCommand: (command: AssistantTransportCommand) => {\n commandQueue.enqueue(command);\n },\n state: agentStateRef.current as UserExternalState,\n } satisfies AssistantTransportExtras,\n onNew: async (message: AppendMessage): Promise<void> => {\n parentIdRef.current = message.parentId;\n const command = convertAppendMessageToCommand(message);\n commandQueue.enqueue(command);\n },\n ...(options.capabilities?.edit && {\n onEdit: async (message: AppendMessage): Promise<void> => {\n parentIdRef.current = message.parentId;\n const command = convertAppendMessageToCommand(message);\n commandQueue.enqueue(command);\n },\n }),\n onCancel: async () => {\n runManager.cancel();\n await toolInvocations.abort();\n },\n onResume: async () => {\n if (!options.resumeApi)\n throw new Error(\"Must pass resumeApi to options to resume runs\");\n\n resumeFlagRef.current = true;\n runManager.schedule();\n },\n onAddToolResult: async (\n toolOptions: AddToolResultOptions,\n ): Promise<void> => {\n const command: AddToolResultCommand = {\n type: \"add-tool-result\",\n toolCallId: toolOptions.toolCallId,\n result: toolOptions.result as ReadonlyJSONObject,\n toolName: toolOptions.toolName,\n isError: toolOptions.isError,\n ...(toolOptions.artifact && { artifact: toolOptions.artifact }),\n };\n\n commandQueue.enqueue(command);\n },\n onLoadExternalState: async (state) => {\n agentStateRef.current = state as T;\n toolInvocations.reset();\n rerender((prev) => prev + 1);\n },\n });\n\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const toolInvocations = useToolInvocations({\n state: converted,\n getTools: () => runtime.thread.getModelContext().tools,\n onResult: commandQueue.enqueue,\n setToolStatuses,\n });\n\n return runtime;\n};\n\n/**\n * @alpha This is an experimental API that is subject to change.\n */\nexport const useAssistantTransportRuntime = <T>(\n options: AssistantTransportOptions<T>,\n): AssistantRuntime => {\n const runtime = useRemoteThreadListRuntime({\n runtimeHook: function RuntimeHook() {\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n return useAssistantTransportThreadRuntime(options);\n },\n adapter: new InMemoryThreadListAdapter(),\n allowNesting: true,\n });\n return runtime;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useAssistantTransportRuntime.js","names":["createInitialMessage"],"sources":["../../../../src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts"],"sourcesContent":["\"use client\";\n\nimport type { AppendMessage } from \"@assistant-ui/core\";\nimport {\n type ReadonlyJSONObject,\n type ReadonlyJSONValue,\n asAsyncIterableStream,\n} from \"assistant-stream/utils\";\nimport { useExternalStoreRuntime } from \"../external-store/useExternalStoreRuntime\";\nimport type { AssistantRuntime } from \"../../runtime/AssistantRuntime\";\nimport type { AddToolResultOptions } from \"@assistant-ui/core\";\nimport { useMemo, useRef, useState } from \"react\";\nimport {\n AssistantMessageAccumulator,\n DataStreamDecoder,\n AssistantTransportDecoder,\n unstable_createInitialMessage as createInitialMessage,\n toToolsJSONSchema,\n} from \"assistant-stream\";\nimport type {\n AssistantTransportOptions,\n AddMessageCommand,\n AddToolResultCommand,\n UserMessagePart,\n QueuedCommand,\n AssistantTransportCommand,\n SendCommandsRequestBody,\n} from \"./types\";\nimport { useCommandQueue } from \"./commandQueue\";\nimport { useRunManager } from \"./runManager\";\nimport { useConvertedState } from \"./useConvertedState\";\nimport type { ToolExecutionStatus } from \"@assistant-ui/core\";\nimport { createRequestHeaders } from \"@assistant-ui/core\";\nimport { useRemoteThreadListRuntime } from \"../remote-thread-list/useRemoteThreadListRuntime\";\nimport { InMemoryThreadListAdapter } from \"@assistant-ui/core\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport type { UserExternalState } from \"../../../augmentations\";\n\nconst convertAppendMessageToCommand = (\n message: AppendMessage,\n): AddMessageCommand => {\n if (message.role !== \"user\")\n throw new Error(\"Only user messages are supported\");\n\n const parts: UserMessagePart[] = [];\n const content = [\n ...message.content,\n ...(message.attachments?.flatMap((a) => a.content) ?? []),\n ];\n for (const contentPart of content) {\n if (contentPart.type === \"text\") {\n parts.push({ type: \"text\", text: contentPart.text });\n } else if (contentPart.type === \"image\") {\n parts.push({ type: \"image\", image: contentPart.image });\n }\n }\n\n return {\n type: \"add-message\",\n message: {\n role: \"user\",\n parts,\n },\n parentId: message.parentId,\n sourceId: message.sourceId,\n };\n};\n\nconst symbolAssistantTransportExtras = Symbol(\"assistant-transport-extras\");\ntype AssistantTransportExtras = {\n [symbolAssistantTransportExtras]: true;\n sendCommand: (command: AssistantTransportCommand) => void;\n state: UserExternalState;\n};\n\nconst asAssistantTransportExtras = (\n extras: unknown,\n): AssistantTransportExtras => {\n if (\n typeof extras !== \"object\" ||\n extras == null ||\n !(symbolAssistantTransportExtras in extras)\n )\n throw new Error(\n \"This method can only be called when you are using useAssistantTransportRuntime\",\n );\n\n return extras as AssistantTransportExtras;\n};\n\nexport const useAssistantTransportSendCommand = () => {\n const aui = useAui();\n\n return (command: AssistantTransportCommand) => {\n const extras = aui.thread().getState().extras;\n const transportExtras = asAssistantTransportExtras(extras);\n transportExtras.sendCommand(command);\n };\n};\n\nexport function useAssistantTransportState(): UserExternalState;\nexport function useAssistantTransportState<T>(\n selector: (state: UserExternalState) => T,\n): T;\nexport function useAssistantTransportState<T>(\n selector: (state: UserExternalState) => T = (t) => t as T,\n): T | UserExternalState {\n return useAuiState((s) =>\n selector(asAssistantTransportExtras(s.thread.extras).state),\n );\n}\n\nconst useAssistantTransportThreadRuntime = <T>(\n options: AssistantTransportOptions<T>,\n): AssistantRuntime => {\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const agentStateRef = useRef(options.initialState);\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const [, rerender] = useState(0);\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const resumeFlagRef = useRef(false);\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const parentIdRef = useRef<string | null | undefined>(undefined);\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const commandQueue = useCommandQueue({\n onQueue: () => runManager.schedule(),\n });\n\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const threadId = useAuiState((s) => s.threadListItem.remoteId);\n\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const runManager = useRunManager({\n onRun: async (signal: AbortSignal) => {\n const isResume = resumeFlagRef.current;\n resumeFlagRef.current = false;\n const commands: QueuedCommand[] = isResume ? [] : commandQueue.flush();\n if (commands.length === 0 && !isResume)\n throw new Error(\"No commands to send\");\n\n const headers = await createRequestHeaders(options.headers);\n const bodyValue =\n typeof options.body === \"function\"\n ? await options.body()\n : options.body;\n const context = runtime.thread.getModelContext();\n\n let requestBody: Record<string, unknown> = {\n commands,\n state: agentStateRef.current,\n system: context.system,\n tools: context.tools ? toToolsJSONSchema(context.tools) : undefined,\n threadId,\n ...(parentIdRef.current !== undefined && {\n parentId: parentIdRef.current,\n }),\n // nested (new format, aligned with AssistantChatTransport)\n callSettings: context.callSettings,\n config: context.config,\n // @deprecated spread at top level — use nested `callSettings`/`config` instead. Will be removed in a future version.\n ...context.callSettings,\n ...context.config,\n ...(bodyValue ?? {}),\n };\n\n if (options.prepareSendCommandsRequest) {\n requestBody = await options.prepareSendCommandsRequest(\n requestBody as SendCommandsRequestBody,\n );\n }\n\n const response = await fetch(\n isResume ? options.resumeApi! : options.api,\n {\n method: \"POST\",\n headers,\n body: JSON.stringify(requestBody),\n signal,\n },\n );\n\n options.onResponse?.(response);\n\n if (!response.ok) {\n throw new Error(`Status ${response.status}: ${await response.text()}`);\n }\n\n if (!response.body) {\n throw new Error(\"Response body is null\");\n }\n\n // Select decoder based on protocol option\n const protocol = options.protocol ?? \"data-stream\";\n const decoder =\n protocol === \"assistant-transport\"\n ? new AssistantTransportDecoder()\n : new DataStreamDecoder();\n\n let err: string | undefined;\n const stream = response.body.pipeThrough(decoder).pipeThrough(\n new AssistantMessageAccumulator({\n initialMessage: createInitialMessage({\n unstable_state:\n (agentStateRef.current as ReadonlyJSONValue) ?? null,\n }),\n throttle: isResume,\n onError: (error) => {\n err = error;\n },\n }),\n );\n\n let markedDelivered = false;\n\n for await (const chunk of asAsyncIterableStream(stream)) {\n if (chunk.metadata.unstable_state === agentStateRef.current) continue;\n\n if (!markedDelivered) {\n commandQueue.markDelivered();\n markedDelivered = true;\n }\n\n agentStateRef.current = chunk.metadata.unstable_state as T;\n rerender((prev) => prev + 1);\n }\n\n if (err) {\n throw new Error(err);\n }\n },\n onFinish: options.onFinish,\n onCancel: () => {\n const cmds = [\n ...commandQueue.state.inTransit,\n ...commandQueue.state.queued,\n ];\n\n commandQueue.reset();\n\n options.onCancel?.({\n commands: cmds,\n updateState: (updater) => {\n agentStateRef.current = updater(agentStateRef.current);\n rerender((prev) => prev + 1);\n },\n });\n },\n onError: async (error) => {\n const inTransitCmds = [...commandQueue.state.inTransit];\n const queuedCmds = [...commandQueue.state.queued];\n\n commandQueue.reset();\n\n try {\n await options.onError?.(error as Error, {\n commands: inTransitCmds,\n updateState: (updater) => {\n agentStateRef.current = updater(agentStateRef.current);\n rerender((prev) => prev + 1);\n },\n });\n } finally {\n options.onCancel?.({\n commands: queuedCmds,\n updateState: (updater) => {\n agentStateRef.current = updater(agentStateRef.current);\n rerender((prev) => prev + 1);\n },\n error: error as Error,\n });\n }\n },\n });\n\n // Tool execution status state\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const [toolStatuses, setToolStatuses] = useState<\n Record<string, ToolExecutionStatus>\n >({});\n\n // Reactive conversion of agent state + connection metadata → UI state\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const pendingCommands = useMemo(\n () => [...commandQueue.state.inTransit, ...commandQueue.state.queued],\n [commandQueue.state],\n );\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const converted = useConvertedState(\n options.converter,\n agentStateRef.current,\n pendingCommands,\n runManager.isRunning,\n toolStatuses,\n );\n\n // Create runtime\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n const runtime = useExternalStoreRuntime({\n messages: converted.messages,\n state: converted.state,\n isRunning: converted.isRunning,\n adapters: options.adapters,\n unstable_enableToolInvocations: true,\n setToolStatuses,\n extras: {\n [symbolAssistantTransportExtras]: true,\n sendCommand: (command: AssistantTransportCommand) => {\n commandQueue.enqueue(command);\n },\n state: agentStateRef.current as UserExternalState,\n } satisfies AssistantTransportExtras,\n onNew: async (message: AppendMessage): Promise<void> => {\n parentIdRef.current = message.parentId;\n const command = convertAppendMessageToCommand(message);\n commandQueue.enqueue(command);\n },\n ...(options.capabilities?.edit && {\n onEdit: async (message: AppendMessage): Promise<void> => {\n parentIdRef.current = message.parentId;\n const command = convertAppendMessageToCommand(message);\n commandQueue.enqueue(command);\n },\n }),\n onCancel: async () => {\n runManager.cancel();\n },\n onResume: async () => {\n if (!options.resumeApi)\n throw new Error(\"Must pass resumeApi to options to resume runs\");\n\n resumeFlagRef.current = true;\n runManager.schedule();\n },\n onAddToolResult: async (\n toolOptions: AddToolResultOptions,\n ): Promise<void> => {\n const command: AddToolResultCommand = {\n type: \"add-tool-result\",\n toolCallId: toolOptions.toolCallId,\n result: toolOptions.result as ReadonlyJSONObject,\n toolName: toolOptions.toolName,\n isError: toolOptions.isError,\n ...(toolOptions.artifact && { artifact: toolOptions.artifact }),\n ...(toolOptions.modelContent !== undefined && {\n modelContent: toolOptions.modelContent,\n }),\n };\n\n commandQueue.enqueue(command);\n },\n onLoadExternalState: async (state) => {\n agentStateRef.current = state as T;\n rerender((prev) => prev + 1);\n },\n });\n\n return runtime;\n};\n\n/**\n * @alpha This is an experimental API that is subject to change.\n */\nexport const useAssistantTransportRuntime = <T>(\n options: AssistantTransportOptions<T>,\n): AssistantRuntime => {\n const runtime = useRemoteThreadListRuntime({\n runtimeHook: function RuntimeHook() {\n // biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage\n return useAssistantTransportThreadRuntime(options);\n },\n adapter: new InMemoryThreadListAdapter(),\n allowNesting: true,\n });\n return runtime;\n};\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ToolExecutionStatus } from "./useToolInvocations.js";
|
|
2
1
|
import { AssistantTransportCommand, AssistantTransportState, AssistantTransportStateConverter } from "./types.js";
|
|
2
|
+
import { ToolExecutionStatus } from "@assistant-ui/core";
|
|
3
3
|
|
|
4
4
|
//#region src/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.d.ts
|
|
5
5
|
declare function useConvertedState<T>(converter: AssistantTransportStateConverter<T>, agentState: T, pendingCommands: AssistantTransportCommand[], isSending: boolean, toolStatuses: Record<string, ToolExecutionStatus>): AssistantTransportState;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useConvertedState.js","names":[],"sources":["../../../../src/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport type {\n AssistantTransportCommand,\n AssistantTransportState,\n AssistantTransportStateConverter,\n} from \"./types\";\nimport type { ToolExecutionStatus } from \"
|
|
1
|
+
{"version":3,"file":"useConvertedState.js","names":[],"sources":["../../../../src/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport type {\n AssistantTransportCommand,\n AssistantTransportState,\n AssistantTransportStateConverter,\n} from \"./types\";\nimport type { ToolExecutionStatus } from \"@assistant-ui/core\";\n\nexport function useConvertedState<T>(\n converter: AssistantTransportStateConverter<T>,\n agentState: T,\n pendingCommands: AssistantTransportCommand[],\n isSending: boolean,\n toolStatuses: Record<string, ToolExecutionStatus>,\n): AssistantTransportState {\n return useMemo(\n () => converter(agentState, { pendingCommands, isSending, toolStatuses }),\n [converter, agentState, pendingCommands, isSending, toolStatuses],\n );\n}\n"],"mappings":";;AAQA,SAAgB,kBACd,WACA,YACA,iBACA,WACA,cACyB;CACzB,OAAO,cACC,UAAU,YAAY;EAAE;EAAiB;EAAW;CAAa,CAAC,GACxE;EAAC;EAAW;EAAY;EAAiB;EAAW;CAAY,CAClE;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpAppRenderer.js","names":[],"sources":["../../src/mcp-apps/McpAppRenderer.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n useEffect,\n useMemo,\n useRef,\n useState,\n type MutableRefObject,\n type ReactNode,\n} from \"react\";\nimport type { McpAppMetadata } from \"@assistant-ui/core\";\nimport type {\n ToolCallMessagePartComponent,\n ToolCallMessagePartProps,\n} from \"@assistant-ui/core/react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport {\n resource,\n tapConst,\n tapRef,\n tapResource,\n type ResourceElement,\n} from \"@assistant-ui/tap\";\nimport { McpAppFrame } from \"./app-frame\";\nimport type {\n McpAppBridgeHandlers,\n McpAppHostContext,\n McpAppHostInfo,\n McpAppResource,\n McpAppSandboxConfig,\n McpAppsHost,\n} from \"./types\";\nimport { getMcpAppFromToolPart } from \"./utils\";\n\nexport type McpAppRendererOptions = {\n /**\n * Provides the data-plane operations the widget can request\n * (`loadResource`, `callTool`, `readResource`, `listResources`). Use\n * `McpAppsRemoteHost({ url })` for the default HTTP-route convention.\n */\n host: ResourceElement<McpAppsHost>;\n /** Sandbox + container styling. Passes through to SafeContentFrame. */\n sandbox?: McpAppSandboxConfig;\n /**\n * Upper bound (in pixels) applied to the widget-driven auto-resize height.\n * Defaults to 800.\n */\n maxHeight?: number;\n /** Identifies the host to the widget in the `ui/initialize` response. */\n hostInfo?: McpAppHostInfo;\n /** Delivered to the widget on initialize and pushed via `notifications/host_context/changed` on change. */\n hostContext?: McpAppHostContext;\n /** Rendered when no MCP app is on the part, or while load is in flight / failed (unless overridden). */\n fallback?: ReactNode;\n /** Rendered while the resource is loading. Defaults to `fallback`. */\n loadingFallback?: ReactNode;\n /** Rendered when the resource load rejects. Defaults to `fallback`. */\n errorFallback?: ReactNode | ((error: Error) => ReactNode);\n};\n\ntype LoadedResourceState = {\n resourceUri: string;\n resource?: McpAppResource;\n error?: Error;\n};\n\nfunction getInput(part: {\n status: { type: string };\n argsText: string;\n args: unknown;\n}): unknown {\n if (\n part.status.type === \"running\" &&\n (part.argsText === \"\" || part.argsText === \"{}\")\n ) {\n return undefined;\n }\n return part.args;\n}\n\nconst defaultOpenLink = ({ url }: { url: string }) => {\n window.open(url, \"_blank\", \"noopener,noreferrer\");\n};\n\nfunction extractSendMessageText(params: unknown): string | undefined {\n if (typeof params === \"string\") return params;\n if (!params || typeof params !== \"object\") return undefined;\n const obj = params as Record<string, unknown>;\n if (typeof obj[\"prompt\"] === \"string\") return obj[\"prompt\"];\n if (typeof obj[\"text\"] === \"string\") return obj[\"text\"];\n if (typeof obj[\"message\"] === \"string\") return obj[\"message\"];\n return undefined;\n}\n\nfunction InlineRenderer({\n part,\n internalsRef,\n optionsRef,\n}: {\n part: ToolCallMessagePartProps;\n internalsRef: MutableRefObject<{ host: McpAppsHost }>;\n optionsRef: MutableRefObject<McpAppRendererOptions>;\n}) {\n const opts = optionsRef.current;\n const aui = useAui();\n const app = getMcpAppFromToolPart(part);\n const cachedAppRef = useRef<McpAppMetadata | undefined>(undefined);\n if (app != null && cachedAppRef.current?.resourceUri !== app.resourceUri) {\n cachedAppRef.current = app;\n }\n const appForRender = app ?? cachedAppRef.current;\n\n const [loadedResource, setLoadedResource] = useState<LoadedResourceState>();\n\n const resourceUri = appForRender?.resourceUri;\n
|
|
1
|
+
{"version":3,"file":"McpAppRenderer.js","names":[],"sources":["../../src/mcp-apps/McpAppRenderer.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n useEffect,\n useMemo,\n useRef,\n useState,\n type MutableRefObject,\n type ReactNode,\n} from \"react\";\nimport type { McpAppMetadata } from \"@assistant-ui/core\";\nimport type {\n ToolCallMessagePartComponent,\n ToolCallMessagePartProps,\n} from \"@assistant-ui/core/react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport {\n resource,\n tapConst,\n tapRef,\n tapResource,\n type ResourceElement,\n} from \"@assistant-ui/tap\";\nimport { McpAppFrame } from \"./app-frame\";\nimport type {\n McpAppBridgeHandlers,\n McpAppHostContext,\n McpAppHostInfo,\n McpAppResource,\n McpAppSandboxConfig,\n McpAppsHost,\n} from \"./types\";\nimport { getMcpAppFromToolPart } from \"./utils\";\n\nexport type McpAppRendererOptions = {\n /**\n * Provides the data-plane operations the widget can request\n * (`loadResource`, `callTool`, `readResource`, `listResources`). Use\n * `McpAppsRemoteHost({ url })` for the default HTTP-route convention.\n */\n host: ResourceElement<McpAppsHost>;\n /** Sandbox + container styling. Passes through to SafeContentFrame. */\n sandbox?: McpAppSandboxConfig;\n /**\n * Upper bound (in pixels) applied to the widget-driven auto-resize height.\n * Defaults to 800.\n */\n maxHeight?: number;\n /** Identifies the host to the widget in the `ui/initialize` response. */\n hostInfo?: McpAppHostInfo;\n /** Delivered to the widget on initialize and pushed via `notifications/host_context/changed` on change. */\n hostContext?: McpAppHostContext;\n /** Rendered when no MCP app is on the part, or while load is in flight / failed (unless overridden). */\n fallback?: ReactNode;\n /** Rendered while the resource is loading. Defaults to `fallback`. */\n loadingFallback?: ReactNode;\n /** Rendered when the resource load rejects. Defaults to `fallback`. */\n errorFallback?: ReactNode | ((error: Error) => ReactNode);\n};\n\ntype LoadedResourceState = {\n resourceUri: string;\n resource?: McpAppResource;\n error?: Error;\n};\n\nfunction getInput(part: {\n status: { type: string };\n argsText: string;\n args: unknown;\n}): unknown {\n if (\n part.status.type === \"running\" &&\n (part.argsText === \"\" || part.argsText === \"{}\")\n ) {\n return undefined;\n }\n return part.args;\n}\n\nconst defaultOpenLink = ({ url }: { url: string }) => {\n window.open(url, \"_blank\", \"noopener,noreferrer\");\n};\n\nfunction extractSendMessageText(params: unknown): string | undefined {\n if (typeof params === \"string\") return params;\n if (!params || typeof params !== \"object\") return undefined;\n const obj = params as Record<string, unknown>;\n if (typeof obj[\"prompt\"] === \"string\") return obj[\"prompt\"];\n if (typeof obj[\"text\"] === \"string\") return obj[\"text\"];\n if (typeof obj[\"message\"] === \"string\") return obj[\"message\"];\n return undefined;\n}\n\nfunction InlineRenderer({\n part,\n internalsRef,\n optionsRef,\n}: {\n part: ToolCallMessagePartProps;\n internalsRef: MutableRefObject<{ host: McpAppsHost }>;\n optionsRef: MutableRefObject<McpAppRendererOptions>;\n}) {\n const opts = optionsRef.current;\n const aui = useAui();\n const app = getMcpAppFromToolPart(part);\n const cachedAppRef = useRef<McpAppMetadata | undefined>(undefined);\n if (app != null && cachedAppRef.current?.resourceUri !== app.resourceUri) {\n cachedAppRef.current = app;\n }\n const appForRender = app ?? cachedAppRef.current;\n\n const [loadedResource, setLoadedResource] = useState<LoadedResourceState>();\n\n const resourceUri = appForRender?.resourceUri;\n useEffect(() => {\n if (appForRender == null || resourceUri == null) return;\n let cancelled = false;\n const targetUri = resourceUri;\n\n internalsRef.current.host\n .loadResource({ uri: targetUri })\n .then((res) => {\n if (!cancelled)\n setLoadedResource({ resourceUri: targetUri, resource: res });\n })\n .catch((error: unknown) => {\n if (!cancelled) {\n setLoadedResource({\n resourceUri: targetUri,\n error: error instanceof Error ? error : new Error(String(error)),\n });\n }\n });\n\n return () => {\n cancelled = true;\n };\n // oxlint-disable-next-line tap-hooks/exhaustive-deps -- re-fetch only when URI changes; appForRender identity is unstable and internalsRef is a stable ref\n }, [resourceUri]);\n\n const bridgeHandlers = useMemo<McpAppBridgeHandlers>(\n () => ({\n openLink: defaultOpenLink,\n sendMessage: (params) => {\n const text = extractSendMessageText(params);\n if (!text) return { ok: false, reason: \"unrecognised params shape\" };\n aui.thread().append({ content: [{ type: \"text\", text }] });\n return { ok: true };\n },\n callTool: (params) => internalsRef.current.host.callTool(params),\n readResource: (params) => internalsRef.current.host.readResource(params),\n listResources: (params) =>\n internalsRef.current.host.listResources(params),\n }),\n [aui, internalsRef],\n );\n\n const loadedResourceForApp =\n loadedResource?.resourceUri === appForRender?.resourceUri\n ? loadedResource\n : undefined;\n const appResource = loadedResourceForApp?.resource;\n const error = loadedResourceForApp?.error;\n\n const fallback = opts.fallback ?? null;\n if (appForRender == null) {\n return <>{fallback}</>;\n }\n if (error != null) {\n const errorFallback = opts.errorFallback;\n if (errorFallback === undefined) return <>{fallback}</>;\n if (typeof errorFallback === \"function\") return <>{errorFallback(error)}</>;\n return <>{errorFallback}</>;\n }\n if (appResource == null) {\n return <>{opts.loadingFallback ?? fallback}</>;\n }\n\n return (\n <McpAppFrame\n app={appForRender}\n resource={appResource}\n input={getInput(part)}\n output={part.result}\n sandbox={opts.sandbox}\n handlers={bridgeHandlers}\n hostInfo={opts.hostInfo}\n hostContext={opts.hostContext}\n maxHeight={opts.maxHeight}\n />\n );\n}\n\n/**\n * Creates a tool-call renderer for MCP Apps embedded in assistant messages.\n *\n * Compose this into the `Tools` resource through its `mcpApp` option. When a\n * tool-call part carries `mcp.app` metadata for a `ui://` resource, the\n * renderer loads that resource from the configured host and displays it in a\n * sandboxed frame.\n */\nexport const McpAppRenderer = resource(\n (\n options: McpAppRendererOptions,\n ): { readonly render: ToolCallMessagePartComponent } => {\n const host = tapResource(options.host);\n\n const optionsRef = tapRef<McpAppRendererOptions>(options);\n optionsRef.current = options;\n\n const internalsRef = tapRef<{ host: McpAppsHost }>({ host });\n internalsRef.current = { host };\n\n const render = tapConst((): ToolCallMessagePartComponent => {\n const Render: ToolCallMessagePartComponent = (props) => (\n <InlineRenderer\n part={props}\n internalsRef={internalsRef}\n optionsRef={optionsRef}\n />\n );\n Render.displayName = \"McpAppRenderer\";\n return Render;\n }, []);\n\n return { render };\n },\n);\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-frame.js","names":[],"sources":["../../src/mcp-apps/app-frame.tsx"],"sourcesContent":["\"use client\";\n\nimport { type MutableRefObject, useEffect, useRef, useState } from \"react\";\nimport { type RenderedFrame, SafeContentFrame } from \"safe-content-frame\";\nimport { type McpAppBridge, createMcpAppBridge } from \"./bridge\";\nimport type {\n McpAppBridgeHandlers,\n McpAppFrameProps,\n McpAppHostContext,\n} from \"./types\";\n\nconst DEFAULT_PRODUCT = \"assistant-ui-mcp-app\";\nconst INIT_TIMEOUT_MS = 5000;\nconst DEFAULT_MAX_HEIGHT = 800;\n\nfunction useBridgeNotify<T>(\n value: T | undefined,\n bridgeRef: MutableRefObject<McpAppBridge | null>,\n widgetReadyRef: MutableRefObject<boolean>,\n pendingRef: MutableRefObject<T | undefined>,\n lastSentRef: MutableRefObject<T | undefined>,\n notify: (bridge: McpAppBridge, v: T) => void,\n) {\n // biome-ignore lint/correctness/useExhaustiveDependencies: refs and notify are stable; we re-run only when value changes.\n useEffect(() => {\n if (!bridgeRef.current) return;\n if (value === undefined) return;\n if (lastSentRef.current === value) return;\n if (!widgetReadyRef.current) {\n pendingRef.current = value;\n return;\n }\n notify(bridgeRef.current, value);\n lastSentRef.current = value;\n }, [value]);\n}\n\ntype LiveSnapshot = {\n handlers: McpAppBridgeHandlers | undefined;\n hostInfo: McpAppFrameProps[\"hostInfo\"];\n hostContext: McpAppFrameProps[\"hostContext\"];\n input: unknown;\n output: unknown;\n};\n\n// Proxy each per-call handler through liveRef so the bridge always dispatches\n// to the latest handler reference (e.g. inline callbacks closing over state).\n// Capability presence is snapshot at mount: a handler added later requires a\n// remount (keyed on resource URI) to expose the capability to the widget.\nfunction buildLiveHandlers(\n initial: McpAppBridgeHandlers | undefined,\n liveRef: { readonly current: LiveSnapshot },\n): McpAppBridgeHandlers {\n const live = () => liveRef.current.handlers;\n const has = <K extends keyof McpAppBridgeHandlers>(key: K) =>\n initial?.[key] !== undefined;\n const out: McpAppBridgeHandlers = {};\n if (has(\"allowedTools\")) {\n Object.defineProperty(out, \"allowedTools\", {\n get: () => live()?.allowedTools,\n enumerable: true,\n configurable: true,\n });\n }\n const liveCall = <K extends keyof McpAppBridgeHandlers>(\n key: K,\n ): NonNullable<McpAppBridgeHandlers[K]> =>\n ((p: unknown) => {\n const fn = live()?.[key] as ((p: unknown) => unknown) | undefined;\n if (!fn) {\n throw new Error(`${key} handler is no longer available`);\n }\n return fn(p);\n }) as NonNullable<McpAppBridgeHandlers[K]>;\n if (has(\"callTool\")) out.callTool = liveCall(\"callTool\");\n if (has(\"readResource\")) out.readResource = liveCall(\"readResource\");\n if (has(\"listResources\")) out.listResources = liveCall(\"listResources\");\n if (has(\"openLink\")) out.openLink = liveCall(\"openLink\");\n if (has(\"sendMessage\")) out.sendMessage = liveCall(\"sendMessage\");\n if (has(\"updateModelContext\"))\n out.updateModelContext = liveCall(\"updateModelContext\");\n if (has(\"requestDisplayMode\"))\n out.requestDisplayMode = liveCall(\"requestDisplayMode\");\n out.onSizeChange = (p) => live()?.onSizeChange?.(p);\n out.onInitialized = () => live()?.onInitialized?.();\n out.onRequestTeardown = (p) => live()?.onRequestTeardown?.(p);\n out.onLog = (p) => live()?.onLog?.(p);\n out.onError = (e) => live()?.onError?.(e);\n return out;\n}\n\nexport function McpAppFrame({\n app,\n resource,\n input,\n output,\n sandbox,\n handlers,\n hostInfo,\n hostContext,\n maxHeight = DEFAULT_MAX_HEIGHT,\n}: McpAppFrameProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [contentHeight, setContentHeight] = useState<number | undefined>(\n undefined,\n );\n const bridgeRef = useRef<McpAppBridge | null>(null);\n const lastSentInputRef = useRef<unknown>(undefined);\n const lastSentOutputRef = useRef<unknown>(undefined);\n const lastSentHostContextRef = useRef<McpAppHostContext | undefined>(\n undefined,\n );\n // Per MCP Apps spec, the host should defer notifications until the widget\n // signals readiness via `notifications/initialized`. Until then, we record\n // pending values and flush them on init.\n const widgetReadyRef = useRef(false);\n const pendingInputRef = useRef<unknown>(undefined);\n const pendingOutputRef = useRef<unknown>(undefined);\n const pendingHostContextRef = useRef<McpAppHostContext | undefined>(\n undefined,\n );\n\n const liveRef = useRef<LiveSnapshot>(null!);\n liveRef.current = {\n handlers,\n hostInfo,\n hostContext,\n input,\n output,\n };\n\n const resourceUri = resource.uri;\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: re-mounts only on resource URI; live values flow through liveRef\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n let cancelled = false;\n let initTimeoutId: ReturnType<typeof setTimeout> | null = null;\n let frame: RenderedFrame | null = null;\n const sb = sandbox;\n const html = resource.html;\n\n const scf = new SafeContentFrame(sb?.product ?? DEFAULT_PRODUCT, {\n ...(sb?.sandbox !== undefined && { sandbox: sb.sandbox }),\n ...(sb?.useShadowDom !== undefined && { useShadowDom: sb.useShadowDom }),\n ...(sb?.enableBrowserCaching !== undefined && {\n enableBrowserCaching: sb.enableBrowserCaching,\n }),\n ...(sb?.salt !== undefined && { salt: sb.salt }),\n });\n\n const renderOpts =\n sb?.unsafeDocumentWrite !== undefined\n ? { unsafeDocumentWrite: sb.unsafeDocumentWrite }\n : undefined;\n\n scf\n .renderHtml(html, container, renderOpts)\n .then((rendered) => {\n if (cancelled) {\n rendered.dispose();\n return;\n }\n frame = rendered;\n const current = liveRef.current;\n const liveHandlers = buildLiveHandlers(current.handlers, liveRef);\n const liveOnInitialized = liveHandlers.onInitialized;\n const flushPending = () => {\n if (widgetReadyRef.current) return;\n widgetReadyRef.current = true;\n const b = bridgeRef.current;\n if (!b) return;\n if (pendingInputRef.current !== undefined) {\n b.notifyToolInput(pendingInputRef.current);\n lastSentInputRef.current = pendingInputRef.current;\n pendingInputRef.current = undefined;\n }\n if (pendingOutputRef.current !== undefined) {\n b.notifyToolResult(pendingOutputRef.current);\n lastSentOutputRef.current = pendingOutputRef.current;\n pendingOutputRef.current = undefined;\n }\n if (pendingHostContextRef.current !== undefined) {\n b.notifyHostContextChanged(pendingHostContextRef.current);\n lastSentHostContextRef.current = pendingHostContextRef.current;\n pendingHostContextRef.current = undefined;\n }\n };\n const wrappedHandlers: McpAppBridgeHandlers = {\n ...liveHandlers,\n onInitialized: () => {\n if (initTimeoutId !== null) {\n clearTimeout(initTimeoutId);\n initTimeoutId = null;\n }\n flushPending();\n liveOnInitialized?.();\n },\n onSizeChange: (p) => {\n if (\n typeof p.height === \"number\" &&\n Number.isFinite(p.height) &&\n p.height > 0\n ) {\n setContentHeight(p.height);\n }\n liveHandlers.onSizeChange?.(p);\n },\n };\n // Safety net: if the widget never sends notifications/initialized\n // (broken or non-spec-compliant), flush the queue anyway so the host\n // doesn't appear hung.\n initTimeoutId = setTimeout(() => {\n initTimeoutId = null;\n flushPending();\n }, INIT_TIMEOUT_MS);\n bridgeRef.current = createMcpAppBridge({\n frame: rendered,\n handlers: wrappedHandlers,\n hostInfo: current.hostInfo,\n hostContext: current.hostContext,\n });\n\n if (current.input !== undefined)\n pendingInputRef.current = current.input;\n if (current.output !== undefined)\n pendingOutputRef.current = current.output;\n // hostContext is delivered inside the ui/initialize response; subsequent\n // changes flow through useBridgeNotify's pending path.\n })\n .catch((err) => {\n liveRef.current.handlers?.onError?.(\n err instanceof Error ? err : new Error(String(err)),\n );\n });\n\n return () => {\n cancelled = true;\n if (initTimeoutId !== null) {\n clearTimeout(initTimeoutId);\n initTimeoutId = null;\n }\n bridgeRef.current?.dispose();\n bridgeRef.current = null;\n frame?.dispose();\n frame = null;\n lastSentInputRef.current = undefined;\n lastSentOutputRef.current = undefined;\n lastSentHostContextRef.current = undefined;\n widgetReadyRef.current = false;\n pendingInputRef.current = undefined;\n pendingOutputRef.current = undefined;\n pendingHostContextRef.current = undefined;\n setContentHeight(undefined);\n };\n }, [resourceUri]);\n\n useBridgeNotify(\n input,\n bridgeRef,\n widgetReadyRef,\n pendingInputRef,\n lastSentInputRef,\n (b, v) => b.notifyToolInput(v),\n );\n useBridgeNotify(\n output,\n bridgeRef,\n widgetReadyRef,\n pendingOutputRef,\n lastSentOutputRef,\n (b, v) => b.notifyToolResult(v),\n );\n useBridgeNotify(\n hostContext,\n bridgeRef,\n widgetReadyRef,\n pendingHostContextRef,\n lastSentHostContextRef,\n (b, v) => b.notifyHostContextChanged(v),\n );\n\n const resolvedHeight =\n contentHeight != null ? Math.min(contentHeight, maxHeight) : undefined;\n const mergedStyle =\n resolvedHeight != null\n ? { ...sandbox?.style, height: resolvedHeight }\n : sandbox?.style;\n\n return (\n <div\n ref={containerRef}\n className={sandbox?.className}\n style={mergedStyle}\n data-mcp-app-resource={app.resourceUri}\n data-mcp-app-prefers-border={\n resource.meta?.prefersBorder ? \"\" : undefined\n }\n />\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"app-frame.js","names":[],"sources":["../../src/mcp-apps/app-frame.tsx"],"sourcesContent":["\"use client\";\n\nimport { type MutableRefObject, useEffect, useRef, useState } from \"react\";\nimport { type RenderedFrame, SafeContentFrame } from \"safe-content-frame\";\nimport { type McpAppBridge, createMcpAppBridge } from \"./bridge\";\nimport type {\n McpAppBridgeHandlers,\n McpAppFrameProps,\n McpAppHostContext,\n} from \"./types\";\n\nconst DEFAULT_PRODUCT = \"assistant-ui-mcp-app\";\nconst INIT_TIMEOUT_MS = 5000;\nconst DEFAULT_MAX_HEIGHT = 800;\n\nfunction useBridgeNotify<T>(\n value: T | undefined,\n bridgeRef: MutableRefObject<McpAppBridge | null>,\n widgetReadyRef: MutableRefObject<boolean>,\n pendingRef: MutableRefObject<T | undefined>,\n lastSentRef: MutableRefObject<T | undefined>,\n notify: (bridge: McpAppBridge, v: T) => void,\n) {\n useEffect(() => {\n if (!bridgeRef.current) return;\n if (value === undefined) return;\n if (lastSentRef.current === value) return;\n if (!widgetReadyRef.current) {\n pendingRef.current = value;\n return;\n }\n notify(bridgeRef.current, value);\n lastSentRef.current = value;\n // oxlint-disable-next-line tap-hooks/exhaustive-deps -- refs are stable; notify is assumed stable; re-run only when value changes\n }, [value]);\n}\n\ntype LiveSnapshot = {\n handlers: McpAppBridgeHandlers | undefined;\n hostInfo: McpAppFrameProps[\"hostInfo\"];\n hostContext: McpAppFrameProps[\"hostContext\"];\n input: unknown;\n output: unknown;\n};\n\n// Proxy each per-call handler through liveRef so the bridge always dispatches\n// to the latest handler reference (e.g. inline callbacks closing over state).\n// Capability presence is snapshot at mount: a handler added later requires a\n// remount (keyed on resource URI) to expose the capability to the widget.\nfunction buildLiveHandlers(\n initial: McpAppBridgeHandlers | undefined,\n liveRef: { readonly current: LiveSnapshot },\n): McpAppBridgeHandlers {\n const live = () => liveRef.current.handlers;\n const has = <K extends keyof McpAppBridgeHandlers>(key: K) =>\n initial?.[key] !== undefined;\n const out: McpAppBridgeHandlers = {};\n if (has(\"allowedTools\")) {\n Object.defineProperty(out, \"allowedTools\", {\n get: () => live()?.allowedTools,\n enumerable: true,\n configurable: true,\n });\n }\n const liveCall = <K extends keyof McpAppBridgeHandlers>(\n key: K,\n ): NonNullable<McpAppBridgeHandlers[K]> =>\n ((p: unknown) => {\n const fn = live()?.[key] as ((p: unknown) => unknown) | undefined;\n if (!fn) {\n throw new Error(`${key} handler is no longer available`);\n }\n return fn(p);\n }) as NonNullable<McpAppBridgeHandlers[K]>;\n if (has(\"callTool\")) out.callTool = liveCall(\"callTool\");\n if (has(\"readResource\")) out.readResource = liveCall(\"readResource\");\n if (has(\"listResources\")) out.listResources = liveCall(\"listResources\");\n if (has(\"openLink\")) out.openLink = liveCall(\"openLink\");\n if (has(\"sendMessage\")) out.sendMessage = liveCall(\"sendMessage\");\n if (has(\"updateModelContext\"))\n out.updateModelContext = liveCall(\"updateModelContext\");\n if (has(\"requestDisplayMode\"))\n out.requestDisplayMode = liveCall(\"requestDisplayMode\");\n out.onSizeChange = (p) => live()?.onSizeChange?.(p);\n out.onInitialized = () => live()?.onInitialized?.();\n out.onRequestTeardown = (p) => live()?.onRequestTeardown?.(p);\n out.onLog = (p) => live()?.onLog?.(p);\n out.onError = (e) => live()?.onError?.(e);\n return out;\n}\n\nexport function McpAppFrame({\n app,\n resource,\n input,\n output,\n sandbox,\n handlers,\n hostInfo,\n hostContext,\n maxHeight = DEFAULT_MAX_HEIGHT,\n}: McpAppFrameProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [contentHeight, setContentHeight] = useState<number | undefined>(\n undefined,\n );\n const bridgeRef = useRef<McpAppBridge | null>(null);\n const lastSentInputRef = useRef<unknown>(undefined);\n const lastSentOutputRef = useRef<unknown>(undefined);\n const lastSentHostContextRef = useRef<McpAppHostContext | undefined>(\n undefined,\n );\n // Per MCP Apps spec, the host should defer notifications until the widget\n // signals readiness via `notifications/initialized`. Until then, we record\n // pending values and flush them on init.\n const widgetReadyRef = useRef(false);\n const pendingInputRef = useRef<unknown>(undefined);\n const pendingOutputRef = useRef<unknown>(undefined);\n const pendingHostContextRef = useRef<McpAppHostContext | undefined>(\n undefined,\n );\n\n const liveRef = useRef<LiveSnapshot>(null!);\n liveRef.current = {\n handlers,\n hostInfo,\n hostContext,\n input,\n output,\n };\n\n const resourceUri = resource.uri;\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n let cancelled = false;\n let initTimeoutId: ReturnType<typeof setTimeout> | null = null;\n let frame: RenderedFrame | null = null;\n const sb = sandbox;\n const html = resource.html;\n\n const scf = new SafeContentFrame(sb?.product ?? DEFAULT_PRODUCT, {\n ...(sb?.sandbox !== undefined && { sandbox: sb.sandbox }),\n ...(sb?.useShadowDom !== undefined && { useShadowDom: sb.useShadowDom }),\n ...(sb?.enableBrowserCaching !== undefined && {\n enableBrowserCaching: sb.enableBrowserCaching,\n }),\n ...(sb?.salt !== undefined && { salt: sb.salt }),\n });\n\n const renderOpts =\n sb?.unsafeDocumentWrite !== undefined\n ? { unsafeDocumentWrite: sb.unsafeDocumentWrite }\n : undefined;\n\n scf\n .renderHtml(html, container, renderOpts)\n .then((rendered) => {\n if (cancelled) {\n rendered.dispose();\n return;\n }\n frame = rendered;\n const current = liveRef.current;\n const liveHandlers = buildLiveHandlers(current.handlers, liveRef);\n const liveOnInitialized = liveHandlers.onInitialized;\n const flushPending = () => {\n if (widgetReadyRef.current) return;\n widgetReadyRef.current = true;\n const b = bridgeRef.current;\n if (!b) return;\n if (pendingInputRef.current !== undefined) {\n b.notifyToolInput(pendingInputRef.current);\n lastSentInputRef.current = pendingInputRef.current;\n pendingInputRef.current = undefined;\n }\n if (pendingOutputRef.current !== undefined) {\n b.notifyToolResult(pendingOutputRef.current);\n lastSentOutputRef.current = pendingOutputRef.current;\n pendingOutputRef.current = undefined;\n }\n if (pendingHostContextRef.current !== undefined) {\n b.notifyHostContextChanged(pendingHostContextRef.current);\n lastSentHostContextRef.current = pendingHostContextRef.current;\n pendingHostContextRef.current = undefined;\n }\n };\n const wrappedHandlers: McpAppBridgeHandlers = {\n ...liveHandlers,\n onInitialized: () => {\n if (initTimeoutId !== null) {\n clearTimeout(initTimeoutId);\n initTimeoutId = null;\n }\n flushPending();\n liveOnInitialized?.();\n },\n onSizeChange: (p) => {\n if (\n typeof p.height === \"number\" &&\n Number.isFinite(p.height) &&\n p.height > 0\n ) {\n setContentHeight(p.height);\n }\n liveHandlers.onSizeChange?.(p);\n },\n };\n // Safety net: if the widget never sends notifications/initialized\n // (broken or non-spec-compliant), flush the queue anyway so the host\n // doesn't appear hung.\n initTimeoutId = setTimeout(() => {\n initTimeoutId = null;\n flushPending();\n }, INIT_TIMEOUT_MS);\n bridgeRef.current = createMcpAppBridge({\n frame: rendered,\n handlers: wrappedHandlers,\n hostInfo: current.hostInfo,\n hostContext: current.hostContext,\n });\n\n if (current.input !== undefined)\n pendingInputRef.current = current.input;\n if (current.output !== undefined)\n pendingOutputRef.current = current.output;\n // hostContext is delivered inside the ui/initialize response; subsequent\n // changes flow through useBridgeNotify's pending path.\n })\n .catch((err) => {\n liveRef.current.handlers?.onError?.(\n err instanceof Error ? err : new Error(String(err)),\n );\n });\n\n return () => {\n cancelled = true;\n if (initTimeoutId !== null) {\n clearTimeout(initTimeoutId);\n initTimeoutId = null;\n }\n bridgeRef.current?.dispose();\n bridgeRef.current = null;\n frame?.dispose();\n frame = null;\n lastSentInputRef.current = undefined;\n lastSentOutputRef.current = undefined;\n lastSentHostContextRef.current = undefined;\n widgetReadyRef.current = false;\n pendingInputRef.current = undefined;\n pendingOutputRef.current = undefined;\n pendingHostContextRef.current = undefined;\n setContentHeight(undefined);\n };\n // oxlint-disable-next-line tap-hooks/exhaustive-deps -- re-mount only on resource URI change; live values flow through liveRef\n }, [resourceUri]);\n\n useBridgeNotify(\n input,\n bridgeRef,\n widgetReadyRef,\n pendingInputRef,\n lastSentInputRef,\n (b, v) => b.notifyToolInput(v),\n );\n useBridgeNotify(\n output,\n bridgeRef,\n widgetReadyRef,\n pendingOutputRef,\n lastSentOutputRef,\n (b, v) => b.notifyToolResult(v),\n );\n useBridgeNotify(\n hostContext,\n bridgeRef,\n widgetReadyRef,\n pendingHostContextRef,\n lastSentHostContextRef,\n (b, v) => b.notifyHostContextChanged(v),\n );\n\n const resolvedHeight =\n contentHeight != null ? Math.min(contentHeight, maxHeight) : undefined;\n const mergedStyle =\n resolvedHeight != null\n ? { ...sandbox?.style, height: resolvedHeight }\n : sandbox?.style;\n\n return (\n <div\n ref={containerRef}\n className={sandbox?.className}\n style={mergedStyle}\n data-mcp-app-resource={app.resourceUri}\n data-mcp-app-prefers-border={\n resource.meta?.prefersBorder ? \"\" : undefined\n }\n />\n );\n}\n"],"mappings":"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"}
|
|
@@ -14,7 +14,11 @@ const useAssistantModalOpenState = ({ defaultOpen = false, unstable_openOnRunSta
|
|
|
14
14
|
return aui.on("thread.runStart", () => {
|
|
15
15
|
setOpen(true);
|
|
16
16
|
});
|
|
17
|
-
}, [
|
|
17
|
+
}, [
|
|
18
|
+
unstable_openOnRunStart,
|
|
19
|
+
aui,
|
|
20
|
+
setOpen
|
|
21
|
+
]);
|
|
18
22
|
return state;
|
|
19
23
|
};
|
|
20
24
|
const AssistantModalPrimitiveRoot = ({ __scopeAssistantModal, defaultOpen, unstable_openOnRunStart, open, onOpenChange, ...rest }) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantModalRoot.js","names":["PopoverPrimitive"],"sources":["../../../src/primitives/assistantModal/AssistantModalRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport { type FC, useEffect, useState } from \"react\";\nimport { Popover as PopoverPrimitive } from \"radix-ui\";\nimport { type ScopedProps, usePopoverScope } from \"./scope\";\nimport { useAui } from \"@assistant-ui/store\";\n\nexport namespace AssistantModalPrimitiveRoot {\n export type Props = PopoverPrimitive.PopoverProps & {\n unstable_openOnRunStart?: boolean | undefined;\n };\n}\n\nconst useAssistantModalOpenState = ({\n defaultOpen = false,\n unstable_openOnRunStart = true,\n}: {\n defaultOpen?: boolean | undefined;\n unstable_openOnRunStart?: boolean | undefined;\n}) => {\n const state = useState(defaultOpen);\n\n const [, setOpen] = state;\n const aui = useAui();\n useEffect(() => {\n if (!unstable_openOnRunStart) return undefined;\n\n return aui.on(\"thread.runStart\", () => {\n setOpen(true);\n });\n }, [unstable_openOnRunStart, aui]);\n\n return state;\n};\n\nexport const AssistantModalPrimitiveRoot: FC<\n AssistantModalPrimitiveRoot.Props\n> = ({\n __scopeAssistantModal,\n defaultOpen,\n unstable_openOnRunStart,\n open,\n onOpenChange,\n ...rest\n}: ScopedProps<AssistantModalPrimitiveRoot.Props>) => {\n const scope = usePopoverScope(__scopeAssistantModal);\n\n const [modalOpen, setOpen] = useAssistantModalOpenState({\n defaultOpen,\n unstable_openOnRunStart,\n });\n\n const openChangeHandler = (open: boolean) => {\n onOpenChange?.(open);\n setOpen(open);\n };\n\n return (\n <PopoverPrimitive.Root\n {...scope}\n open={open === undefined ? modalOpen : open}\n onOpenChange={openChangeHandler}\n {...rest}\n />\n );\n};\n\nAssistantModalPrimitiveRoot.displayName = \"AssistantModalPrimitive.Root\";\n"],"mappings":";;;;;;;AAaA,MAAM,8BAA8B,EAClC,cAAc,OACd,0BAA0B,WAItB;CACJ,MAAM,QAAQ,SAAS,WAAW;CAElC,MAAM,GAAG,WAAW;CACpB,MAAM,MAAM,OAAO;CACnB,gBAAgB;EACd,IAAI,CAAC,yBAAyB,OAAO,KAAA;EAErC,OAAO,IAAI,GAAG,yBAAyB;GACrC,QAAQ,IAAI;EACd,CAAC;CACH,GAAG,CAAC
|
|
1
|
+
{"version":3,"file":"AssistantModalRoot.js","names":["PopoverPrimitive"],"sources":["../../../src/primitives/assistantModal/AssistantModalRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport { type FC, useEffect, useState } from \"react\";\nimport { Popover as PopoverPrimitive } from \"radix-ui\";\nimport { type ScopedProps, usePopoverScope } from \"./scope\";\nimport { useAui } from \"@assistant-ui/store\";\n\nexport namespace AssistantModalPrimitiveRoot {\n export type Props = PopoverPrimitive.PopoverProps & {\n unstable_openOnRunStart?: boolean | undefined;\n };\n}\n\nconst useAssistantModalOpenState = ({\n defaultOpen = false,\n unstable_openOnRunStart = true,\n}: {\n defaultOpen?: boolean | undefined;\n unstable_openOnRunStart?: boolean | undefined;\n}) => {\n const state = useState(defaultOpen);\n\n const [, setOpen] = state;\n const aui = useAui();\n useEffect(() => {\n if (!unstable_openOnRunStart) return undefined;\n\n return aui.on(\"thread.runStart\", () => {\n setOpen(true);\n });\n }, [unstable_openOnRunStart, aui, setOpen]);\n\n return state;\n};\n\nexport const AssistantModalPrimitiveRoot: FC<\n AssistantModalPrimitiveRoot.Props\n> = ({\n __scopeAssistantModal,\n defaultOpen,\n unstable_openOnRunStart,\n open,\n onOpenChange,\n ...rest\n}: ScopedProps<AssistantModalPrimitiveRoot.Props>) => {\n const scope = usePopoverScope(__scopeAssistantModal);\n\n const [modalOpen, setOpen] = useAssistantModalOpenState({\n defaultOpen,\n unstable_openOnRunStart,\n });\n\n const openChangeHandler = (open: boolean) => {\n onOpenChange?.(open);\n setOpen(open);\n };\n\n return (\n <PopoverPrimitive.Root\n {...scope}\n open={open === undefined ? modalOpen : open}\n onOpenChange={openChangeHandler}\n {...rest}\n />\n );\n};\n\nAssistantModalPrimitiveRoot.displayName = \"AssistantModalPrimitive.Root\";\n"],"mappings":";;;;;;;AAaA,MAAM,8BAA8B,EAClC,cAAc,OACd,0BAA0B,WAItB;CACJ,MAAM,QAAQ,SAAS,WAAW;CAElC,MAAM,GAAG,WAAW;CACpB,MAAM,MAAM,OAAO;CACnB,gBAAgB;EACd,IAAI,CAAC,yBAAyB,OAAO,KAAA;EAErC,OAAO,IAAI,GAAG,yBAAyB;GACrC,QAAQ,IAAI;EACd,CAAC;CACH,GAAG;EAAC;EAAyB;EAAK;CAAO,CAAC;CAE1C,OAAO;AACT;AAEA,MAAa,+BAER,EACH,uBACA,aACA,yBACA,MACA,cACA,GAAG,WACiD;CACpD,MAAM,QAAQ,gBAAgB,qBAAqB;CAEnD,MAAM,CAAC,WAAW,WAAW,2BAA2B;EACtD;EACA;CACF,CAAC;CAED,MAAM,qBAAqB,SAAkB;EAC3C,eAAe,IAAI;EACnB,QAAQ,IAAI;CACd;CAEA,OACE,oBAACA,QAAiB,MAAlB;EACE,GAAI;EACJ,MAAM,SAAS,KAAA,IAAY,YAAY;EACvC,cAAc;EACd,GAAI;CACL,CAAA;AAEL;AAEA,4BAA4B,cAAc"}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
//#region src/utils/useToolArgsFieldStatus.d.ts
|
|
2
2
|
declare const useToolArgsFieldStatus: (fieldPath: (string | number)[]) => {
|
|
3
|
-
type: string;
|
|
4
|
-
} | {
|
|
5
3
|
readonly type: "running";
|
|
6
4
|
} | {
|
|
7
5
|
readonly type: "complete";
|
|
@@ -9,6 +7,8 @@ declare const useToolArgsFieldStatus: (fieldPath: (string | number)[]) => {
|
|
|
9
7
|
readonly type: "incomplete";
|
|
10
8
|
readonly reason: "cancelled" | "length" | "content-filter" | "other" | "error";
|
|
11
9
|
readonly error?: unknown;
|
|
10
|
+
} | {
|
|
11
|
+
type: string;
|
|
12
12
|
};
|
|
13
13
|
//#endregion
|
|
14
14
|
export { useToolArgsFieldStatus };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useToolArgsFieldStatus.d.ts","names":[],"sources":["../../src/utils/useToolArgsFieldStatus.ts"],"mappings":";cAKa,sBAAA,GAA0B,SAAA"}
|
|
1
|
+
{"version":3,"file":"useToolArgsFieldStatus.d.ts","names":[],"sources":["../../src/utils/useToolArgsFieldStatus.ts"],"mappings":";cAKa,sBAAA,GAA0B,SAAA;EAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.11",
|
|
4
4
|
"description": "Open-source TypeScript/React library for building production-grade AI chat experiences",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"radix-ui",
|
|
@@ -48,9 +48,9 @@
|
|
|
48
48
|
],
|
|
49
49
|
"sideEffects": false,
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@assistant-ui/core": "^0.2.
|
|
51
|
+
"@assistant-ui/core": "^0.2.7",
|
|
52
52
|
"@assistant-ui/store": "^0.2.12",
|
|
53
|
-
"@assistant-ui/tap": "^0.5.
|
|
53
|
+
"@assistant-ui/tap": "^0.5.13",
|
|
54
54
|
"@radix-ui/primitive": "^1.1.3",
|
|
55
55
|
"@radix-ui/react-compose-refs": "^1.1.2",
|
|
56
56
|
"@radix-ui/react-context": "^1.1.3",
|
package/src/internal.ts
CHANGED
|
@@ -23,10 +23,7 @@ export type {
|
|
|
23
23
|
|
|
24
24
|
// React-specific (stay in react)
|
|
25
25
|
export { splitLocalRuntimeOptions } from "./legacy-runtime/runtime-cores/local/LocalRuntimeOptions";
|
|
26
|
-
export {
|
|
27
|
-
useToolInvocations,
|
|
28
|
-
type ToolExecutionStatus,
|
|
29
|
-
} from "@assistant-ui/core/react";
|
|
26
|
+
export type { ToolExecutionStatus } from "@assistant-ui/core";
|
|
30
27
|
|
|
31
28
|
export { useSmooth } from "./utils/smooth/useSmooth";
|
|
32
29
|
export {
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
export type {
|
|
2
|
-
ThreadListState,
|
|
3
|
-
ThreadListRuntime,
|
|
4
|
-
} from "@assistant-ui/core";
|
|
1
|
+
export type { ThreadListState, ThreadListRuntime } from "@assistant-ui/core";
|
|
5
2
|
|
|
6
3
|
export type { ThreadListRuntimeCoreBinding } from "@assistant-ui/core/internal";
|
|
7
4
|
export { ThreadListRuntimeImpl } from "@assistant-ui/core/internal";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ToolModelContentPart } from "assistant-stream";
|
|
1
2
|
import type { ThreadMessage } from "@assistant-ui/core";
|
|
2
3
|
import type { ReadonlyJSONValue } from "assistant-stream/utils";
|
|
3
4
|
import type {
|
|
@@ -7,7 +8,7 @@ import type {
|
|
|
7
8
|
LanguageModelConfig,
|
|
8
9
|
} from "@assistant-ui/core";
|
|
9
10
|
import type { UserCommands } from "../../../augmentations";
|
|
10
|
-
import type { ToolExecutionStatus } from "
|
|
11
|
+
import type { ToolExecutionStatus } from "@assistant-ui/core";
|
|
11
12
|
|
|
12
13
|
// Message part types
|
|
13
14
|
export type TextPart = {
|
|
@@ -47,6 +48,7 @@ export type AddToolResultCommand = {
|
|
|
47
48
|
readonly result: ReadonlyJSONValue;
|
|
48
49
|
readonly isError: boolean;
|
|
49
50
|
readonly artifact?: ReadonlyJSONValue;
|
|
51
|
+
readonly modelContent?: readonly ToolModelContentPart[];
|
|
50
52
|
};
|
|
51
53
|
|
|
52
54
|
export type AssistantTransportCommand =
|
package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
import { useExternalStoreRuntime } from "../external-store/useExternalStoreRuntime";
|
|
10
10
|
import type { AssistantRuntime } from "../../runtime/AssistantRuntime";
|
|
11
11
|
import type { AddToolResultOptions } from "@assistant-ui/core";
|
|
12
|
-
import {
|
|
12
|
+
import { useMemo, useRef, useState } from "react";
|
|
13
13
|
import {
|
|
14
14
|
AssistantMessageAccumulator,
|
|
15
15
|
DataStreamDecoder,
|
|
@@ -29,10 +29,7 @@ import type {
|
|
|
29
29
|
import { useCommandQueue } from "./commandQueue";
|
|
30
30
|
import { useRunManager } from "./runManager";
|
|
31
31
|
import { useConvertedState } from "./useConvertedState";
|
|
32
|
-
import {
|
|
33
|
-
type ToolExecutionStatus,
|
|
34
|
-
useToolInvocations,
|
|
35
|
-
} from "./useToolInvocations";
|
|
32
|
+
import type { ToolExecutionStatus } from "@assistant-ui/core";
|
|
36
33
|
import { createRequestHeaders } from "@assistant-ui/core";
|
|
37
34
|
import { useRemoteThreadListRuntime } from "../remote-thread-list/useRemoteThreadListRuntime";
|
|
38
35
|
import { InMemoryThreadListAdapter } from "@assistant-ui/core";
|
|
@@ -303,6 +300,8 @@ const useAssistantTransportThreadRuntime = <T>(
|
|
|
303
300
|
state: converted.state,
|
|
304
301
|
isRunning: converted.isRunning,
|
|
305
302
|
adapters: options.adapters,
|
|
303
|
+
unstable_enableToolInvocations: true,
|
|
304
|
+
setToolStatuses,
|
|
306
305
|
extras: {
|
|
307
306
|
[symbolAssistantTransportExtras]: true,
|
|
308
307
|
sendCommand: (command: AssistantTransportCommand) => {
|
|
@@ -324,7 +323,6 @@ const useAssistantTransportThreadRuntime = <T>(
|
|
|
324
323
|
}),
|
|
325
324
|
onCancel: async () => {
|
|
326
325
|
runManager.cancel();
|
|
327
|
-
await toolInvocations.abort();
|
|
328
326
|
},
|
|
329
327
|
onResume: async () => {
|
|
330
328
|
if (!options.resumeApi)
|
|
@@ -343,25 +341,19 @@ const useAssistantTransportThreadRuntime = <T>(
|
|
|
343
341
|
toolName: toolOptions.toolName,
|
|
344
342
|
isError: toolOptions.isError,
|
|
345
343
|
...(toolOptions.artifact && { artifact: toolOptions.artifact }),
|
|
344
|
+
...(toolOptions.modelContent !== undefined && {
|
|
345
|
+
modelContent: toolOptions.modelContent,
|
|
346
|
+
}),
|
|
346
347
|
};
|
|
347
348
|
|
|
348
349
|
commandQueue.enqueue(command);
|
|
349
350
|
},
|
|
350
351
|
onLoadExternalState: async (state) => {
|
|
351
352
|
agentStateRef.current = state as T;
|
|
352
|
-
toolInvocations.reset();
|
|
353
353
|
rerender((prev) => prev + 1);
|
|
354
354
|
},
|
|
355
355
|
});
|
|
356
356
|
|
|
357
|
-
// biome-ignore lint/correctness/useHookAtTopLevel: intentional conditional/nested hook usage
|
|
358
|
-
const toolInvocations = useToolInvocations({
|
|
359
|
-
state: converted,
|
|
360
|
-
getTools: () => runtime.thread.getModelContext().tools,
|
|
361
|
-
onResult: commandQueue.enqueue,
|
|
362
|
-
setToolStatuses,
|
|
363
|
-
});
|
|
364
|
-
|
|
365
357
|
return runtime;
|
|
366
358
|
};
|
|
367
359
|
|
|
@@ -4,7 +4,7 @@ import type {
|
|
|
4
4
|
AssistantTransportState,
|
|
5
5
|
AssistantTransportStateConverter,
|
|
6
6
|
} from "./types";
|
|
7
|
-
import type { ToolExecutionStatus } from "
|
|
7
|
+
import type { ToolExecutionStatus } from "@assistant-ui/core";
|
|
8
8
|
|
|
9
9
|
export function useConvertedState<T>(
|
|
10
10
|
converter: AssistantTransportStateConverter<T>,
|
|
@@ -113,7 +113,6 @@ function InlineRenderer({
|
|
|
113
113
|
const [loadedResource, setLoadedResource] = useState<LoadedResourceState>();
|
|
114
114
|
|
|
115
115
|
const resourceUri = appForRender?.resourceUri;
|
|
116
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: re-fetches only when URI changes; mcp.app object identity is unstable across renders
|
|
117
116
|
useEffect(() => {
|
|
118
117
|
if (appForRender == null || resourceUri == null) return;
|
|
119
118
|
let cancelled = false;
|
|
@@ -137,6 +136,7 @@ function InlineRenderer({
|
|
|
137
136
|
return () => {
|
|
138
137
|
cancelled = true;
|
|
139
138
|
};
|
|
139
|
+
// oxlint-disable-next-line tap-hooks/exhaustive-deps -- re-fetch only when URI changes; appForRender identity is unstable and internalsRef is a stable ref
|
|
140
140
|
}, [resourceUri]);
|
|
141
141
|
|
|
142
142
|
const bridgeHandlers = useMemo<McpAppBridgeHandlers>(
|