@agent-native/toolkit 0.228.0-nightly-20261111212500 → 0.228.0
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/agent-native.eject.json +2 -0
- package/dist/app/agentkit/react/approval-card.d.ts.map +1 -1
- package/dist/app/agentkit/react/approval-card.js +5 -3
- package/dist/app/agentkit/react/approval-card.js.map +1 -1
- package/dist/app/agentkit/react/code-block.d.ts +32 -0
- package/dist/app/agentkit/react/code-block.d.ts.map +1 -0
- package/dist/app/agentkit/react/code-block.js +125 -0
- package/dist/app/agentkit/react/code-block.js.map +1 -0
- package/dist/app/agentkit/react/components.d.ts +33 -14
- package/dist/app/agentkit/react/components.d.ts.map +1 -1
- package/dist/app/agentkit/react/components.js +843 -168
- package/dist/app/agentkit/react/components.js.map +1 -1
- package/dist/app/agentkit/react/context-usage.d.ts +45 -0
- package/dist/app/agentkit/react/context-usage.d.ts.map +1 -0
- package/dist/app/agentkit/react/context-usage.js +93 -0
- package/dist/app/agentkit/react/context-usage.js.map +1 -0
- package/dist/app/agentkit/react/context.d.ts +42 -3
- package/dist/app/agentkit/react/context.d.ts.map +1 -1
- package/dist/app/agentkit/react/context.js +46 -1
- package/dist/app/agentkit/react/context.js.map +1 -1
- package/dist/app/agentkit/react/disclosure-content.d.ts +12 -0
- package/dist/app/agentkit/react/disclosure-content.d.ts.map +1 -0
- package/dist/app/agentkit/react/disclosure-content.js +33 -0
- package/dist/app/agentkit/react/disclosure-content.js.map +1 -0
- package/dist/app/agentkit/react/duration.d.ts +6 -0
- package/dist/app/agentkit/react/duration.d.ts.map +1 -0
- package/dist/app/agentkit/react/duration.js +21 -0
- package/dist/app/agentkit/react/duration.js.map +1 -0
- package/dist/app/agentkit/react/file-change.d.ts +33 -0
- package/dist/app/agentkit/react/file-change.d.ts.map +1 -0
- package/dist/app/agentkit/react/file-change.js +83 -0
- package/dist/app/agentkit/react/file-change.js.map +1 -0
- package/dist/app/agentkit/react/file-diff.d.ts +17 -0
- package/dist/app/agentkit/react/file-diff.d.ts.map +1 -0
- package/dist/app/agentkit/react/file-diff.js +94 -0
- package/dist/app/agentkit/react/file-diff.js.map +1 -0
- package/dist/app/agentkit/react/index.d.ts +7 -0
- package/dist/app/agentkit/react/index.d.ts.map +1 -1
- package/dist/app/agentkit/react/index.js +7 -0
- package/dist/app/agentkit/react/index.js.map +1 -1
- package/dist/app/agentkit/react/question-card.d.ts +67 -0
- package/dist/app/agentkit/react/question-card.d.ts.map +1 -0
- package/dist/app/agentkit/react/question-card.js +198 -0
- package/dist/app/agentkit/react/question-card.js.map +1 -0
- package/dist/app/agentkit/react/root.d.ts +3 -2
- package/dist/app/agentkit/react/root.d.ts.map +1 -1
- package/dist/app/agentkit/react/root.js +2 -2
- package/dist/app/agentkit/react/root.js.map +1 -1
- package/dist/app/agentkit/react/roving-focus.d.ts +4 -0
- package/dist/app/agentkit/react/roving-focus.d.ts.map +1 -0
- package/dist/app/agentkit/react/roving-focus.js +75 -0
- package/dist/app/agentkit/react/roving-focus.js.map +1 -0
- package/dist/app/agentkit/react/stack-trace.d.ts +34 -0
- package/dist/app/agentkit/react/stack-trace.d.ts.map +1 -0
- package/dist/app/agentkit/react/stack-trace.js +121 -0
- package/dist/app/agentkit/react/stack-trace.js.map +1 -0
- package/dist/app/agentkit/react/stop-button.d.ts.map +1 -1
- package/dist/app/agentkit/react/stop-button.js +22 -6
- package/dist/app/agentkit/react/stop-button.js.map +1 -1
- package/dist/app/agentkit/react/styles.css +1306 -395
- package/dist/app/agentkit/react/terminal.d.ts +28 -0
- package/dist/app/agentkit/react/terminal.d.ts.map +1 -0
- package/dist/app/agentkit/react/terminal.js +84 -0
- package/dist/app/agentkit/react/terminal.js.map +1 -0
- package/dist/app/agentkit/react/trace.d.ts +31 -0
- package/dist/app/agentkit/react/trace.d.ts.map +1 -0
- package/dist/app/agentkit/react/trace.js +73 -0
- package/dist/app/agentkit/react/trace.js.map +1 -0
- package/dist/app/blocks/library/HighlightedCode.d.ts +2 -1
- package/dist/app/blocks/library/HighlightedCode.d.ts.map +1 -1
- package/dist/app/blocks/library/HighlightedCode.js +8 -111
- package/dist/app/blocks/library/HighlightedCode.js.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.js +23 -245
- package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
- package/dist/app/chat/agentkit-chat/guided-questions.d.ts +15 -1
- package/dist/app/chat/agentkit-chat/guided-questions.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/guided-questions.js +142 -2
- package/dist/app/chat/agentkit-chat/guided-questions.js.map +1 -1
- package/dist/app/chat/agentkit-chat/history.js +1 -1
- package/dist/app/chat/agentkit-chat/history.js.map +1 -1
- package/dist/app/chat/agentkit-chat/index.d.ts +1 -1
- package/dist/app/chat/agentkit-chat/index.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/index.js +1 -1
- package/dist/app/chat/agentkit-chat/index.js.map +1 -1
- package/dist/app/chat/agentkit-chat/labels.d.ts +5 -0
- package/dist/app/chat/agentkit-chat/labels.d.ts.map +1 -0
- package/dist/app/chat/agentkit-chat/labels.js +245 -0
- package/dist/app/chat/agentkit-chat/labels.js.map +1 -0
- package/dist/app/chat/agentkit-chat/questions.d.ts +1 -1
- package/dist/app/chat/agentkit-chat/questions.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/questions.js +1 -1
- package/dist/app/chat/agentkit-chat/questions.js.map +1 -1
- package/dist/app/chat/agentkit-chat/root.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/root.js +5 -64
- package/dist/app/chat/agentkit-chat/root.js.map +1 -1
- package/dist/app/chat/agentkit-chat/work-step-labels.d.ts +2 -0
- package/dist/app/chat/agentkit-chat/work-step-labels.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/work-step-labels.js +31 -0
- package/dist/app/chat/agentkit-chat/work-step-labels.js.map +1 -1
- package/dist/app/chat/chat/markdown-renderer.d.ts +2 -12
- package/dist/app/chat/chat/markdown-renderer.d.ts.map +1 -1
- package/dist/app/chat/chat/markdown-renderer.js +2 -41
- package/dist/app/chat/chat/markdown-renderer.js.map +1 -1
- package/dist/app/chat/chat/tool-call-display.d.ts.map +1 -1
- package/dist/app/chat/chat/tool-call-display.js +2 -2
- package/dist/app/chat/chat/tool-call-display.js.map +1 -1
- package/dist/app/chat/conversation/AgentConversation.d.ts.map +1 -1
- package/dist/app/chat/conversation/AgentConversation.js +2 -37
- package/dist/app/chat/conversation/AgentConversation.js.map +1 -1
- package/dist/app/chat/tool-cells/BashCell.d.ts +3 -1
- package/dist/app/chat/tool-cells/BashCell.d.ts.map +1 -1
- package/dist/app/chat/tool-cells/BashCell.js +4 -40
- package/dist/app/chat/tool-cells/BashCell.js.map +1 -1
- package/dist/app/chat/tool-cells/EditCell.d.ts.map +1 -1
- package/dist/app/chat/tool-cells/EditCell.js +5 -108
- package/dist/app/chat/tool-cells/EditCell.js.map +1 -1
- package/dist/app/chat/tool-cells/FilesChangedSummary.d.ts.map +1 -1
- package/dist/app/chat/tool-cells/FilesChangedSummary.js +23 -81
- package/dist/app/chat/tool-cells/FilesChangedSummary.js.map +1 -1
- package/dist/app/chat/tool-cells/WriteCell.d.ts.map +1 -1
- package/dist/app/chat/tool-cells/WriteCell.js +5 -24
- package/dist/app/chat/tool-cells/WriteCell.js.map +1 -1
- package/dist/app/code-highlighter.d.ts +11 -0
- package/dist/app/code-highlighter.d.ts.map +1 -0
- package/dist/app/code-highlighter.js +122 -0
- package/dist/app/code-highlighter.js.map +1 -0
- package/dist/app/context-xray/ThreadContextRing.js +3 -3
- package/dist/app/context-xray/ThreadContextRing.js.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.js +22 -0
- package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.js +22 -0
- package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
- package/dist/app/i18n/catalogs/en-US.d.ts +22 -0
- package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/en-US.js +22 -0
- package/dist/app/i18n/catalogs/en-US.js.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.js +22 -0
- package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.js +22 -0
- package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.js +22 -0
- package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.js +22 -0
- package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.js +22 -0
- package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.js +22 -0
- package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.js +22 -0
- package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.js +22 -0
- package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
- package/dist/app/onboarding/first-run-first-paint.d.ts +10 -0
- package/dist/app/onboarding/first-run-first-paint.d.ts.map +1 -0
- package/dist/app/onboarding/first-run-first-paint.js +22 -0
- package/dist/app/onboarding/first-run-first-paint.js.map +1 -0
- package/dist/app/onboarding/first-run-startup-gate.d.ts +3 -1
- package/dist/app/onboarding/first-run-startup-gate.d.ts.map +1 -1
- package/dist/app/onboarding/first-run-startup-gate.js +30 -6
- package/dist/app/onboarding/first-run-startup-gate.js.map +1 -1
- package/dist/app/providers/AppProviders.d.ts.map +1 -1
- package/dist/app/providers/AppProviders.js +9 -2
- package/dist/app/providers/AppProviders.js.map +1 -1
- package/dist/app/providers/BuilderSignupActivation.d.ts +2 -1
- package/dist/app/providers/BuilderSignupActivation.d.ts.map +1 -1
- package/dist/app/providers/BuilderSignupActivation.js +6 -3
- package/dist/app/providers/BuilderSignupActivation.js.map +1 -1
- package/dist/app/shared/AppShellSkeleton.d.ts.map +1 -1
- package/dist/app/shared/AppShellSkeleton.js +11 -1
- package/dist/app/shared/AppShellSkeleton.js.map +1 -1
- package/dist/app/styles/agent-native.css +2 -2
- package/dist/app/styles/tokens/agent-kit.css +20 -3
- package/dist/composer/AgentSuggestionBar.d.ts.map +1 -1
- package/dist/composer/AgentSuggestionBar.js +2 -2
- package/dist/composer/AgentSuggestionBar.js.map +1 -1
- package/dist/composer/ComposerContextMenu.js +4 -4
- package/dist/composer/ComposerContextMenu.js.map +1 -1
- package/dist/composer/ComposerContextPicker.d.ts.map +1 -1
- package/dist/composer/ComposerContextPicker.js +2 -2
- package/dist/composer/ComposerContextPicker.js.map +1 -1
- package/dist/composer/ComposerContextPickerDialog.d.ts.map +1 -1
- package/dist/composer/ComposerContextPickerDialog.js +2 -2
- package/dist/composer/ComposerContextPickerDialog.js.map +1 -1
- package/dist/composer/ComposerModePicker.d.ts.map +1 -1
- package/dist/composer/ComposerModePicker.js +2 -1
- package/dist/composer/ComposerModePicker.js.map +1 -1
- package/dist/composer/ComposerPlusMenu.js +1 -1
- package/dist/composer/ComposerPlusMenu.js.map +1 -1
- package/dist/composer/MentionChip.js +1 -1
- package/dist/composer/MentionChip.js.map +1 -1
- package/dist/composer/MentionPopover.d.ts +3 -0
- package/dist/composer/MentionPopover.d.ts.map +1 -1
- package/dist/composer/MentionPopover.js +34 -15
- package/dist/composer/MentionPopover.js.map +1 -1
- package/dist/composer/MessageQueueDrawer.d.ts.map +1 -1
- package/dist/composer/MessageQueueDrawer.js +14 -9
- package/dist/composer/MessageQueueDrawer.js.map +1 -1
- package/dist/composer/PastedTextChip.d.ts.map +1 -1
- package/dist/composer/PastedTextChip.js +14 -18
- package/dist/composer/PastedTextChip.js.map +1 -1
- package/dist/composer/PromptBar.js +1 -1
- package/dist/composer/PromptBar.js.map +1 -1
- package/dist/composer/PromptComposer.d.ts.map +1 -1
- package/dist/composer/PromptComposer.js +11 -9
- package/dist/composer/PromptComposer.js.map +1 -1
- package/dist/composer/RealtimeVoiceMode.js +2 -2
- package/dist/composer/RealtimeVoiceMode.js.map +1 -1
- package/dist/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/composer/TiptapComposer.js +52 -89
- package/dist/composer/TiptapComposer.js.map +1 -1
- package/dist/composer/VoiceButton.d.ts +3 -1
- package/dist/composer/VoiceButton.d.ts.map +1 -1
- package/dist/composer/VoiceButton.js +10 -8
- package/dist/composer/VoiceButton.js.map +1 -1
- package/dist/composer/composer-notice.js +3 -3
- package/dist/composer/composer-notice.js.map +1 -1
- package/dist/composer/extensions/FileReference.js +1 -1
- package/dist/composer/extensions/FileReference.js.map +1 -1
- package/dist/composer/extensions/SkillReference.js +1 -1
- package/dist/composer/extensions/SkillReference.js.map +1 -1
- package/dist/composer/popover-surface.d.ts +1 -1
- package/dist/composer/popover-surface.d.ts.map +1 -1
- package/dist/composer/popover-surface.js +1 -1
- package/dist/composer/popover-surface.js.map +1 -1
- package/dist/context-ui/ContextUsageRing.d.ts +1 -45
- package/dist/context-ui/ContextUsageRing.d.ts.map +1 -1
- package/dist/context-ui/ContextUsageRing.js +2 -87
- package/dist/context-ui/ContextUsageRing.js.map +1 -1
- package/dist/context-ui/index.d.ts +2 -2
- package/dist/context-ui/index.d.ts.map +1 -1
- package/dist/context-ui/index.js +2 -2
- package/dist/context-ui/index.js.map +1 -1
- package/package.json +4 -4
- package/src/app/agentkit/react/approval-card.tsx +14 -2
- package/src/app/agentkit/react/code-block.spec.tsx +154 -0
- package/src/app/agentkit/react/code-block.tsx +317 -0
- package/src/app/agentkit/react/components.interaction.spec.tsx +7 -0
- package/src/app/agentkit/react/components.papercuts.spec.tsx +143 -2
- package/src/app/agentkit/react/components.spec.tsx +40 -47
- package/src/app/agentkit/react/components.tsx +1533 -413
- package/src/app/agentkit/react/composer-context.spec.tsx +35 -0
- package/src/app/agentkit/react/context-usage.tsx +353 -0
- package/src/app/agentkit/react/context.tsx +93 -1
- package/src/app/agentkit/react/disclosure-content.ts +42 -0
- package/src/app/agentkit/react/disclosures.spec.tsx +62 -4
- package/src/app/agentkit/react/duration.ts +22 -0
- package/src/app/agentkit/react/file-change.spec.tsx +124 -0
- package/src/app/agentkit/react/file-change.tsx +228 -0
- package/src/app/agentkit/react/file-diff.ts +110 -0
- package/src/app/agentkit/react/index.ts +7 -0
- package/src/app/agentkit/react/lifecycle.spec.tsx +565 -20
- package/src/app/agentkit/react/markdown.spec.tsx +18 -8
- package/src/app/agentkit/react/message-actions.spec.tsx +55 -1
- package/src/app/agentkit/react/notices.spec.tsx +4 -4
- package/src/app/agentkit/react/optimistic-send.spec.tsx +43 -0
- package/src/app/agentkit/react/question-card.spec.tsx +276 -0
- package/src/app/agentkit/react/question-card.tsx +533 -0
- package/src/app/agentkit/react/root.tsx +4 -0
- package/src/app/agentkit/react/roving-focus.spec.tsx +114 -0
- package/src/app/agentkit/react/roving-focus.ts +82 -0
- package/src/app/agentkit/react/stack-trace.spec.tsx +177 -0
- package/src/app/agentkit/react/stack-trace.tsx +281 -0
- package/src/app/agentkit/react/stop-button.spec.tsx +40 -0
- package/src/app/agentkit/react/stop-button.tsx +22 -5
- package/src/app/agentkit/react/styles.css +1306 -395
- package/src/app/agentkit/react/subagent-step.spec.tsx +554 -0
- package/src/app/agentkit/react/terminal.spec.tsx +228 -0
- package/src/app/agentkit/react/terminal.tsx +228 -0
- package/src/app/agentkit/react/trace.spec.tsx +120 -0
- package/src/app/agentkit/react/trace.tsx +239 -0
- package/src/app/blocks/library/HighlightedCode.tsx +14 -126
- package/src/app/chat/AgentKitAssistantChat.spec.tsx +28 -12
- package/src/app/chat/AgentKitAssistantChat.tsx +27 -253
- package/src/app/chat/agentkit-chat/approval-card.spec.tsx +1 -0
- package/src/app/chat/agentkit-chat/guided-questions.flow.spec.tsx +381 -0
- package/src/app/chat/agentkit-chat/guided-questions.tsx +236 -3
- package/src/app/chat/agentkit-chat/history.tsx +1 -1
- package/src/app/chat/agentkit-chat/index.ts +2 -0
- package/src/app/chat/agentkit-chat/labels.ts +259 -0
- package/src/app/chat/agentkit-chat/questions.ts +2 -0
- package/src/app/chat/agentkit-chat/root.spec.tsx +18 -0
- package/src/app/chat/agentkit-chat/root.tsx +5 -64
- package/src/app/chat/agentkit-chat/work-step-labels.ts +42 -0
- package/src/app/chat/chat/markdown-renderer.tsx +2 -59
- package/src/app/chat/chat/tool-call-display.spec.tsx +26 -14
- package/src/app/chat/chat/tool-call-display.tsx +2 -1
- package/src/app/chat/conversation/AgentConversation.tsx +2 -50
- package/src/app/chat/tool-cells/BashCell.tsx +14 -136
- package/src/app/chat/tool-cells/EditCell.tsx +11 -248
- package/src/app/chat/tool-cells/FilesChangedSummary.spec.tsx +62 -5
- package/src/app/chat/tool-cells/FilesChangedSummary.tsx +31 -162
- package/src/app/chat/tool-cells/WriteCell.tsx +11 -121
- package/src/app/code-highlighter.ts +131 -0
- package/src/app/context-xray/ThreadContextRing.tsx +7 -7
- package/src/app/i18n/catalogs/ar-SA.ts +22 -0
- package/src/app/i18n/catalogs/de-DE.ts +22 -0
- package/src/app/i18n/catalogs/en-US.ts +22 -0
- package/src/app/i18n/catalogs/es-ES.ts +22 -0
- package/src/app/i18n/catalogs/fr-FR.ts +22 -0
- package/src/app/i18n/catalogs/hi-IN.ts +22 -0
- package/src/app/i18n/catalogs/ja-JP.ts +22 -0
- package/src/app/i18n/catalogs/ko-KR.ts +22 -0
- package/src/app/i18n/catalogs/pt-BR.ts +22 -0
- package/src/app/i18n/catalogs/zh-CN.ts +22 -0
- package/src/app/i18n/catalogs/zh-TW.ts +22 -0
- package/src/app/onboarding/first-run-first-paint.spec.ts +39 -0
- package/src/app/onboarding/first-run-first-paint.ts +24 -0
- package/src/app/onboarding/first-run-startup-gate.spec.tsx +125 -13
- package/src/app/onboarding/first-run-startup-gate.tsx +48 -5
- package/src/app/providers/AppProviders.tsx +18 -0
- package/src/app/providers/BuilderSignupActivation.tsx +8 -2
- package/src/app/shared/AppShellSkeleton.spec.tsx +60 -1
- package/src/app/shared/AppShellSkeleton.tsx +31 -1
- package/src/app/styles/agent-native.css +2 -2
- package/src/app/styles/agent-native.spec.ts +2 -2
- package/src/app/styles/tokens/agent-kit.css +20 -3
- package/src/composer/AgentSuggestionBar.spec.tsx +3 -1
- package/src/composer/AgentSuggestionBar.tsx +5 -2
- package/src/composer/ComposerContextMenu.tsx +4 -4
- package/src/composer/ComposerContextPicker.tsx +8 -2
- package/src/composer/ComposerContextPickerDialog.tsx +5 -2
- package/src/composer/ComposerModePicker.tsx +2 -1
- package/src/composer/ComposerPlusMenu.tsx +1 -1
- package/src/composer/MentionChip.tsx +1 -1
- package/src/composer/MentionPopover.spec.tsx +108 -2
- package/src/composer/MentionPopover.tsx +93 -17
- package/src/composer/MessageQueueDrawer.spec.tsx +5 -5
- package/src/composer/MessageQueueDrawer.tsx +19 -12
- package/src/composer/PastedTextChip.spec.tsx +36 -0
- package/src/composer/PastedTextChip.tsx +52 -58
- package/src/composer/PromptBar.tsx +1 -1
- package/src/composer/PromptComposer.context.spec.tsx +2 -2
- package/src/composer/PromptComposer.submission.spec.tsx +27 -0
- package/src/composer/PromptComposer.tsx +17 -9
- package/src/composer/RealtimeVoiceMode.tsx +2 -2
- package/src/composer/TiptapComposer.model-picker.spec.tsx +23 -82
- package/src/composer/TiptapComposer.tsx +68 -145
- package/src/composer/VoiceButton.tsx +12 -7
- package/src/composer/composer-notice.tsx +3 -3
- package/src/composer/extensions/FileReference.tsx +1 -1
- package/src/composer/extensions/SkillReference.tsx +1 -1
- package/src/composer/popover-surface.ts +1 -1
- package/src/context-ui/ContextUsageRing.spec.tsx +33 -8
- package/src/context-ui/ContextUsageRing.tsx +13 -348
- package/src/context-ui/index.ts +7 -1
- package/dist/composer/ModelPickerParts.d.ts +0 -20
- package/dist/composer/ModelPickerParts.d.ts.map +0 -1
- package/dist/composer/ModelPickerParts.js +0 -77
- package/dist/composer/ModelPickerParts.js.map +0 -1
- package/src/composer/ModelPickerParts.tsx +0 -188
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
|
|
3
|
+
import { act } from "react";
|
|
4
|
+
import { createRoot, type Root } from "react-dom/client";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
|
|
7
|
+
import { loadCodeHighlighter } from "../../code-highlighter.js";
|
|
8
|
+
import {
|
|
9
|
+
CodeBlock,
|
|
10
|
+
CodeBlockContent,
|
|
11
|
+
CodeBlockCopyButton,
|
|
12
|
+
CodeBlockLine,
|
|
13
|
+
} from "./code-block.js";
|
|
14
|
+
|
|
15
|
+
let root: Root;
|
|
16
|
+
let container: HTMLDivElement;
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
// Real throttle timers let a highlight pass land early under load.
|
|
20
|
+
vi.useFakeTimers();
|
|
21
|
+
vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
|
|
22
|
+
container = document.createElement("div");
|
|
23
|
+
document.body.append(container);
|
|
24
|
+
root = createRoot(container);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
act(() => root.unmount());
|
|
29
|
+
container.remove();
|
|
30
|
+
vi.unstubAllGlobals();
|
|
31
|
+
vi.useRealTimers();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
async function render(node: React.ReactNode) {
|
|
35
|
+
await act(async () => root.render(node));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function settle() {
|
|
39
|
+
await act(async () => {
|
|
40
|
+
vi.advanceTimersByTime(100);
|
|
41
|
+
await loadCodeHighlighter();
|
|
42
|
+
await Promise.resolve();
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function lineText() {
|
|
47
|
+
return Array.from(
|
|
48
|
+
container.querySelectorAll(".agentkit-code-line"),
|
|
49
|
+
(line) => line.textContent,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
describe("CodeBlock", () => {
|
|
54
|
+
it("renders plain lines first, then highlighted tokens with the same text", async () => {
|
|
55
|
+
const code = "const ready = true;\nexport { ready };\n";
|
|
56
|
+
await render(
|
|
57
|
+
<CodeBlock>
|
|
58
|
+
<CodeBlockContent language="ts">{code}</CodeBlockContent>
|
|
59
|
+
</CodeBlock>,
|
|
60
|
+
);
|
|
61
|
+
expect(container.querySelector(".agentkit-code-token")).toBeNull();
|
|
62
|
+
expect(lineText()).toEqual(["const ready = true;", "export { ready };"]);
|
|
63
|
+
|
|
64
|
+
await settle();
|
|
65
|
+
expect(container.querySelector(".agentkit-code-token")).not.toBeNull();
|
|
66
|
+
expect(lineText()).toEqual(["const ready = true;", "export { ready };"]);
|
|
67
|
+
expect(container.querySelector("code")?.textContent).toBe(
|
|
68
|
+
"const ready = true;\nexport { ready };",
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("keeps finished lines highlighted while a new line streams in", async () => {
|
|
73
|
+
const view = (code: string) => (
|
|
74
|
+
<CodeBlockContent language="ts">{code}</CodeBlockContent>
|
|
75
|
+
);
|
|
76
|
+
await render(view("const a = 1;"));
|
|
77
|
+
await settle();
|
|
78
|
+
await render(view("const a = 1;\nconst b"));
|
|
79
|
+
const [first, second] = Array.from(
|
|
80
|
+
container.querySelectorAll(".agentkit-code-line"),
|
|
81
|
+
);
|
|
82
|
+
expect(first?.querySelector(".agentkit-code-token")).not.toBeNull();
|
|
83
|
+
expect(second?.querySelector(".agentkit-code-token")).toBeNull();
|
|
84
|
+
expect(second?.textContent).toBe("const b");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("colors terminal output and never shows its escape codes", async () => {
|
|
88
|
+
await render(
|
|
89
|
+
<CodeBlockContent language="ansi">
|
|
90
|
+
{"\u001b[31mfailed\u001b[0m 2 tests\nok"}
|
|
91
|
+
</CodeBlockContent>,
|
|
92
|
+
);
|
|
93
|
+
expect(lineText()).toEqual(["failed 2 tests", "ok"]);
|
|
94
|
+
await settle();
|
|
95
|
+
expect(lineText()).toEqual(["failed 2 tests", "ok"]);
|
|
96
|
+
const failed = container.querySelector<HTMLElement>(".agentkit-code-token");
|
|
97
|
+
expect(failed?.textContent).toBe("failed");
|
|
98
|
+
expect(failed?.style.getPropertyValue("--shiki-light")).not.toBe("");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("renders an unknown language as plain text", async () => {
|
|
102
|
+
await render(
|
|
103
|
+
<CodeBlockContent language="cobol">{"DISPLAY 'HI'."}</CodeBlockContent>,
|
|
104
|
+
);
|
|
105
|
+
await settle();
|
|
106
|
+
expect(container.querySelector(".agentkit-code-token")).toBeNull();
|
|
107
|
+
expect(lineText()).toEqual(["DISPLAY 'HI'."]);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("keeps diff markers and line numbers out of the text", async () => {
|
|
111
|
+
await render(
|
|
112
|
+
<CodeBlockContent lineNumbers startLine={10}>
|
|
113
|
+
<CodeBlockLine variant="removed">old</CodeBlockLine>
|
|
114
|
+
<CodeBlockLine variant="added">new</CodeBlockLine>
|
|
115
|
+
</CodeBlockContent>,
|
|
116
|
+
);
|
|
117
|
+
const pre = container.querySelector("pre")!;
|
|
118
|
+
expect(pre.dataset.lineNumbers).toBe("true");
|
|
119
|
+
expect(pre.style.getPropertyValue("--agentkit-code-start-line")).toBe("9");
|
|
120
|
+
expect(pre.getAttribute("dir")).toBe("ltr");
|
|
121
|
+
// The visible +/− is generated content; screen readers hear the word.
|
|
122
|
+
expect(pre.textContent).toBe("Removed old\nAdded new");
|
|
123
|
+
expect(
|
|
124
|
+
Array.from(pre.querySelectorAll(".agentkit-code-line-change"), (label) =>
|
|
125
|
+
label.classList.contains("agentkit-visually-hidden"),
|
|
126
|
+
),
|
|
127
|
+
).toEqual([true, true]);
|
|
128
|
+
expect(
|
|
129
|
+
Array.from(
|
|
130
|
+
pre.querySelectorAll<HTMLElement>(".agentkit-code-line"),
|
|
131
|
+
(line) => line.dataset.variant,
|
|
132
|
+
),
|
|
133
|
+
).toEqual(["removed", "added"]);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("copies its value outside an AgentKit provider", async () => {
|
|
137
|
+
const writeText = vi.fn(async () => undefined);
|
|
138
|
+
vi.stubGlobal("navigator", { ...navigator, clipboard: { writeText } });
|
|
139
|
+
await render(<CodeBlockCopyButton value="pnpm build" />);
|
|
140
|
+
const button = container.querySelector("button")!;
|
|
141
|
+
expect(button.getAttribute("aria-label")).toBe("Copy code");
|
|
142
|
+
await act(async () => {
|
|
143
|
+
button.click();
|
|
144
|
+
await Promise.resolve();
|
|
145
|
+
await Promise.resolve();
|
|
146
|
+
});
|
|
147
|
+
expect(writeText).toHaveBeenCalledWith("pnpm build");
|
|
148
|
+
expect(button.getAttribute("aria-label")).toBe("Copied");
|
|
149
|
+
// Said once in a polite region; a renamed focused button is often silent.
|
|
150
|
+
expect(container.querySelector('[aria-live="polite"]')?.textContent).toBe(
|
|
151
|
+
"Copied",
|
|
152
|
+
);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { IconCheck, IconCopy } from "@tabler/icons-react";
|
|
2
|
+
import {
|
|
3
|
+
Children,
|
|
4
|
+
useEffect,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
type ComponentProps,
|
|
8
|
+
type CSSProperties,
|
|
9
|
+
type ReactNode,
|
|
10
|
+
} from "react";
|
|
11
|
+
import type { ThemedToken } from "shiki/core";
|
|
12
|
+
|
|
13
|
+
import { writeClipboardText } from "../../../agentkit.js";
|
|
14
|
+
import { IconButton } from "../../../design-system/index.js";
|
|
15
|
+
import {
|
|
16
|
+
CODE_THEMES,
|
|
17
|
+
loadCodeHighlighter,
|
|
18
|
+
resolveCodeLanguage,
|
|
19
|
+
} from "../../code-highlighter.js";
|
|
20
|
+
import { useAgentKitLabels } from "./context.js";
|
|
21
|
+
|
|
22
|
+
export type CodeBlockLineVariant =
|
|
23
|
+
| "default"
|
|
24
|
+
| "added"
|
|
25
|
+
| "removed"
|
|
26
|
+
| "highlighted";
|
|
27
|
+
|
|
28
|
+
function classes(...names: Array<string | false | null | undefined>): string {
|
|
29
|
+
return names.filter(Boolean).join(" ");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function CodeBlock({
|
|
33
|
+
className,
|
|
34
|
+
size = "default",
|
|
35
|
+
variant = "default",
|
|
36
|
+
...props
|
|
37
|
+
}: ComponentProps<"div"> & {
|
|
38
|
+
size?: "default" | "sm";
|
|
39
|
+
variant?: "default" | "destructive";
|
|
40
|
+
}) {
|
|
41
|
+
return (
|
|
42
|
+
<div
|
|
43
|
+
data-slot="code-block"
|
|
44
|
+
data-size={size}
|
|
45
|
+
data-variant={variant}
|
|
46
|
+
className={classes("agentkit-code-block", className)}
|
|
47
|
+
{...props}
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function CodeBlockHeader({
|
|
53
|
+
className,
|
|
54
|
+
...props
|
|
55
|
+
}: ComponentProps<"div">) {
|
|
56
|
+
return (
|
|
57
|
+
<div
|
|
58
|
+
data-slot="code-block-header"
|
|
59
|
+
className={classes("agentkit-code-block-header", className)}
|
|
60
|
+
{...props}
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** A filename, language, or command. Truncates. */
|
|
66
|
+
export function CodeBlockTitle({
|
|
67
|
+
className,
|
|
68
|
+
...props
|
|
69
|
+
}: ComponentProps<"span">) {
|
|
70
|
+
return (
|
|
71
|
+
<span
|
|
72
|
+
data-slot="code-block-title"
|
|
73
|
+
className={classes("agentkit-code-block-title", className)}
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Outside a header, floats over the content and shows on hover or focus. */
|
|
80
|
+
export function CodeBlockActions({
|
|
81
|
+
className,
|
|
82
|
+
...props
|
|
83
|
+
}: ComponentProps<"div">) {
|
|
84
|
+
return (
|
|
85
|
+
<div
|
|
86
|
+
data-slot="code-block-actions"
|
|
87
|
+
className={classes("agentkit-code-block-actions", className)}
|
|
88
|
+
{...props}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function CodeBlockCopyButton({
|
|
94
|
+
value,
|
|
95
|
+
label,
|
|
96
|
+
copiedLabel,
|
|
97
|
+
}: {
|
|
98
|
+
value: string;
|
|
99
|
+
label?: string;
|
|
100
|
+
copiedLabel?: string;
|
|
101
|
+
}) {
|
|
102
|
+
const labels = useAgentKitLabels();
|
|
103
|
+
const [copied, setCopied] = useState(false);
|
|
104
|
+
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
105
|
+
useEffect(
|
|
106
|
+
() => () => {
|
|
107
|
+
if (timer.current) clearTimeout(timer.current);
|
|
108
|
+
},
|
|
109
|
+
[],
|
|
110
|
+
);
|
|
111
|
+
const copy = async () => {
|
|
112
|
+
if (!(await writeClipboardText(value))) return;
|
|
113
|
+
setCopied(true);
|
|
114
|
+
if (timer.current) clearTimeout(timer.current);
|
|
115
|
+
timer.current = setTimeout(() => setCopied(false), 2_000);
|
|
116
|
+
};
|
|
117
|
+
const copiedText = copiedLabel ?? labels.codeCopied;
|
|
118
|
+
return (
|
|
119
|
+
<>
|
|
120
|
+
<IconButton
|
|
121
|
+
label={copied ? copiedText : (label ?? labels.copyCode)}
|
|
122
|
+
icon={
|
|
123
|
+
copied ? (
|
|
124
|
+
<IconCheck aria-hidden="true" size={16} />
|
|
125
|
+
) : (
|
|
126
|
+
<IconCopy aria-hidden="true" size={16} />
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
size="compact"
|
|
130
|
+
className="size-6 rounded-[var(--agentkit-code-action-radius,var(--agentkit-radius-xs))]"
|
|
131
|
+
onPress={() => void copy()}
|
|
132
|
+
/>
|
|
133
|
+
{/* A changed name on the focused button is not reliably read. */}
|
|
134
|
+
<span className="agentkit-visually-hidden" aria-live="polite">
|
|
135
|
+
{copied ? copiedText : ""}
|
|
136
|
+
</span>
|
|
137
|
+
</>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Diff variants carry a +/− marker, not color alone. */
|
|
142
|
+
export function CodeBlockLine({
|
|
143
|
+
className,
|
|
144
|
+
variant = "default",
|
|
145
|
+
children,
|
|
146
|
+
...props
|
|
147
|
+
}: ComponentProps<"span"> & { variant?: CodeBlockLineVariant }) {
|
|
148
|
+
const labels = useAgentKitLabels();
|
|
149
|
+
// Screen readers hear this word, not the silent CSS +/−; it stays out of selections.
|
|
150
|
+
const change =
|
|
151
|
+
variant === "added"
|
|
152
|
+
? labels.workStepLineAdded
|
|
153
|
+
: variant === "removed"
|
|
154
|
+
? labels.workStepLineRemoved
|
|
155
|
+
: undefined;
|
|
156
|
+
return (
|
|
157
|
+
<span
|
|
158
|
+
data-slot="code-block-line"
|
|
159
|
+
data-variant={variant === "default" ? undefined : variant}
|
|
160
|
+
className={classes("agentkit-code-line", className)}
|
|
161
|
+
{...props}
|
|
162
|
+
>
|
|
163
|
+
{change ? (
|
|
164
|
+
<span className="agentkit-visually-hidden agentkit-code-line-change">
|
|
165
|
+
{`${change} `}
|
|
166
|
+
</span>
|
|
167
|
+
) : null}
|
|
168
|
+
{children}
|
|
169
|
+
</span>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Line numbers stay out of copied text and the accessibility tree. */
|
|
174
|
+
export function CodeBlockContent({
|
|
175
|
+
children,
|
|
176
|
+
language,
|
|
177
|
+
wrap = false,
|
|
178
|
+
lineNumbers = false,
|
|
179
|
+
startLine = 1,
|
|
180
|
+
tabIndex = 0,
|
|
181
|
+
className,
|
|
182
|
+
style,
|
|
183
|
+
...props
|
|
184
|
+
}: Omit<ComponentProps<"pre">, "children"> & {
|
|
185
|
+
children: string | ReactNode;
|
|
186
|
+
language?: string | undefined;
|
|
187
|
+
wrap?: boolean;
|
|
188
|
+
lineNumbers?: boolean;
|
|
189
|
+
startLine?: number;
|
|
190
|
+
}) {
|
|
191
|
+
const source = typeof children === "string" ? children : "";
|
|
192
|
+
const highlighted = useHighlightedCode(
|
|
193
|
+
source,
|
|
194
|
+
typeof children === "string" ? language : undefined,
|
|
195
|
+
);
|
|
196
|
+
const lines =
|
|
197
|
+
typeof children === "string"
|
|
198
|
+
? highlighted.map((line, index) => (
|
|
199
|
+
<CodeBlockLine key={index}>{line}</CodeBlockLine>
|
|
200
|
+
))
|
|
201
|
+
: Children.toArray(children);
|
|
202
|
+
return (
|
|
203
|
+
<pre
|
|
204
|
+
data-slot="code-block-content"
|
|
205
|
+
data-wrap={wrap ? "true" : undefined}
|
|
206
|
+
data-line-numbers={lineNumbers ? "true" : undefined}
|
|
207
|
+
dir="ltr"
|
|
208
|
+
tabIndex={tabIndex}
|
|
209
|
+
className={classes("agentkit-code-block-content", className)}
|
|
210
|
+
style={
|
|
211
|
+
lineNumbers && startLine !== 1
|
|
212
|
+
? ({
|
|
213
|
+
...style,
|
|
214
|
+
"--agentkit-code-start-line": startLine - 1,
|
|
215
|
+
} as CSSProperties)
|
|
216
|
+
: style
|
|
217
|
+
}
|
|
218
|
+
{...props}
|
|
219
|
+
>
|
|
220
|
+
<code
|
|
221
|
+
className={language ? `language-${language}` : undefined}
|
|
222
|
+
data-language={language}
|
|
223
|
+
>
|
|
224
|
+
{lines.flatMap((line, index) => (index ? ["\n", line] : [line]))}
|
|
225
|
+
</code>
|
|
226
|
+
</pre>
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function CodeBlockFooter({
|
|
231
|
+
className,
|
|
232
|
+
...props
|
|
233
|
+
}: ComponentProps<"div">) {
|
|
234
|
+
return (
|
|
235
|
+
<div
|
|
236
|
+
data-slot="code-block-footer"
|
|
237
|
+
className={classes("agentkit-code-block-footer", className)}
|
|
238
|
+
{...props}
|
|
239
|
+
/>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function codeLines(code: string): string[] {
|
|
244
|
+
return code.replace(/\n$/, "").split("\n");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// An `ansi` pass drops these from token text, so the plain line must too.
|
|
248
|
+
const ANSI_SEQUENCE = /\u001b\[[0-9;]*m/g;
|
|
249
|
+
|
|
250
|
+
function visibleLine(line: string, language: string | undefined): string {
|
|
251
|
+
return language === "ansi" ? line.replace(ANSI_SEQUENCE, "") : line;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function tokenText(tokens: ThemedToken[]): string {
|
|
255
|
+
return tokens.map((token) => token.content).join("");
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
interface HighlightPass {
|
|
259
|
+
language: string;
|
|
260
|
+
lines: ThemedToken[][];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const HIGHLIGHT_INTERVAL_MS = 80;
|
|
264
|
+
|
|
265
|
+
/** A line keeps its last tokens until its text changes, so streaming never flashes it plain. */
|
|
266
|
+
export function useHighlightedCode(
|
|
267
|
+
code: string,
|
|
268
|
+
language: string | undefined,
|
|
269
|
+
): ReactNode[] {
|
|
270
|
+
const [pass, setPass] = useState<HighlightPass | null>(null);
|
|
271
|
+
const lastRun = useRef(0);
|
|
272
|
+
useEffect(() => {
|
|
273
|
+
if (!language) return;
|
|
274
|
+
let cancelled = false;
|
|
275
|
+
const wait = Math.max(
|
|
276
|
+
0,
|
|
277
|
+
HIGHLIGHT_INTERVAL_MS - (Date.now() - lastRun.current),
|
|
278
|
+
);
|
|
279
|
+
const timer = setTimeout(() => {
|
|
280
|
+
lastRun.current = Date.now();
|
|
281
|
+
void loadCodeHighlighter().then(
|
|
282
|
+
(highlighter) => {
|
|
283
|
+
if (cancelled) return;
|
|
284
|
+
const lang = resolveCodeLanguage(highlighter, language);
|
|
285
|
+
if (!lang) return setPass(null);
|
|
286
|
+
const { tokens } = highlighter.codeToTokens(code.replace(/\n$/, ""), {
|
|
287
|
+
lang,
|
|
288
|
+
themes: CODE_THEMES,
|
|
289
|
+
defaultColor: false,
|
|
290
|
+
});
|
|
291
|
+
setPass({ language, lines: tokens });
|
|
292
|
+
},
|
|
293
|
+
// Highlighting is decoration: the plain lines already show the code.
|
|
294
|
+
() => undefined,
|
|
295
|
+
);
|
|
296
|
+
}, wait);
|
|
297
|
+
return () => {
|
|
298
|
+
cancelled = true;
|
|
299
|
+
clearTimeout(timer);
|
|
300
|
+
};
|
|
301
|
+
}, [code, language]);
|
|
302
|
+
const usable = pass && pass.language === language ? pass.lines : undefined;
|
|
303
|
+
return codeLines(code).map((source, index) => {
|
|
304
|
+
const line = visibleLine(source, language);
|
|
305
|
+
const tokens = usable?.[index];
|
|
306
|
+
if (!tokens || tokenText(tokens) !== line) return line;
|
|
307
|
+
return tokens.map((token, tokenIndex) => (
|
|
308
|
+
<span
|
|
309
|
+
key={tokenIndex}
|
|
310
|
+
className="agentkit-code-token"
|
|
311
|
+
style={token.htmlStyle as CSSProperties | undefined}
|
|
312
|
+
>
|
|
313
|
+
{token.content}
|
|
314
|
+
</span>
|
|
315
|
+
));
|
|
316
|
+
});
|
|
317
|
+
}
|
|
@@ -183,6 +183,13 @@ describe("AgentActivityItem replay privacy", () => {
|
|
|
183
183
|
?.querySelector('a[href^="https://example.test/report"]')
|
|
184
184
|
?.closest(`[${SESSION_REPLAY_BLOCK_ATTRIBUTE}]`) !== null,
|
|
185
185
|
).toBe(status === "failed");
|
|
186
|
+
|
|
187
|
+
// Closing swaps in a fresh, blocked node rather than re-blocking the
|
|
188
|
+
// node replay recorded while it was open.
|
|
189
|
+
await act(async () => disclosure.click());
|
|
190
|
+
const closed = container.querySelector(".agentkit-activity-summary");
|
|
191
|
+
expect(summary?.isConnected).toBe(false);
|
|
192
|
+
expect(closed?.hasAttribute(SESSION_REPLAY_BLOCK_ATTRIBUTE)).toBe(true);
|
|
186
193
|
} finally {
|
|
187
194
|
await act(async () => root.unmount());
|
|
188
195
|
await client.shutdown();
|
|
@@ -13,7 +13,7 @@ import type {
|
|
|
13
13
|
AgentEvent,
|
|
14
14
|
AgentTask,
|
|
15
15
|
} from "@agent-native/agentkit/protocol";
|
|
16
|
-
import { act, createElement, type ReactNode } from "react";
|
|
16
|
+
import { act, createElement, useEffect, useRef, type ReactNode } from "react";
|
|
17
17
|
import { createRoot, type Root } from "react-dom/client";
|
|
18
18
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
19
19
|
|
|
@@ -62,7 +62,7 @@ import {
|
|
|
62
62
|
AgentTaskItem,
|
|
63
63
|
useAgentKitFollowTranscript,
|
|
64
64
|
} from "./components.js";
|
|
65
|
-
import { AgentKitProvider } from "./context.js";
|
|
65
|
+
import { AgentKitProvider, useAgentKit } from "./context.js";
|
|
66
66
|
|
|
67
67
|
let container: HTMLDivElement;
|
|
68
68
|
let root: Root;
|
|
@@ -160,6 +160,17 @@ function runningThread(threadId: string, runId: string): AgentThreadState {
|
|
|
160
160
|
};
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
// Registered after the chat's own composer, so a focus request lands here.
|
|
164
|
+
function ComposerFocusTarget() {
|
|
165
|
+
const { registerComposerFocus, threadId } = useAgentKit();
|
|
166
|
+
const target = useRef<HTMLButtonElement>(null);
|
|
167
|
+
useEffect(
|
|
168
|
+
() => registerComposerFocus(threadId, () => target.current?.focus()),
|
|
169
|
+
[registerComposerFocus, threadId],
|
|
170
|
+
);
|
|
171
|
+
return <button ref={target}>Composer focus target</button>;
|
|
172
|
+
}
|
|
173
|
+
|
|
163
174
|
describe("AgentKitChat papercuts", () => {
|
|
164
175
|
it("offers a default Stop that cancels once however often it is clicked", async () => {
|
|
165
176
|
const thread = runningThread("thread-stop", "run-stop");
|
|
@@ -195,6 +206,37 @@ describe("AgentKitChat papercuts", () => {
|
|
|
195
206
|
expect(stop?.disabled).toBe(false);
|
|
196
207
|
});
|
|
197
208
|
|
|
209
|
+
it("offers a skip link past the transcript to the composer", async () => {
|
|
210
|
+
const thread = runningThread("thread-skip", "run-skip");
|
|
211
|
+
const store = observableController(snapshotOf(thread));
|
|
212
|
+
await render(
|
|
213
|
+
<AgentKitProvider controller={store.controller} threadId={thread.id}>
|
|
214
|
+
<AgentKitChat
|
|
215
|
+
composerProps={{
|
|
216
|
+
autoFocus: false,
|
|
217
|
+
modelStatusChecksEnabled: false,
|
|
218
|
+
voiceEnabled: false,
|
|
219
|
+
}}
|
|
220
|
+
/>
|
|
221
|
+
<ComposerFocusTarget />
|
|
222
|
+
</AgentKitProvider>,
|
|
223
|
+
);
|
|
224
|
+
const skip = container.querySelector<HTMLButtonElement>(
|
|
225
|
+
'[data-agentkit-skip-link="composer"]',
|
|
226
|
+
);
|
|
227
|
+
const transcript = container.querySelector(".agentkit-transcript");
|
|
228
|
+
expect(skip?.textContent).toBe("Skip to message box");
|
|
229
|
+
expect(
|
|
230
|
+
transcript && skip
|
|
231
|
+
? skip.compareDocumentPosition(transcript) &
|
|
232
|
+
Node.DOCUMENT_POSITION_FOLLOWING
|
|
233
|
+
: 0,
|
|
234
|
+
).toBeTruthy();
|
|
235
|
+
|
|
236
|
+
await act(async () => skip?.click());
|
|
237
|
+
expect(document.activeElement?.textContent).toBe("Composer focus target");
|
|
238
|
+
});
|
|
239
|
+
|
|
198
240
|
it("announces a finished response once in the shared status region", async () => {
|
|
199
241
|
const thread = runningThread("thread-announce", "run-announce");
|
|
200
242
|
const store = observableController(snapshotOf(thread));
|
|
@@ -232,6 +274,105 @@ describe("AgentKitChat papercuts", () => {
|
|
|
232
274
|
).toHaveLength(1);
|
|
233
275
|
});
|
|
234
276
|
|
|
277
|
+
it.each([
|
|
278
|
+
["awaiting_approval", "Waiting for approval"],
|
|
279
|
+
["awaiting_input", "Waiting for input"],
|
|
280
|
+
["cancelled", "Stopped"],
|
|
281
|
+
] as const)("announces a run that goes %s", async (status, announcement) => {
|
|
282
|
+
const thread = runningThread("thread-pause", "run-pause");
|
|
283
|
+
const store = observableController(snapshotOf(thread));
|
|
284
|
+
await render(
|
|
285
|
+
<AgentKitProvider controller={store.controller} threadId={thread.id}>
|
|
286
|
+
<AgentKitChat composer={false} />
|
|
287
|
+
</AgentKitProvider>,
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
await act(async () => {
|
|
291
|
+
store.update(
|
|
292
|
+
snapshotOf(
|
|
293
|
+
{
|
|
294
|
+
...thread,
|
|
295
|
+
runs: {
|
|
296
|
+
"run-pause": { ...thread.runs["run-pause"]!, status },
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
1,
|
|
300
|
+
),
|
|
301
|
+
);
|
|
302
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
303
|
+
});
|
|
304
|
+
expect(
|
|
305
|
+
container.querySelector("[data-agentkit-live-status]")?.textContent,
|
|
306
|
+
).toBe(announcement);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it.each([
|
|
310
|
+
["with an error, which its own alert reads", true, ""],
|
|
311
|
+
["without an error", false, "Response failed"],
|
|
312
|
+
] as const)(
|
|
313
|
+
"announces a failed run once: %s",
|
|
314
|
+
async (_case, withError, announcement) => {
|
|
315
|
+
const thread = runningThread("thread-fail", "run-fail");
|
|
316
|
+
const store = observableController(snapshotOf(thread));
|
|
317
|
+
await render(
|
|
318
|
+
<AgentKitProvider controller={store.controller} threadId={thread.id}>
|
|
319
|
+
<AgentKitChat composer={false} />
|
|
320
|
+
</AgentKitProvider>,
|
|
321
|
+
);
|
|
322
|
+
await act(async () => {
|
|
323
|
+
store.update(
|
|
324
|
+
snapshotOf(
|
|
325
|
+
{
|
|
326
|
+
...thread,
|
|
327
|
+
activeRunIds: [],
|
|
328
|
+
runs: {
|
|
329
|
+
"run-fail": {
|
|
330
|
+
...thread.runs["run-fail"]!,
|
|
331
|
+
status: "failed",
|
|
332
|
+
...(withError
|
|
333
|
+
? {
|
|
334
|
+
error: {
|
|
335
|
+
code: "provider_error",
|
|
336
|
+
message: "The model provider returned an error.",
|
|
337
|
+
},
|
|
338
|
+
}
|
|
339
|
+
: {}),
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
1,
|
|
344
|
+
),
|
|
345
|
+
);
|
|
346
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
347
|
+
});
|
|
348
|
+
expect(
|
|
349
|
+
container.querySelector("[data-agentkit-live-status]")?.textContent,
|
|
350
|
+
).toBe(announcement);
|
|
351
|
+
expect(container.querySelectorAll('[role="alert"]')).toHaveLength(
|
|
352
|
+
withError ? 1 : 0,
|
|
353
|
+
);
|
|
354
|
+
},
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
it("gives every turn a heading for heading navigation", async () => {
|
|
358
|
+
const thread = runningThread("thread-headings", "run-headings");
|
|
359
|
+
const store = observableController(snapshotOf(thread));
|
|
360
|
+
await render(
|
|
361
|
+
<AgentKitProvider controller={store.controller} threadId={thread.id}>
|
|
362
|
+
<AgentKitChat composer={false} />
|
|
363
|
+
</AgentKitProvider>,
|
|
364
|
+
);
|
|
365
|
+
const articles = [
|
|
366
|
+
...container.querySelectorAll("article.agentkit-message"),
|
|
367
|
+
];
|
|
368
|
+
expect(articles.length).toBeGreaterThan(0);
|
|
369
|
+
expect(
|
|
370
|
+
articles.map(
|
|
371
|
+
(article) => article.querySelector(":scope > h2")?.textContent,
|
|
372
|
+
),
|
|
373
|
+
).toEqual(articles.map((article) => article.getAttribute("aria-label")));
|
|
374
|
+
});
|
|
375
|
+
|
|
235
376
|
it("marks a stopped run under its turn", async () => {
|
|
236
377
|
const thread: AgentThreadState = {
|
|
237
378
|
...createAgentThreadState("thread-cancelled"),
|