@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,82 @@
|
|
|
1
|
+
import { useEffect, type RefObject } from "react";
|
|
2
|
+
|
|
3
|
+
const ROVING_CANDIDATES = "button, a[href]";
|
|
4
|
+
|
|
5
|
+
function rovingItems(container: HTMLElement): HTMLElement[] {
|
|
6
|
+
return Array.from(
|
|
7
|
+
container.querySelectorAll<HTMLElement>(ROVING_CANDIDATES),
|
|
8
|
+
).filter(
|
|
9
|
+
(item) =>
|
|
10
|
+
!(item instanceof HTMLButtonElement && item.disabled) &&
|
|
11
|
+
// An inert subtree takes no focus, so it gets no tab stop either.
|
|
12
|
+
!item.closest("[inert]"),
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** One tab stop for `container`, landing on the last-used item; arrows move. */
|
|
17
|
+
export function useRovingFocus(
|
|
18
|
+
containerRef: RefObject<HTMLElement | null>,
|
|
19
|
+
enabled = true,
|
|
20
|
+
): void {
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const container = containerRef.current;
|
|
23
|
+
if (!container || !enabled) return;
|
|
24
|
+
let current: HTMLElement | null = null;
|
|
25
|
+
const sync = () => {
|
|
26
|
+
const items = rovingItems(container);
|
|
27
|
+
if (!current || !items.includes(current)) current = items[0] ?? null;
|
|
28
|
+
// Every candidate, so a newly enabled item joins at -1, not as a stop.
|
|
29
|
+
for (const item of container.querySelectorAll<HTMLElement>(
|
|
30
|
+
ROVING_CANDIDATES,
|
|
31
|
+
))
|
|
32
|
+
item.tabIndex = item === current ? 0 : -1;
|
|
33
|
+
};
|
|
34
|
+
const onFocusIn = (event: FocusEvent) => {
|
|
35
|
+
const target = event.target;
|
|
36
|
+
if (!(target instanceof HTMLElement)) return;
|
|
37
|
+
if (!rovingItems(container).includes(target)) return;
|
|
38
|
+
current = target;
|
|
39
|
+
sync();
|
|
40
|
+
};
|
|
41
|
+
const onKeyDown = (event: KeyboardEvent) => {
|
|
42
|
+
if (event.altKey || event.ctrlKey || event.metaKey) return;
|
|
43
|
+
const items = rovingItems(container);
|
|
44
|
+
const index = items.indexOf(document.activeElement as HTMLElement);
|
|
45
|
+
if (index < 0) return;
|
|
46
|
+
const rtl =
|
|
47
|
+
getComputedStyle(container).direction === "rtl" ||
|
|
48
|
+
container.closest("[dir]")?.getAttribute("dir") === "rtl";
|
|
49
|
+
const forward = rtl ? "ArrowLeft" : "ArrowRight";
|
|
50
|
+
const backward = rtl ? "ArrowRight" : "ArrowLeft";
|
|
51
|
+
const next =
|
|
52
|
+
event.key === forward
|
|
53
|
+
? items[(index + 1) % items.length]
|
|
54
|
+
: event.key === backward
|
|
55
|
+
? items[(index - 1 + items.length) % items.length]
|
|
56
|
+
: event.key === "Home"
|
|
57
|
+
? items[0]
|
|
58
|
+
: event.key === "End"
|
|
59
|
+
? items[items.length - 1]
|
|
60
|
+
: undefined;
|
|
61
|
+
if (!next) return;
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
next.focus();
|
|
64
|
+
};
|
|
65
|
+
sync();
|
|
66
|
+
// Items and their disabled/inert state change without a re-render.
|
|
67
|
+
const observer = new MutationObserver(sync);
|
|
68
|
+
observer.observe(container, {
|
|
69
|
+
subtree: true,
|
|
70
|
+
childList: true,
|
|
71
|
+
attributes: true,
|
|
72
|
+
attributeFilter: ["disabled", "inert"],
|
|
73
|
+
});
|
|
74
|
+
container.addEventListener("focusin", onFocusIn);
|
|
75
|
+
container.addEventListener("keydown", onKeyDown);
|
|
76
|
+
return () => {
|
|
77
|
+
observer.disconnect();
|
|
78
|
+
container.removeEventListener("focusin", onFocusIn);
|
|
79
|
+
container.removeEventListener("keydown", onKeyDown);
|
|
80
|
+
};
|
|
81
|
+
}, [containerRef, enabled]);
|
|
82
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
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 { parseStackTrace, StackTrace, type StackFrame } from "./stack-trace.js";
|
|
8
|
+
|
|
9
|
+
const NODE_CRASH = [
|
|
10
|
+
"stdout:",
|
|
11
|
+
"starting",
|
|
12
|
+
"",
|
|
13
|
+
"stderr:",
|
|
14
|
+
"file:///tmp/run/sandbox.mjs:1",
|
|
15
|
+
"function f(){ return x.y }",
|
|
16
|
+
" ^",
|
|
17
|
+
"",
|
|
18
|
+
"ReferenceError: x is not defined",
|
|
19
|
+
" at f (file:///tmp/run/sandbox.mjs:1:15)",
|
|
20
|
+
" at file:///tmp/run/sandbox.mjs:2:1",
|
|
21
|
+
" at ModuleJob.run (node:internal/modules/esm/module_job:569:25)",
|
|
22
|
+
" at async node:internal/modules/esm/loader:650:26",
|
|
23
|
+
" at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:105:5)",
|
|
24
|
+
"",
|
|
25
|
+
"Node.js v26.12.1",
|
|
26
|
+
].join("\n");
|
|
27
|
+
|
|
28
|
+
const PYTHON_CRASH = [
|
|
29
|
+
"Traceback (most recent call last):",
|
|
30
|
+
' File "/work/app.py", line 4, in <module>',
|
|
31
|
+
" main()",
|
|
32
|
+
' File "/usr/lib/python3.12/json/__init__.py", line 346, in loads',
|
|
33
|
+
" return _default_decoder.decode(s)",
|
|
34
|
+
"json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)",
|
|
35
|
+
].join("\n");
|
|
36
|
+
|
|
37
|
+
describe("parseStackTrace", () => {
|
|
38
|
+
it("finds a Node stack inside other output", () => {
|
|
39
|
+
const trace = parseStackTrace(NODE_CRASH)!;
|
|
40
|
+
expect(trace.error).toBe("ReferenceError: x is not defined");
|
|
41
|
+
expect(trace.whole).toBe(false);
|
|
42
|
+
expect(trace.text.split("\n")).toHaveLength(6);
|
|
43
|
+
expect(trace.frames[0]).toEqual({
|
|
44
|
+
text: "at f (file:///tmp/run/sandbox.mjs:1:15)",
|
|
45
|
+
name: "f",
|
|
46
|
+
file: "file:///tmp/run/sandbox.mjs",
|
|
47
|
+
line: 1,
|
|
48
|
+
column: 15,
|
|
49
|
+
internal: false,
|
|
50
|
+
});
|
|
51
|
+
expect(trace.frames[1]).toMatchObject({
|
|
52
|
+
file: "file:///tmp/run/sandbox.mjs",
|
|
53
|
+
line: 2,
|
|
54
|
+
internal: false,
|
|
55
|
+
});
|
|
56
|
+
expect(trace.frames[1]?.name).toBeUndefined();
|
|
57
|
+
expect(trace.frames.slice(2).map((frame) => frame.internal)).toEqual([
|
|
58
|
+
true,
|
|
59
|
+
true,
|
|
60
|
+
true,
|
|
61
|
+
]);
|
|
62
|
+
expect(trace.frames[4]?.name).toBe("asyncRunEntryPointWithESMLoader");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("reads a Python traceback, marking the standard library", () => {
|
|
66
|
+
const trace = parseStackTrace(PYTHON_CRASH)!;
|
|
67
|
+
expect(trace.error).toBe(
|
|
68
|
+
"json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)",
|
|
69
|
+
);
|
|
70
|
+
expect(trace.whole).toBe(true);
|
|
71
|
+
expect(
|
|
72
|
+
trace.frames.map((frame) => [frame.file, frame.line, frame.internal]),
|
|
73
|
+
).toEqual([
|
|
74
|
+
["/work/app.py", 4, false],
|
|
75
|
+
["/usr/lib/python3.12/json/__init__.py", 346, true],
|
|
76
|
+
]);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("finds nothing in output without frames", () => {
|
|
80
|
+
expect(parseStackTrace("stdout:\nok\n\nstderr:\ndone")).toBeUndefined();
|
|
81
|
+
expect(parseStackTrace("Unhandled error: boom")).toBeUndefined();
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
let root: Root;
|
|
86
|
+
let container: HTMLDivElement;
|
|
87
|
+
|
|
88
|
+
beforeEach(() => {
|
|
89
|
+
vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
|
|
90
|
+
container = document.createElement("div");
|
|
91
|
+
document.body.append(container);
|
|
92
|
+
root = createRoot(container);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
afterEach(() => {
|
|
96
|
+
act(() => root.unmount());
|
|
97
|
+
container.remove();
|
|
98
|
+
vi.unstubAllGlobals();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("StackTrace", () => {
|
|
102
|
+
it("titles the error and folds the framework frames after the caller's", async () => {
|
|
103
|
+
const opened: StackFrame[] = [];
|
|
104
|
+
await act(async () =>
|
|
105
|
+
root.render(
|
|
106
|
+
<StackTrace
|
|
107
|
+
trace={NODE_CRASH}
|
|
108
|
+
defaultOpen
|
|
109
|
+
onFilePathClick={(frame) => opened.push(frame)}
|
|
110
|
+
/>,
|
|
111
|
+
),
|
|
112
|
+
);
|
|
113
|
+
const trigger = container.querySelector<HTMLButtonElement>(
|
|
114
|
+
"button.agentkit-disclosure-summary",
|
|
115
|
+
)!;
|
|
116
|
+
expect(trigger.textContent).toBe("ReferenceError: x is not defined");
|
|
117
|
+
expect(trigger.getAttribute("aria-expanded")).toBe("true");
|
|
118
|
+
expect(
|
|
119
|
+
container
|
|
120
|
+
.querySelector('[data-slot="trace-line"]')
|
|
121
|
+
?.getAttribute("data-trace-status"),
|
|
122
|
+
).toBe("error");
|
|
123
|
+
|
|
124
|
+
const own = Array.from(
|
|
125
|
+
container.querySelectorAll(
|
|
126
|
+
".agentkit-stack-trace-body > ol > .agentkit-stack-frame",
|
|
127
|
+
),
|
|
128
|
+
(frame) => frame.textContent,
|
|
129
|
+
);
|
|
130
|
+
expect(own).toEqual(["fsandbox.mjs:1:15", "sandbox.mjs:2:1"]);
|
|
131
|
+
expect(
|
|
132
|
+
container
|
|
133
|
+
.querySelector(".agentkit-stack-frame-location")
|
|
134
|
+
?.getAttribute("title"),
|
|
135
|
+
).toBe("file:///tmp/run/sandbox.mjs:1:15");
|
|
136
|
+
const folded = container.querySelector(".agentkit-stack-frames-folded");
|
|
137
|
+
expect(folded?.querySelector("button")?.textContent).toBe(
|
|
138
|
+
"3 framework frames",
|
|
139
|
+
);
|
|
140
|
+
// Folded frames stay findable in the page.
|
|
141
|
+
expect(
|
|
142
|
+
folded
|
|
143
|
+
?.querySelector('[data-slot="trace-collapsible-content"]')
|
|
144
|
+
?.getAttribute("hidden"),
|
|
145
|
+
).toBe("until-found");
|
|
146
|
+
|
|
147
|
+
await act(async () =>
|
|
148
|
+
container
|
|
149
|
+
.querySelector<HTMLButtonElement>(
|
|
150
|
+
"button.agentkit-stack-frame-location",
|
|
151
|
+
)
|
|
152
|
+
?.click(),
|
|
153
|
+
);
|
|
154
|
+
expect(opened.map((frame) => frame.line)).toEqual([1]);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("copies only the trace", async () => {
|
|
158
|
+
const writeText = vi.fn(async () => undefined);
|
|
159
|
+
vi.stubGlobal("navigator", { ...navigator, clipboard: { writeText } });
|
|
160
|
+
await act(async () => root.render(<StackTrace trace={NODE_CRASH} />));
|
|
161
|
+
const copy = container.querySelector<HTMLButtonElement>(
|
|
162
|
+
".agentkit-stack-trace-actions button",
|
|
163
|
+
)!;
|
|
164
|
+
expect(copy.getAttribute("aria-label")).toBe("Copy stack trace");
|
|
165
|
+
await act(async () => {
|
|
166
|
+
copy.click();
|
|
167
|
+
await Promise.resolve();
|
|
168
|
+
await Promise.resolve();
|
|
169
|
+
});
|
|
170
|
+
expect(writeText).toHaveBeenCalledWith(parseStackTrace(NODE_CRASH)!.text);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("renders nothing for text without a stack", async () => {
|
|
174
|
+
await act(async () => root.render(<StackTrace trace="all good" />));
|
|
175
|
+
expect(container.innerHTML).toBe("");
|
|
176
|
+
});
|
|
177
|
+
});
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { IconAlertTriangle } from "@tabler/icons-react";
|
|
2
|
+
|
|
3
|
+
import { CodeBlockCopyButton } from "./code-block.js";
|
|
4
|
+
import { useAgentKitLabels } from "./context.js";
|
|
5
|
+
import {
|
|
6
|
+
Trace,
|
|
7
|
+
TraceCollapsible,
|
|
8
|
+
TraceCollapsibleContent,
|
|
9
|
+
TraceCollapsibleTrigger,
|
|
10
|
+
TraceIcon,
|
|
11
|
+
TraceLine,
|
|
12
|
+
TraceTitle,
|
|
13
|
+
type TraceCollapsibleOpenProps,
|
|
14
|
+
} from "./trace.js";
|
|
15
|
+
|
|
16
|
+
export interface StackFrame {
|
|
17
|
+
text: string;
|
|
18
|
+
name?: string;
|
|
19
|
+
/** Without the line and column. */
|
|
20
|
+
file?: string;
|
|
21
|
+
line?: number;
|
|
22
|
+
column?: number;
|
|
23
|
+
/** Runtime, dependency, or standard library code rather than the caller's. */
|
|
24
|
+
internal: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ParsedStackTrace {
|
|
28
|
+
error: string;
|
|
29
|
+
frames: StackFrame[];
|
|
30
|
+
/** The trace exactly as printed, for copying. */
|
|
31
|
+
text: string;
|
|
32
|
+
/** Whether the text holds nothing but the trace. */
|
|
33
|
+
whole: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const V8_FRAME = /^\s+at\s+(.+)$/;
|
|
37
|
+
const PYTHON_HEADER = /^Traceback \(most recent call last\):\s*$/;
|
|
38
|
+
const PYTHON_FRAME = /^\s+File "([^"]+)", line (\d+)(?:, in (.+))?$/;
|
|
39
|
+
const INTERNAL_FILE =
|
|
40
|
+
/(?:^|[/\\])node_modules[/\\]|^node:|^internal[/\\]|[/\\]site-packages[/\\]|[/\\]lib[/\\]python\d|^<frozen /;
|
|
41
|
+
|
|
42
|
+
/** The first V8 or Python trace in `text`, without the output around it. */
|
|
43
|
+
export function parseStackTrace(text: string): ParsedStackTrace | undefined {
|
|
44
|
+
const lines = text.split("\n");
|
|
45
|
+
return parseV8(lines) ?? parsePython(lines);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function parseV8(lines: string[]): ParsedStackTrace | undefined {
|
|
49
|
+
const first = lines.findIndex((line) => V8_FRAME.test(line));
|
|
50
|
+
const error = lines[first - 1]?.trim();
|
|
51
|
+
if (first < 1 || !error) return undefined;
|
|
52
|
+
const frames: StackFrame[] = [];
|
|
53
|
+
for (const line of lines.slice(first)) {
|
|
54
|
+
const body = V8_FRAME.exec(line)?.[1];
|
|
55
|
+
if (body === undefined) break;
|
|
56
|
+
frames.push(v8Frame(body.trim()));
|
|
57
|
+
}
|
|
58
|
+
return result(lines, first - 1, first + frames.length, error, frames);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function v8Frame(body: string): StackFrame {
|
|
62
|
+
const named = /^(.*?) \((.+)\)$/.exec(body);
|
|
63
|
+
const name = group(named, 1)?.replace(/^async /, "") || undefined;
|
|
64
|
+
const location = group(named, 2) ?? body.replace(/^async /, "");
|
|
65
|
+
const position = /^(.*?):(\d+):(\d+)$/.exec(location);
|
|
66
|
+
const file = group(position, 1) ?? location;
|
|
67
|
+
const line = group(position, 2);
|
|
68
|
+
const column = group(position, 3);
|
|
69
|
+
return {
|
|
70
|
+
text: `at ${body}`,
|
|
71
|
+
...(name ? { name } : {}),
|
|
72
|
+
file,
|
|
73
|
+
...(line && column ? { line: Number(line), column: Number(column) } : {}),
|
|
74
|
+
internal: INTERNAL_FILE.test(file),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function group(
|
|
79
|
+
match: RegExpExecArray | null,
|
|
80
|
+
index: number,
|
|
81
|
+
): string | undefined {
|
|
82
|
+
return match?.[index];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function parsePython(lines: string[]): ParsedStackTrace | undefined {
|
|
86
|
+
const start = lines.findIndex((line) => PYTHON_HEADER.test(line));
|
|
87
|
+
if (start < 0) return undefined;
|
|
88
|
+
const frames: StackFrame[] = [];
|
|
89
|
+
let index = start + 1;
|
|
90
|
+
for (const line of lines.slice(start + 1)) {
|
|
91
|
+
const [, file, number, name] = PYTHON_FRAME.exec(line) ?? [];
|
|
92
|
+
if (file && number) {
|
|
93
|
+
frames.push({
|
|
94
|
+
text: line.trim(),
|
|
95
|
+
...(name ? { name } : {}),
|
|
96
|
+
file,
|
|
97
|
+
line: Number(number),
|
|
98
|
+
internal: INTERNAL_FILE.test(file),
|
|
99
|
+
});
|
|
100
|
+
} else if (!/^\s/.test(line)) {
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
index++;
|
|
104
|
+
}
|
|
105
|
+
const error = lines[index]?.trim();
|
|
106
|
+
if (!error || !frames.length) return undefined;
|
|
107
|
+
return result(lines, start, index + 1, error, frames);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function result(
|
|
111
|
+
lines: string[],
|
|
112
|
+
start: number,
|
|
113
|
+
end: number,
|
|
114
|
+
error: string,
|
|
115
|
+
frames: StackFrame[],
|
|
116
|
+
): ParsedStackTrace {
|
|
117
|
+
const outside = [...lines.slice(0, start), ...lines.slice(end)];
|
|
118
|
+
return {
|
|
119
|
+
error,
|
|
120
|
+
frames,
|
|
121
|
+
text: lines.slice(start, end).join("\n"),
|
|
122
|
+
whole: outside.every((line) => !line.trim()),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Copy sits beside the trigger: a button cannot nest in the trigger. */
|
|
127
|
+
export function StackTrace({
|
|
128
|
+
trace,
|
|
129
|
+
defaultOpen,
|
|
130
|
+
onFilePathClick,
|
|
131
|
+
className,
|
|
132
|
+
...openProps
|
|
133
|
+
}: TraceCollapsibleOpenProps & {
|
|
134
|
+
trace: string | ParsedStackTrace;
|
|
135
|
+
defaultOpen?: boolean;
|
|
136
|
+
/** Makes the caller's frames links to their file and line. */
|
|
137
|
+
onFilePathClick?: (frame: StackFrame) => void;
|
|
138
|
+
className?: string;
|
|
139
|
+
}) {
|
|
140
|
+
const labels = useAgentKitLabels();
|
|
141
|
+
const parsed = typeof trace === "string" ? parseStackTrace(trace) : trace;
|
|
142
|
+
if (!parsed) return null;
|
|
143
|
+
return (
|
|
144
|
+
<Trace
|
|
145
|
+
data-slot="stack-trace"
|
|
146
|
+
className={
|
|
147
|
+
className ? `agentkit-stack-trace ${className}` : "agentkit-stack-trace"
|
|
148
|
+
}
|
|
149
|
+
>
|
|
150
|
+
<TraceCollapsible {...openProps} defaultOpen={defaultOpen}>
|
|
151
|
+
<div className="agentkit-stack-trace-row">
|
|
152
|
+
<TraceCollapsibleTrigger>
|
|
153
|
+
<TraceLine status="error">
|
|
154
|
+
<TraceIcon>
|
|
155
|
+
<IconAlertTriangle
|
|
156
|
+
aria-hidden="true"
|
|
157
|
+
className="agentkit-icon"
|
|
158
|
+
/>
|
|
159
|
+
</TraceIcon>
|
|
160
|
+
<TraceTitle>{parsed.error}</TraceTitle>
|
|
161
|
+
</TraceLine>
|
|
162
|
+
</TraceCollapsibleTrigger>
|
|
163
|
+
<span className="agentkit-stack-trace-actions">
|
|
164
|
+
<CodeBlockCopyButton
|
|
165
|
+
value={parsed.text}
|
|
166
|
+
label={labels.workStepCopyStackTrace}
|
|
167
|
+
/>
|
|
168
|
+
</span>
|
|
169
|
+
</div>
|
|
170
|
+
<TraceCollapsibleContent
|
|
171
|
+
keepMounted
|
|
172
|
+
className="agentkit-stack-trace-body"
|
|
173
|
+
>
|
|
174
|
+
{/* The title truncates, so the full error leads the frames. */}
|
|
175
|
+
<p className="agentkit-stack-trace-error">{parsed.error}</p>
|
|
176
|
+
<StackTraceFrames
|
|
177
|
+
frames={parsed.frames}
|
|
178
|
+
onFilePathClick={onFilePathClick}
|
|
179
|
+
/>
|
|
180
|
+
</TraceCollapsibleContent>
|
|
181
|
+
</TraceCollapsible>
|
|
182
|
+
</Trace>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function StackTraceFrames({
|
|
187
|
+
frames,
|
|
188
|
+
onFilePathClick,
|
|
189
|
+
}: {
|
|
190
|
+
frames: StackFrame[];
|
|
191
|
+
onFilePathClick?: ((frame: StackFrame) => void) | undefined;
|
|
192
|
+
}) {
|
|
193
|
+
const labels = useAgentKitLabels();
|
|
194
|
+
const runs: Array<{
|
|
195
|
+
start: number;
|
|
196
|
+
internal: boolean;
|
|
197
|
+
frames: StackFrame[];
|
|
198
|
+
}> = [];
|
|
199
|
+
frames.forEach((frame, index) => {
|
|
200
|
+
const last = runs.at(-1);
|
|
201
|
+
if (last?.internal && frame.internal) last.frames.push(frame);
|
|
202
|
+
else runs.push({ start: index, internal: frame.internal, frames: [frame] });
|
|
203
|
+
});
|
|
204
|
+
const items = runs.map(({ start, frames: run }) => {
|
|
205
|
+
if (run.length < 2) {
|
|
206
|
+
return run.map((frame) => (
|
|
207
|
+
<StackTraceFrame
|
|
208
|
+
key={start}
|
|
209
|
+
frame={frame}
|
|
210
|
+
onFilePathClick={onFilePathClick}
|
|
211
|
+
/>
|
|
212
|
+
));
|
|
213
|
+
}
|
|
214
|
+
return (
|
|
215
|
+
<li key={start} className="agentkit-stack-frames-folded">
|
|
216
|
+
<TraceCollapsible>
|
|
217
|
+
<TraceCollapsibleTrigger>
|
|
218
|
+
<TraceLine>
|
|
219
|
+
<span className="agentkit-disclosure-label">
|
|
220
|
+
{labels.workStepFrameworkFrames.replace(
|
|
221
|
+
"{{count}}",
|
|
222
|
+
() => `${run.length}`,
|
|
223
|
+
)}
|
|
224
|
+
</span>
|
|
225
|
+
</TraceLine>
|
|
226
|
+
</TraceCollapsibleTrigger>
|
|
227
|
+
<TraceCollapsibleContent keepMounted>
|
|
228
|
+
<ol className="agentkit-stack-frames">
|
|
229
|
+
{run.map((frame, offset) => (
|
|
230
|
+
<StackTraceFrame key={start + offset} frame={frame} />
|
|
231
|
+
))}
|
|
232
|
+
</ol>
|
|
233
|
+
</TraceCollapsibleContent>
|
|
234
|
+
</TraceCollapsible>
|
|
235
|
+
</li>
|
|
236
|
+
);
|
|
237
|
+
});
|
|
238
|
+
return <ol className="agentkit-stack-frames">{items}</ol>;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function StackTraceFrame({
|
|
242
|
+
frame,
|
|
243
|
+
onFilePathClick,
|
|
244
|
+
}: {
|
|
245
|
+
frame: StackFrame;
|
|
246
|
+
onFilePathClick?: ((frame: StackFrame) => void) | undefined;
|
|
247
|
+
}) {
|
|
248
|
+
const position =
|
|
249
|
+
frame.line !== undefined
|
|
250
|
+
? `:${frame.line}${frame.column !== undefined ? `:${frame.column}` : ""}`
|
|
251
|
+
: "";
|
|
252
|
+
// The file name only, so a long path never hides the line number.
|
|
253
|
+
const location = frame.file
|
|
254
|
+
? `${frame.file.split(/[/\\]/).at(-1) || frame.file}${position}`
|
|
255
|
+
: frame.text;
|
|
256
|
+
const fullLocation = frame.file ? `${frame.file}${position}` : frame.text;
|
|
257
|
+
return (
|
|
258
|
+
<li
|
|
259
|
+
className="agentkit-stack-frame"
|
|
260
|
+
data-internal={frame.internal ? "true" : undefined}
|
|
261
|
+
>
|
|
262
|
+
{frame.name ? (
|
|
263
|
+
<span className="agentkit-stack-frame-name">{frame.name}</span>
|
|
264
|
+
) : null}
|
|
265
|
+
{onFilePathClick && frame.file && !frame.internal ? (
|
|
266
|
+
<button
|
|
267
|
+
type="button"
|
|
268
|
+
className="agentkit-stack-frame-location"
|
|
269
|
+
title={fullLocation}
|
|
270
|
+
onClick={() => onFilePathClick(frame)}
|
|
271
|
+
>
|
|
272
|
+
{location}
|
|
273
|
+
</button>
|
|
274
|
+
) : (
|
|
275
|
+
<span className="agentkit-stack-frame-location" title={fullLocation}>
|
|
276
|
+
{location}
|
|
277
|
+
</span>
|
|
278
|
+
)}
|
|
279
|
+
</li>
|
|
280
|
+
);
|
|
281
|
+
}
|
|
@@ -14,9 +14,14 @@ const mocks = vi.hoisted(() => ({
|
|
|
14
14
|
activeRunIds: [] as string[],
|
|
15
15
|
runs: {} as Record<string, { status: string }>,
|
|
16
16
|
cancel: vi.fn(async () => undefined),
|
|
17
|
+
requestComposerFocus: vi.fn(),
|
|
17
18
|
}));
|
|
18
19
|
|
|
19
20
|
vi.mock("./context.js", () => ({
|
|
21
|
+
useAgentKit: () => ({
|
|
22
|
+
requestComposerFocus: mocks.requestComposerFocus,
|
|
23
|
+
threadId: "thread-1",
|
|
24
|
+
}),
|
|
20
25
|
useAgentThread: () => ({
|
|
21
26
|
activeRunIds: mocks.activeRunIds,
|
|
22
27
|
runs: mocks.runs,
|
|
@@ -48,6 +53,7 @@ describe("useAgentKitStopButton", () => {
|
|
|
48
53
|
mocks.cancel.mockReset();
|
|
49
54
|
mocks.cancel.mockResolvedValue(undefined);
|
|
50
55
|
onError.mockClear();
|
|
56
|
+
mocks.requestComposerFocus.mockClear();
|
|
51
57
|
beforeCancel = undefined;
|
|
52
58
|
});
|
|
53
59
|
|
|
@@ -249,6 +255,40 @@ describe("useAgentKitStopButton", () => {
|
|
|
249
255
|
await act(async () => cancelled.resolve(undefined));
|
|
250
256
|
});
|
|
251
257
|
|
|
258
|
+
it.each([
|
|
259
|
+
["keyboard", 0, 1],
|
|
260
|
+
["pointer", 1, 0],
|
|
261
|
+
] as const)(
|
|
262
|
+
"after a %s stop, hands focus to the composer only for the keyboard",
|
|
263
|
+
async (_input, detail, focusCalls) => {
|
|
264
|
+
mocks.activeRunIds = ["run-1"];
|
|
265
|
+
await render();
|
|
266
|
+
await act(async () => {
|
|
267
|
+
container
|
|
268
|
+
.querySelector('[data-agent-composer-slot="stop-button"]')
|
|
269
|
+
?.dispatchEvent(new MouseEvent("click", { bubbles: true, detail }));
|
|
270
|
+
});
|
|
271
|
+
mocks.activeRunIds = [];
|
|
272
|
+
await act(async () => root.render(<Harness />));
|
|
273
|
+
expect(container.querySelector("button")).toBeNull();
|
|
274
|
+
expect(mocks.requestComposerFocus).toHaveBeenCalledTimes(focusCalls);
|
|
275
|
+
},
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
it("leaves focus alone when beforeCancel keeps the run going", async () => {
|
|
279
|
+
mocks.activeRunIds = ["run-1"];
|
|
280
|
+
beforeCancel = () => false;
|
|
281
|
+
await render();
|
|
282
|
+
await act(async () => {
|
|
283
|
+
container
|
|
284
|
+
.querySelector("button")
|
|
285
|
+
?.dispatchEvent(new MouseEvent("click", { bubbles: true, detail: 0 }));
|
|
286
|
+
});
|
|
287
|
+
mocks.activeRunIds = [];
|
|
288
|
+
await act(async () => root.render(<Harness />));
|
|
289
|
+
expect(mocks.requestComposerFocus).not.toHaveBeenCalled();
|
|
290
|
+
});
|
|
291
|
+
|
|
252
292
|
it("hides Stop once every retained run is terminal", async () => {
|
|
253
293
|
mocks.activeRunIds = ["run-1"];
|
|
254
294
|
mocks.runs = { "run-1": { status: "completed" } };
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { hasActiveAgentRuns } from "@agent-native/agentkit";
|
|
2
2
|
import { IconPlayerStopFilled } from "@tabler/icons-react";
|
|
3
|
-
import { useRef, useState } from "react";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
4
|
|
|
5
5
|
import { Tooltip } from "../../../design-system/index.js";
|
|
6
6
|
import {
|
|
7
|
+
useAgentKit,
|
|
7
8
|
useAgentKitControl,
|
|
8
9
|
useAgentKitSubmissionStarting,
|
|
9
10
|
useAgentThread,
|
|
@@ -53,7 +54,17 @@ export function useAgentKitStopButton(options: {
|
|
|
53
54
|
onErrorRef.current = options.onError;
|
|
54
55
|
const beforeCancelRef = useRef(options.beforeCancel);
|
|
55
56
|
beforeCancelRef.current = options.beforeCancel;
|
|
56
|
-
|
|
57
|
+
const { requestComposerFocus, threadId: providerThreadId } = useAgentKit();
|
|
58
|
+
// Keyboard stops only: refocusing the composer on touch opens the keyboard.
|
|
59
|
+
const refocusComposerRef = useRef(false);
|
|
60
|
+
const visible = running || starting;
|
|
61
|
+
const focusThreadId = options.threadId ?? providerThreadId;
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (visible || !refocusComposerRef.current) return;
|
|
64
|
+
refocusComposerRef.current = false;
|
|
65
|
+
requestComposerFocus(focusThreadId);
|
|
66
|
+
}, [focusThreadId, requestComposerFocus, visible]);
|
|
67
|
+
if (!visible) return undefined;
|
|
57
68
|
const { label } = options;
|
|
58
69
|
return (
|
|
59
70
|
<Tooltip
|
|
@@ -61,8 +72,10 @@ export function useAgentKitStopButton(options: {
|
|
|
61
72
|
trigger={
|
|
62
73
|
<button
|
|
63
74
|
type="button"
|
|
64
|
-
onClick={() => {
|
|
75
|
+
onClick={(event) => {
|
|
65
76
|
if (stoppingRef.current === activeRunKey) return;
|
|
77
|
+
// Enter and Space fire a click with no pointer detail.
|
|
78
|
+
refocusComposerRef.current = event.detail === 0;
|
|
66
79
|
const runKey = activeRunKey;
|
|
67
80
|
stoppingRef.current = runKey;
|
|
68
81
|
setStoppingRunKey(runKey);
|
|
@@ -74,11 +87,15 @@ export function useAgentKitStopButton(options: {
|
|
|
74
87
|
const knownRunIds = running ? thread.activeRunIds : undefined;
|
|
75
88
|
void (async () => {
|
|
76
89
|
const beforeCancel = beforeCancelRef.current;
|
|
77
|
-
if (beforeCancel && (await beforeCancel()) === false)
|
|
90
|
+
if (beforeCancel && (await beforeCancel()) === false) {
|
|
91
|
+
refocusComposerRef.current = false;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
78
94
|
const runIds = knownRunIds ?? (await control.runIdsOnceStarted());
|
|
79
95
|
await Promise.all(runIds.map((runId) => control.cancel(runId)));
|
|
80
96
|
})()
|
|
81
97
|
.catch((error: unknown) => {
|
|
98
|
+
refocusComposerRef.current = false;
|
|
82
99
|
onErrorRef.current(
|
|
83
100
|
error instanceof Error ? error : new Error(String(error)),
|
|
84
101
|
);
|
|
@@ -89,7 +106,7 @@ export function useAgentKitStopButton(options: {
|
|
|
89
106
|
aria-busy={stopping || undefined}
|
|
90
107
|
disabled={stopping}
|
|
91
108
|
data-agent-composer-slot="stop-button"
|
|
92
|
-
className={`flex size-[var(--agentkit-composer-primary-control-size,2rem)] shrink-0 items-center justify-center rounded-
|
|
109
|
+
className={`flex size-[var(--agentkit-composer-primary-control-size,2rem)] shrink-0 items-center justify-center rounded-[var(--agent-kit-radius-sm,0.5rem)] bg-primary text-primary-foreground transition-[opacity,background-color,color] duration-150 hover:opacity-90 disabled:cursor-default disabled:opacity-35 ${STOP_BESIDE_PRIMARY}`}
|
|
93
110
|
>
|
|
94
111
|
<IconPlayerStopFilled aria-hidden="true" className="size-4" />
|
|
95
112
|
</button>
|