@assistant-ui/core 0.3.2 → 0.3.3

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.
Files changed (56) hide show
  1. package/dist/index.d.ts +4 -4
  2. package/dist/index.js.map +1 -1
  3. package/dist/internal.d.ts +6 -6
  4. package/dist/internal.js +5 -5
  5. package/dist/react/primitive-hooks/useActionBarCopy.js +51 -24
  6. package/dist/react/primitive-hooks/useActionBarCopy.js.map +1 -1
  7. package/dist/react/primitives/message/MessageGroupedParts.d.ts +2 -1
  8. package/dist/react/primitives/message/MessageGroupedParts.d.ts.map +1 -1
  9. package/dist/react/primitives/message/MessageGroupedParts.js +2 -2
  10. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  11. package/dist/react/runtimes/cloud/auiV0.d.ts +24 -2
  12. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  13. package/dist/react/runtimes/cloud/auiV0.js +24 -5
  14. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  15. package/dist/runtime/api/message-runtime.d.ts.map +1 -1
  16. package/dist/runtime/api/message-runtime.js +5 -1
  17. package/dist/runtime/api/message-runtime.js.map +1 -1
  18. package/dist/runtime/internal.d.ts +4 -4
  19. package/dist/runtime/internal.js +4 -4
  20. package/dist/store/clients/chain-of-thought-client.d.ts.map +1 -1
  21. package/dist/store/clients/chain-of-thought-client.js +29 -23
  22. package/dist/store/clients/chain-of-thought-client.js.map +1 -1
  23. package/dist/store/clients/thread-message-client.d.ts.map +1 -1
  24. package/dist/store/clients/thread-message-client.js +101 -89
  25. package/dist/store/clients/thread-message-client.js.map +1 -1
  26. package/dist/types/index.d.ts +2 -2
  27. package/dist/types/message.d.ts +11 -1
  28. package/dist/types/message.d.ts.map +1 -1
  29. package/dist/types/message.js.map +1 -1
  30. package/dist/utils/getGroupStatus.d.ts +9 -0
  31. package/dist/utils/getGroupStatus.d.ts.map +1 -0
  32. package/dist/utils/getGroupStatus.js +15 -0
  33. package/dist/utils/getGroupStatus.js.map +1 -0
  34. package/dist/utils/normalizePartStatus.d.ts +8 -0
  35. package/dist/utils/normalizePartStatus.d.ts.map +1 -0
  36. package/dist/utils/normalizePartStatus.js +39 -0
  37. package/dist/utils/normalizePartStatus.js.map +1 -0
  38. package/package.json +2 -2
  39. package/src/index.ts +1 -0
  40. package/src/internal.ts +1 -0
  41. package/src/react/primitive-hooks/useActionBarCopy.test.ts +109 -14
  42. package/src/react/primitive-hooks/useActionBarCopy.ts +27 -2
  43. package/src/react/primitives/message/MessageGroupedParts.tsx +4 -4
  44. package/src/react/runtimes/cloud/auiV0.ts +51 -12
  45. package/src/runtime/api/message-runtime.test.ts +140 -0
  46. package/src/runtime/api/message-runtime.ts +10 -5
  47. package/src/store/clients/chain-of-thought-client.ts +2 -7
  48. package/src/store/clients/thread-message-client.test.ts +84 -0
  49. package/src/store/clients/thread-message-client.ts +14 -4
  50. package/src/tests/auiV0Encode.test.ts +93 -0
  51. package/src/types/index.ts +1 -0
  52. package/src/types/message.ts +19 -0
  53. package/src/utils/getGroupStatus.test.ts +36 -0
  54. package/src/utils/getGroupStatus.ts +31 -0
  55. package/src/utils/normalizePartStatus.test.ts +59 -0
  56. package/src/utils/normalizePartStatus.ts +55 -0
