@copilotkit/vue 1.57.1
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/AGENTS.md +50 -0
- package/CHANGELOG.md +13 -0
- package/PARITY.md +434 -0
- package/README.md +396 -0
- package/dist/components/copilot-provider/CopilotKit.vue.d.ts +20 -0
- package/dist/components/copilot-provider/CopilotKit.vue.d.ts.map +1 -0
- package/dist/components/copilot-provider/index.d.ts +3 -0
- package/dist/components/copilot-provider/index.d.ts.map +1 -0
- package/dist/components/copilot-provider/types.d.ts +22 -0
- package/dist/components/copilot-provider/types.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +7 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/use-copilot-action.d.ts +27 -0
- package/dist/hooks/use-copilot-action.d.ts.map +1 -0
- package/dist/hooks/use-copilot-readable.d.ts +20 -0
- package/dist/hooks/use-copilot-readable.d.ts.map +1 -0
- package/dist/hooks/use-frontend-tool.d.ts +21 -0
- package/dist/hooks/use-frontend-tool.d.ts.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +10 -0
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +252 -0
- package/dist/index.mjs.map +1 -0
- package/dist/styles.css +2 -0
- package/dist/use-render-activity-message-BRL1Rpl-.cjs +85 -0
- package/dist/use-render-activity-message-BRL1Rpl-.cjs.map +1 -0
- package/dist/use-render-activity-message-CqtxiFSs.js +8927 -0
- package/dist/use-render-activity-message-CqtxiFSs.js.map +1 -0
- package/dist/v2/components/A2UIMessageRenderer.d.ts +9 -0
- package/dist/v2/components/A2UIMessageRenderer.d.ts.map +1 -0
- package/dist/v2/components/A2UISurfaceActivityRenderer.vue.d.ts +16 -0
- package/dist/v2/components/A2UISurfaceActivityRenderer.vue.d.ts.map +1 -0
- package/dist/v2/components/CopilotKitInspector.vue.d.ts +7 -0
- package/dist/v2/components/CopilotKitInspector.vue.d.ts.map +1 -0
- package/dist/v2/components/InlineFeatureWarning.vue.d.ts +6 -0
- package/dist/v2/components/InlineFeatureWarning.vue.d.ts.map +1 -0
- package/dist/v2/components/LicenseWarningBanner.vue.d.ts +18 -0
- package/dist/v2/components/LicenseWarningBanner.vue.d.ts.map +1 -0
- package/dist/v2/components/MCPAppsActivityRenderer.d.ts +88 -0
- package/dist/v2/components/MCPAppsActivityRenderer.d.ts.map +1 -0
- package/dist/v2/components/OpenGenerativeUIRenderer.d.ts +154 -0
- package/dist/v2/components/OpenGenerativeUIRenderer.d.ts.map +1 -0
- package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts +19 -0
- package/dist/v2/components/a2ui/A2UIBuiltInToolCallRenderer.d.ts.map +1 -0
- package/dist/v2/components/a2ui/A2UICatalogContext.d.ts +16 -0
- package/dist/v2/components/a2ui/A2UICatalogContext.d.ts.map +1 -0
- package/dist/v2/components/a2ui/VueSurface.d.ts +62 -0
- package/dist/v2/components/a2ui/VueSurface.d.ts.map +1 -0
- package/dist/v2/components/a2ui/adapter.d.ts +38 -0
- package/dist/v2/components/a2ui/adapter.d.ts.map +1 -0
- package/dist/v2/components/a2ui/catalog.d.ts +29 -0
- package/dist/v2/components/a2ui/catalog.d.ts.map +1 -0
- package/dist/v2/components/a2ui/index.d.ts +5 -0
- package/dist/v2/components/a2ui/index.d.ts.map +1 -0
- package/dist/v2/components/a2ui/utils.d.ts +18 -0
- package/dist/v2/components/a2ui/utils.d.ts.map +1 -0
- package/dist/v2/components/a2ui.d.ts +12 -0
- package/dist/v2/components/a2ui.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChat.vue.d.ts +50 -0
- package/dist/v2/components/chat/CopilotChat.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts +164 -0
- package/dist/v2/components/chat/CopilotChatAssistantMessage.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatAttachmentQueue.vue.d.ts +12 -0
- package/dist/v2/components/chat/CopilotChatAttachmentQueue.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatAttachmentRenderer.vue.d.ts +7 -0
- package/dist/v2/components/chat/CopilotChatAttachmentRenderer.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatAudioRecorder.vue.d.ts +12 -0
- package/dist/v2/components/chat/CopilotChatAudioRecorder.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatInput.vue.d.ts +290 -0
- package/dist/v2/components/chat/CopilotChatInput.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts +72 -0
- package/dist/v2/components/chat/CopilotChatMessageView.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatReasoningMessage.vue.d.ts +65 -0
- package/dist/v2/components/chat/CopilotChatReasoningMessage.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatSuggestionPill.vue.d.ts +27 -0
- package/dist/v2/components/chat/CopilotChatSuggestionPill.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatSuggestionView.vue.d.ts +26 -0
- package/dist/v2/components/chat/CopilotChatSuggestionView.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatToggleButton.vue.d.ts +17 -0
- package/dist/v2/components/chat/CopilotChatToggleButton.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatToggleButtonCloseIcon.d.ts +5 -0
- package/dist/v2/components/chat/CopilotChatToggleButtonCloseIcon.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatToggleButtonOpenIcon.d.ts +5 -0
- package/dist/v2/components/chat/CopilotChatToggleButtonOpenIcon.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts +21 -0
- package/dist/v2/components/chat/CopilotChatToolCallsView.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatUserMessage.vue.d.ts +34 -0
- package/dist/v2/components/chat/CopilotChatUserMessage.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotChatView.vue.d.ts +106 -0
- package/dist/v2/components/chat/CopilotChatView.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotModalHeader.vue.d.ts +15 -0
- package/dist/v2/components/chat/CopilotModalHeader.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotModalHeaderCloseButton.d.ts +5 -0
- package/dist/v2/components/chat/CopilotModalHeaderCloseButton.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotModalHeaderTitle.d.ts +5 -0
- package/dist/v2/components/chat/CopilotModalHeaderTitle.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotPopup.vue.d.ts +50 -0
- package/dist/v2/components/chat/CopilotPopup.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotPopupView.vue.d.ts +55 -0
- package/dist/v2/components/chat/CopilotPopupView.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts +55 -0
- package/dist/v2/components/chat/CopilotPopupViewInternal.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotPopupWelcomeScreen.vue.d.ts +28 -0
- package/dist/v2/components/chat/CopilotPopupWelcomeScreen.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotSidebar.vue.d.ts +48 -0
- package/dist/v2/components/chat/CopilotSidebar.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotSidebarView.vue.d.ts +62 -0
- package/dist/v2/components/chat/CopilotSidebarView.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts +53 -0
- package/dist/v2/components/chat/CopilotSidebarViewInternal.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/CopilotSidebarWelcomeScreen.vue.d.ts +28 -0
- package/dist/v2/components/chat/CopilotSidebarWelcomeScreen.vue.d.ts.map +1 -0
- package/dist/v2/components/chat/audioRecorder.d.ts +11 -0
- package/dist/v2/components/chat/audioRecorder.d.ts.map +1 -0
- package/dist/v2/components/chat/index.d.ts +682 -0
- package/dist/v2/components/chat/index.d.ts.map +1 -0
- package/dist/v2/components/chat/last-user-message-context.d.ts +29 -0
- package/dist/v2/components/chat/last-user-message-context.d.ts.map +1 -0
- package/dist/v2/components/chat/normalize-auto-scroll.d.ts +3 -0
- package/dist/v2/components/chat/normalize-auto-scroll.d.ts.map +1 -0
- package/dist/v2/components/chat/types.d.ts +380 -0
- package/dist/v2/components/chat/types.d.ts.map +1 -0
- package/dist/v2/components/icons/index.d.ts +2 -0
- package/dist/v2/components/icons/index.d.ts.map +1 -0
- package/dist/v2/components/index.d.ts +8 -0
- package/dist/v2/components/index.d.ts.map +1 -0
- package/dist/v2/hooks/index.d.ts +24 -0
- package/dist/v2/hooks/index.d.ts.map +1 -0
- package/dist/v2/hooks/use-agent-context.d.ts +24 -0
- package/dist/v2/hooks/use-agent-context.d.ts.map +1 -0
- package/dist/v2/hooks/use-agent.d.ts +53 -0
- package/dist/v2/hooks/use-agent.d.ts.map +1 -0
- package/dist/v2/hooks/use-attachments.d.ts +21 -0
- package/dist/v2/hooks/use-attachments.d.ts.map +1 -0
- package/dist/v2/hooks/use-capabilities.d.ts +16 -0
- package/dist/v2/hooks/use-capabilities.d.ts.map +1 -0
- package/dist/v2/hooks/use-component.d.ts +13 -0
- package/dist/v2/hooks/use-component.d.ts.map +1 -0
- package/dist/v2/hooks/use-configure-suggestions.d.ts +24 -0
- package/dist/v2/hooks/use-configure-suggestions.d.ts.map +1 -0
- package/dist/v2/hooks/use-default-render-tool.d.ts +14 -0
- package/dist/v2/hooks/use-default-render-tool.d.ts.map +1 -0
- package/dist/v2/hooks/use-frontend-tool.d.ts +19 -0
- package/dist/v2/hooks/use-frontend-tool.d.ts.map +1 -0
- package/dist/v2/hooks/use-human-in-the-loop.d.ts +19 -0
- package/dist/v2/hooks/use-human-in-the-loop.d.ts.map +1 -0
- package/dist/v2/hooks/use-interrupt.d.ts +36 -0
- package/dist/v2/hooks/use-interrupt.d.ts.map +1 -0
- package/dist/v2/hooks/use-katex-styles.d.ts +22 -0
- package/dist/v2/hooks/use-katex-styles.d.ts.map +1 -0
- package/dist/v2/hooks/use-keyboard-height.d.ts +33 -0
- package/dist/v2/hooks/use-keyboard-height.d.ts.map +1 -0
- package/dist/v2/hooks/use-pin-to-send.d.ts +28 -0
- package/dist/v2/hooks/use-pin-to-send.d.ts.map +1 -0
- package/dist/v2/hooks/use-render-activity-message.d.ts +21 -0
- package/dist/v2/hooks/use-render-activity-message.d.ts.map +1 -0
- package/dist/v2/hooks/use-render-custom-messages.d.ts +27 -0
- package/dist/v2/hooks/use-render-custom-messages.d.ts.map +1 -0
- package/dist/v2/hooks/use-render-tool.d.ts +36 -0
- package/dist/v2/hooks/use-render-tool.d.ts.map +1 -0
- package/dist/v2/hooks/use-suggestions.d.ts +26 -0
- package/dist/v2/hooks/use-suggestions.d.ts.map +1 -0
- package/dist/v2/hooks/use-threads.d.ts +42 -0
- package/dist/v2/hooks/use-threads.d.ts.map +1 -0
- package/dist/v2/index.cjs +2 -0
- package/dist/v2/index.cjs.map +1 -0
- package/dist/v2/index.d.cts +9 -0
- package/dist/v2/index.d.mts +9 -0
- package/dist/v2/index.d.ts +9 -0
- package/dist/v2/index.d.ts.map +1 -0
- package/dist/v2/index.mjs +75 -0
- package/dist/v2/index.mjs.map +1 -0
- package/dist/v2/lib/processPartialHtml.d.ts +3 -0
- package/dist/v2/lib/processPartialHtml.d.ts.map +1 -0
- package/dist/v2/lib/shallow-stable.d.ts +7 -0
- package/dist/v2/lib/shallow-stable.d.ts.map +1 -0
- package/dist/v2/lib/transcription-client.d.ts +19 -0
- package/dist/v2/lib/transcription-client.d.ts.map +1 -0
- package/dist/v2/lib/vue-core.d.ts +47 -0
- package/dist/v2/lib/vue-core.d.ts.map +1 -0
- package/dist/v2/providers/CopilotChatConfigurationProvider.types.d.ts +15 -0
- package/dist/v2/providers/CopilotChatConfigurationProvider.types.d.ts.map +1 -0
- package/dist/v2/providers/CopilotChatConfigurationProvider.vue.d.ts +17 -0
- package/dist/v2/providers/CopilotChatConfigurationProvider.vue.d.ts.map +1 -0
- package/dist/v2/providers/CopilotKitProvider.types.d.ts +61 -0
- package/dist/v2/providers/CopilotKitProvider.types.d.ts.map +1 -0
- package/dist/v2/providers/CopilotKitProvider.vue.d.ts +37 -0
- package/dist/v2/providers/CopilotKitProvider.vue.d.ts.map +1 -0
- package/dist/v2/providers/SandboxFunctionsContext.d.ts +4 -0
- package/dist/v2/providers/SandboxFunctionsContext.d.ts.map +1 -0
- package/dist/v2/providers/index.d.ts +13 -0
- package/dist/v2/providers/index.d.ts.map +1 -0
- package/dist/v2/providers/keys.d.ts +17 -0
- package/dist/v2/providers/keys.d.ts.map +1 -0
- package/dist/v2/providers/license-context.d.ts +7 -0
- package/dist/v2/providers/license-context.d.ts.map +1 -0
- package/dist/v2/providers/types.d.ts +38 -0
- package/dist/v2/providers/types.d.ts.map +1 -0
- package/dist/v2/providers/useCopilotChatConfiguration.d.ts +4 -0
- package/dist/v2/providers/useCopilotChatConfiguration.d.ts.map +1 -0
- package/dist/v2/providers/useCopilotKit.d.ts +2 -0
- package/dist/v2/providers/useCopilotKit.d.ts.map +1 -0
- package/dist/v2/providers/useLicenseContext.d.ts +14 -0
- package/dist/v2/providers/useLicenseContext.d.ts.map +1 -0
- package/dist/v2/types/a2ui.d.ts +5 -0
- package/dist/v2/types/a2ui.d.ts.map +1 -0
- package/dist/v2/types/defineToolCallRenderer.d.ts +15 -0
- package/dist/v2/types/defineToolCallRenderer.d.ts.map +1 -0
- package/dist/v2/types/frontend-tool.d.ts +6 -0
- package/dist/v2/types/frontend-tool.d.ts.map +1 -0
- package/dist/v2/types/human-in-the-loop.d.ts +29 -0
- package/dist/v2/types/human-in-the-loop.d.ts.map +1 -0
- package/dist/v2/types/index.d.ts +10 -0
- package/dist/v2/types/index.d.ts.map +1 -0
- package/dist/v2/types/interrupt.d.ts +14 -0
- package/dist/v2/types/interrupt.d.ts.map +1 -0
- package/dist/v2/types/sandbox-function.d.ts +8 -0
- package/dist/v2/types/sandbox-function.d.ts.map +1 -0
- package/dist/v2/types/vue-activity-message-renderer.d.ts +18 -0
- package/dist/v2/types/vue-activity-message-renderer.d.ts.map +1 -0
- package/dist/v2/types/vue-custom-message-renderer.d.ts +19 -0
- package/dist/v2/types/vue-custom-message-renderer.d.ts.map +1 -0
- package/dist/v2/types/vue-tool-call-renderer.d.ts +37 -0
- package/dist/v2/types/vue-tool-call-renderer.d.ts.map +1 -0
- package/env.d.ts +7 -0
- package/eslint.config.mjs +42 -0
- package/package.json +130 -0
- package/scripts/scope-preflight.mjs +100 -0
- package/src/components/copilot-provider/CopilotKit.vue +18 -0
- package/src/components/copilot-provider/index.ts +2 -0
- package/src/components/copilot-provider/types.ts +24 -0
- package/src/hooks/index.ts +9 -0
- package/src/hooks/use-copilot-action.ts +168 -0
- package/src/hooks/use-copilot-readable.ts +75 -0
- package/src/hooks/use-frontend-tool.ts +76 -0
- package/src/index.ts +12 -0
- package/src/styles/globals.css +314 -0
- package/src/v2/__tests__/exports.test.ts +35 -0
- package/src/v2/__tests__/mocks/web-inspector.ts +5 -0
- package/src/v2/__tests__/setup.ts +141 -0
- package/src/v2/__tests__/utils/agents.ts +391 -0
- package/src/v2/__tests__/utils/mount.ts +83 -0
- package/src/v2/__tests__/utils/test-helpers.ts +712 -0
- package/src/v2/components/A2UIMessageRenderer.ts +125 -0
- package/src/v2/components/A2UISurfaceActivityRenderer.vue +186 -0
- package/src/v2/components/CopilotKitInspector.vue +42 -0
- package/src/v2/components/InlineFeatureWarning.vue +35 -0
- package/src/v2/components/LicenseWarningBanner.vue +196 -0
- package/src/v2/components/MCPAppsActivityRenderer.ts +778 -0
- package/src/v2/components/OpenGenerativeUIRenderer.ts +550 -0
- package/src/v2/components/__tests__/A2UIMessageRenderer.test.ts +271 -0
- package/src/v2/components/__tests__/CopilotKitInspector.test.ts +57 -0
- package/src/v2/components/__tests__/MCPAppsActivityRenderer.e2e.test.ts +851 -0
- package/src/v2/components/__tests__/MCPAppsActivityRenderer.test.ts +237 -0
- package/src/v2/components/__tests__/OpenGenerativeUIRenderer.test.ts +516 -0
- package/src/v2/components/a2ui/A2UIBuiltInToolCallRenderer.ts +295 -0
- package/src/v2/components/a2ui/A2UICatalogContext.ts +190 -0
- package/src/v2/components/a2ui/VueSurface.ts +144 -0
- package/src/v2/components/a2ui/adapter.ts +156 -0
- package/src/v2/components/a2ui/catalog.ts +858 -0
- package/src/v2/components/a2ui/index.ts +7 -0
- package/src/v2/components/a2ui/utils.ts +67 -0
- package/src/v2/components/a2ui.ts +30 -0
- package/src/v2/components/chat/CopilotChat.vue +777 -0
- package/src/v2/components/chat/CopilotChatAssistantMessage.vue +891 -0
- package/src/v2/components/chat/CopilotChatAttachmentQueue.vue +411 -0
- package/src/v2/components/chat/CopilotChatAttachmentRenderer.vue +87 -0
- package/src/v2/components/chat/CopilotChatAudioRecorder.vue +269 -0
- package/src/v2/components/chat/CopilotChatInput.vue +1271 -0
- package/src/v2/components/chat/CopilotChatMessageView.vue +476 -0
- package/src/v2/components/chat/CopilotChatReasoningMessage.vue +247 -0
- package/src/v2/components/chat/CopilotChatSuggestionPill.vue +56 -0
- package/src/v2/components/chat/CopilotChatSuggestionView.vue +93 -0
- package/src/v2/components/chat/CopilotChatToggleButton.vue +145 -0
- package/src/v2/components/chat/CopilotChatToggleButtonCloseIcon.ts +17 -0
- package/src/v2/components/chat/CopilotChatToggleButtonOpenIcon.ts +18 -0
- package/src/v2/components/chat/CopilotChatToolCallsView.vue +161 -0
- package/src/v2/components/chat/CopilotChatUserMessage.vue +322 -0
- package/src/v2/components/chat/CopilotChatView.vue +740 -0
- package/src/v2/components/chat/CopilotModalHeader.vue +73 -0
- package/src/v2/components/chat/CopilotModalHeaderCloseButton.ts +38 -0
- package/src/v2/components/chat/CopilotModalHeaderTitle.ts +22 -0
- package/src/v2/components/chat/CopilotPopup.vue +182 -0
- package/src/v2/components/chat/CopilotPopupView.vue +168 -0
- package/src/v2/components/chat/CopilotPopupViewInternal.vue +453 -0
- package/src/v2/components/chat/CopilotPopupWelcomeScreen.vue +140 -0
- package/src/v2/components/chat/CopilotSidebar.vue +178 -0
- package/src/v2/components/chat/CopilotSidebarView.vue +172 -0
- package/src/v2/components/chat/CopilotSidebarViewInternal.vue +366 -0
- package/src/v2/components/chat/CopilotSidebarWelcomeScreen.vue +142 -0
- package/src/v2/components/chat/__tests__/CopilotChat.attachments.test.ts +237 -0
- package/src/v2/components/chat/__tests__/CopilotChat.e2e.test.ts +1240 -0
- package/src/v2/components/chat/__tests__/CopilotChat.licenseWarning.test.ts +138 -0
- package/src/v2/components/chat/__tests__/CopilotChat.onError.test.ts +85 -0
- package/src/v2/components/chat/__tests__/CopilotChat.slots.e2e.test.ts +141 -0
- package/src/v2/components/chat/__tests__/CopilotChat.test.ts +652 -0
- package/src/v2/components/chat/__tests__/CopilotChatActivityRendering.e2e.test.ts +683 -0
- package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.slots.e2e.test.ts +768 -0
- package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.test.ts +1108 -0
- package/src/v2/components/chat/__tests__/CopilotChatAssistantMessage.thumbs.test.ts +87 -0
- package/src/v2/components/chat/__tests__/CopilotChatAttachmentQueue.test.ts +277 -0
- package/src/v2/components/chat/__tests__/CopilotChatAttachmentRenderer.test.ts +124 -0
- package/src/v2/components/chat/__tests__/CopilotChatCopyButton.clipboard.test.ts +230 -0
- package/src/v2/components/chat/__tests__/CopilotChatInput.bottomAnchored.test.ts +83 -0
- package/src/v2/components/chat/__tests__/CopilotChatInput.slots.e2e.test.ts +1139 -0
- package/src/v2/components/chat/__tests__/CopilotChatInput.test.ts +1051 -0
- package/src/v2/components/chat/__tests__/CopilotChatMessageView.slots.e2e.test.ts +141 -0
- package/src/v2/components/chat/__tests__/CopilotChatMessageView.test.ts +494 -0
- package/src/v2/components/chat/__tests__/CopilotChatPropsRerender.e2e.test.ts +181 -0
- package/src/v2/components/chat/__tests__/CopilotChatReasoningMessage.test.ts +73 -0
- package/src/v2/components/chat/__tests__/CopilotChatSuggestionPill.test.ts +73 -0
- package/src/v2/components/chat/__tests__/CopilotChatSuggestionView.slots.e2e.test.ts +674 -0
- package/src/v2/components/chat/__tests__/CopilotChatSuggestionView.test.ts +91 -0
- package/src/v2/components/chat/__tests__/CopilotChatToggleButton.test.ts +93 -0
- package/src/v2/components/chat/__tests__/CopilotChatToolCallsView.test.ts +382 -0
- package/src/v2/components/chat/__tests__/CopilotChatToolRendering.e2e.test.ts +1019 -0
- package/src/v2/components/chat/__tests__/CopilotChatToolRerenders.e2e.test.ts +516 -0
- package/src/v2/components/chat/__tests__/CopilotChatUserMessage.slots.e2e.test.ts +701 -0
- package/src/v2/components/chat/__tests__/CopilotChatUserMessage.test.ts +337 -0
- package/src/v2/components/chat/__tests__/CopilotChatView.connectingGate.test.ts +135 -0
- package/src/v2/components/chat/__tests__/CopilotChatView.inputOverlay.test.ts +278 -0
- package/src/v2/components/chat/__tests__/CopilotChatView.onClick.e2e.test.ts +1082 -0
- package/src/v2/components/chat/__tests__/CopilotChatView.pinToSend.test.ts +166 -0
- package/src/v2/components/chat/__tests__/CopilotChatView.slots.e2e.test.ts +1145 -0
- package/src/v2/components/chat/__tests__/CopilotChatView.test.ts +374 -0
- package/src/v2/components/chat/__tests__/CopilotModalHeader.slots.e2e.test.ts +636 -0
- package/src/v2/components/chat/__tests__/CopilotModalHeader.test.ts +112 -0
- package/src/v2/components/chat/__tests__/CopilotPopup.test.ts +58 -0
- package/src/v2/components/chat/__tests__/CopilotPopupView.slots.e2e.test.ts +725 -0
- package/src/v2/components/chat/__tests__/CopilotPopupView.test.ts +112 -0
- package/src/v2/components/chat/__tests__/CopilotSidebar.test.ts +58 -0
- package/src/v2/components/chat/__tests__/CopilotSidebarView.slots.e2e.test.ts +603 -0
- package/src/v2/components/chat/__tests__/CopilotSidebarView.test.ts +214 -0
- package/src/v2/components/chat/__tests__/MCPAppsUiMessage.e2e.test.ts +394 -0
- package/src/v2/components/chat/__tests__/copilot-chat-throttle.test.ts +82 -0
- package/src/v2/components/chat/__tests__/normalize-auto-scroll.test.ts +39 -0
- package/src/v2/components/chat/audioRecorder.ts +15 -0
- package/src/v2/components/chat/index.ts +52 -0
- package/src/v2/components/chat/last-user-message-context.ts +39 -0
- package/src/v2/components/chat/normalize-auto-scroll.ts +17 -0
- package/src/v2/components/chat/types.ts +481 -0
- package/src/v2/components/icons/__tests__/icons.test.ts +86 -0
- package/src/v2/components/icons/index.ts +22 -0
- package/src/v2/components/index.ts +7 -0
- package/src/v2/hooks/__tests__/standard-schema-types.test.ts +149 -0
- package/src/v2/hooks/__tests__/standard-schema.test.ts +315 -0
- package/src/v2/hooks/__tests__/use-agent-context-timing.e2e.test.ts +144 -0
- package/src/v2/hooks/__tests__/use-agent-context.test.ts +271 -0
- package/src/v2/hooks/__tests__/use-agent-error-state.test.ts +64 -0
- package/src/v2/hooks/__tests__/use-agent-stability.test.ts +268 -0
- package/src/v2/hooks/__tests__/use-agent-thread-isolation.test.ts +433 -0
- package/src/v2/hooks/__tests__/use-agent-throttle.test.ts +747 -0
- package/src/v2/hooks/__tests__/use-agent.e2e.test.ts +187 -0
- package/src/v2/hooks/__tests__/use-agent.test.ts +126 -0
- package/src/v2/hooks/__tests__/use-attachments.test.ts +181 -0
- package/src/v2/hooks/__tests__/use-component.test.ts +145 -0
- package/src/v2/hooks/__tests__/use-configure-suggestions.e2e.test.ts +527 -0
- package/src/v2/hooks/__tests__/use-configure-suggestions.test.ts +399 -0
- package/src/v2/hooks/__tests__/use-default-render-tool.test.ts +214 -0
- package/src/v2/hooks/__tests__/use-frontend-tool-available.test.ts +220 -0
- package/src/v2/hooks/__tests__/use-frontend-tool.e2e.test.ts +2320 -0
- package/src/v2/hooks/__tests__/use-frontend-tool.test.ts +648 -0
- package/src/v2/hooks/__tests__/use-human-in-the-loop.e2e.test.ts +1379 -0
- package/src/v2/hooks/__tests__/use-human-in-the-loop.test.ts +282 -0
- package/src/v2/hooks/__tests__/use-interrupt.test.ts +345 -0
- package/src/v2/hooks/__tests__/use-katex-styles.test.ts +69 -0
- package/src/v2/hooks/__tests__/use-keyboard-height.test.ts +199 -0
- package/src/v2/hooks/__tests__/use-pin-to-send.test.ts +363 -0
- package/src/v2/hooks/__tests__/use-render-tool.test.ts +329 -0
- package/src/v2/hooks/__tests__/use-suggestions.e2e.test.ts +397 -0
- package/src/v2/hooks/__tests__/use-suggestions.test.ts +198 -0
- package/src/v2/hooks/__tests__/use-threads.test.ts +1041 -0
- package/src/v2/hooks/__tests__/zod-regression.test.ts +339 -0
- package/src/v2/hooks/index.ts +29 -0
- package/src/v2/hooks/use-agent-context.ts +55 -0
- package/src/v2/hooks/use-agent.ts +345 -0
- package/src/v2/hooks/use-attachments.ts +261 -0
- package/src/v2/hooks/use-capabilities.ts +30 -0
- package/src/v2/hooks/use-component.ts +46 -0
- package/src/v2/hooks/use-configure-suggestions.ts +252 -0
- package/src/v2/hooks/use-default-render-tool.ts +130 -0
- package/src/v2/hooks/use-frontend-tool.ts +68 -0
- package/src/v2/hooks/use-human-in-the-loop.ts +90 -0
- package/src/v2/hooks/use-interrupt.ts +257 -0
- package/src/v2/hooks/use-katex-styles.ts +44 -0
- package/src/v2/hooks/use-keyboard-height.ts +87 -0
- package/src/v2/hooks/use-pin-to-send.ts +160 -0
- package/src/v2/hooks/use-render-activity-message.ts +92 -0
- package/src/v2/hooks/use-render-custom-messages.ts +129 -0
- package/src/v2/hooks/use-render-tool.ts +128 -0
- package/src/v2/hooks/use-suggestions.ts +98 -0
- package/src/v2/hooks/use-threads.ts +208 -0
- package/src/v2/index.ts +11 -0
- package/src/v2/lib/__tests__/processPartialHtml.test.ts +84 -0
- package/src/v2/lib/__tests__/transcription-client.test.ts +65 -0
- package/src/v2/lib/processPartialHtml.ts +21 -0
- package/src/v2/lib/shallow-stable.ts +54 -0
- package/src/v2/lib/transcription-client.ts +151 -0
- package/src/v2/lib/vue-core.ts +161 -0
- package/src/v2/providers/CopilotChatConfigurationProvider.types.ts +15 -0
- package/src/v2/providers/CopilotChatConfigurationProvider.vue +95 -0
- package/src/v2/providers/CopilotKitProvider.types.ts +66 -0
- package/src/v2/providers/CopilotKitProvider.vue +653 -0
- package/src/v2/providers/SandboxFunctionsContext.ts +11 -0
- package/src/v2/providers/__tests__/CopilotChatConfigurationProvider.test.ts +309 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.debug.test.ts +295 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.license.test.ts +110 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.onError.test.ts +67 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.renderCustomMessages.e2e.test.ts +901 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.sandboxFunctions.test.ts +141 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.stability.test.ts +871 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.test.ts +603 -0
- package/src/v2/providers/__tests__/CopilotKitProvider.wildcard.test.ts +104 -0
- package/src/v2/providers/index.ts +21 -0
- package/src/v2/providers/keys.ts +25 -0
- package/src/v2/providers/license-context.ts +16 -0
- package/src/v2/providers/types.ts +40 -0
- package/src/v2/providers/useCopilotChatConfiguration.ts +11 -0
- package/src/v2/providers/useCopilotKit.ts +11 -0
- package/src/v2/providers/useLicenseContext.ts +21 -0
- package/src/v2/types/__tests__/defineToolCallRenderer.test.ts +157 -0
- package/src/v2/types/a2ui.ts +5 -0
- package/src/v2/types/defineToolCallRenderer.ts +32 -0
- package/src/v2/types/frontend-tool.ts +8 -0
- package/src/v2/types/human-in-the-loop.ts +38 -0
- package/src/v2/types/index.ts +9 -0
- package/src/v2/types/interrupt.ts +15 -0
- package/src/v2/types/sandbox-function.ts +8 -0
- package/src/v2/types/vue-activity-message-renderer.ts +22 -0
- package/src/v2/types/vue-custom-message-renderer.ts +24 -0
- package/src/v2/types/vue-tool-call-renderer.ts +44 -0
- package/tsconfig.json +27 -0
- package/vite.config.ts +49 -0
- package/vitest.config.ts +23 -0
|
@@ -0,0 +1,1240 @@
|
|
|
1
|
+
import { cleanup, fireEvent, screen, waitFor } from "@testing-library/vue";
|
|
2
|
+
import { computed, defineComponent, onMounted } from "vue";
|
|
3
|
+
import type { PropType } from "vue";
|
|
4
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { EventType } from "@ag-ui/client";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { useConfigureSuggestions } from "../../../hooks/use-configure-suggestions";
|
|
8
|
+
import {
|
|
9
|
+
SuggestionsProviderAgent,
|
|
10
|
+
emitReasoningSequence,
|
|
11
|
+
MockStepwiseAgent,
|
|
12
|
+
reasoningEndEvent,
|
|
13
|
+
reasoningMessageContentEvent,
|
|
14
|
+
reasoningMessageEndEvent,
|
|
15
|
+
reasoningMessageStartEvent,
|
|
16
|
+
reasoningStartEvent,
|
|
17
|
+
renderWithCopilotKit,
|
|
18
|
+
runFinishedEvent,
|
|
19
|
+
runStartedEvent,
|
|
20
|
+
testId,
|
|
21
|
+
textChunkEvent,
|
|
22
|
+
toolCallChunkEvent,
|
|
23
|
+
toolCallResultEvent,
|
|
24
|
+
} from "../../../__tests__/utils/test-helpers";
|
|
25
|
+
import CopilotChat from "../CopilotChat.vue";
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
cleanup();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
async function submitMessage(value: string) {
|
|
32
|
+
const input = await screen.findByRole("textbox");
|
|
33
|
+
await fireEvent.update(input, value);
|
|
34
|
+
await fireEvent.keyDown(input, { key: "Enter", code: "Enter" });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function submitMessageAndWaitForUserMessage(value: string) {
|
|
38
|
+
await submitMessage(value);
|
|
39
|
+
await waitFor(() => {
|
|
40
|
+
expect(screen.getByText(value)).toBeDefined();
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const ChatWithSuggestions = defineComponent({
|
|
45
|
+
components: { CopilotChat },
|
|
46
|
+
props: {
|
|
47
|
+
consumerAgentId: { type: String, required: true },
|
|
48
|
+
providerAgentId: { type: String, required: true },
|
|
49
|
+
instructions: { type: String, required: false, default: undefined },
|
|
50
|
+
minSuggestions: { type: Number, required: false, default: undefined },
|
|
51
|
+
maxSuggestions: { type: Number, required: false, default: undefined },
|
|
52
|
+
onReady: {
|
|
53
|
+
type: Function as PropType<(() => void) | undefined>,
|
|
54
|
+
required: false,
|
|
55
|
+
default: undefined,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
setup(props) {
|
|
59
|
+
useConfigureSuggestions({
|
|
60
|
+
instructions: props.instructions || "Suggest helpful next actions",
|
|
61
|
+
providerAgentId: props.providerAgentId,
|
|
62
|
+
consumerAgentId: props.consumerAgentId,
|
|
63
|
+
minSuggestions: props.minSuggestions || 2,
|
|
64
|
+
maxSuggestions: props.maxSuggestions || 4,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
onMounted(() => {
|
|
68
|
+
props.onReady?.();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return {};
|
|
72
|
+
},
|
|
73
|
+
template: `
|
|
74
|
+
<CopilotChat :welcome-screen="false" />
|
|
75
|
+
`,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe("CopilotChat E2E - Chat Basics and Streaming Patterns", () => {
|
|
79
|
+
describe("Chat Basics: text input + run", () => {
|
|
80
|
+
it("should display user message and start agent run when Enter is pressed", async () => {
|
|
81
|
+
const agent = new MockStepwiseAgent();
|
|
82
|
+
renderWithCopilotKit({ agent });
|
|
83
|
+
|
|
84
|
+
await submitMessageAndWaitForUserMessage("Hello AI!");
|
|
85
|
+
|
|
86
|
+
const messageId = testId("msg");
|
|
87
|
+
await agent.emit(runStartedEvent());
|
|
88
|
+
agent.emit(textChunkEvent(messageId, "Hello! "));
|
|
89
|
+
agent.emit(textChunkEvent(messageId, "How can I help you today?"));
|
|
90
|
+
agent.emit(runFinishedEvent());
|
|
91
|
+
agent.complete();
|
|
92
|
+
|
|
93
|
+
await waitFor(() => {
|
|
94
|
+
const assistantMessage = screen.getByText(
|
|
95
|
+
"Hello! How can I help you today?",
|
|
96
|
+
);
|
|
97
|
+
expect(assistantMessage).toBeDefined();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("should accumulate text chunks progressively", async () => {
|
|
102
|
+
const agent = new MockStepwiseAgent();
|
|
103
|
+
renderWithCopilotKit({ agent });
|
|
104
|
+
|
|
105
|
+
await submitMessageAndWaitForUserMessage("Tell me a story");
|
|
106
|
+
|
|
107
|
+
const messageId = testId("msg");
|
|
108
|
+
await agent.emit(runStartedEvent());
|
|
109
|
+
|
|
110
|
+
agent.emit(textChunkEvent(messageId, "Once upon"));
|
|
111
|
+
|
|
112
|
+
await waitFor(() => {
|
|
113
|
+
expect(screen.getByText(/Once upon/)).toBeDefined();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
agent.emit(textChunkEvent(messageId, " a time"));
|
|
117
|
+
|
|
118
|
+
await waitFor(() => {
|
|
119
|
+
expect(screen.getByText(/Once upon a time/)).toBeDefined();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
agent.emit(textChunkEvent(messageId, " there was a robot."));
|
|
123
|
+
|
|
124
|
+
await waitFor(() => {
|
|
125
|
+
expect(
|
|
126
|
+
screen.getByText(/Once upon a time there was a robot\./),
|
|
127
|
+
).toBeDefined();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
agent.emit(runFinishedEvent());
|
|
131
|
+
agent.complete();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("should reset chat running state when backend emits RUN_ERROR", async () => {
|
|
135
|
+
const agent = new MockStepwiseAgent();
|
|
136
|
+
|
|
137
|
+
const StatusProbeHost = defineComponent({
|
|
138
|
+
components: { CopilotChat },
|
|
139
|
+
template: `
|
|
140
|
+
<CopilotChat :welcome-screen="false">
|
|
141
|
+
<template #chat-view="{ isRunning, onStop, onSubmitMessage }">
|
|
142
|
+
<div>
|
|
143
|
+
<button data-testid="submit" @click="onSubmitMessage('trigger run')">submit</button>
|
|
144
|
+
<span data-testid="running">{{ isRunning ? "running" : "idle" }}</span>
|
|
145
|
+
<span data-testid="stop-availability">{{ onStop ? "available" : "missing" }}</span>
|
|
146
|
+
</div>
|
|
147
|
+
</template>
|
|
148
|
+
</CopilotChat>
|
|
149
|
+
`,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
renderWithCopilotKit({ agent, children: StatusProbeHost });
|
|
153
|
+
|
|
154
|
+
expect(screen.getByTestId("running").textContent).toBe("idle");
|
|
155
|
+
expect(screen.getByTestId("stop-availability").textContent).toBe(
|
|
156
|
+
"missing",
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
await fireEvent.click(screen.getByTestId("submit"));
|
|
160
|
+
await agent.emit(runStartedEvent());
|
|
161
|
+
|
|
162
|
+
await waitFor(() => {
|
|
163
|
+
expect(screen.getByTestId("running").textContent).toBe("running");
|
|
164
|
+
expect(screen.getByTestId("stop-availability").textContent).toBe(
|
|
165
|
+
"available",
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
await agent.emit({ type: EventType.RUN_ERROR } as any);
|
|
170
|
+
await agent.complete();
|
|
171
|
+
|
|
172
|
+
await waitFor(() => {
|
|
173
|
+
expect(screen.getByTestId("running").textContent).toBe("idle");
|
|
174
|
+
expect(screen.getByTestId("stop-availability").textContent).toBe(
|
|
175
|
+
"missing",
|
|
176
|
+
);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
describe("Single Tool Flow", () => {
|
|
182
|
+
it("should handle complete tool call lifecycle", async () => {
|
|
183
|
+
const agent = new MockStepwiseAgent();
|
|
184
|
+
|
|
185
|
+
const WeatherToolRenderer = defineComponent({
|
|
186
|
+
props: {
|
|
187
|
+
name: { type: String, required: true },
|
|
188
|
+
status: { type: String, required: true },
|
|
189
|
+
args: {
|
|
190
|
+
type: Object as PropType<Record<string, unknown>>,
|
|
191
|
+
required: true,
|
|
192
|
+
},
|
|
193
|
+
result: {
|
|
194
|
+
type: String as PropType<string | undefined>,
|
|
195
|
+
required: false,
|
|
196
|
+
default: undefined,
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
setup(props: {
|
|
200
|
+
name: string;
|
|
201
|
+
status: string;
|
|
202
|
+
args: { location?: string };
|
|
203
|
+
result?: string;
|
|
204
|
+
}) {
|
|
205
|
+
const text = computed(
|
|
206
|
+
() =>
|
|
207
|
+
`Tool: ${props.name} | Status: ${props.status} | Location: ${String(props.args.location ?? "")} |${props.result ? ` Result: ${props.result}` : ""}`,
|
|
208
|
+
);
|
|
209
|
+
return { text };
|
|
210
|
+
},
|
|
211
|
+
template: `<div data-testid="weather-tool">{{ text }}</div>`,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
renderWithCopilotKit({
|
|
215
|
+
agent,
|
|
216
|
+
frontendTools: [
|
|
217
|
+
{
|
|
218
|
+
name: "getWeather",
|
|
219
|
+
parameters: z.object({
|
|
220
|
+
location: z.string(),
|
|
221
|
+
unit: z.string().optional(),
|
|
222
|
+
}),
|
|
223
|
+
render: WeatherToolRenderer,
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
await submitMessageAndWaitForUserMessage("What's the weather?");
|
|
229
|
+
|
|
230
|
+
const messageId = testId("msg");
|
|
231
|
+
const toolCallId = testId("tc");
|
|
232
|
+
|
|
233
|
+
await agent.emit(runStartedEvent());
|
|
234
|
+
agent.emit(
|
|
235
|
+
textChunkEvent(messageId, "Let me check the weather for you."),
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
agent.emit(
|
|
239
|
+
toolCallChunkEvent({
|
|
240
|
+
toolCallId,
|
|
241
|
+
toolCallName: "getWeather",
|
|
242
|
+
parentMessageId: messageId,
|
|
243
|
+
delta: '{"location":"Paris"',
|
|
244
|
+
}),
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
agent.emit(
|
|
248
|
+
toolCallChunkEvent({
|
|
249
|
+
toolCallId,
|
|
250
|
+
parentMessageId: messageId,
|
|
251
|
+
delta: ',"unit":"celsius"}',
|
|
252
|
+
}),
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
await waitFor(() => {
|
|
256
|
+
const tool = screen.getByTestId("weather-tool");
|
|
257
|
+
expect(tool.textContent).toContain("Tool: getWeather");
|
|
258
|
+
expect(tool.textContent).toContain("Location: Paris");
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
agent.emit(
|
|
262
|
+
toolCallResultEvent({
|
|
263
|
+
toolCallId,
|
|
264
|
+
messageId: `${messageId}_result`,
|
|
265
|
+
content: JSON.stringify({ temperature: 22, condition: "Sunny" }),
|
|
266
|
+
}),
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
await waitFor(() => {
|
|
270
|
+
const tool = screen.getByTestId("weather-tool");
|
|
271
|
+
expect(tool.textContent).toContain("temperature");
|
|
272
|
+
expect(tool.textContent).toContain("22");
|
|
273
|
+
expect(tool.textContent).toContain("Sunny");
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
agent.emit(runFinishedEvent());
|
|
277
|
+
agent.complete();
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
describe("Multiple Tools Interleaved", () => {
|
|
282
|
+
it("should handle multiple tool calls in one assistant message", async () => {
|
|
283
|
+
const agent = new MockStepwiseAgent();
|
|
284
|
+
|
|
285
|
+
const WeatherRenderer = defineComponent({
|
|
286
|
+
props: {
|
|
287
|
+
name: { type: String, required: true },
|
|
288
|
+
args: {
|
|
289
|
+
type: Object as PropType<Record<string, unknown>>,
|
|
290
|
+
required: true,
|
|
291
|
+
},
|
|
292
|
+
result: {
|
|
293
|
+
type: String as PropType<string | undefined>,
|
|
294
|
+
required: false,
|
|
295
|
+
default: undefined,
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
setup(props: {
|
|
299
|
+
name: string;
|
|
300
|
+
args: { location?: string };
|
|
301
|
+
result?: string;
|
|
302
|
+
}) {
|
|
303
|
+
const testIdValue = computed(
|
|
304
|
+
() => `weather-${String(props.args.location ?? "")}`,
|
|
305
|
+
);
|
|
306
|
+
const text = computed(
|
|
307
|
+
() =>
|
|
308
|
+
`[${props.name}] Weather for ${String(props.args.location ?? "")}: ${props.result ? props.result : "Loading..."}`,
|
|
309
|
+
);
|
|
310
|
+
return { testIdValue, text };
|
|
311
|
+
},
|
|
312
|
+
template: `<div :data-testid="testIdValue">{{ text }}</div>`,
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const TimeRenderer = defineComponent({
|
|
316
|
+
props: {
|
|
317
|
+
name: { type: String, required: true },
|
|
318
|
+
args: {
|
|
319
|
+
type: Object as PropType<Record<string, unknown>>,
|
|
320
|
+
required: true,
|
|
321
|
+
},
|
|
322
|
+
result: {
|
|
323
|
+
type: String as PropType<string | undefined>,
|
|
324
|
+
required: false,
|
|
325
|
+
default: undefined,
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
setup(props: {
|
|
329
|
+
name: string;
|
|
330
|
+
args: { timezone?: string };
|
|
331
|
+
result?: string;
|
|
332
|
+
}) {
|
|
333
|
+
const testIdValue = computed(
|
|
334
|
+
() => `time-${String(props.args.timezone ?? "")}`,
|
|
335
|
+
);
|
|
336
|
+
const text = computed(
|
|
337
|
+
() =>
|
|
338
|
+
`[${props.name}] Time in ${String(props.args.timezone ?? "")}: ${props.result ? props.result : "Loading..."}`,
|
|
339
|
+
);
|
|
340
|
+
return { testIdValue, text };
|
|
341
|
+
},
|
|
342
|
+
template: `<div :data-testid="testIdValue">{{ text }}</div>`,
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
renderWithCopilotKit({
|
|
346
|
+
agent,
|
|
347
|
+
frontendTools: [
|
|
348
|
+
{
|
|
349
|
+
name: "getWeather",
|
|
350
|
+
parameters: z.object({ location: z.string() }),
|
|
351
|
+
render: WeatherRenderer,
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
name: "getTime",
|
|
355
|
+
parameters: z.object({ timezone: z.string() }),
|
|
356
|
+
render: TimeRenderer,
|
|
357
|
+
},
|
|
358
|
+
],
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
await submitMessageAndWaitForUserMessage("Weather and time please");
|
|
362
|
+
|
|
363
|
+
const messageId = testId("msg");
|
|
364
|
+
const toolCallId1 = testId("tc1");
|
|
365
|
+
const toolCallId2 = testId("tc2");
|
|
366
|
+
|
|
367
|
+
await agent.emit(runStartedEvent());
|
|
368
|
+
agent.emit(textChunkEvent(messageId, "I'll check both for you."));
|
|
369
|
+
|
|
370
|
+
agent.emit(
|
|
371
|
+
toolCallChunkEvent({
|
|
372
|
+
toolCallId: toolCallId1,
|
|
373
|
+
toolCallName: "getWeather",
|
|
374
|
+
parentMessageId: messageId,
|
|
375
|
+
delta: '{"location":"London"}',
|
|
376
|
+
}),
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
agent.emit(
|
|
380
|
+
toolCallChunkEvent({
|
|
381
|
+
toolCallId: toolCallId2,
|
|
382
|
+
toolCallName: "getTime",
|
|
383
|
+
parentMessageId: messageId,
|
|
384
|
+
delta: '{"timezone":"UTC"}',
|
|
385
|
+
}),
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
await waitFor(() => {
|
|
389
|
+
expect(screen.getByTestId("weather-London")).toBeDefined();
|
|
390
|
+
expect(screen.getByTestId("time-UTC")).toBeDefined();
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
agent.emit(
|
|
394
|
+
toolCallResultEvent({
|
|
395
|
+
toolCallId: toolCallId2,
|
|
396
|
+
messageId: `${messageId}_result2`,
|
|
397
|
+
content: JSON.stringify({ time: "12:00 PM" }),
|
|
398
|
+
}),
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
agent.emit(
|
|
402
|
+
toolCallResultEvent({
|
|
403
|
+
toolCallId: toolCallId1,
|
|
404
|
+
messageId: `${messageId}_result1`,
|
|
405
|
+
content: JSON.stringify({ temp: 18, condition: "Cloudy" }),
|
|
406
|
+
}),
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
await waitFor(() => {
|
|
410
|
+
const weatherTool = screen.getByTestId("weather-London");
|
|
411
|
+
const timeTool = screen.getByTestId("time-UTC");
|
|
412
|
+
|
|
413
|
+
expect(weatherTool.textContent).toContain("[getWeather]");
|
|
414
|
+
expect(weatherTool.textContent).toContain("18");
|
|
415
|
+
expect(weatherTool.textContent).toContain("Cloudy");
|
|
416
|
+
expect(timeTool.textContent).toContain("[getTime]");
|
|
417
|
+
expect(timeTool.textContent).toContain("12:00 PM");
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
agent.emit(runFinishedEvent());
|
|
421
|
+
agent.complete();
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
describe("Wildcard Fallback", () => {
|
|
426
|
+
it("should use wildcard renderer when no specific renderer exists", async () => {
|
|
427
|
+
const agent = new MockStepwiseAgent();
|
|
428
|
+
|
|
429
|
+
const WildcardRenderer = defineComponent({
|
|
430
|
+
props: {
|
|
431
|
+
name: { type: String, required: true },
|
|
432
|
+
args: {
|
|
433
|
+
type: Object as PropType<Record<string, unknown>>,
|
|
434
|
+
required: true,
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
setup(props: { name: string; args: Record<string, unknown> }) {
|
|
438
|
+
const text = computed(
|
|
439
|
+
() =>
|
|
440
|
+
`Unknown tool: ${props.name} with args: ${JSON.stringify(props.args)}`,
|
|
441
|
+
);
|
|
442
|
+
return { text };
|
|
443
|
+
},
|
|
444
|
+
template: `<div data-testid="wildcard-renderer">{{ text }}</div>`,
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
renderWithCopilotKit({
|
|
448
|
+
agent,
|
|
449
|
+
frontendTools: [
|
|
450
|
+
{
|
|
451
|
+
name: "*",
|
|
452
|
+
render: WildcardRenderer,
|
|
453
|
+
},
|
|
454
|
+
],
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
await submitMessageAndWaitForUserMessage("Do something unknown");
|
|
458
|
+
|
|
459
|
+
const messageId = testId("msg");
|
|
460
|
+
const toolCallId = testId("tc");
|
|
461
|
+
|
|
462
|
+
await agent.emit(runStartedEvent());
|
|
463
|
+
|
|
464
|
+
agent.emit(
|
|
465
|
+
toolCallChunkEvent({
|
|
466
|
+
toolCallId,
|
|
467
|
+
toolCallName: "unknownTool",
|
|
468
|
+
parentMessageId: messageId,
|
|
469
|
+
delta: '{"param":"value"}',
|
|
470
|
+
}),
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
await waitFor(() => {
|
|
474
|
+
const wildcard = screen.getByTestId("wildcard-renderer");
|
|
475
|
+
expect(wildcard).toBeDefined();
|
|
476
|
+
expect(wildcard.textContent).toContain("Unknown tool: unknownTool");
|
|
477
|
+
expect(wildcard.textContent).toContain("value");
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
agent.emit(runFinishedEvent());
|
|
481
|
+
agent.complete();
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it("should use wildcard renderer without args definition", async () => {
|
|
485
|
+
const agent = new MockStepwiseAgent();
|
|
486
|
+
|
|
487
|
+
const WildcardNoArgsRenderer = defineComponent({
|
|
488
|
+
props: {
|
|
489
|
+
name: { type: String, required: true },
|
|
490
|
+
args: {
|
|
491
|
+
type: Object as PropType<Record<string, unknown>>,
|
|
492
|
+
required: true,
|
|
493
|
+
},
|
|
494
|
+
},
|
|
495
|
+
setup(props: { name: string; args: Record<string, unknown> }) {
|
|
496
|
+
const argsText = computed(() => JSON.stringify(props.args));
|
|
497
|
+
return { argsText };
|
|
498
|
+
},
|
|
499
|
+
template: `
|
|
500
|
+
<div data-testid="wildcard-no-args">
|
|
501
|
+
<span data-testid="tool-name">{{ name }}</span>
|
|
502
|
+
<span data-testid="tool-args">{{ argsText }}</span>
|
|
503
|
+
</div>
|
|
504
|
+
`,
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
renderWithCopilotKit({
|
|
508
|
+
agent,
|
|
509
|
+
frontendTools: [
|
|
510
|
+
{
|
|
511
|
+
name: "*",
|
|
512
|
+
render: WildcardNoArgsRenderer,
|
|
513
|
+
},
|
|
514
|
+
],
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
await submitMessageAndWaitForUserMessage("Do something");
|
|
518
|
+
|
|
519
|
+
const messageId = testId("msg");
|
|
520
|
+
const toolCallId = testId("tc");
|
|
521
|
+
|
|
522
|
+
await agent.emit(runStartedEvent());
|
|
523
|
+
|
|
524
|
+
agent.emit(
|
|
525
|
+
toolCallChunkEvent({
|
|
526
|
+
toolCallId,
|
|
527
|
+
toolCallName: "myCustomTool",
|
|
528
|
+
parentMessageId: messageId,
|
|
529
|
+
delta: '{"param":"test","value":123}',
|
|
530
|
+
}),
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
await waitFor(() => {
|
|
534
|
+
const wildcard = screen.getByTestId("wildcard-no-args");
|
|
535
|
+
expect(wildcard).toBeDefined();
|
|
536
|
+
|
|
537
|
+
const toolName = screen.getByTestId("tool-name");
|
|
538
|
+
expect(toolName.textContent).toBe("myCustomTool");
|
|
539
|
+
expect(toolName.textContent).not.toBe("*");
|
|
540
|
+
|
|
541
|
+
const toolArgs = screen.getByTestId("tool-args");
|
|
542
|
+
const parsedArgs = JSON.parse(toolArgs.textContent || "{}");
|
|
543
|
+
expect(parsedArgs.param).toBe("test");
|
|
544
|
+
expect(parsedArgs.value).toBe(123);
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
agent.emit(runFinishedEvent());
|
|
548
|
+
agent.complete();
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
it("should not show toolbar for messages with only tool calls and no content", async () => {
|
|
552
|
+
const agent = new MockStepwiseAgent();
|
|
553
|
+
|
|
554
|
+
const TestToolRenderer = defineComponent({
|
|
555
|
+
props: {
|
|
556
|
+
args: {
|
|
557
|
+
type: Object as PropType<Record<string, unknown>>,
|
|
558
|
+
required: true,
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
setup(props: { args: { value?: string } }) {
|
|
562
|
+
const text = computed(
|
|
563
|
+
() => `Tool: ${String(props.args.value ?? "")}`,
|
|
564
|
+
);
|
|
565
|
+
return { text };
|
|
566
|
+
},
|
|
567
|
+
template: `<div data-testid="test-tool">{{ text }}</div>`,
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
renderWithCopilotKit({
|
|
571
|
+
agent,
|
|
572
|
+
frontendTools: [
|
|
573
|
+
{
|
|
574
|
+
name: "testTool",
|
|
575
|
+
parameters: z.object({ value: z.string() }),
|
|
576
|
+
render: TestToolRenderer,
|
|
577
|
+
},
|
|
578
|
+
],
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
await submitMessageAndWaitForUserMessage("Use test tool");
|
|
582
|
+
|
|
583
|
+
const messageId = testId("msg");
|
|
584
|
+
const toolCallId = testId("tc");
|
|
585
|
+
|
|
586
|
+
await agent.emit(runStartedEvent());
|
|
587
|
+
|
|
588
|
+
agent.emit(
|
|
589
|
+
toolCallChunkEvent({
|
|
590
|
+
toolCallId,
|
|
591
|
+
toolCallName: "testTool",
|
|
592
|
+
parentMessageId: messageId,
|
|
593
|
+
delta: '{"value":"test"}',
|
|
594
|
+
}),
|
|
595
|
+
);
|
|
596
|
+
|
|
597
|
+
await waitFor(() => {
|
|
598
|
+
const toolRender = screen.getByTestId("test-tool");
|
|
599
|
+
expect(toolRender).toBeDefined();
|
|
600
|
+
expect(toolRender.textContent).toContain("Tool: test");
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
await waitFor(() => {
|
|
604
|
+
const assistantMessageDiv = screen
|
|
605
|
+
.getByTestId("test-tool")
|
|
606
|
+
.closest("[data-message-id]");
|
|
607
|
+
|
|
608
|
+
if (assistantMessageDiv) {
|
|
609
|
+
const copyButtonsInAssistant = assistantMessageDiv.querySelectorAll(
|
|
610
|
+
"button[aria-label*='Copy' i], button[aria-label*='copy' i]",
|
|
611
|
+
);
|
|
612
|
+
expect(copyButtonsInAssistant.length).toBe(0);
|
|
613
|
+
}
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
const messageWithContentId = testId("msg2");
|
|
617
|
+
agent.emit(
|
|
618
|
+
textChunkEvent(
|
|
619
|
+
messageWithContentId,
|
|
620
|
+
"Here is some actual text content",
|
|
621
|
+
),
|
|
622
|
+
);
|
|
623
|
+
|
|
624
|
+
await waitFor(() => {
|
|
625
|
+
const allMessages = screen.getAllByText(
|
|
626
|
+
/Here is some actual text content/,
|
|
627
|
+
);
|
|
628
|
+
expect(allMessages.length).toBeGreaterThan(0);
|
|
629
|
+
|
|
630
|
+
const toolbarButtons = screen.getAllByRole("button");
|
|
631
|
+
const copyButton = toolbarButtons.find((btn) =>
|
|
632
|
+
btn.getAttribute("aria-label")?.toLowerCase().includes("copy"),
|
|
633
|
+
);
|
|
634
|
+
expect(copyButton).toBeDefined();
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
agent.emit(runFinishedEvent());
|
|
638
|
+
agent.complete();
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
it("should prefer specific renderer over wildcard when both exist", async () => {
|
|
642
|
+
const agent = new MockStepwiseAgent();
|
|
643
|
+
|
|
644
|
+
const SpecificRenderer = defineComponent({
|
|
645
|
+
props: {
|
|
646
|
+
args: {
|
|
647
|
+
type: Object as PropType<Record<string, unknown>>,
|
|
648
|
+
required: true,
|
|
649
|
+
},
|
|
650
|
+
},
|
|
651
|
+
setup(props: { args: { value?: string } }) {
|
|
652
|
+
const text = computed(
|
|
653
|
+
() => `Specific: ${String(props.args.value ?? "")}`,
|
|
654
|
+
);
|
|
655
|
+
return { text };
|
|
656
|
+
},
|
|
657
|
+
template: `<div data-testid="specific-renderer">{{ text }}</div>`,
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
const WildcardRenderer = defineComponent({
|
|
661
|
+
props: {
|
|
662
|
+
name: { type: String, required: true },
|
|
663
|
+
},
|
|
664
|
+
setup(props: { name: string }) {
|
|
665
|
+
const text = computed(() => `Wildcard: ${props.name}`);
|
|
666
|
+
return { text };
|
|
667
|
+
},
|
|
668
|
+
template: `<div data-testid="wildcard-renderer">{{ text }}</div>`,
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
renderWithCopilotKit({
|
|
672
|
+
agent,
|
|
673
|
+
frontendTools: [
|
|
674
|
+
{
|
|
675
|
+
name: "specificTool",
|
|
676
|
+
parameters: z.object({ value: z.string() }),
|
|
677
|
+
render: SpecificRenderer,
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
name: "*",
|
|
681
|
+
render: WildcardRenderer,
|
|
682
|
+
},
|
|
683
|
+
],
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
await submitMessageAndWaitForUserMessage("Test specific");
|
|
687
|
+
|
|
688
|
+
const messageId = testId("msg");
|
|
689
|
+
const toolCallId1 = testId("tc1");
|
|
690
|
+
const toolCallId2 = testId("tc2");
|
|
691
|
+
|
|
692
|
+
await agent.emit(runStartedEvent());
|
|
693
|
+
|
|
694
|
+
agent.emit(
|
|
695
|
+
toolCallChunkEvent({
|
|
696
|
+
toolCallId: toolCallId1,
|
|
697
|
+
toolCallName: "specificTool",
|
|
698
|
+
parentMessageId: messageId,
|
|
699
|
+
delta: '{"value":"test123"}',
|
|
700
|
+
}),
|
|
701
|
+
);
|
|
702
|
+
|
|
703
|
+
agent.emit(
|
|
704
|
+
toolCallChunkEvent({
|
|
705
|
+
toolCallId: toolCallId2,
|
|
706
|
+
toolCallName: "unknownTool",
|
|
707
|
+
parentMessageId: messageId,
|
|
708
|
+
delta: '{"data":"xyz"}',
|
|
709
|
+
}),
|
|
710
|
+
);
|
|
711
|
+
|
|
712
|
+
await waitFor(() => {
|
|
713
|
+
const specific = screen.getByTestId("specific-renderer");
|
|
714
|
+
expect(specific).toBeDefined();
|
|
715
|
+
expect(specific.textContent).toContain("test123");
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
await waitFor(() => {
|
|
719
|
+
const wildcard = screen.getByTestId("wildcard-renderer");
|
|
720
|
+
expect(wildcard).toBeDefined();
|
|
721
|
+
expect(wildcard.textContent).toContain("Wildcard: unknownTool");
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
agent.emit(runFinishedEvent());
|
|
725
|
+
agent.complete();
|
|
726
|
+
});
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
describe("Suggestions Flow", () => {
|
|
730
|
+
it("should display suggestions when configured", async () => {
|
|
731
|
+
const consumerAgent = new MockStepwiseAgent();
|
|
732
|
+
const providerAgent = new SuggestionsProviderAgent();
|
|
733
|
+
|
|
734
|
+
providerAgent.setSuggestions([
|
|
735
|
+
{ title: "Option A", message: "Take action A" },
|
|
736
|
+
{ title: "Option B", message: "Take action B" },
|
|
737
|
+
]);
|
|
738
|
+
|
|
739
|
+
let suggestionsReady = false;
|
|
740
|
+
|
|
741
|
+
const SuggestionsHost = defineComponent({
|
|
742
|
+
components: { ChatWithSuggestions },
|
|
743
|
+
setup() {
|
|
744
|
+
return {
|
|
745
|
+
onReady: () => {
|
|
746
|
+
suggestionsReady = true;
|
|
747
|
+
},
|
|
748
|
+
};
|
|
749
|
+
},
|
|
750
|
+
template: `
|
|
751
|
+
<div style="height: 400px;">
|
|
752
|
+
<ChatWithSuggestions
|
|
753
|
+
consumer-agent-id="default"
|
|
754
|
+
provider-agent-id="suggestions-provider"
|
|
755
|
+
:on-ready="onReady"
|
|
756
|
+
/>
|
|
757
|
+
</div>
|
|
758
|
+
`,
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
renderWithCopilotKit({
|
|
762
|
+
agents: {
|
|
763
|
+
default: consumerAgent,
|
|
764
|
+
"suggestions-provider": providerAgent,
|
|
765
|
+
},
|
|
766
|
+
agentId: "default",
|
|
767
|
+
children: SuggestionsHost,
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
await waitFor(() => {
|
|
771
|
+
expect(suggestionsReady).toBe(true);
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
await submitMessageAndWaitForUserMessage("Help me");
|
|
775
|
+
|
|
776
|
+
const messageId = testId("msg");
|
|
777
|
+
await consumerAgent.emit(runStartedEvent());
|
|
778
|
+
consumerAgent.emit(textChunkEvent(messageId, "I can help with that."));
|
|
779
|
+
consumerAgent.emit(runFinishedEvent());
|
|
780
|
+
consumerAgent.complete();
|
|
781
|
+
|
|
782
|
+
await waitFor(() => {
|
|
783
|
+
expect(screen.getByText(/I can help with that/)).toBeDefined();
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
await waitFor(
|
|
787
|
+
() => {
|
|
788
|
+
expect(screen.getByText("Option A")).toBeDefined();
|
|
789
|
+
expect(screen.getByText("Option B")).toBeDefined();
|
|
790
|
+
},
|
|
791
|
+
{ timeout: 5000 },
|
|
792
|
+
);
|
|
793
|
+
|
|
794
|
+
const suggestionA = screen.getByText("Option A");
|
|
795
|
+
await fireEvent.click(suggestionA);
|
|
796
|
+
|
|
797
|
+
await waitFor(() => {
|
|
798
|
+
const messages = screen.getAllByText(/Take action A/);
|
|
799
|
+
expect(messages.length).toBeGreaterThan(0);
|
|
800
|
+
});
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
it("should stream suggestion titles token by token", async () => {
|
|
804
|
+
const consumerAgent = new MockStepwiseAgent();
|
|
805
|
+
const providerAgent = new SuggestionsProviderAgent();
|
|
806
|
+
|
|
807
|
+
providerAgent.setSuggestions([
|
|
808
|
+
{ title: "First Action", message: "Do first action" },
|
|
809
|
+
{ title: "Second Action", message: "Do second action" },
|
|
810
|
+
]);
|
|
811
|
+
|
|
812
|
+
let suggestionsReady = false;
|
|
813
|
+
|
|
814
|
+
const SuggestionsHost = defineComponent({
|
|
815
|
+
components: { ChatWithSuggestions },
|
|
816
|
+
setup() {
|
|
817
|
+
return {
|
|
818
|
+
onReady: () => {
|
|
819
|
+
suggestionsReady = true;
|
|
820
|
+
},
|
|
821
|
+
};
|
|
822
|
+
},
|
|
823
|
+
template: `
|
|
824
|
+
<div style="height: 400px;">
|
|
825
|
+
<ChatWithSuggestions
|
|
826
|
+
consumer-agent-id="default"
|
|
827
|
+
provider-agent-id="suggestions-provider"
|
|
828
|
+
:on-ready="onReady"
|
|
829
|
+
/>
|
|
830
|
+
</div>
|
|
831
|
+
`,
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
renderWithCopilotKit({
|
|
835
|
+
agents: {
|
|
836
|
+
default: consumerAgent,
|
|
837
|
+
"suggestions-provider": providerAgent,
|
|
838
|
+
},
|
|
839
|
+
agentId: "default",
|
|
840
|
+
children: SuggestionsHost,
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
await waitFor(() => {
|
|
844
|
+
expect(suggestionsReady).toBe(true);
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
await submitMessageAndWaitForUserMessage("What can I do?");
|
|
848
|
+
|
|
849
|
+
const messageId = testId("msg");
|
|
850
|
+
await consumerAgent.emit(runStartedEvent());
|
|
851
|
+
consumerAgent.emit(textChunkEvent(messageId, "Here are some options."));
|
|
852
|
+
consumerAgent.emit(runFinishedEvent());
|
|
853
|
+
consumerAgent.complete();
|
|
854
|
+
|
|
855
|
+
await waitFor(
|
|
856
|
+
() => {
|
|
857
|
+
expect(screen.getByText("First Action")).toBeDefined();
|
|
858
|
+
expect(screen.getByText("Second Action")).toBeDefined();
|
|
859
|
+
},
|
|
860
|
+
{ timeout: 5000 },
|
|
861
|
+
);
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
it("should handle multiple suggestions streaming concurrently", async () => {
|
|
865
|
+
const consumerAgent = new MockStepwiseAgent();
|
|
866
|
+
const providerAgent = new SuggestionsProviderAgent();
|
|
867
|
+
|
|
868
|
+
providerAgent.setSuggestions([
|
|
869
|
+
{ title: "Alpha", message: "Do alpha" },
|
|
870
|
+
{ title: "Beta", message: "Do beta" },
|
|
871
|
+
{ title: "Gamma", message: "Do gamma" },
|
|
872
|
+
]);
|
|
873
|
+
|
|
874
|
+
let suggestionsReady = false;
|
|
875
|
+
|
|
876
|
+
const SuggestionsHost = defineComponent({
|
|
877
|
+
components: { ChatWithSuggestions },
|
|
878
|
+
setup() {
|
|
879
|
+
return {
|
|
880
|
+
onReady: () => {
|
|
881
|
+
suggestionsReady = true;
|
|
882
|
+
},
|
|
883
|
+
};
|
|
884
|
+
},
|
|
885
|
+
template: `
|
|
886
|
+
<div style="height: 400px;">
|
|
887
|
+
<ChatWithSuggestions
|
|
888
|
+
consumer-agent-id="default"
|
|
889
|
+
provider-agent-id="suggestions-provider"
|
|
890
|
+
:min-suggestions="3"
|
|
891
|
+
:max-suggestions="5"
|
|
892
|
+
:on-ready="onReady"
|
|
893
|
+
/>
|
|
894
|
+
</div>
|
|
895
|
+
`,
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
renderWithCopilotKit({
|
|
899
|
+
agents: {
|
|
900
|
+
default: consumerAgent,
|
|
901
|
+
"suggestions-provider": providerAgent,
|
|
902
|
+
},
|
|
903
|
+
agentId: "default",
|
|
904
|
+
children: SuggestionsHost,
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
await waitFor(() => {
|
|
908
|
+
expect(suggestionsReady).toBe(true);
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
await submitMessageAndWaitForUserMessage("Show me options");
|
|
912
|
+
|
|
913
|
+
const messageId = testId("msg");
|
|
914
|
+
await consumerAgent.emit(runStartedEvent());
|
|
915
|
+
consumerAgent.emit(textChunkEvent(messageId, "Here you go."));
|
|
916
|
+
consumerAgent.emit(runFinishedEvent());
|
|
917
|
+
consumerAgent.complete();
|
|
918
|
+
|
|
919
|
+
await waitFor(
|
|
920
|
+
() => {
|
|
921
|
+
expect(screen.getByText("Alpha")).toBeDefined();
|
|
922
|
+
expect(screen.getByText("Beta")).toBeDefined();
|
|
923
|
+
expect(screen.getByText("Gamma")).toBeDefined();
|
|
924
|
+
},
|
|
925
|
+
{ timeout: 5000 },
|
|
926
|
+
);
|
|
927
|
+
});
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
describe("Reasoning Message Flow", () => {
|
|
931
|
+
it("should display reasoning message with 'Thinking...' label while streaming", async () => {
|
|
932
|
+
const agent = new MockStepwiseAgent();
|
|
933
|
+
renderWithCopilotKit({ agent });
|
|
934
|
+
|
|
935
|
+
await submitMessageAndWaitForUserMessage("Think about this");
|
|
936
|
+
|
|
937
|
+
const reasoningId = testId("reasoning");
|
|
938
|
+
|
|
939
|
+
await agent.emit(runStartedEvent());
|
|
940
|
+
await agent.emit(reasoningStartEvent(reasoningId));
|
|
941
|
+
await agent.emit(reasoningMessageStartEvent(reasoningId));
|
|
942
|
+
await agent.emit(
|
|
943
|
+
reasoningMessageContentEvent(reasoningId, "Let me analyze..."),
|
|
944
|
+
);
|
|
945
|
+
|
|
946
|
+
await agent.emit(reasoningMessageEndEvent(reasoningId));
|
|
947
|
+
await agent.emit(reasoningEndEvent(reasoningId));
|
|
948
|
+
await agent.emit(runFinishedEvent());
|
|
949
|
+
await agent.complete();
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
it("should display 'Thought for X seconds' after reasoning completes", async () => {
|
|
953
|
+
const agent = new MockStepwiseAgent();
|
|
954
|
+
renderWithCopilotKit({ agent });
|
|
955
|
+
|
|
956
|
+
await submitMessageAndWaitForUserMessage("Reason please");
|
|
957
|
+
|
|
958
|
+
const reasoningId = testId("reasoning");
|
|
959
|
+
const textId = testId("text");
|
|
960
|
+
|
|
961
|
+
await agent.emit(runStartedEvent());
|
|
962
|
+
await emitReasoningSequence(agent, reasoningId, "Some deep thought");
|
|
963
|
+
await agent.emit(textChunkEvent(textId, "Here is my answer."));
|
|
964
|
+
await agent.emit(runFinishedEvent());
|
|
965
|
+
await agent.complete();
|
|
966
|
+
|
|
967
|
+
await waitFor(() => {
|
|
968
|
+
expect(screen.getByText(/Thought for/)).toBeDefined();
|
|
969
|
+
});
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
it("should accumulate content from multiple delta events", async () => {
|
|
973
|
+
const agent = new MockStepwiseAgent();
|
|
974
|
+
renderWithCopilotKit({ agent });
|
|
975
|
+
|
|
976
|
+
await submitMessageAndWaitForUserMessage("Elaborate");
|
|
977
|
+
|
|
978
|
+
const reasoningId = testId("reasoning");
|
|
979
|
+
|
|
980
|
+
await agent.emit(runStartedEvent());
|
|
981
|
+
await agent.emit(reasoningStartEvent(reasoningId));
|
|
982
|
+
await agent.emit(reasoningMessageStartEvent(reasoningId));
|
|
983
|
+
await agent.emit(reasoningMessageContentEvent(reasoningId, "Part 1"));
|
|
984
|
+
await agent.emit(reasoningMessageContentEvent(reasoningId, " Part 2"));
|
|
985
|
+
await agent.emit(reasoningMessageContentEvent(reasoningId, " Part 3"));
|
|
986
|
+
await agent.emit(reasoningMessageEndEvent(reasoningId));
|
|
987
|
+
await agent.emit(reasoningEndEvent(reasoningId));
|
|
988
|
+
|
|
989
|
+
await waitFor(() => {
|
|
990
|
+
expect(screen.getByText(/Part 1 Part 2 Part 3/)).toBeDefined();
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
await agent.emit(runFinishedEvent());
|
|
994
|
+
await agent.complete();
|
|
995
|
+
});
|
|
996
|
+
|
|
997
|
+
it("should render reasoning before text in a single agent run", async () => {
|
|
998
|
+
const agent = new MockStepwiseAgent();
|
|
999
|
+
renderWithCopilotKit({ agent });
|
|
1000
|
+
|
|
1001
|
+
await submitMessageAndWaitForUserMessage("Answer with thought");
|
|
1002
|
+
|
|
1003
|
+
const reasoningId = testId("reasoning");
|
|
1004
|
+
const textId = testId("text");
|
|
1005
|
+
|
|
1006
|
+
await agent.emit(runStartedEvent());
|
|
1007
|
+
await emitReasoningSequence(
|
|
1008
|
+
agent,
|
|
1009
|
+
reasoningId,
|
|
1010
|
+
"Thinking about the answer",
|
|
1011
|
+
);
|
|
1012
|
+
await agent.emit(textChunkEvent(textId, "The answer is 42."));
|
|
1013
|
+
await agent.emit(runFinishedEvent());
|
|
1014
|
+
await agent.complete();
|
|
1015
|
+
|
|
1016
|
+
await waitFor(() => {
|
|
1017
|
+
expect(screen.getByText(/Thought for/)).toBeDefined();
|
|
1018
|
+
expect(screen.getByText("The answer is 42.")).toBeDefined();
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1021
|
+
const reasoningEl = screen
|
|
1022
|
+
.getByText(/Thought for/)
|
|
1023
|
+
.closest("[data-message-id]");
|
|
1024
|
+
const textEl = screen
|
|
1025
|
+
.getByText("The answer is 42.")
|
|
1026
|
+
.closest("[data-message-id]");
|
|
1027
|
+
|
|
1028
|
+
if (reasoningEl && textEl) {
|
|
1029
|
+
const position = reasoningEl.compareDocumentPosition(textEl);
|
|
1030
|
+
expect(position & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
|
1031
|
+
}
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
it("should handle reasoning-only response (no text output)", async () => {
|
|
1035
|
+
const agent = new MockStepwiseAgent();
|
|
1036
|
+
renderWithCopilotKit({ agent });
|
|
1037
|
+
|
|
1038
|
+
await submitMessageAndWaitForUserMessage("Just think");
|
|
1039
|
+
|
|
1040
|
+
const reasoningId = testId("reasoning");
|
|
1041
|
+
|
|
1042
|
+
await agent.emit(runStartedEvent());
|
|
1043
|
+
await emitReasoningSequence(
|
|
1044
|
+
agent,
|
|
1045
|
+
reasoningId,
|
|
1046
|
+
"Only reasoning, no text",
|
|
1047
|
+
);
|
|
1048
|
+
await agent.emit(runFinishedEvent());
|
|
1049
|
+
await agent.complete();
|
|
1050
|
+
|
|
1051
|
+
await waitFor(() => {
|
|
1052
|
+
expect(screen.getByText(/Thought for/)).toBeDefined();
|
|
1053
|
+
});
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
it("should not show cursor when last message is reasoning", async () => {
|
|
1057
|
+
const agent = new MockStepwiseAgent();
|
|
1058
|
+
renderWithCopilotKit({ agent });
|
|
1059
|
+
|
|
1060
|
+
await submitMessageAndWaitForUserMessage("Think deeply");
|
|
1061
|
+
|
|
1062
|
+
const reasoningId = testId("reasoning");
|
|
1063
|
+
|
|
1064
|
+
await agent.emit(runStartedEvent());
|
|
1065
|
+
await agent.emit(reasoningStartEvent(reasoningId));
|
|
1066
|
+
await agent.emit(reasoningMessageStartEvent(reasoningId));
|
|
1067
|
+
await agent.emit(
|
|
1068
|
+
reasoningMessageContentEvent(reasoningId, "Deep reasoning..."),
|
|
1069
|
+
);
|
|
1070
|
+
|
|
1071
|
+
await waitFor(() => {
|
|
1072
|
+
const chatLevelCursor = screen.queryByTestId("copilot-chat-cursor");
|
|
1073
|
+
expect(chatLevelCursor).toBeNull();
|
|
1074
|
+
});
|
|
1075
|
+
|
|
1076
|
+
await agent.emit(reasoningMessageEndEvent(reasoningId));
|
|
1077
|
+
await agent.emit(reasoningEndEvent(reasoningId));
|
|
1078
|
+
await agent.emit(runFinishedEvent());
|
|
1079
|
+
await agent.complete();
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
it("should show cursor after reasoning when text message follows", async () => {
|
|
1083
|
+
const agent = new MockStepwiseAgent();
|
|
1084
|
+
renderWithCopilotKit({ agent });
|
|
1085
|
+
|
|
1086
|
+
await submitMessageAndWaitForUserMessage("Think then answer");
|
|
1087
|
+
|
|
1088
|
+
const reasoningId = testId("reasoning");
|
|
1089
|
+
const textId = testId("text");
|
|
1090
|
+
|
|
1091
|
+
await agent.emit(runStartedEvent());
|
|
1092
|
+
await emitReasoningSequence(agent, reasoningId, "Let me think first");
|
|
1093
|
+
|
|
1094
|
+
await agent.emit(textChunkEvent(textId, "Starting answer..."));
|
|
1095
|
+
|
|
1096
|
+
await waitFor(() => {
|
|
1097
|
+
expect(screen.getByText(/Starting answer/)).toBeDefined();
|
|
1098
|
+
});
|
|
1099
|
+
|
|
1100
|
+
await waitFor(() => {
|
|
1101
|
+
const chatLevelCursor = screen.queryByTestId("copilot-chat-cursor");
|
|
1102
|
+
expect(chatLevelCursor).not.toBeNull();
|
|
1103
|
+
});
|
|
1104
|
+
|
|
1105
|
+
await agent.emit(runFinishedEvent());
|
|
1106
|
+
await agent.complete();
|
|
1107
|
+
});
|
|
1108
|
+
|
|
1109
|
+
it("should not auto-collapse when user manually toggled during streaming", async () => {
|
|
1110
|
+
const agent = new MockStepwiseAgent();
|
|
1111
|
+
renderWithCopilotKit({ agent });
|
|
1112
|
+
|
|
1113
|
+
await submitMessageAndWaitForUserMessage("User toggle test");
|
|
1114
|
+
|
|
1115
|
+
const reasoningId = testId("reasoning");
|
|
1116
|
+
const textId = testId("text");
|
|
1117
|
+
|
|
1118
|
+
await agent.emit(runStartedEvent());
|
|
1119
|
+
await agent.emit(reasoningStartEvent(reasoningId));
|
|
1120
|
+
await agent.emit(reasoningMessageStartEvent(reasoningId));
|
|
1121
|
+
await agent.emit(
|
|
1122
|
+
reasoningMessageContentEvent(reasoningId, "Deep analysis in progress"),
|
|
1123
|
+
);
|
|
1124
|
+
|
|
1125
|
+
await waitFor(() => {
|
|
1126
|
+
const button = screen.getByText("Thinking…").closest("button");
|
|
1127
|
+
expect(button?.getAttribute("aria-expanded")).toBe("true");
|
|
1128
|
+
});
|
|
1129
|
+
|
|
1130
|
+
const streamingHeaderButton = screen
|
|
1131
|
+
.getByText("Thinking…")
|
|
1132
|
+
.closest("button");
|
|
1133
|
+
if (streamingHeaderButton) {
|
|
1134
|
+
await fireEvent.click(streamingHeaderButton);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
await waitFor(() => {
|
|
1138
|
+
const button = screen.getByText("Thinking…").closest("button");
|
|
1139
|
+
expect(button?.getAttribute("aria-expanded")).toBe("false");
|
|
1140
|
+
});
|
|
1141
|
+
|
|
1142
|
+
await agent.emit(reasoningMessageEndEvent(reasoningId));
|
|
1143
|
+
await agent.emit(reasoningEndEvent(reasoningId));
|
|
1144
|
+
await agent.emit(textChunkEvent(textId, "Done."));
|
|
1145
|
+
await agent.emit(runFinishedEvent());
|
|
1146
|
+
await agent.complete();
|
|
1147
|
+
|
|
1148
|
+
await waitFor(() => {
|
|
1149
|
+
const button = screen.getByText(/Thought for/).closest("button");
|
|
1150
|
+
expect(button?.getAttribute("aria-expanded")).toBe("false");
|
|
1151
|
+
});
|
|
1152
|
+
});
|
|
1153
|
+
|
|
1154
|
+
it("should keep panel open when user re-expands during streaming", async () => {
|
|
1155
|
+
const agent = new MockStepwiseAgent();
|
|
1156
|
+
renderWithCopilotKit({ agent });
|
|
1157
|
+
|
|
1158
|
+
await submitMessageAndWaitForUserMessage("Re-expand toggle test");
|
|
1159
|
+
|
|
1160
|
+
const reasoningId = testId("reasoning");
|
|
1161
|
+
const textId = testId("text");
|
|
1162
|
+
|
|
1163
|
+
await agent.emit(runStartedEvent());
|
|
1164
|
+
await agent.emit(reasoningStartEvent(reasoningId));
|
|
1165
|
+
await agent.emit(reasoningMessageStartEvent(reasoningId));
|
|
1166
|
+
await agent.emit(
|
|
1167
|
+
reasoningMessageContentEvent(reasoningId, "Thinking hard"),
|
|
1168
|
+
);
|
|
1169
|
+
|
|
1170
|
+
await waitFor(() => {
|
|
1171
|
+
const button = screen.getByText("Thinking…").closest("button");
|
|
1172
|
+
expect(button?.getAttribute("aria-expanded")).toBe("true");
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
const streamingHeaderButton = screen
|
|
1176
|
+
.getByText("Thinking…")
|
|
1177
|
+
.closest("button");
|
|
1178
|
+
if (streamingHeaderButton) {
|
|
1179
|
+
await fireEvent.click(streamingHeaderButton);
|
|
1180
|
+
await fireEvent.click(streamingHeaderButton);
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
await waitFor(() => {
|
|
1184
|
+
const button = screen.getByText("Thinking…").closest("button");
|
|
1185
|
+
expect(button?.getAttribute("aria-expanded")).toBe("true");
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1188
|
+
await agent.emit(reasoningMessageEndEvent(reasoningId));
|
|
1189
|
+
await agent.emit(reasoningEndEvent(reasoningId));
|
|
1190
|
+
await agent.emit(textChunkEvent(textId, "All done."));
|
|
1191
|
+
await agent.emit(runFinishedEvent());
|
|
1192
|
+
await agent.complete();
|
|
1193
|
+
|
|
1194
|
+
await waitFor(() => {
|
|
1195
|
+
const button = screen.getByText(/Thought for/).closest("button");
|
|
1196
|
+
expect(button?.getAttribute("aria-expanded")).toBe("true");
|
|
1197
|
+
});
|
|
1198
|
+
});
|
|
1199
|
+
|
|
1200
|
+
it("should expand and collapse reasoning content on click", async () => {
|
|
1201
|
+
const agent = new MockStepwiseAgent();
|
|
1202
|
+
renderWithCopilotKit({ agent });
|
|
1203
|
+
|
|
1204
|
+
await submitMessageAndWaitForUserMessage("Toggle test");
|
|
1205
|
+
|
|
1206
|
+
const reasoningId = testId("reasoning");
|
|
1207
|
+
const textId = testId("text");
|
|
1208
|
+
|
|
1209
|
+
await agent.emit(runStartedEvent());
|
|
1210
|
+
await emitReasoningSequence(
|
|
1211
|
+
agent,
|
|
1212
|
+
reasoningId,
|
|
1213
|
+
"This is expandable reasoning content",
|
|
1214
|
+
);
|
|
1215
|
+
await agent.emit(textChunkEvent(textId, "Done thinking."));
|
|
1216
|
+
await agent.emit(runFinishedEvent());
|
|
1217
|
+
await agent.complete();
|
|
1218
|
+
|
|
1219
|
+
await waitFor(() => {
|
|
1220
|
+
const header = screen.getByText(/Thought for/);
|
|
1221
|
+
expect(header).toBeDefined();
|
|
1222
|
+
const button = header.closest("button");
|
|
1223
|
+
expect(button?.getAttribute("aria-expanded")).toBe("false");
|
|
1224
|
+
});
|
|
1225
|
+
|
|
1226
|
+
const header = screen.getByText(/Thought for/);
|
|
1227
|
+
const button = header.closest("button");
|
|
1228
|
+
if (button) {
|
|
1229
|
+
await fireEvent.click(button);
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
await waitFor(() => {
|
|
1233
|
+
const expandedButton = screen
|
|
1234
|
+
.getByText(/Thought for/)
|
|
1235
|
+
.closest("button");
|
|
1236
|
+
expect(expandedButton?.getAttribute("aria-expanded")).toBe("true");
|
|
1237
|
+
});
|
|
1238
|
+
});
|
|
1239
|
+
});
|
|
1240
|
+
});
|