@@ -1 +1 @@
1
- {"version":3,"file":"thread-message-client.js","names":["ThreadAssistantMessagePart","ThreadUserMessagePart","ThreadMessage","Attachment","useMemo","useState","useResource","resource","withKey","ClientOutput","useClientLookup","MessageState","PartState","NoOpComposerClient","getThreadMessageText","useThreadMessagePartClient","t0","$","_c","part","t1","Symbol","for","type","t2","status","state","t3","getState","addToolResult","_temp","resumeToolCall","_temp2","respondToToolApproval","_temp3","ThreadMessagePartClient","useThreadMessageAttachmentClient","attachment","remove","Error","ThreadMessageAttachmentClient","ThreadMessageClientProps","message","index","isLast","branchNumber","branchCount","useThreadMessageClient","undefined","isCopiedState","setIsCopied","isHoveringState","setIsHovering","t4","content","map","_temp4","parts","t5","attachments","t6","_temp5","t7","composer","t8","composerState","t9","parentId","speech","isCopied","isHovering","t10","t11","t12","selector","get","key","toolCallId","t13","selector_0","id","t14","t15","delete","_temp6","reload","_temp7","speak","_temp8","stopSpeaking","_temp9","submitFeedback","_temp0","switchToBranch","_temp1","getCopyText","ThreadMessageClient","idx"],"sources":["../../../src/store/clients/thread-message-client.ts"],"sourcesContent":["import type {\n ThreadAssistantMessagePart,\n ThreadUserMessagePart,\n ThreadMessage,\n} from \"../../types/message\";\nimport type { Attachment } from \"../../types/attachment\";\nimport { useMemo, useState } from \"react\";\nimport { useResource, resource, withKey } from \"@assistant-ui/tap\";\nimport type { ClientOutput } from \"@assistant-ui/store\";\nimport { useClientLookup } from \"@assistant-ui/store\";\nimport type { MessageState } from \"../scopes/message\";\nimport type { PartState } from \"../scopes/part\";\nimport { NoOpComposerClient } from \"./no-op-composer-client\";\nimport { getThreadMessageText } from \"../../utils/text\";\n\nconst useThreadMessagePartClient = ({\n part,\n}: {\n part: ThreadAssistantMessagePart | ThreadUserMessagePart;\n}): ClientOutput<\"part\"> => {\n const state = useMemo<PartState>(() => {\n return {\n ...part,\n status: { type: \"complete\" },\n };\n }, [part]);\n\n return {\n getState: () => state,\n addToolResult: () => {\n throw new Error(\"Not supported\");\n },\n resumeToolCall: () => {\n throw new Error(\"Not supported\");\n },\n respondToToolApproval: () => {\n throw new Error(\"Not supported\");\n },\n };\n};\n\nconst ThreadMessagePartClient = resource(useThreadMessagePartClient);\n\nconst useThreadMessageAttachmentClient = ({\n attachment,\n}: {\n attachment: Attachment;\n}): ClientOutput<\"attachment\"> => {\n return {\n getState: () => attachment,\n remove: () => {\n throw new Error(\"Not supported\");\n },\n };\n};\n\nconst ThreadMessageAttachmentClient = resource(\n useThreadMessageAttachmentClient,\n);\nexport type ThreadMessageClientProps = {\n message: ThreadMessage;\n index: number;\n isLast?: boolean;\n branchNumber?: number;\n branchCount?: number;\n};\n\nconst useThreadMessageClient = ({\n message,\n index,\n isLast = true,\n branchNumber = 1,\n branchCount = 1,\n}: ThreadMessageClientProps): ClientOutput<\"message\"> => {\n const [isCopiedState, setIsCopied] = useState(false);\n const [isHoveringState, setIsHovering] = useState(false);\n\n const parts = useClientLookup(\n message.content.map((part, idx) =>\n withKey(\n \"toolCallId\" in part && part.toolCallId != null\n ? `toolCallId-${part.toolCallId}`\n : `index-${idx}`,\n ThreadMessagePartClient({ part }),\n [part],\n ),\n ),\n );\n\n const attachments = useClientLookup(\n (message.attachments ?? []).map((attachment) =>\n withKey(attachment.id, ThreadMessageAttachmentClient({ attachment }), [\n attachment,\n ]),\n ),\n );\n\n const composer = useResource(NoOpComposerClient({ type: \"edit\" }));\n const composerState = composer.getState();\n\n const state = useMemo<MessageState>(() => {\n return {\n ...message,\n parts: parts.state,\n composer: composerState,\n parentId: null,\n index,\n isLast,\n branchNumber,\n branchCount,\n speech: undefined,\n isCopied: isCopiedState,\n isHovering: isHoveringState,\n };\n }, [\n message,\n index,\n isCopiedState,\n isHoveringState,\n isLast,\n parts.state,\n composerState,\n branchNumber,\n branchCount,\n ]);\n\n return {\n getState: () => state,\n composer: () => composer,\n part: (selector) => {\n if (\"index\" in selector) {\n return parts.get({ index: selector.index });\n } else {\n return parts.get({ key: `toolCallId-${selector.toolCallId}` });\n }\n },\n attachment: (selector) => {\n if (\"id\" in selector) {\n return attachments.get({ key: selector.id });\n } else {\n return attachments.get(selector);\n }\n },\n delete: () => {\n throw new Error(\"Not supported in ThreadMessageProvider\");\n },\n reload: () => {\n throw new Error(\"Not supported in ThreadMessageProvider\");\n },\n speak: () => {\n throw new Error(\"Not supported in ThreadMessageProvider\");\n },\n stopSpeaking: () => {\n throw new Error(\"Not supported in ThreadMessageProvider\");\n },\n submitFeedback: () => {\n throw new Error(\"Not supported in ThreadMessageProvider\");\n },\n switchToBranch: () => {\n throw new Error(\"Not supported in ThreadMessageProvider\");\n },\n getCopyText: () => {\n return getThreadMessageText(message);\n },\n setIsCopied,\n setIsHovering,\n };\n};\n\nexport const ThreadMessageClient = resource(useThreadMessageClient);\n"],"mappings":"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"}
1
+ {"version":3,"file":"thread-message-client.js","names":["ThreadAssistantMessagePart","ThreadUserMessagePart","ThreadMessage","Attachment","useMemo","useState","useResource","resource","withKey","ClientOutput","useClientLookup","MessageState","PartState","NoOpComposerClient","COMPLETE_STATUS","normalizePartStatus","getThreadMessageText","useThreadMessagePartClient","t0","$","_c","part","isMessageRunning","t1","t2","status","state","t3","getState","addToolResult","_temp","resumeToolCall","_temp2","respondToToolApproval","_temp3","ThreadMessagePartClient","useThreadMessageAttachmentClient","attachment","remove","Error","ThreadMessageAttachmentClient","ThreadMessageClientProps","message","index","isLast","branchNumber","branchCount","useThreadMessageClient","undefined","isCopiedState","setIsCopied","isHoveringState","setIsHovering","role","type","t4","content","t5","idx","toolCallId","map","parts","attachments","t6","_temp4","t7","Symbol","for","composer","t8","composerState","t9","parentId","speech","isCopied","isHovering","t10","t11","t12","selector","get","key","t13","selector_0","id","t14","t15","delete","_temp5","reload","_temp6","speak","_temp7","stopSpeaking","_temp8","submitFeedback","_temp9","switchToBranch","_temp0","getCopyText","ThreadMessageClient"],"sources":["../../../src/store/clients/thread-message-client.ts"],"sourcesContent":["import type {\n ThreadAssistantMessagePart,\n ThreadUserMessagePart,\n ThreadMessage,\n} from \"../../types/message\";\nimport type { Attachment } from \"../../types/attachment\";\nimport { useMemo, useState } from \"react\";\nimport { useResource, resource, withKey } from \"@assistant-ui/tap\";\nimport type { ClientOutput } from \"@assistant-ui/store\";\nimport { useClientLookup } from \"@assistant-ui/store\";\nimport type { MessageState } from \"../scopes/message\";\nimport type { PartState } from \"../scopes/part\";\nimport { NoOpComposerClient } from \"./no-op-composer-client\";\nimport {\n COMPLETE_STATUS,\n normalizePartStatus,\n} from \"../../utils/normalizePartStatus\";\nimport { getThreadMessageText } from \"../../utils/text\";\n\nconst useThreadMessagePartClient = ({\n part,\n isMessageRunning,\n}: {\n part: ThreadAssistantMessagePart | ThreadUserMessagePart;\n isMessageRunning: boolean;\n}): ClientOutput<\"part\"> => {\n const state = useMemo<PartState>(() => {\n return {\n ...part,\n status: isMessageRunning\n ? (normalizePartStatus(part) ?? COMPLETE_STATUS)\n : COMPLETE_STATUS,\n };\n }, [part, isMessageRunning]);\n\n return {\n getState: () => state,\n addToolResult: () => {\n throw new Error(\"Not supported\");\n },\n resumeToolCall: () => {\n throw new Error(\"Not supported\");\n },\n respondToToolApproval: () => {\n throw new Error(\"Not supported\");\n },\n };\n};\n\nconst ThreadMessagePartClient = resource(useThreadMessagePartClient);\n\nconst useThreadMessageAttachmentClient = ({\n attachment,\n}: {\n attachment: Attachment;\n}): ClientOutput<\"attachment\"> => {\n return {\n getState: () => attachment,\n remove: () => {\n throw new Error(\"Not supported\");\n },\n };\n};\n\nconst ThreadMessageAttachmentClient = resource(\n useThreadMessageAttachmentClient,\n);\nexport type ThreadMessageClientProps = {\n message: ThreadMessage;\n index: number;\n isLast?: boolean;\n branchNumber?: number;\n branchCount?: number;\n};\n\nconst useThreadMessageClient = ({\n message,\n index,\n isLast = true,\n branchNumber = 1,\n branchCount = 1,\n}: ThreadMessageClientProps): ClientOutput<\"message\"> => {\n const [isCopiedState, setIsCopied] = useState(false);\n const [isHoveringState, setIsHovering] = useState(false);\n const isMessageRunning =\n message.role === \"assistant\" && message.status.type === \"running\";\n\n const parts = useClientLookup(\n message.content.map((part, idx) =>\n withKey(\n \"toolCallId\" in part && part.toolCallId != null\n ? `toolCallId-${part.toolCallId}`\n : `index-${idx}`,\n ThreadMessagePartClient({ part, isMessageRunning }),\n [part, isMessageRunning],\n ),\n ),\n );\n\n const attachments = useClientLookup(\n (message.attachments ?? []).map((attachment) =>\n withKey(attachment.id, ThreadMessageAttachmentClient({ attachment }), [\n attachment,\n ]),\n ),\n );\n\n const composer = useResource(NoOpComposerClient({ type: \"edit\" }));\n const composerState = composer.getState();\n\n const state = useMemo<MessageState>(() => {\n return {\n ...message,\n parts: parts.state,\n composer: composerState,\n parentId: null,\n index,\n isLast,\n branchNumber,\n branchCount,\n speech: undefined,\n isCopied: isCopiedState,\n isHovering: isHoveringState,\n };\n }, [\n message,\n index,\n isCopiedState,\n isHoveringState,\n isLast,\n parts.state,\n composerState,\n branchNumber,\n branchCount,\n ]);\n\n return {\n getState: () => state,\n composer: () => composer,\n part: (selector) => {\n if (\"index\" in selector) {\n return parts.get({ index: selector.index });\n } else {\n return parts.get({ key: `toolCallId-${selector.toolCallId}` });\n }\n },\n attachment: (selector) => {\n if (\"id\" in selector) {\n return attachments.get({ key: selector.id });\n } else {\n return attachments.get(selector);\n }\n },\n delete: () => {\n throw new Error(\"Not supported in ThreadMessageProvider\");\n },\n reload: () => {\n throw new Error(\"Not supported in ThreadMessageProvider\");\n },\n speak: () => {\n throw new Error(\"Not supported in ThreadMessageProvider\");\n },\n stopSpeaking: () => {\n throw new Error(\"Not supported in ThreadMessageProvider\");\n },\n submitFeedback: () => {\n throw new Error(\"Not supported in ThreadMessageProvider\");\n },\n switchToBranch: () => {\n throw new Error(\"Not supported in ThreadMessageProvider\");\n },\n getCopyText: () => {\n return getThreadMessageText(message);\n },\n setIsCopied,\n setIsHovering,\n };\n};\n\nexport const ThreadMessageClient = resource(useThreadMessageClient);\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- import { AppendMessage, DataMessagePart, FileMessagePart, GenerativeUIMessagePart, GenerativeUINode, GenerativeUISpec, ImageMessagePart, MessagePartStatus, MessageRole, MessageStatus, MessageTiming, PartProviderMetadata, ReasoningMessagePart, RunConfig, SourceMessagePart, SourceProviderMetadata, TextMessagePart, ThreadAssistantMessage, ThreadAssistantMessagePart, ThreadMessage, ThreadStep, ThreadSystemMessage, ThreadUserMessage, ThreadUserMessagePart, ToolCallMessagePart, ToolCallMessagePartStatus, ToolModelContentPart, Unstable_AudioMessagePart } from "./message.js";
1
+ import { AppendMessage, DataMessagePart, FileMessagePart, GenerativeUIMessagePart, GenerativeUINode, GenerativeUISpec, ImageMessagePart, MessagePartStatus, MessagePartStreamStatus, MessageRole, MessageStatus, MessageTiming, PartProviderMetadata, ReasoningMessagePart, RunConfig, SourceMessagePart, SourceProviderMetadata, TextMessagePart, ThreadAssistantMessage, ThreadAssistantMessagePart, ThreadMessage, ThreadStep, ThreadSystemMessage, ThreadUserMessage, ThreadUserMessagePart, ToolCallMessagePart, ToolCallMessagePartStatus, ToolModelContentPart, Unstable_AudioMessagePart } from "./message.js";
2
2
  import { Attachment, AttachmentStatus, CompleteAttachment, CreateAttachment, PendingAttachment } from "./attachment.js";
3
3
  import { Unstable_TriggerCategory, Unstable_TriggerItem } from "./trigger.js";
4
4
  import { Unstable_DirectiveFormatter, Unstable_DirectiveSegment } from "./directive.js";
5
5
  import { Unsubscribe } from "./unsubscribe.js";
6
6
  import { QuoteInfo } from "./quote.js";
7
- export type { AppendMessage, Attachment, AttachmentStatus, CompleteAttachment, CreateAttachment, DataMessagePart, FileMessagePart, GenerativeUIMessagePart, GenerativeUINode, GenerativeUISpec, ImageMessagePart, MessagePartStatus, MessageRole, MessageStatus, MessageTiming, PartProviderMetadata, PendingAttachment, QuoteInfo, ReasoningMessagePart, RunConfig, SourceMessagePart, SourceProviderMetadata, TextMessagePart, ThreadAssistantMessage, ThreadAssistantMessagePart, ThreadMessage, ThreadStep, ThreadSystemMessage, ThreadUserMessage, ThreadUserMessagePart, ToolCallMessagePart, ToolCallMessagePartStatus, ToolModelContentPart, Unstable_AudioMessagePart, Unstable_DirectiveFormatter, Unstable_DirectiveSegment, Unstable_TriggerCategory, Unstable_TriggerItem, Unsubscribe };
7
+ export type { AppendMessage, Attachment, AttachmentStatus, CompleteAttachment, CreateAttachment, DataMessagePart, FileMessagePart, GenerativeUIMessagePart, GenerativeUINode, GenerativeUISpec, ImageMessagePart, MessagePartStatus, MessagePartStreamStatus, MessageRole, MessageStatus, MessageTiming, PartProviderMetadata, PendingAttachment, QuoteInfo, ReasoningMessagePart, RunConfig, SourceMessagePart, SourceProviderMetadata, TextMessagePart, ThreadAssistantMessage, ThreadAssistantMessagePart, ThreadMessage, ThreadStep, ThreadSystemMessage, ThreadUserMessage, ThreadUserMessagePart, ToolCallMessagePart, ToolCallMessagePartStatus, ToolModelContentPart, Unstable_AudioMessagePart, Unstable_DirectiveFormatter, Unstable_DirectiveSegment, Unstable_TriggerCategory, Unstable_TriggerItem, Unsubscribe };
@@ -8,12 +8,14 @@ type PartProviderMetadata = {
8
8
  type TextMessagePart = {
9
9
  readonly type: "text";
10
10
  readonly text: string;
11
+ readonly status?: MessagePartStreamStatus;
11
12
  readonly providerMetadata?: PartProviderMetadata;
12
13
  readonly parentId?: string;
13
14
  };
14
15
  type ReasoningMessagePart = {
15
16
  readonly type: "reasoning";
16
17
  readonly text: string;
18
+ readonly status?: MessagePartStreamStatus;
17
19
  readonly providerMetadata?: PartProviderMetadata;
18
20
  readonly parentId?: string;
19
21
  };
@@ -214,6 +216,14 @@ type MessagePartStatus = {
214
216
  readonly reason: "cancelled" | "length" | "content-filter" | "other" | "error";
215
217
  readonly error?: unknown;
216
218
  };
219
+ type MessagePartStreamStatus = {
220
+ readonly type: "running";
221
+ } | {
222
+ readonly type: "complete";
223
+ } | {
224
+ readonly type: "incomplete";
225
+ readonly reason: "cancelled" | "length" | "content-filter" | "other" | "error";
226
+ };
217
227
  type ToolCallMessagePartStatus = {
218
228
  /** The tool call is waiting for UI or human input before continuing. */
219
229
  readonly type: "requires-action";
@@ -333,5 +343,5 @@ type AppendMessage = Omit<ThreadMessage, "id"> & {
333
343
  steer?: boolean | undefined;
334
344
  };
335
345
  //#endregion
336
- export { AppendMessage, DataMessagePart, FileMessagePart, GenerativeUIMessagePart, GenerativeUINode, GenerativeUISpec, ImageMessagePart, MCP_APP_URI_SCHEME, McpAppMetadata, MessagePartStatus, MessageRole, MessageStatus, MessageTiming, PartProviderMetadata, ReasoningMessagePart, RunConfig, SourceMessagePart, SourceProviderMetadata, TextMessagePart, ThreadAssistantMessage, ThreadAssistantMessagePart, ThreadMessage, ThreadStep, ThreadSystemMessage, ThreadUserMessage, ThreadUserMessagePart, ToolApprovalOption, ToolApprovalOptionKind, ToolApprovalResponse, ToolCallMessagePart, ToolCallMessagePartMcpMetadata, ToolCallMessagePartStatus, type ToolCallTiming, type ToolModelContentPart, Unstable_AudioMessagePart, isMcpAppUri };
346
+ export { AppendMessage, DataMessagePart, FileMessagePart, GenerativeUIMessagePart, GenerativeUINode, GenerativeUISpec, ImageMessagePart, MCP_APP_URI_SCHEME, McpAppMetadata, MessagePartStatus, MessagePartStreamStatus, MessageRole, MessageStatus, MessageTiming, PartProviderMetadata, ReasoningMessagePart, RunConfig, SourceMessagePart, SourceProviderMetadata, TextMessagePart, ThreadAssistantMessage, ThreadAssistantMessagePart, ThreadMessage, ThreadStep, ThreadSystemMessage, ThreadUserMessage, ThreadUserMessagePart, ToolApprovalOption, ToolApprovalOptionKind, ToolApprovalResponse, ToolCallMessagePart, ToolCallMessagePartMcpMetadata, ToolCallMessagePartStatus, type ToolCallTiming, type ToolModelContentPart, Unstable_AudioMessagePart, isMcpAppUri };
337
347
  //# sourceMappingURL=message.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"message.d.ts","names":[],"sources":["../../src/types/message.ts"],"mappings":";;;;KASY;YACA,uBAAuB;;KAGvB;WACD;WACA;WACA,mBAAmB;WACnB;;KAGC;WACD;WACA;WACA,mBAAmB;WACnB;;KAGC,yBAAyB;KAEzB;WAEG;WACA;WACA;WACA;WACA;WACA,mBAAmB;WACnB;;WAGA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,mBAAmB;WACnB;;KAGH;WACD;WACA;WACA;;KAGC;WACD;WACA;WACA;WACA;WACA;;KAGC;WACD;WACA;aACE;aACA;;;KAID,gBAAgB;WACjB;WACA;WACA,MAAM;;;;;;;;;;;KAYL;;WAIG;;WAEA,QAAQ;;WAER,oBAAoB;;WAEpB;;;;;KAMH;;WAED,MAAM,4BAA4B;;;;;;;;;;KAWjC;WACD;;WAEA,MAAM;;WAEN;WACA;;KAGC;WACD;WACA;WACA;;WAEA;;cAGE;cAEA,cAAe;KAGhB;WACD,MAAM;;KAGL;KAMA;;WAED;;;;;;;WAOA,MAAM;;WAEN;WACA;;WAEA;;WAEA;IAAsB;IAAgB;;;KAGrC;WACG;WAA4B;;WAC5B;WAA2B;;WAE3B;WACA;WACA;;KAGH,oBACV,QAAQ,oBACR;;WAGS;;WAEA;;WAEA;;;;;;WAMA,MAAM;;WAEN,SAAS;;WAET;;WAEA;;WAEA;;WAEA,SAAS;;WAET,MAAM;;WAEN,mBAAmB;;WAEnB,wBAAwB;;WAExB;IAAc;IAAe;;;WAE7B;aACE;aACA;aACA;aACA;;aAEA,mBAAmB;;aAEnB;;aAEA;;;WAGF;;;;;WAKA,oBAAoB;;KAGnB,wBACR,kBACA,mBACA,kBACA,kBACA;KAEQ,6BACR,kBACA,uBACA,sBACA,oBACA,kBACA,mBACA,kBACA;KAEQ;WAEG;;WAGA;;WAGA;WACA;WAMA;;KAGH;;WAGG;;WAEA;IAEX;KAEQ;WAEG;;WAGA;WACA;;WAGA;WACA;;WAGA;WACA;WAOA,QAAQ;;KAGX;WACD;WACA;WACA;WACA;WACA;WACA;WACA;;KAGC;WACD;WACA;aAEM;aACA;;;KAKZ;WACM;WACA,WAAW;;KAGV,sBAAsB;WACvB;WACA,mBAAmB;WACnB;aACE;aACA;aACA;aACA;aACA;aACA;aACA,QAAQ;;;KAIT,oBAAoB;WACrB;WACA,kBAAkB;WAClB,sBAAsB;WACtB;aACE;aACA;aACA;aACA;aACA;aACA;aACA;aACA,QAAQ;;;KAIT,yBAAyB;WAC1B;WACA,kBAAkB;WAClB,QAAQ;WACR;aACE,gBAAgB;aAChB,+BAA+B;aAC/B,wBAAwB;aACxB,gBAAgB;aAChB;eAA+B;;aAC/B,SAAS;;;;;aAKT;aACA,QAAQ;;;KAIhB;WACM,SAAS;WACT;aACE,iBAAiB;aACjB,gCAAgC;aAChC,yBAAyB;aACzB,iBAAiB;aACjB;eAA+B;;aAC/B,SAAS;aACT;aACA,QAAQ;;WAEV,cAAc;;KAGb,gBAAgB,qBACzB,sBAAsB,oBAAoB;KAEjC,cAAc;KAEd;WACD,SAAS;;KAGR,gBAAgB,KAAK;EAC/B;;EAGA;EACA,WAAW;EACX;;EAEA"}
1
+ {"version":3,"file":"message.d.ts","names":[],"sources":["../../src/types/message.ts"],"mappings":";;;;KASY;YACA,uBAAuB;;KAGvB;WACD;WACA;WACA,SAAS;WACT,mBAAmB;WACnB;;KAGC;WACD;WACA;WACA,SAAS;WACT,mBAAmB;WACnB;;KAGC,yBAAyB;KAEzB;WAEG;WACA;WACA;WACA;WACA;WACA,mBAAmB;WACnB;;WAGA;WACA;WACA;WACA;WACA;WACA;WACA;WACA,mBAAmB;WACnB;;KAGH;WACD;WACA;WACA;;KAGC;WACD;WACA;WACA;WACA;WACA;;KAGC;WACD;WACA;aACE;aACA;;;KAID,gBAAgB;WACjB;WACA;WACA,MAAM;;;;;;;;;;;KAYL;;WAIG;;WAEA,QAAQ;;WAER,oBAAoB;;WAEpB;;;;;KAMH;;WAED,MAAM,4BAA4B;;;;;;;;;;KAWjC;WACD;;WAEA,MAAM;;WAEN;WACA;;KAGC;WACD;WACA;WACA;;WAEA;;cAGE;cAEA,cAAe;KAGhB;WACD,MAAM;;KAGL;KAMA;;WAED;;;;;;;WAOA,MAAM;;WAEN;WACA;;WAEA;;WAEA;IAAsB;IAAgB;;;KAGrC;WACG;WAA4B;;WAC5B;WAA2B;;WAE3B;WACA;WACA;;KAGH,oBACV,QAAQ,oBACR;;WAGS;;WAEA;;WAEA;;;;;;WAMA,MAAM;;WAEN,SAAS;;WAET;;WAEA;;WAEA;;WAEA,SAAS;;WAET,MAAM;;WAEN,mBAAmB;;WAEnB,wBAAwB;;WAExB;IAAc;IAAe;;;WAE7B;aACE;aACA;aACA;aACA;;aAEA,mBAAmB;;aAEnB;;aAEA;;;WAGF;;;;;WAKA,oBAAoB;;KAGnB,wBACR,kBACA,mBACA,kBACA,kBACA;KAEQ,6BACR,kBACA,uBACA,sBACA,oBACA,kBACA,mBACA,kBACA;KAEQ;WAEG;;WAGA;;WAGA;WACA;WAMA;;KAGH;WAEG;;WAGA;;WAGA;WACA;;KAQH;;WAGG;;WAEA;IAEX;KAEQ;WAEG;;WAGA;WACA;;WAGA;WACA;;WAGA;WACA;WAOA,QAAQ;;KAGX;WACD;WACA;WACA;WACA;WACA;WACA;WACA;;KAGC;WACD;WACA;aAEM;aACA;;;KAKZ;WACM;WACA,WAAW;;KAGV,sBAAsB;WACvB;WACA,mBAAmB;WACnB;aACE;aACA;aACA;aACA;aACA;aACA;aACA,QAAQ;;;KAIT,oBAAoB;WACrB;WACA,kBAAkB;WAClB,sBAAsB;WACtB;aACE;aACA;aACA;aACA;aACA;aACA;aACA;aACA,QAAQ;;;KAIT,yBAAyB;WAC1B;WACA,kBAAkB;WAClB,QAAQ;WACR;aACE,gBAAgB;aAChB,+BAA+B;aAC/B,wBAAwB;aACxB,gBAAgB;aAChB;eAA+B;;aAC/B,SAAS;;;;;aAKT;aACA,QAAQ;;;KAIhB;WACM,SAAS;WACT;aACE,iBAAiB;aACjB,gCAAgC;aAChC,yBAAyB;aACzB,iBAAiB;aACjB;eAA+B;;aAC/B,SAAS;aACT;aACA,QAAQ;;WAEV,cAAc;;KAGb,gBAAgB,qBACzB,sBAAsB,oBAAoB;KAEjC,cAAc;KAEd;WACD,SAAS;;KAGR,gBAAgB,KAAK;EAC/B;;EAGA;EACA,WAAW;EACX;;EAEA"}
@@ -1 +1 @@
1
- {"version":3,"file":"message.js","names":["ReadonlyJSONObject","ReadonlyJSONValue","ToolCallTiming","ToolModelContentPart","CompleteAttachment","PartProviderMetadata","providerName","TextMessagePart","type","text","providerMetadata","parentId","ReasoningMessagePart","SourceProviderMetadata","SourceMessagePart","sourceType","id","url","title","mediaType","filename","ImageMessagePart","image","FileMessagePart","data","mimeType","Unstable_AudioMessagePart","audio","format","DataMessagePart","name","T","GenerativeUINode","component","props","Record","children","key","GenerativeUISpec","root","GenerativeUIMessagePart","spec","McpAppMetadata","resourceUri","visibility","serverId","MCP_APP_URI_SCHEME","isMcpAppUri","uri","startsWith","ToolCallMessagePartMcpMetadata","app","ToolApprovalOptionKind","ToolApprovalOption","kind","label","description","grants","confirm","ToolApprovalResponse","approved","reason","optionId","ToolCallMessagePart","toolCallId","toolName","args","TArgs","result","TResult","isError","argsText","artifact","timing","mcp","modelContent","interrupt","payload","approval","isAutomatic","options","resolution","messages","ThreadMessage","ThreadUserMessagePart","ThreadAssistantMessagePart","MessagePartStatus","error","ToolCallMessagePartStatus","MessageStatus","MessageTiming","streamStartTime","firstTokenTime","totalStreamTime","tokenCount","tokensPerSecond","totalChunks","toolCallCount","ThreadStep","messageId","usage","inputTokens","outputTokens","MessageCommonProps","createdAt","Date","ThreadSystemMessage","role","content","metadata","unstable_state","unstable_annotations","unstable_data","steps","submittedFeedback","custom","ThreadUserMessage","attachments","isOptimistic","ThreadAssistantMessage","status","BaseThreadMessage","MessageRole","RunConfig","AppendMessage","Omit","sourceId","runConfig","startRun","steer"],"sources":["../../src/types/message.ts"],"sourcesContent":["import type {\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"assistant-stream/utils\";\nimport type { ToolCallTiming, ToolModelContentPart } from \"assistant-stream\";\nimport type { CompleteAttachment } from \"./attachment\";\n\nexport type { ToolCallTiming, ToolModelContentPart };\n\nexport type PartProviderMetadata = {\n readonly [providerName: string]: ReadonlyJSONObject;\n};\n\nexport type TextMessagePart = {\n readonly type: \"text\";\n readonly text: string;\n readonly providerMetadata?: PartProviderMetadata;\n readonly parentId?: string;\n};\n\nexport type ReasoningMessagePart = {\n readonly type: \"reasoning\";\n readonly text: string;\n readonly providerMetadata?: PartProviderMetadata;\n readonly parentId?: string;\n};\n\nexport type SourceProviderMetadata = PartProviderMetadata;\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 /** Routable server identity emitted by the agent stack when multiple MCP servers back one agent; for @ag-ui/mcp-apps-middleware this is the configured serverId, falling back to its serverHash. */\n readonly serverId?: string;\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 ToolApprovalOptionKind =\n | \"allow-once\"\n | \"allow-always\"\n | \"reject-once\"\n | \"reject-always\";\n\nexport type ToolApprovalOption = {\n /** Opaque, host-defined identifier. Scope semantics (session vs project vs global) belong to the option supplier. */\n readonly id: string;\n /**\n * Decision class. Drives approved-resolution and default rendering.\n * Open union: `_`-prefixed custom kinds are never auto-resolved and the\n * default kit skips them; they must be answered with an explicit\n * `approved` value (optionally alongside the `optionId`).\n */\n readonly kind: ToolApprovalOptionKind | (string & {});\n /** Human label. Renderers supply defaults per kind when omitted. */\n readonly label?: string;\n readonly description?: string;\n /** Patterns or rules this option would persist, shown before the user commits. Supplied by the host; never derived by the library. */\n readonly grants?: readonly string[];\n /** Opt-in confirmation step before this option resolves. */\n readonly confirm?: boolean | { title?: string; description?: string };\n};\n\nexport type ToolApprovalResponse =\n | { readonly approved: boolean; readonly reason?: string }\n | { readonly optionId: string; readonly reason?: string }\n | {\n readonly approved: boolean;\n readonly optionId: string;\n readonly reason?: string;\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 /** Wall-clock timing for this call, when the runtime or host tracks it. */\n readonly timing?: ToolCallTiming;\n /** MCP app metadata associated with this tool call, when present. */\n readonly mcp?: ToolCallMessagePartMcpMetadata;\n /** Provider metadata associated with this tool call, when present. */\n readonly providerMetadata?: PartProviderMetadata;\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. `respondToApproval` may only be called while `approved` is undefined and no `resolution` is recorded. */\n readonly approval?: {\n readonly id: string;\n readonly approved?: boolean;\n readonly reason?: string;\n readonly isAutomatic?: boolean;\n /** Available decisions for this call. Absent: a plain allow / deny pair. */\n readonly options?: readonly ToolApprovalOption[];\n /** The option chosen at resolution, when options were present. */\n readonly optionId?: string;\n /** Terminal non-decision state: the request was cancelled or expired without a user decision. Set by the host. */\n readonly resolution?: \"cancelled\" | \"expired\";\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 isOptimistic?: boolean;\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 /** Process this message next; only meaningful for queue-capable runtimes. */\n steer?: boolean | undefined;\n};\n"],"mappings":";AAsIA,MAAa8C,qBAAqB;AAElC,MAAaC,eAAeC,QAC1B,CAAC,CAACA,KAAKC,WAAWH,kBAAkB"}
1
+ {"version":3,"file":"message.js","names":["ReadonlyJSONObject","ReadonlyJSONValue","ToolCallTiming","ToolModelContentPart","CompleteAttachment","PartProviderMetadata","providerName","TextMessagePart","type","text","status","MessagePartStreamStatus","providerMetadata","parentId","ReasoningMessagePart","SourceProviderMetadata","SourceMessagePart","sourceType","id","url","title","mediaType","filename","ImageMessagePart","image","FileMessagePart","data","mimeType","Unstable_AudioMessagePart","audio","format","DataMessagePart","name","T","GenerativeUINode","component","props","Record","children","key","GenerativeUISpec","root","GenerativeUIMessagePart","spec","McpAppMetadata","resourceUri","visibility","serverId","MCP_APP_URI_SCHEME","isMcpAppUri","uri","startsWith","ToolCallMessagePartMcpMetadata","app","ToolApprovalOptionKind","ToolApprovalOption","kind","label","description","grants","confirm","ToolApprovalResponse","approved","reason","optionId","ToolCallMessagePart","toolCallId","toolName","args","TArgs","result","TResult","isError","argsText","artifact","timing","mcp","modelContent","interrupt","payload","approval","isAutomatic","options","resolution","messages","ThreadMessage","ThreadUserMessagePart","ThreadAssistantMessagePart","MessagePartStatus","error","ToolCallMessagePartStatus","MessageStatus","MessageTiming","streamStartTime","firstTokenTime","totalStreamTime","tokenCount","tokensPerSecond","totalChunks","toolCallCount","ThreadStep","messageId","usage","inputTokens","outputTokens","MessageCommonProps","createdAt","Date","ThreadSystemMessage","role","content","metadata","unstable_state","unstable_annotations","unstable_data","steps","submittedFeedback","custom","ThreadUserMessage","attachments","isOptimistic","ThreadAssistantMessage","BaseThreadMessage","MessageRole","RunConfig","AppendMessage","Omit","sourceId","runConfig","startRun","steer"],"sources":["../../src/types/message.ts"],"sourcesContent":["import type {\n ReadonlyJSONObject,\n ReadonlyJSONValue,\n} from \"assistant-stream/utils\";\nimport type { ToolCallTiming, ToolModelContentPart } from \"assistant-stream\";\nimport type { CompleteAttachment } from \"./attachment\";\n\nexport type { ToolCallTiming, ToolModelContentPart };\n\nexport type PartProviderMetadata = {\n readonly [providerName: string]: ReadonlyJSONObject;\n};\n\nexport type TextMessagePart = {\n readonly type: \"text\";\n readonly text: string;\n readonly status?: MessagePartStreamStatus;\n readonly providerMetadata?: PartProviderMetadata;\n readonly parentId?: string;\n};\n\nexport type ReasoningMessagePart = {\n readonly type: \"reasoning\";\n readonly text: string;\n readonly status?: MessagePartStreamStatus;\n readonly providerMetadata?: PartProviderMetadata;\n readonly parentId?: string;\n};\n\nexport type SourceProviderMetadata = PartProviderMetadata;\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 /** Routable server identity emitted by the agent stack when multiple MCP servers back one agent; for @ag-ui/mcp-apps-middleware this is the configured serverId, falling back to its serverHash. */\n readonly serverId?: string;\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 ToolApprovalOptionKind =\n | \"allow-once\"\n | \"allow-always\"\n | \"reject-once\"\n | \"reject-always\";\n\nexport type ToolApprovalOption = {\n /** Opaque, host-defined identifier. Scope semantics (session vs project vs global) belong to the option supplier. */\n readonly id: string;\n /**\n * Decision class. Drives approved-resolution and default rendering.\n * Open union: `_`-prefixed custom kinds are never auto-resolved and the\n * default kit skips them; they must be answered with an explicit\n * `approved` value (optionally alongside the `optionId`).\n */\n readonly kind: ToolApprovalOptionKind | (string & {});\n /** Human label. Renderers supply defaults per kind when omitted. */\n readonly label?: string;\n readonly description?: string;\n /** Patterns or rules this option would persist, shown before the user commits. Supplied by the host; never derived by the library. */\n readonly grants?: readonly string[];\n /** Opt-in confirmation step before this option resolves. */\n readonly confirm?: boolean | { title?: string; description?: string };\n};\n\nexport type ToolApprovalResponse =\n | { readonly approved: boolean; readonly reason?: string }\n | { readonly optionId: string; readonly reason?: string }\n | {\n readonly approved: boolean;\n readonly optionId: string;\n readonly reason?: string;\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 /** Wall-clock timing for this call, when the runtime or host tracks it. */\n readonly timing?: ToolCallTiming;\n /** MCP app metadata associated with this tool call, when present. */\n readonly mcp?: ToolCallMessagePartMcpMetadata;\n /** Provider metadata associated with this tool call, when present. */\n readonly providerMetadata?: PartProviderMetadata;\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. `respondToApproval` may only be called while `approved` is undefined and no `resolution` is recorded. */\n readonly approval?: {\n readonly id: string;\n readonly approved?: boolean;\n readonly reason?: string;\n readonly isAutomatic?: boolean;\n /** Available decisions for this call. Absent: a plain allow / deny pair. */\n readonly options?: readonly ToolApprovalOption[];\n /** The option chosen at resolution, when options were present. */\n readonly optionId?: string;\n /** Terminal non-decision state: the request was cancelled or expired without a user decision. Set by the host. */\n readonly resolution?: \"cancelled\" | \"expired\";\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 MessagePartStreamStatus =\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 };\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 isOptimistic?: boolean;\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 /** Process this message next; only meaningful for queue-capable runtimes. */\n steer?: boolean | undefined;\n};\n"],"mappings":";AAwIA,MAAagD,qBAAqB;AAElC,MAAaC,eAAeC,QAC1B,CAAC,CAACA,KAAKC,WAAWH,kBAAkB"}
@@ -0,0 +1,9 @@
1
+ import { MessagePartStatus, ToolCallMessagePartStatus } from "../types/message.js";
2
+ //#region src/utils/getGroupStatus.d.ts
3
+ type PartWithStatus = {
4
+ readonly status: MessagePartStatus | ToolCallMessagePartStatus;
5
+ };
6
+ declare const getGroupStatus: (parts: readonly (PartWithStatus | undefined)[], indices?: readonly number[]) => MessagePartStatus | ToolCallMessagePartStatus;
7
+ //#endregion
8
+ export { getGroupStatus };
9
+ //# sourceMappingURL=getGroupStatus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getGroupStatus.d.ts","names":[],"sources":["../../src/utils/getGroupStatus.ts"],"mappings":";;KAMK;WACM,QAAQ,oBAAoB;;cAG1B,iBACX,iBAAiB,+BACjB,gCACC,oBAAoB"}
@@ -0,0 +1,15 @@
1
+ import { COMPLETE_STATUS, RUNNING_STATUS } from "./normalizePartStatus.js";
2
+ //#region src/utils/getGroupStatus.ts
3
+ const getGroupStatus = (parts, indices) => {
4
+ if (indices) {
5
+ for (const index of indices) if (parts[index]?.status.type === "running") return RUNNING_STATUS;
6
+ const lastIndex = indices.at(-1);
7
+ return lastIndex === void 0 ? COMPLETE_STATUS : parts[lastIndex]?.status ?? COMPLETE_STATUS;
8
+ }
9
+ for (const part of parts) if (part?.status.type === "running") return RUNNING_STATUS;
10
+ return parts.at(-1)?.status ?? COMPLETE_STATUS;
11
+ };
12
+ //#endregion
13
+ export { getGroupStatus };
14
+
15
+ //# sourceMappingURL=getGroupStatus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getGroupStatus.js","names":["MessagePartStatus","ToolCallMessagePartStatus","COMPLETE_STATUS","RUNNING_STATUS","PartWithStatus","status","getGroupStatus","parts","indices","index","type","lastIndex","at","undefined","part"],"sources":["../../src/utils/getGroupStatus.ts"],"sourcesContent":["import type {\n MessagePartStatus,\n ToolCallMessagePartStatus,\n} from \"../types/message\";\nimport { COMPLETE_STATUS, RUNNING_STATUS } from \"./normalizePartStatus\";\n\ntype PartWithStatus = {\n readonly status: MessagePartStatus | ToolCallMessagePartStatus;\n};\n\nexport const getGroupStatus = (\n parts: readonly (PartWithStatus | undefined)[],\n indices?: readonly number[],\n): MessagePartStatus | ToolCallMessagePartStatus => {\n if (indices) {\n for (const index of indices) {\n if (parts[index]?.status.type === \"running\") return RUNNING_STATUS;\n }\n\n const lastIndex = indices.at(-1);\n return lastIndex === undefined\n ? COMPLETE_STATUS\n : (parts[lastIndex]?.status ?? COMPLETE_STATUS);\n }\n\n for (const part of parts) {\n if (part?.status.type === \"running\") return RUNNING_STATUS;\n }\n\n return parts.at(-1)?.status ?? COMPLETE_STATUS;\n};\n"],"mappings":";;AAUA,MAAaM,kBACXC,OACAC,YACkD;CAClD,IAAIA,SAAS;EACX,KAAK,MAAMC,SAASD,SAClB,IAAID,MAAME,MAAM,EAAEJ,OAAOK,SAAS,WAAW,OAAOP;EAGtD,MAAMQ,YAAYH,QAAQI,GAAG,EAAE;EAC/B,OAAOD,cAAcE,KAAAA,IACjBX,kBACCK,MAAMI,UAAU,EAAEN,UAAUH;CACnC;CAEA,KAAK,MAAMY,QAAQP,OACjB,IAAIO,MAAMT,OAAOK,SAAS,WAAW,OAAOP;CAG9C,OAAOI,MAAMK,GAAG,EAAE,CAAC,EAAEP,UAAUH;AACjC"}
@@ -0,0 +1,8 @@
1
+ import { MessagePartStatus, ThreadAssistantMessagePart, ThreadUserMessagePart } from "../types/message.js";
2
+ //#region src/utils/normalizePartStatus.d.ts
3
+ declare const COMPLETE_STATUS: MessagePartStatus;
4
+ declare const RUNNING_STATUS: MessagePartStatus;
5
+ declare const normalizePartStatus: (part: ThreadUserMessagePart | ThreadAssistantMessagePart) => MessagePartStatus | undefined;
6
+ //#endregion
7
+ export { COMPLETE_STATUS, RUNNING_STATUS, normalizePartStatus };
8
+ //# sourceMappingURL=normalizePartStatus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalizePartStatus.d.ts","names":[],"sources":["../../src/utils/normalizePartStatus.ts"],"mappings":";;cAOa,iBAAiB;cAGjB,gBAAgB;cAoBhB,sBACX,MAAM,wBAAwB,+BAC7B"}
@@ -0,0 +1,39 @@
1
+ //#region src/utils/normalizePartStatus.ts
2
+ const COMPLETE_STATUS = Object.freeze({ type: "complete" });
3
+ const RUNNING_STATUS = Object.freeze({ type: "running" });
4
+ const INCOMPLETE_STATUSES = Object.freeze({
5
+ cancelled: Object.freeze({
6
+ type: "incomplete",
7
+ reason: "cancelled"
8
+ }),
9
+ length: Object.freeze({
10
+ type: "incomplete",
11
+ reason: "length"
12
+ }),
13
+ "content-filter": Object.freeze({
14
+ type: "incomplete",
15
+ reason: "content-filter"
16
+ }),
17
+ other: Object.freeze({
18
+ type: "incomplete",
19
+ reason: "other"
20
+ }),
21
+ error: Object.freeze({
22
+ type: "incomplete",
23
+ reason: "error"
24
+ })
25
+ });
26
+ const normalizePartStatus = (part) => {
27
+ const status = part.status;
28
+ if (!status || typeof status !== "object") return void 0;
29
+ const { type } = status;
30
+ if (type === "running") return RUNNING_STATUS;
31
+ if (type === "complete") return COMPLETE_STATUS;
32
+ if (type !== "incomplete") return void 0;
33
+ const { reason } = status;
34
+ return INCOMPLETE_STATUSES[reason === "cancelled" || reason === "length" || reason === "content-filter" || reason === "other" || reason === "error" ? reason : "other"];
35
+ };
36
+ //#endregion
37
+ export { COMPLETE_STATUS, RUNNING_STATUS, normalizePartStatus };
38
+
39
+ //# sourceMappingURL=normalizePartStatus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalizePartStatus.js","names":["MessagePartStatus","MessagePartStreamStatus","ThreadAssistantMessagePart","ThreadUserMessagePart","COMPLETE_STATUS","Object","freeze","type","RUNNING_STATUS","IncompleteMessagePartStatus","Extract","INCOMPLETE_STATUSES","Readonly","Record","cancelled","reason","length","other","error","normalizePartStatus","part","status","undefined","normalizedReason"],"sources":["../../src/utils/normalizePartStatus.ts"],"sourcesContent":["import type {\n MessagePartStatus,\n MessagePartStreamStatus,\n ThreadAssistantMessagePart,\n ThreadUserMessagePart,\n} from \"../types/message\";\n\nexport const COMPLETE_STATUS: MessagePartStatus = Object.freeze({\n type: \"complete\",\n});\nexport const RUNNING_STATUS: MessagePartStatus = Object.freeze({\n type: \"running\",\n});\ntype IncompleteMessagePartStatus = Extract<\n MessagePartStreamStatus,\n { readonly type: \"incomplete\" }\n>;\nconst INCOMPLETE_STATUSES: Readonly<\n Record<IncompleteMessagePartStatus[\"reason\"], IncompleteMessagePartStatus>\n> = Object.freeze({\n cancelled: Object.freeze({ type: \"incomplete\", reason: \"cancelled\" }),\n length: Object.freeze({ type: \"incomplete\", reason: \"length\" }),\n \"content-filter\": Object.freeze({\n type: \"incomplete\",\n reason: \"content-filter\",\n }),\n other: Object.freeze({ type: \"incomplete\", reason: \"other\" }),\n error: Object.freeze({ type: \"incomplete\", reason: \"error\" }),\n});\n\nexport const normalizePartStatus = (\n part: ThreadUserMessagePart | ThreadAssistantMessagePart,\n): MessagePartStatus | undefined => {\n const status = (part as { readonly status?: unknown }).status;\n if (!status || typeof status !== \"object\") return undefined;\n\n const { type } = status as { readonly type?: unknown };\n if (type === \"running\") return RUNNING_STATUS;\n if (type === \"complete\") return COMPLETE_STATUS;\n if (type !== \"incomplete\") return undefined;\n\n const { reason } = status as {\n readonly reason?: unknown;\n };\n const normalizedReason: IncompleteMessagePartStatus[\"reason\"] =\n reason === \"cancelled\" ||\n reason === \"length\" ||\n reason === \"content-filter\" ||\n reason === \"other\" ||\n reason === \"error\"\n ? reason\n : \"other\";\n\n return INCOMPLETE_STATUSES[normalizedReason];\n};\n"],"mappings":";AAOA,MAAaI,kBAAqCC,OAAOC,OAAO,EAC9DC,MAAM,WACR,CAAC;AACD,MAAaC,iBAAoCH,OAAOC,OAAO,EAC7DC,MAAM,UACR,CAAC;AAKD,MAAMI,sBAEFN,OAAOC,OAAO;CAChBQ,WAAWT,OAAOC,OAAO;EAAEC,MAAM;EAAcQ,QAAQ;CAAY,CAAC;CACpEC,QAAQX,OAAOC,OAAO;EAAEC,MAAM;EAAcQ,QAAQ;CAAS,CAAC;CAC9D,kBAAkBV,OAAOC,OAAO;EAC9BC,MAAM;EACNQ,QAAQ;CACV,CAAC;CACDE,OAAOZ,OAAOC,OAAO;EAAEC,MAAM;EAAcQ,QAAQ;CAAQ,CAAC;CAC5DG,OAAOb,OAAOC,OAAO;EAAEC,MAAM;EAAcQ,QAAQ;CAAQ,CAAC;AAC9D,CAAC;AAED,MAAaI,uBACXC,SACkC;CAClC,MAAMC,SAAUD,KAAuCC;CACvD,IAAI,CAACA,UAAU,OAAOA,WAAW,UAAU,OAAOC,KAAAA;CAElD,MAAM,EAAEf,SAASc;CACjB,IAAId,SAAS,WAAW,OAAOC;CAC/B,IAAID,SAAS,YAAY,OAAOH;CAChC,IAAIG,SAAS,cAAc,OAAOe,KAAAA;CAElC,MAAM,EAAEP,WAAWM;CAYnB,OAAOV,oBARLI,WAAW,eACXA,WAAW,YACXA,WAAW,oBACXA,WAAW,WACXA,WAAW,UACPA,SACA;AAGR"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/core",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "Framework-agnostic core runtime for assistant-ui",
5
5
  "keywords": [
6
6
  "assistant",
@@ -96,7 +96,7 @@
96
96
  "vitest": "^4.1.10",
97
97
  "zustand": "^5.0.14",
98
98
  "@assistant-ui/store": "0.3.2",
99
- "@assistant-ui/tap": "0.9.8",
99
+ "@assistant-ui/tap": "0.9.9",
100
100
  "@assistant-ui/vite": "0.0.11",
101
101
  "@assistant-ui/x-buildutils": "0.0.20",
102
102
  "assistant-cloud": "0.1.37"
package/src/index.ts CHANGED
@@ -34,6 +34,7 @@ export type {
34
34
  ThreadAssistantMessagePart,
35
35
  // Message status
36
36
  MessagePartStatus,
37
+ MessagePartStreamStatus,
37
38
  ToolCallMessagePartStatus,
38
39
  MessageStatus,
39
40
  // Thread messages
package/src/internal.ts CHANGED
@@ -32,6 +32,7 @@ export {
32
32
 
33
33
  // Message utilities
34
34
  export { getThreadMessageText } from "./utils/text";
35
+ export { toMessagePartStatus } from "./runtime/api/message-runtime";
35
36
  export { resolveToolApprovalResponse } from "./runtime/utils/resolveToolApprovalResponse";
36
37
  export { consumeSuggestionResult } from "./adapters/suggestion";
37
38
 
@@ -1,3 +1,5 @@
1
+ /** @vitest-environment jsdom */
2
+ import { cleanup, renderHook } from "@testing-library/react";
1
3
  import { afterEach, describe, expect, it, vi } from "vitest";
2
4
 
3
5
  const mocks = vi.hoisted(() => {
@@ -23,18 +25,18 @@ const mocks = vi.hoisted(() => {
23
25
  setIsCopied,
24
26
  },
25
27
  },
28
+ currentAui: {
29
+ message: {
30
+ getCopyText: () => "Hello",
31
+ setIsCopied,
32
+ },
33
+ },
26
34
  };
27
35
  });
28
36
 
29
- vi.mock("react", async (importOriginal) => ({
30
- ...(await importOriginal<typeof import("react")>()),
31
- useCallback: ((callback: unknown) =>
32
- callback) as typeof import("react").useCallback,
33
- }));
34
-
35
37
  vi.mock("@assistant-ui/store", async (importOriginal) => ({
36
38
  ...(await importOriginal<typeof import("@assistant-ui/store")>()),
37
- useAui: () => mocks.aui,
39
+ useAui: () => mocks.currentAui,
38
40
  useAuiState: ((selector: (state: typeof mocks.state) => unknown) =>
39
41
  selector(mocks.state)) as typeof import("@assistant-ui/store").useAuiState,
40
42
  }));
@@ -42,25 +44,28 @@ vi.mock("@assistant-ui/store", async (importOriginal) => ({
42
44
  import { useActionBarCopy } from "./useActionBarCopy";
43
45
 
44
46
  afterEach(() => {
47
+ cleanup();
45
48
  vi.clearAllMocks();
49
+ vi.useRealTimers();
50
+ mocks.currentAui = mocks.aui;
46
51
  });
47
52
 
48
53
  describe("useActionBarCopy", () => {
49
54
  it("does not report copy success without a clipboard handler", async () => {
50
- const { copy, disabled } = useActionBarCopy();
55
+ const { result } = renderHook(() => useActionBarCopy());
51
56
 
52
- copy();
57
+ result.current.copy();
53
58
  await Promise.resolve();
54
59
 
55
- expect(disabled).toBe(true);
60
+ expect(result.current.disabled).toBe(true);
56
61
  expect(mocks.setIsCopied).not.toHaveBeenCalled();
57
62
  });
58
63
 
59
64
  it("reports copy success after the clipboard handler resolves", async () => {
60
65
  const copyToClipboard = vi.fn();
61
- const { copy } = useActionBarCopy({ copyToClipboard });
66
+ const { result } = renderHook(() => useActionBarCopy({ copyToClipboard }));
62
67
 
63
- copy();
68
+ result.current.copy();
64
69
  await Promise.resolve();
65
70
 
66
71
  expect(copyToClipboard).toHaveBeenCalledWith("Hello");
@@ -69,11 +74,101 @@ describe("useActionBarCopy", () => {
69
74
 
70
75
  it("does not report copy success when the clipboard handler rejects", async () => {
71
76
  const copyToClipboard = vi.fn().mockRejectedValue(new Error("denied"));
72
- const { copy } = useActionBarCopy({ copyToClipboard });
77
+ const { result } = renderHook(() => useActionBarCopy({ copyToClipboard }));
78
+
79
+ result.current.copy();
80
+ await Promise.resolve();
81
+
82
+ expect(mocks.setIsCopied).not.toHaveBeenCalled();
83
+ });
84
+
85
+ it("keeps copy success visible for the full duration after copying again", async () => {
86
+ vi.useFakeTimers();
87
+ const copyToClipboard = vi.fn();
88
+ const { result } = renderHook(() =>
89
+ useActionBarCopy({
90
+ copiedDuration: 3000,
91
+ copyToClipboard,
92
+ }),
93
+ );
94
+
95
+ result.current.copy();
96
+ await Promise.resolve();
97
+ await vi.advanceTimersByTimeAsync(1000);
98
+
99
+ result.current.copy();
100
+ await Promise.resolve();
101
+ mocks.setIsCopied.mockClear();
102
+
103
+ await vi.advanceTimersByTimeAsync(2000);
104
+ expect(mocks.setIsCopied).not.toHaveBeenCalledWith(false);
73
105
 
74
- copy();
106
+ await vi.advanceTimersByTimeAsync(1000);
107
+ expect(mocks.setIsCopied).toHaveBeenCalledWith(false);
108
+ });
109
+
110
+ it("resets copy feedback when unmounted", async () => {
111
+ vi.useFakeTimers();
112
+ const copyToClipboard = vi.fn();
113
+ const { result, unmount } = renderHook(() =>
114
+ useActionBarCopy({ copyToClipboard }),
115
+ );
116
+
117
+ result.current.copy();
118
+ await Promise.resolve();
119
+ expect(vi.getTimerCount()).toBe(1);
120
+
121
+ mocks.setIsCopied.mockClear();
122
+ unmount();
123
+
124
+ expect(vi.getTimerCount()).toBe(0);
125
+ expect(mocks.setIsCopied).toHaveBeenCalledWith(false);
126
+ });
127
+
128
+ it("ignores clipboard success after unmount", async () => {
129
+ vi.useFakeTimers();
130
+ let resolveCopy: (() => void) | undefined;
131
+ const copyToClipboard = vi.fn(
132
+ () =>
133
+ new Promise<void>((resolve) => {
134
+ resolveCopy = resolve;
135
+ }),
136
+ );
137
+ const { result, unmount } = renderHook(() =>
138
+ useActionBarCopy({ copyToClipboard }),
139
+ );
140
+
141
+ result.current.copy();
142
+ unmount();
143
+ resolveCopy?.();
75
144
  await Promise.resolve();
76
145
 
77
146
  expect(mocks.setIsCopied).not.toHaveBeenCalled();
147
+ expect(vi.getTimerCount()).toBe(0);
148
+ });
149
+
150
+ it("resets feedback for the previous message scope", async () => {
151
+ vi.useFakeTimers();
152
+ const copyToClipboard = vi.fn();
153
+ const nextSetIsCopied = vi.fn();
154
+ const { result, rerender } = renderHook(() =>
155
+ useActionBarCopy({ copyToClipboard }),
156
+ );
157
+
158
+ result.current.copy();
159
+ await Promise.resolve();
160
+ mocks.setIsCopied.mockClear();
161
+
162
+ mocks.currentAui = {
163
+ message: {
164
+ getCopyText: () => "Next message",
165
+ setIsCopied: nextSetIsCopied,
166
+ },
167
+ };
168
+ rerender();
169
+
170
+ expect(mocks.setIsCopied).toHaveBeenCalledWith(false);
171
+ expect(nextSetIsCopied).not.toHaveBeenCalled();
172
+ expect(vi.getTimerCount()).toBe(0);
78
173
  });
79
174
  });
@@ -1,4 +1,4 @@
1
- import { useCallback } from "react";
1
+ import { useCallback, useEffect, useRef } from "react";
2
2
  import { useAui, useAuiState } from "@assistant-ui/store";
3
3
 
4
4
  export type UseActionBarCopyOptions = {
@@ -21,19 +21,44 @@ export const useActionBarCopy = ({
21
21
  const isCopied = useAuiState((s) => s.message.isCopied);
22
22
  const isEditing = useAuiState((s) => s.composer.isEditing);
23
23
  const composerValue = useAuiState((s) => s.composer.text);
24
+ const copiedTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(
25
+ undefined,
26
+ );
27
+ const scopeGenerationRef = useRef(0);
28
+
29
+ useEffect(
30
+ () => () => {
31
+ scopeGenerationRef.current += 1;
32
+ if (copiedTimerRef.current === undefined) return;
33
+
34
+ clearTimeout(copiedTimerRef.current);
35
+ copiedTimerRef.current = undefined;
36
+ aui.message.setIsCopied(false);
37
+ },
38
+ [aui],
39
+ );
24
40
 
25
41
  const copy = useCallback(() => {
26
42
  if (!copyToClipboard) return;
27
43
 
28
44
  const valueToCopy = isEditing ? composerValue : aui.message.getCopyText();
29
45
  if (!valueToCopy) return;
46
+ const scopeGeneration = scopeGenerationRef.current;
30
47
 
31
48
  // The rejection handler swallows clipboard write failures (permission denied,
32
49
  // API unavailable) so they don't surface as unhandled promise rejections.
33
50
  Promise.resolve(copyToClipboard(valueToCopy)).then(
34
51
  () => {
52
+ if (scopeGeneration !== scopeGenerationRef.current) return;
53
+
54
+ if (copiedTimerRef.current !== undefined) {
55
+ clearTimeout(copiedTimerRef.current);
56
+ }
35
57
  aui.message.setIsCopied(true);
36
- setTimeout(() => aui.message.setIsCopied(false), copiedDuration);
58
+ copiedTimerRef.current = setTimeout(() => {
59
+ copiedTimerRef.current = undefined;
60
+ aui.message.setIsCopied(false);
61
+ }, copiedDuration);
37
62
  },
38
63
  () => {},
39
64
  );
@@ -8,6 +8,7 @@ import type {
8
8
  MessagePartStatus,
9
9
  ToolCallMessagePartStatus,
10
10
  } from "../../../types/message";
11
+ import { getGroupStatus } from "../../../utils/getGroupStatus";
11
12
  import {
12
13
  buildGroupTree,
13
14
  GROUPBY_MEMO_KEY,
@@ -21,7 +22,8 @@ export namespace MessagePrimitiveGroupedParts {
21
22
  * A coalesced group of adjacent parts. Surfaced through the same
22
23
  * `{ part }` channel as a leaf {@link EnrichedPartState} so consumers
23
24
  * dispatch on a single `switch (part.type)`. `type` is the group key
24
- * (always `"group-…"`); `status` mirrors the last contained part.
25
+ * (always `"group-…"`); `status` is running when any contained part runs,
26
+ * otherwise it mirrors the last contained part.
25
27
  */
26
28
  export type GroupPart<TKey extends `group-${string}` = `group-${string}`> = {
27
29
  readonly type: TKey;
@@ -133,8 +135,6 @@ export namespace MessagePrimitiveGroupedParts {
133
135
  };
134
136
  }
135
137
 
136
- const COMPLETE_STATUS: MessagePartStatus = Object.freeze({ type: "complete" });
137
-
138
138
  const shouldShowIndicator = (
139
139
  mode: MessagePrimitiveGroupedParts.IndicatorMode,
140
140
  parts: readonly PartState[],
@@ -194,7 +194,7 @@ const renderNode = <TKey extends `group-${string}`>(
194
194
  );
195
195
  }
196
196
 
197
- const status = parts[node.indices.at(-1)!]?.status ?? COMPLETE_STATUS;
197
+ const status = getGroupStatus(parts, node.indices);
198
198
  const groupPart: MessagePrimitiveGroupedParts.GroupPart<TKey> = {
199
199
  type: node.key as TKey,
200
200
  status,