@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
|
@@ -5,13 +5,11 @@ import {
|
|
|
5
5
|
IconThumbDownFilled,
|
|
6
6
|
IconThumbDown,
|
|
7
7
|
IconActivity,
|
|
8
|
-
IconAlertCircle,
|
|
9
8
|
IconAlertTriangle,
|
|
10
9
|
IconArrowDown,
|
|
11
10
|
IconArrowFork,
|
|
12
11
|
IconBook2,
|
|
13
12
|
IconBrain,
|
|
14
|
-
IconCheck,
|
|
15
13
|
IconChevronLeft,
|
|
16
14
|
IconChevronRight,
|
|
17
15
|
IconCircle,
|
|
@@ -125,6 +123,10 @@ import {
|
|
|
125
123
|
type AgentKitComposerSubmission,
|
|
126
124
|
} from "./composer-submission.js";
|
|
127
125
|
import { CoreFileStorageSetupContext } from "./core-file-storage.js";
|
|
126
|
+
import {
|
|
127
|
+
AgentKitDeferClosedContentContext,
|
|
128
|
+
useUntilFoundContent,
|
|
129
|
+
} from "./disclosure-content.js";
|
|
128
130
|
export type { AgentKitComposerSubmission } from "./composer-submission.js";
|
|
129
131
|
|
|
130
132
|
import { splitAgentKitMessageContext } from "@agent-native/agentkit";
|
|
@@ -153,9 +155,20 @@ import {
|
|
|
153
155
|
type AgentThread,
|
|
154
156
|
type AgentToolCall,
|
|
155
157
|
type AgentWidget,
|
|
158
|
+
type CitationPart,
|
|
156
159
|
type RunId,
|
|
157
160
|
} from "@agent-native/agentkit/protocol";
|
|
158
161
|
|
|
162
|
+
import { prettyLanguageName } from "../../code-highlighter.js";
|
|
163
|
+
import {
|
|
164
|
+
CodeBlock,
|
|
165
|
+
CodeBlockActions,
|
|
166
|
+
CodeBlockContent,
|
|
167
|
+
CodeBlockCopyButton,
|
|
168
|
+
CodeBlockFooter,
|
|
169
|
+
CodeBlockHeader,
|
|
170
|
+
CodeBlockTitle,
|
|
171
|
+
} from "./code-block.js";
|
|
159
172
|
import {
|
|
160
173
|
defaultAgentKitLabels,
|
|
161
174
|
useAgentCapability,
|
|
@@ -180,6 +193,8 @@ import {
|
|
|
180
193
|
type AgentKitSuggestionsRenderProps,
|
|
181
194
|
type AgentRunFailureRenderProps,
|
|
182
195
|
} from "./context.js";
|
|
196
|
+
import { formatAgentKitDuration } from "./duration.js";
|
|
197
|
+
import { FileChange, type FileChangeData } from "./file-change.js";
|
|
183
198
|
import { scheduleIdleMount } from "./idle-mount.js";
|
|
184
199
|
import { orderMessageContent } from "./message-content-order.js";
|
|
185
200
|
import {
|
|
@@ -187,12 +202,28 @@ import {
|
|
|
187
202
|
agentKitTurnInProgress,
|
|
188
203
|
pendingSubmissionFiles,
|
|
189
204
|
} from "./pending-submissions.js";
|
|
205
|
+
import { parseStackTrace, StackTrace } from "./stack-trace.js";
|
|
206
|
+
import { Terminal } from "./terminal.js";
|
|
207
|
+
|
|
208
|
+
export { formatAgentKitDuration };
|
|
209
|
+
import { useRovingFocus } from "./roving-focus.js";
|
|
190
210
|
import { useAgentKitStopButton } from "./stop-button.js";
|
|
191
211
|
import {
|
|
192
212
|
AgentStreamingText,
|
|
193
213
|
usePrefersReducedMotion,
|
|
194
214
|
useStreamingRevealOrigin,
|
|
195
215
|
} from "./streaming-text.js";
|
|
216
|
+
import {
|
|
217
|
+
Trace,
|
|
218
|
+
TraceCollapsible,
|
|
219
|
+
TraceCollapsibleContent,
|
|
220
|
+
TraceCollapsibleTrigger,
|
|
221
|
+
TraceDescription,
|
|
222
|
+
TraceIcon,
|
|
223
|
+
TraceLine,
|
|
224
|
+
TraceTitle,
|
|
225
|
+
type TraceStatus,
|
|
226
|
+
} from "./trace.js";
|
|
196
227
|
import { TranscriptGlide } from "./transcript-glide.js";
|
|
197
228
|
import {
|
|
198
229
|
workedForRowChevronStyle,
|
|
@@ -329,7 +360,7 @@ const AgentKitSurfaceBoundary = memo(function AgentKitSurfaceBoundary({
|
|
|
329
360
|
data-render-surface={surface}
|
|
330
361
|
role="alert"
|
|
331
362
|
>
|
|
332
|
-
<
|
|
363
|
+
<IconAlertTriangle aria-hidden="true" className="agentkit-icon" />
|
|
333
364
|
<span>{labels.renderError}</span>
|
|
334
365
|
</div>
|
|
335
366
|
}
|
|
@@ -463,12 +494,31 @@ function AgentLink({
|
|
|
463
494
|
);
|
|
464
495
|
}
|
|
465
496
|
|
|
466
|
-
function
|
|
497
|
+
function fencedCode(children: ReactNode): {
|
|
498
|
+
text: string;
|
|
499
|
+
language: string | undefined;
|
|
500
|
+
} {
|
|
467
501
|
const code = Array.isArray(children) ? children[0] : children;
|
|
468
|
-
if (!
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
502
|
+
if (!isValidElement<{ className?: unknown; children?: ReactNode }>(code)) {
|
|
503
|
+
return { text: nodeText(children), language: undefined };
|
|
504
|
+
}
|
|
505
|
+
const className = code.props.className;
|
|
506
|
+
return {
|
|
507
|
+
text: nodeText(code.props.children),
|
|
508
|
+
language:
|
|
509
|
+
typeof className === "string"
|
|
510
|
+
? /(?:^|\s)language-([\w+#.-]+)/.exec(className)?.[1]
|
|
511
|
+
: undefined,
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function nodeText(node: ReactNode): string {
|
|
516
|
+
if (typeof node === "string" || typeof node === "number") return `${node}`;
|
|
517
|
+
if (Array.isArray(node)) return node.map(nodeText).join("");
|
|
518
|
+
if (isValidElement<{ children?: ReactNode }>(node)) {
|
|
519
|
+
return nodeText(node.props.children);
|
|
520
|
+
}
|
|
521
|
+
return "";
|
|
472
522
|
}
|
|
473
523
|
|
|
474
524
|
function AgentMarkdownCodeBlock({
|
|
@@ -485,64 +535,36 @@ function AgentMarkdownCodeBlock({
|
|
|
485
535
|
.filter((name) => name && name !== STREAMING_REVEAL_BLOCK_CLASS)
|
|
486
536
|
.join(" ") || undefined;
|
|
487
537
|
const { labels } = useAgentKit();
|
|
488
|
-
const
|
|
489
|
-
const
|
|
490
|
-
const copiedTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
491
|
-
useEffect(
|
|
492
|
-
() => () => {
|
|
493
|
-
if (copiedTimer.current) clearTimeout(copiedTimer.current);
|
|
494
|
-
},
|
|
495
|
-
[],
|
|
496
|
-
);
|
|
497
|
-
const language = codeBlockLanguage(children);
|
|
498
|
-
const copy = async () => {
|
|
499
|
-
const text = preRef.current?.textContent ?? "";
|
|
500
|
-
if (!(await writeClipboardText(text))) return;
|
|
501
|
-
setCopied(true);
|
|
502
|
-
if (copiedTimer.current) clearTimeout(copiedTimer.current);
|
|
503
|
-
copiedTimer.current = setTimeout(() => setCopied(false), 2_000);
|
|
504
|
-
};
|
|
538
|
+
const { text, language } = fencedCode(children);
|
|
539
|
+
const languageName = prettyLanguageName(language);
|
|
505
540
|
return (
|
|
506
|
-
<
|
|
507
|
-
className={
|
|
508
|
-
revealsAsBlock
|
|
509
|
-
? `agentkit-code-block ${STREAMING_REVEAL_BLOCK_CLASS}`
|
|
510
|
-
: "agentkit-code-block"
|
|
511
|
-
}
|
|
541
|
+
<CodeBlock
|
|
542
|
+
className={revealsAsBlock ? STREAMING_REVEAL_BLOCK_CLASS : undefined}
|
|
512
543
|
>
|
|
513
|
-
<
|
|
514
|
-
<
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
copied ? (
|
|
521
|
-
<IconCheck aria-hidden="true" size={16} />
|
|
522
|
-
) : (
|
|
523
|
-
<IconCopy aria-hidden="true" size={16} />
|
|
524
|
-
)
|
|
525
|
-
}
|
|
526
|
-
size="compact"
|
|
527
|
-
className="size-6"
|
|
528
|
-
onPress={() => void copy()}
|
|
529
|
-
/>
|
|
530
|
-
</div>
|
|
531
|
-
<pre
|
|
544
|
+
<CodeBlockHeader>
|
|
545
|
+
<CodeBlockTitle aria-hidden="true">{languageName}</CodeBlockTitle>
|
|
546
|
+
<CodeBlockActions>
|
|
547
|
+
<CodeBlockCopyButton value={text} />
|
|
548
|
+
</CodeBlockActions>
|
|
549
|
+
</CodeBlockHeader>
|
|
550
|
+
<CodeBlockContent
|
|
532
551
|
{...props}
|
|
533
552
|
className={preClassName}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
role="
|
|
553
|
+
language={language}
|
|
554
|
+
// A group, not a region: a landmark per code block buries the real ones.
|
|
555
|
+
role="group"
|
|
537
556
|
aria-label={
|
|
538
|
-
|
|
539
|
-
? labels.codeBlockLanguage.replace(
|
|
557
|
+
languageName
|
|
558
|
+
? labels.codeBlockLanguage.replace(
|
|
559
|
+
"{{language}}",
|
|
560
|
+
() => languageName,
|
|
561
|
+
)
|
|
540
562
|
: labels.codeBlock
|
|
541
563
|
}
|
|
542
564
|
>
|
|
543
|
-
{
|
|
544
|
-
</
|
|
545
|
-
</
|
|
565
|
+
{text}
|
|
566
|
+
</CodeBlockContent>
|
|
567
|
+
</CodeBlock>
|
|
546
568
|
);
|
|
547
569
|
}
|
|
548
570
|
|
|
@@ -557,7 +579,7 @@ function AgentMarkdownTable({
|
|
|
557
579
|
<div
|
|
558
580
|
className="agentkit-markdown-table"
|
|
559
581
|
tabIndex={0}
|
|
560
|
-
role="
|
|
582
|
+
role="group"
|
|
561
583
|
aria-label={labels.table}
|
|
562
584
|
>
|
|
563
585
|
<table ref={table} {...props} />
|
|
@@ -804,6 +826,8 @@ export interface AgentKitDisclosureSummaryProps {
|
|
|
804
826
|
rowClassName?: string | undefined;
|
|
805
827
|
style?: CSSProperties | undefined;
|
|
806
828
|
chevronStyle?: CSSProperties | undefined;
|
|
829
|
+
/** Omit for finished work. */
|
|
830
|
+
status?: TraceStatus | undefined;
|
|
807
831
|
/** Replaces the default icon, label and chevron. */
|
|
808
832
|
children?: ReactNode;
|
|
809
833
|
}
|
|
@@ -811,7 +835,7 @@ export interface AgentKitDisclosureSummaryProps {
|
|
|
811
835
|
const DISCLOSURE_ROW_INTERACTIVE =
|
|
812
836
|
"button, a, input, textarea, select, [role='button'], [contenteditable='true']";
|
|
813
837
|
|
|
814
|
-
/**
|
|
838
|
+
/** The label keeps its own styling; `TraceTitle` is for host rows. */
|
|
815
839
|
export function AgentKitDisclosureSummary({
|
|
816
840
|
open,
|
|
817
841
|
onToggle,
|
|
@@ -825,16 +849,17 @@ export function AgentKitDisclosureSummary({
|
|
|
825
849
|
rowClassName,
|
|
826
850
|
style,
|
|
827
851
|
chevronStyle,
|
|
852
|
+
status,
|
|
828
853
|
children,
|
|
829
854
|
}: AgentKitDisclosureSummaryProps) {
|
|
830
855
|
const content = children ?? (
|
|
831
856
|
<>
|
|
832
|
-
{
|
|
833
|
-
<
|
|
834
|
-
|
|
857
|
+
<TraceLine status={status}>
|
|
858
|
+
{icon !== undefined ? <TraceIcon>{icon}</TraceIcon> : null}
|
|
859
|
+
<span data-slot="trace-title" className="agentkit-disclosure-label">
|
|
860
|
+
{label}
|
|
835
861
|
</span>
|
|
836
|
-
|
|
837
|
-
<span className="agentkit-disclosure-label">{label}</span>
|
|
862
|
+
</TraceLine>
|
|
838
863
|
{expandable && as === "button" ? (
|
|
839
864
|
<IconChevronRight
|
|
840
865
|
aria-hidden="true"
|
|
@@ -859,6 +884,7 @@ export function AgentKitDisclosureSummary({
|
|
|
859
884
|
) : (
|
|
860
885
|
<button
|
|
861
886
|
type="button"
|
|
887
|
+
data-slot={expandable ? "trace-collapsible-trigger" : undefined}
|
|
862
888
|
className={["agentkit-disclosure-summary", className]
|
|
863
889
|
.filter(Boolean)
|
|
864
890
|
.join(" ")}
|
|
@@ -906,14 +932,7 @@ export function AgentKitDisclosureSummary({
|
|
|
906
932
|
);
|
|
907
933
|
}
|
|
908
934
|
|
|
909
|
-
/**
|
|
910
|
-
const AgentKitDeferClosedContentContext = createContext(false);
|
|
911
|
-
|
|
912
|
-
/**
|
|
913
|
-
* Mounted while closed, like <details>, so find-in-page can reveal it. In a
|
|
914
|
-
* long thread, closed content mounts in the next idle period rather than with
|
|
915
|
-
* the transcript, so the history paints before every collapsed step renders.
|
|
916
|
-
*/
|
|
935
|
+
/** Mounted while closed, like <details>, so find-in-page can reveal it. */
|
|
917
936
|
export function AgentKitDisclosureContent({
|
|
918
937
|
id,
|
|
919
938
|
open,
|
|
@@ -925,32 +944,17 @@ export function AgentKitDisclosureContent({
|
|
|
925
944
|
onReveal?: (() => void) | undefined;
|
|
926
945
|
children: ReactNode;
|
|
927
946
|
}) {
|
|
928
|
-
const ref =
|
|
929
|
-
const revealRef = useRef(onReveal);
|
|
930
|
-
revealRef.current = onReveal;
|
|
931
|
-
const deferClosed = useContext(AgentKitDeferClosedContentContext);
|
|
932
|
-
const [contentMounted, setContentMounted] = useState(open || !deferClosed);
|
|
933
|
-
if (open && !contentMounted) setContentMounted(true);
|
|
934
|
-
useEffect(() => {
|
|
935
|
-
if (contentMounted) return;
|
|
936
|
-
return scheduleIdleMount(() => setContentMounted(true));
|
|
937
|
-
}, [contentMounted]);
|
|
938
|
-
useEffect(() => {
|
|
939
|
-
const element = ref.current;
|
|
940
|
-
if (!element || open) return;
|
|
941
|
-
element.setAttribute("hidden", "until-found");
|
|
942
|
-
const reveal = () => revealRef.current?.();
|
|
943
|
-
element.addEventListener("beforematch", reveal);
|
|
944
|
-
return () => element.removeEventListener("beforematch", reveal);
|
|
945
|
-
}, [open]);
|
|
947
|
+
const { ref, mounted } = useUntilFoundContent(open, onReveal);
|
|
946
948
|
return (
|
|
947
949
|
<div
|
|
948
950
|
ref={ref}
|
|
949
951
|
id={id}
|
|
952
|
+
data-slot="trace-collapsible-content"
|
|
953
|
+
data-state={open ? "open" : "closed"}
|
|
950
954
|
className="agentkit-disclosure-content"
|
|
951
955
|
hidden={!open}
|
|
952
956
|
>
|
|
953
|
-
{
|
|
957
|
+
{mounted ? children : null}
|
|
954
958
|
</div>
|
|
955
959
|
);
|
|
956
960
|
}
|
|
@@ -970,7 +974,7 @@ function interactionIcon(kind: string): ReactNode {
|
|
|
970
974
|
case "completed":
|
|
971
975
|
return <IconCircleCheck {...props} />;
|
|
972
976
|
case "failed":
|
|
973
|
-
return <
|
|
977
|
+
return <IconAlertTriangle {...props} />;
|
|
974
978
|
case "closed":
|
|
975
979
|
return <IconX {...props} />;
|
|
976
980
|
default:
|
|
@@ -1497,6 +1501,82 @@ function formatActivityToolValue(
|
|
|
1497
1501
|
return `${formatted.slice(0, contentLimit).trimEnd()}…${omissionMarker}`;
|
|
1498
1502
|
}
|
|
1499
1503
|
|
|
1504
|
+
interface ActivityToolInput {
|
|
1505
|
+
text: string;
|
|
1506
|
+
language?: string | undefined;
|
|
1507
|
+
/** The call's other arguments, when `text` is its source. */
|
|
1508
|
+
options?: Array<[string, string]>;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
/** Fields whose string is the program a tool runs, with its grammar. */
|
|
1512
|
+
const ACTIVITY_TOOL_SOURCE_FIELDS: Record<string, string | undefined> = {
|
|
1513
|
+
command: "bash",
|
|
1514
|
+
script: undefined,
|
|
1515
|
+
code: undefined,
|
|
1516
|
+
};
|
|
1517
|
+
|
|
1518
|
+
function formatActivityToolInput(
|
|
1519
|
+
value: unknown,
|
|
1520
|
+
labels: Partial<AgentKitLabels> = {},
|
|
1521
|
+
): ActivityToolInput | undefined {
|
|
1522
|
+
const source = activityToolSource(value);
|
|
1523
|
+
if (!source) {
|
|
1524
|
+
const text = formatActivityToolValue(value, labels);
|
|
1525
|
+
return text ? { text } : undefined;
|
|
1526
|
+
}
|
|
1527
|
+
const text = formatActivityToolValue(source.text, labels);
|
|
1528
|
+
if (!text) return undefined;
|
|
1529
|
+
const options = Object.entries(source.rest).flatMap(
|
|
1530
|
+
([key, option]): Array<[string, string]> => {
|
|
1531
|
+
const formatted = formatActivityToolValue(option, labels);
|
|
1532
|
+
return formatted ? [[key, formatted.replace(/\s+/g, " ")]] : [];
|
|
1533
|
+
},
|
|
1534
|
+
);
|
|
1535
|
+
return {
|
|
1536
|
+
text,
|
|
1537
|
+
language: source.language,
|
|
1538
|
+
...(options.length ? { options } : {}),
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
function activityToolSource(value: unknown):
|
|
1543
|
+
| {
|
|
1544
|
+
text: string;
|
|
1545
|
+
language: string | undefined;
|
|
1546
|
+
rest: Record<string, unknown>;
|
|
1547
|
+
}
|
|
1548
|
+
| undefined {
|
|
1549
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1550
|
+
return undefined;
|
|
1551
|
+
}
|
|
1552
|
+
const input = value as Record<string, unknown>;
|
|
1553
|
+
const field = Object.keys(ACTIVITY_TOOL_SOURCE_FIELDS).find((key) => {
|
|
1554
|
+
const text = input[key];
|
|
1555
|
+
// `code` also names short values such as a country or a promo code.
|
|
1556
|
+
return (
|
|
1557
|
+
typeof text === "string" &&
|
|
1558
|
+
(key === "command" ? text.trim() !== "" : /\n|.{40}/.test(text))
|
|
1559
|
+
);
|
|
1560
|
+
});
|
|
1561
|
+
if (!field) return undefined;
|
|
1562
|
+
const languageField = ["language", "lang"].find(
|
|
1563
|
+
(key) => typeof input[key] === "string",
|
|
1564
|
+
);
|
|
1565
|
+
const rest = Object.fromEntries(
|
|
1566
|
+
Object.entries(input).filter(
|
|
1567
|
+
([key, option]) =>
|
|
1568
|
+
key !== field && key !== languageField && option !== undefined,
|
|
1569
|
+
),
|
|
1570
|
+
);
|
|
1571
|
+
return {
|
|
1572
|
+
text: input[field] as string,
|
|
1573
|
+
language: languageField
|
|
1574
|
+
? (input[languageField] as string)
|
|
1575
|
+
: ACTIVITY_TOOL_SOURCE_FIELDS[field],
|
|
1576
|
+
rest,
|
|
1577
|
+
};
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1500
1580
|
function activityLabel(activity: AgentActivity, workingLabel: string): string {
|
|
1501
1581
|
const label = readableText(activity.label);
|
|
1502
1582
|
if (label.toLowerCase() === "processing") return workingLabel;
|
|
@@ -1520,6 +1600,16 @@ function AgentIdentityChip({
|
|
|
1520
1600
|
kind?: string;
|
|
1521
1601
|
status?: AgentParticipant["status"];
|
|
1522
1602
|
}) {
|
|
1603
|
+
const { labels } = useAgentKit();
|
|
1604
|
+
// The chip's tint and shimmer carry the state, so it is said in words too.
|
|
1605
|
+
const statusLabel =
|
|
1606
|
+
status === "working"
|
|
1607
|
+
? labels.taskStatusRunning
|
|
1608
|
+
: status === "failed"
|
|
1609
|
+
? labels.taskStatusFailed
|
|
1610
|
+
: status === "waiting"
|
|
1611
|
+
? labels.taskStatusAwaitingInput
|
|
1612
|
+
: undefined;
|
|
1523
1613
|
return (
|
|
1524
1614
|
<span
|
|
1525
1615
|
className="agentkit-agent"
|
|
@@ -1527,9 +1617,15 @@ function AgentIdentityChip({
|
|
|
1527
1617
|
data-agent-kind={kind}
|
|
1528
1618
|
data-status={status}
|
|
1529
1619
|
>
|
|
1620
|
+
{status === "failed" ? (
|
|
1621
|
+
<IconAlertTriangle aria-hidden="true" className="agentkit-icon" />
|
|
1622
|
+
) : null}
|
|
1530
1623
|
<span className="agentkit-agent-name">
|
|
1531
1624
|
{readableText(name) || fallbackName}
|
|
1532
1625
|
</span>
|
|
1626
|
+
{statusLabel ? (
|
|
1627
|
+
<span className="agentkit-visually-hidden">{statusLabel}</span>
|
|
1628
|
+
) : null}
|
|
1533
1629
|
</span>
|
|
1534
1630
|
);
|
|
1535
1631
|
}
|
|
@@ -1565,8 +1661,11 @@ export function AgentInteractionItem({
|
|
|
1565
1661
|
value: interaction,
|
|
1566
1662
|
threadId,
|
|
1567
1663
|
}: AgentKitRenderProps<AgentInteraction>) {
|
|
1568
|
-
const { slots, registry, labels } =
|
|
1664
|
+
const { slots, registry, labels, formatAgentInteractionDetail } =
|
|
1665
|
+
useAgentKit();
|
|
1569
1666
|
const thread = useAgentThreadSlice(sameAgents, threadId);
|
|
1667
|
+
const showAgent =
|
|
1668
|
+
useContext(AgentSubagentStepContext) !== interaction.agentId;
|
|
1570
1669
|
const agent = thread.agents[interaction.agentId];
|
|
1571
1670
|
const target = interaction.targetAgentId
|
|
1572
1671
|
? thread.agents[interaction.targetAgentId]
|
|
@@ -1604,16 +1703,21 @@ export function AgentInteractionItem({
|
|
|
1604
1703
|
const visibleLabel =
|
|
1605
1704
|
humanizeAgentLabel(label) ||
|
|
1606
1705
|
defaultInteractionLabel({ ...interaction, label: undefined }, labels);
|
|
1607
|
-
const detail = readableText(
|
|
1706
|
+
const detail = readableText(
|
|
1707
|
+
formatAgentInteractionDetail?.(interaction) ?? interaction.detail,
|
|
1708
|
+
);
|
|
1608
1709
|
return (
|
|
1609
1710
|
<div
|
|
1610
1711
|
className="agentkit-agent-interaction"
|
|
1611
1712
|
data-kind={interaction.kind}
|
|
1612
1713
|
data-scope={interaction.scope}
|
|
1613
1714
|
>
|
|
1614
|
-
<
|
|
1615
|
-
|
|
1616
|
-
{
|
|
1715
|
+
<TraceLine
|
|
1716
|
+
className="agentkit-agent-interaction-head"
|
|
1717
|
+
status={interaction.kind === "failed" ? "error" : undefined}
|
|
1718
|
+
>
|
|
1719
|
+
<TraceIcon>{interactionIcon(interaction.kind)}</TraceIcon>
|
|
1720
|
+
{!showAgent ? null : AgentRenderer ? (
|
|
1617
1721
|
<AgentRenderer value={agent} threadId={threadId} />
|
|
1618
1722
|
) : (
|
|
1619
1723
|
<AgentIdentityChip
|
|
@@ -1622,7 +1726,12 @@ export function AgentInteractionItem({
|
|
|
1622
1726
|
fallbackName={labels.assistant}
|
|
1623
1727
|
/>
|
|
1624
1728
|
)}
|
|
1625
|
-
<span
|
|
1729
|
+
<span
|
|
1730
|
+
data-slot="trace-title"
|
|
1731
|
+
className="agentkit-agent-interaction-label"
|
|
1732
|
+
>
|
|
1733
|
+
{visibleLabel}
|
|
1734
|
+
</span>
|
|
1626
1735
|
{target && TargetRenderer ? (
|
|
1627
1736
|
<TargetRenderer value={target} threadId={threadId} />
|
|
1628
1737
|
) : interaction.targetAgentId ? (
|
|
@@ -1633,7 +1742,7 @@ export function AgentInteractionItem({
|
|
|
1633
1742
|
/>
|
|
1634
1743
|
) : null}
|
|
1635
1744
|
{object ? <ObjectRenderer value={object} threadId={threadId} /> : null}
|
|
1636
|
-
</
|
|
1745
|
+
</TraceLine>
|
|
1637
1746
|
{detail && (!object || interaction.kind === "failed") ? (
|
|
1638
1747
|
<span
|
|
1639
1748
|
{...(interaction.kind === "failed"
|
|
@@ -1685,6 +1794,7 @@ export function AgentActivityItem({
|
|
|
1685
1794
|
previous.tools[activity.id] === next.tools[activity.id],
|
|
1686
1795
|
);
|
|
1687
1796
|
const [open, setOpen] = useState(false);
|
|
1797
|
+
const showAgent = useContext(AgentSubagentStepContext) !== activity.agentId;
|
|
1688
1798
|
const agent = activity.agentId ? thread.agents[activity.agentId] : undefined;
|
|
1689
1799
|
const agentName = resolveAgentName(
|
|
1690
1800
|
thread,
|
|
@@ -1694,13 +1804,26 @@ export function AgentActivityItem({
|
|
|
1694
1804
|
);
|
|
1695
1805
|
const tool = thread.tools[activity.id] as AgentToolCall | undefined;
|
|
1696
1806
|
const toolInput = useMemo(
|
|
1697
|
-
() => (tool ?
|
|
1807
|
+
() => (tool ? formatActivityToolInput(tool.input, labels) : undefined),
|
|
1698
1808
|
[labels, tool],
|
|
1699
1809
|
);
|
|
1700
1810
|
const toolResult = useMemo(
|
|
1701
1811
|
() => (tool ? formatActivityToolValue(tool.output, labels) : undefined),
|
|
1702
1812
|
[labels, tool],
|
|
1703
1813
|
);
|
|
1814
|
+
const stackTrace = useMemo(() => {
|
|
1815
|
+
if (!tool) return undefined;
|
|
1816
|
+
const sources = [
|
|
1817
|
+
toolResult,
|
|
1818
|
+
formatActivityToolValue(tool.error?.details, labels),
|
|
1819
|
+
formatActivityToolValue(tool.error?.message, labels),
|
|
1820
|
+
];
|
|
1821
|
+
for (const source of sources) {
|
|
1822
|
+
const parsed = source ? parseStackTrace(source) : undefined;
|
|
1823
|
+
if (parsed) return { parsed, fromResult: source === toolResult };
|
|
1824
|
+
}
|
|
1825
|
+
return undefined;
|
|
1826
|
+
}, [labels, tool, toolResult]);
|
|
1704
1827
|
const toolInputLabel = labels.toolInput;
|
|
1705
1828
|
const toolResultLabel = labels.toolResult;
|
|
1706
1829
|
const expandable = Boolean(
|
|
@@ -1716,6 +1839,14 @@ export function AgentActivityItem({
|
|
|
1716
1839
|
activity.object ??
|
|
1717
1840
|
sourceObjectUnlessRepresented(activity.source, thread, [activity.agentId]);
|
|
1718
1841
|
const failed = activity.status === "failed";
|
|
1842
|
+
const statusWords =
|
|
1843
|
+
activity.status === "running"
|
|
1844
|
+
? labels.taskStatusRunning
|
|
1845
|
+
: failed
|
|
1846
|
+
? labels.taskStatusFailed
|
|
1847
|
+
: activity.status === "cancelled"
|
|
1848
|
+
? labels.runStopped
|
|
1849
|
+
: undefined;
|
|
1719
1850
|
const errorPreview = activityErrorPreview(activity, tool, labels);
|
|
1720
1851
|
// A tool call names what it acted on, so rows in a group tell apart.
|
|
1721
1852
|
const toolObject =
|
|
@@ -1726,134 +1857,347 @@ export function AgentActivityItem({
|
|
|
1726
1857
|
tool || failed
|
|
1727
1858
|
? readableText(toolObject?.text)
|
|
1728
1859
|
: readableText(activity.detail);
|
|
1729
|
-
const
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
)
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
meta={
|
|
1768
|
-
object || detail ? (
|
|
1769
|
-
<>
|
|
1770
|
-
{object ? (
|
|
1771
|
-
<ObjectRenderer value={object} threadId={threadId} />
|
|
1772
|
-
) : null}
|
|
1773
|
-
{detail ? (
|
|
1774
|
-
<span
|
|
1775
|
-
{...(toolObject ? SESSION_REPLAY_MASK_PROPS : {})}
|
|
1776
|
-
title={toolObject ? undefined : detail}
|
|
1777
|
-
className="agentkit-activity-detail"
|
|
1778
|
-
data-kind={toolObject?.kind}
|
|
1779
|
-
>
|
|
1780
|
-
{detail}
|
|
1781
|
-
</span>
|
|
1782
|
-
) : null}
|
|
1783
|
-
</>
|
|
1784
|
-
) : null
|
|
1785
|
-
}
|
|
1786
|
-
/>
|
|
1787
|
-
{errorPreview ? (
|
|
1788
|
-
// Replays mask text but record attributes, so failure text gets no title.
|
|
1789
|
-
<span
|
|
1790
|
-
{...SESSION_REPLAY_MASK_PROPS}
|
|
1791
|
-
className="agentkit-activity-error"
|
|
1860
|
+
const line = (
|
|
1861
|
+
<TraceLine status={traceStatus(activity.status)}>
|
|
1862
|
+
<TraceIcon>
|
|
1863
|
+
{failed ? (
|
|
1864
|
+
<IconAlertTriangle aria-hidden="true" className="agentkit-icon" />
|
|
1865
|
+
) : (
|
|
1866
|
+
<ActivitySourceIcon activity={activity} threadId={threadId} />
|
|
1867
|
+
)}
|
|
1868
|
+
</TraceIcon>
|
|
1869
|
+
<span className="agentkit-disclosure-label">
|
|
1870
|
+
{!showAgent ? null : agent && AgentRenderer ? (
|
|
1871
|
+
<AgentRenderer value={agent} threadId={threadId} />
|
|
1872
|
+
) : activity.agentId ? (
|
|
1873
|
+
<AgentIdentityChip
|
|
1874
|
+
id={activity.agentId}
|
|
1875
|
+
name={agentName}
|
|
1876
|
+
fallbackName={labels.assistant}
|
|
1877
|
+
/>
|
|
1878
|
+
) : null}
|
|
1879
|
+
<TraceTitle className="agentkit-activity-label">
|
|
1880
|
+
{activityLabel(activity, labels.working)}
|
|
1881
|
+
</TraceTitle>
|
|
1882
|
+
{/* State shows only as color, motion and icon, so say it in words too. */}
|
|
1883
|
+
{statusWords ? (
|
|
1884
|
+
<span className="agentkit-visually-hidden">{statusWords}</span>
|
|
1885
|
+
) : null}
|
|
1886
|
+
</span>
|
|
1887
|
+
</TraceLine>
|
|
1888
|
+
);
|
|
1889
|
+
const meta = (
|
|
1890
|
+
<>
|
|
1891
|
+
{object ? <ObjectRenderer value={object} threadId={threadId} /> : null}
|
|
1892
|
+
{detail ? (
|
|
1893
|
+
<TraceDescription
|
|
1894
|
+
{...(toolObject ? SESSION_REPLAY_MASK_PROPS : {})}
|
|
1895
|
+
title={toolObject ? undefined : detail}
|
|
1896
|
+
className="agentkit-activity-detail"
|
|
1897
|
+
data-kind={toolObject?.kind}
|
|
1792
1898
|
>
|
|
1793
|
-
{
|
|
1794
|
-
</
|
|
1899
|
+
{detail}
|
|
1900
|
+
</TraceDescription>
|
|
1795
1901
|
) : null}
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
>
|
|
1847
|
-
{toolResult}
|
|
1848
|
-
</pre>
|
|
1849
|
-
</div>
|
|
1850
|
-
) : null}
|
|
1902
|
+
</>
|
|
1903
|
+
);
|
|
1904
|
+
const errorLine = errorPreview ? (
|
|
1905
|
+
// Replays mask text but record attributes, so failure text gets no title.
|
|
1906
|
+
<span {...SESSION_REPLAY_MASK_PROPS} className="agentkit-activity-error">
|
|
1907
|
+
{errorPreview}
|
|
1908
|
+
</span>
|
|
1909
|
+
) : null;
|
|
1910
|
+
const rootProps = {
|
|
1911
|
+
className: "agentkit-activity-item",
|
|
1912
|
+
"data-activity-kind": activity.kind,
|
|
1913
|
+
"data-status": activity.status,
|
|
1914
|
+
"data-open": expandable && open ? "true" : "false",
|
|
1915
|
+
};
|
|
1916
|
+
const fileChange = tool ? fileChangeTool(tool) : undefined;
|
|
1917
|
+
if (fileChange) {
|
|
1918
|
+
return (
|
|
1919
|
+
<div {...rootProps}>
|
|
1920
|
+
<FileChange
|
|
1921
|
+
change={fileChange}
|
|
1922
|
+
running={activity.status === "running"}
|
|
1923
|
+
/>
|
|
1924
|
+
{errorLine}
|
|
1925
|
+
</div>
|
|
1926
|
+
);
|
|
1927
|
+
}
|
|
1928
|
+
const terminal = tool ? terminalTool(tool) : undefined;
|
|
1929
|
+
if (terminal) {
|
|
1930
|
+
return (
|
|
1931
|
+
<div {...rootProps}>
|
|
1932
|
+
<Terminal
|
|
1933
|
+
command={terminal.command}
|
|
1934
|
+
output={typeof tool?.output === "string" ? tool.output : toolResult}
|
|
1935
|
+
running={activity.status === "running"}
|
|
1936
|
+
exitCode={terminal.exitCode}
|
|
1937
|
+
durationMs={terminal.durationMs}
|
|
1938
|
+
timedOut={terminal.timedOut}
|
|
1939
|
+
failed={failed}
|
|
1940
|
+
/>
|
|
1941
|
+
</div>
|
|
1942
|
+
);
|
|
1943
|
+
}
|
|
1944
|
+
if (!expandable) {
|
|
1945
|
+
return (
|
|
1946
|
+
<div {...rootProps}>
|
|
1947
|
+
<div className="agentkit-disclosure-row agentkit-activity-row">
|
|
1948
|
+
<span className="agentkit-disclosure-summary" data-expandable="false">
|
|
1949
|
+
{line}
|
|
1950
|
+
</span>
|
|
1951
|
+
{meta}
|
|
1851
1952
|
</div>
|
|
1953
|
+
{errorLine}
|
|
1954
|
+
</div>
|
|
1955
|
+
);
|
|
1956
|
+
}
|
|
1957
|
+
return (
|
|
1958
|
+
<TraceCollapsible {...rootProps} open={open} onOpenChange={setOpen}>
|
|
1959
|
+
<div
|
|
1960
|
+
className="agentkit-disclosure-row agentkit-activity-row"
|
|
1961
|
+
data-expandable="true"
|
|
1962
|
+
onClick={(event) => {
|
|
1963
|
+
// Meta controls handle their own clicks; a text-selecting drag is no toggle.
|
|
1964
|
+
const target = event.target;
|
|
1965
|
+
if (
|
|
1966
|
+
target instanceof Element &&
|
|
1967
|
+
target.closest(DISCLOSURE_ROW_INTERACTIVE)
|
|
1968
|
+
)
|
|
1969
|
+
return;
|
|
1970
|
+
if (globalThis.getSelection()?.toString()) return;
|
|
1971
|
+
setOpen((current) => !current);
|
|
1972
|
+
}}
|
|
1973
|
+
>
|
|
1974
|
+
<TraceCollapsibleTrigger>{line}</TraceCollapsibleTrigger>
|
|
1975
|
+
{meta}
|
|
1976
|
+
</div>
|
|
1977
|
+
{errorLine}
|
|
1978
|
+
<TraceCollapsibleContent
|
|
1979
|
+
// A fresh node on each toggle: the recorder can drop a block marker
|
|
1980
|
+
// changed on a node it has recorded, and the closed body stays mounted.
|
|
1981
|
+
key={open ? "open" : "closed"}
|
|
1982
|
+
keepMounted
|
|
1983
|
+
{...(activity.status === "failed" || !open
|
|
1984
|
+
? SESSION_REPLAY_BLOCK_PROPS
|
|
1985
|
+
: {})}
|
|
1986
|
+
className="agentkit-activity-summary"
|
|
1987
|
+
>
|
|
1988
|
+
{activity.summary?.map((part, index) => {
|
|
1989
|
+
const safePart =
|
|
1990
|
+
part.type === "text"
|
|
1991
|
+
? {
|
|
1992
|
+
...part,
|
|
1993
|
+
text: part.text.trim()
|
|
1994
|
+
? formatActivityText(part.text, labels)
|
|
1995
|
+
: "",
|
|
1996
|
+
}
|
|
1997
|
+
: part;
|
|
1998
|
+
return safePart.type === "text" && !safePart.text ? null : (
|
|
1999
|
+
<AgentMessagePartView
|
|
2000
|
+
key={`${activity.id}-summary-${index}`}
|
|
2001
|
+
value={safePart}
|
|
2002
|
+
threadId={threadId}
|
|
2003
|
+
/>
|
|
2004
|
+
);
|
|
2005
|
+
})}
|
|
2006
|
+
{toolInput ? (
|
|
2007
|
+
<ActivityToolValue
|
|
2008
|
+
label={toolInputLabel}
|
|
2009
|
+
value={toolInput.text}
|
|
2010
|
+
language={toolInput.language}
|
|
2011
|
+
options={toolInput.options}
|
|
2012
|
+
/>
|
|
2013
|
+
) : null}
|
|
2014
|
+
{stackTrace ? (
|
|
2015
|
+
<StackTrace trace={stackTrace.parsed} defaultOpen />
|
|
2016
|
+
) : null}
|
|
2017
|
+
{toolResult && !(stackTrace?.fromResult && stackTrace.parsed.whole) ? (
|
|
2018
|
+
<ActivityToolValue label={toolResultLabel} value={toolResult} />
|
|
2019
|
+
) : null}
|
|
2020
|
+
</TraceCollapsibleContent>
|
|
2021
|
+
</TraceCollapsible>
|
|
2022
|
+
);
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
function terminalTool(tool: AgentToolCall):
|
|
2026
|
+
| {
|
|
2027
|
+
command: string;
|
|
2028
|
+
exitCode: number | null | undefined;
|
|
2029
|
+
durationMs: number | undefined;
|
|
2030
|
+
timedOut: boolean | undefined;
|
|
2031
|
+
}
|
|
2032
|
+
| undefined {
|
|
2033
|
+
const metadata = tool.metadata;
|
|
2034
|
+
if (metadata?.toolKind !== "bash" || typeof metadata.command !== "string") {
|
|
2035
|
+
return undefined;
|
|
2036
|
+
}
|
|
2037
|
+
return {
|
|
2038
|
+
command: metadata.command,
|
|
2039
|
+
exitCode:
|
|
2040
|
+
typeof metadata.exitCode === "number" || metadata.exitCode === null
|
|
2041
|
+
? metadata.exitCode
|
|
2042
|
+
: undefined,
|
|
2043
|
+
durationMs:
|
|
2044
|
+
typeof metadata.durationMs === "number" ? metadata.durationMs : undefined,
|
|
2045
|
+
timedOut: metadata.timedOut === true ? true : undefined,
|
|
2046
|
+
};
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
function fileChangeTool(tool: AgentToolCall): FileChangeData | undefined {
|
|
2050
|
+
const metadata = tool.metadata;
|
|
2051
|
+
const kind = metadata?.toolKind;
|
|
2052
|
+
if (
|
|
2053
|
+
(kind !== "edit" && kind !== "write") ||
|
|
2054
|
+
typeof metadata?.filePath !== "string"
|
|
2055
|
+
) {
|
|
2056
|
+
return undefined;
|
|
2057
|
+
}
|
|
2058
|
+
const text = (value: unknown) =>
|
|
2059
|
+
typeof value === "string" ? value : undefined;
|
|
2060
|
+
return {
|
|
2061
|
+
filePath: metadata.filePath,
|
|
2062
|
+
kind,
|
|
2063
|
+
oldText: text(metadata.oldText),
|
|
2064
|
+
newText: text(metadata.newText),
|
|
2065
|
+
content: text(metadata.content),
|
|
2066
|
+
lineCount:
|
|
2067
|
+
typeof metadata.lineCount === "number" ? metadata.lineCount : undefined,
|
|
2068
|
+
truncated: metadata.truncated === true,
|
|
2069
|
+
};
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
function ActivityToolValue({
|
|
2073
|
+
label,
|
|
2074
|
+
value,
|
|
2075
|
+
language: sourceLanguage,
|
|
2076
|
+
options,
|
|
2077
|
+
}: {
|
|
2078
|
+
label: string;
|
|
2079
|
+
value: string;
|
|
2080
|
+
language?: string | undefined;
|
|
2081
|
+
options?: Array<[string, string]> | undefined;
|
|
2082
|
+
}) {
|
|
2083
|
+
const language = useMemo(
|
|
2084
|
+
() => sourceLanguage ?? (isJsonText(value) ? "json" : undefined),
|
|
2085
|
+
[sourceLanguage, value],
|
|
2086
|
+
);
|
|
2087
|
+
return (
|
|
2088
|
+
<CodeBlock size="sm" className="agentkit-activity-summary-section">
|
|
2089
|
+
<CodeBlockHeader>
|
|
2090
|
+
<CodeBlockTitle>{label}</CodeBlockTitle>
|
|
2091
|
+
<CodeBlockActions>
|
|
2092
|
+
<CodeBlockCopyButton value={value} />
|
|
2093
|
+
</CodeBlockActions>
|
|
2094
|
+
</CodeBlockHeader>
|
|
2095
|
+
<CodeBlockContent
|
|
2096
|
+
wrap
|
|
2097
|
+
language={language}
|
|
2098
|
+
role="group"
|
|
2099
|
+
aria-label={label}
|
|
2100
|
+
className="agentkit-activity-summary-value"
|
|
2101
|
+
>
|
|
2102
|
+
{value}
|
|
2103
|
+
</CodeBlockContent>
|
|
2104
|
+
{options ? (
|
|
2105
|
+
<CodeBlockFooter>
|
|
2106
|
+
<dl className="agentkit-activity-tool-options">
|
|
2107
|
+
{options.map(([key, option]) => (
|
|
2108
|
+
<div key={key}>
|
|
2109
|
+
<dt>{key}</dt>
|
|
2110
|
+
<dd>{option}</dd>
|
|
2111
|
+
</div>
|
|
2112
|
+
))}
|
|
2113
|
+
</dl>
|
|
2114
|
+
</CodeBlockFooter>
|
|
1852
2115
|
) : null}
|
|
1853
|
-
</
|
|
2116
|
+
</CodeBlock>
|
|
1854
2117
|
);
|
|
1855
2118
|
}
|
|
1856
2119
|
|
|
2120
|
+
function isJsonText(text: string): boolean {
|
|
2121
|
+
const start = text.trimStart()[0];
|
|
2122
|
+
if (start !== "{" && start !== "[") return false;
|
|
2123
|
+
try {
|
|
2124
|
+
JSON.parse(text);
|
|
2125
|
+
return true;
|
|
2126
|
+
// coercion-ok: text that does not parse is the predicate's false answer.
|
|
2127
|
+
} catch {
|
|
2128
|
+
return false;
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
/** The same page cited twice counts as one source. */
|
|
2133
|
+
export function AgentSources({ citations }: { citations: CitationPart[] }) {
|
|
2134
|
+
const { labels } = useAgentKit();
|
|
2135
|
+
const sources = useMemo(() => {
|
|
2136
|
+
const seen = new Set<string>();
|
|
2137
|
+
return citations.filter((citation) => {
|
|
2138
|
+
const key = safeAgentHref(citation.url) ?? `title:${citation.title}`;
|
|
2139
|
+
if (seen.has(key)) return false;
|
|
2140
|
+
seen.add(key);
|
|
2141
|
+
return true;
|
|
2142
|
+
});
|
|
2143
|
+
}, [citations]);
|
|
2144
|
+
const label = (
|
|
2145
|
+
sources.length === 1
|
|
2146
|
+
? labels.workStepSourcesOne
|
|
2147
|
+
: labels.workStepSourcesOther
|
|
2148
|
+
).replace("{{count}}", () => `${sources.length}`);
|
|
2149
|
+
return (
|
|
2150
|
+
<Trace className="agentkit-sources">
|
|
2151
|
+
<TraceCollapsible>
|
|
2152
|
+
<TraceCollapsibleTrigger>
|
|
2153
|
+
<TraceLine>
|
|
2154
|
+
<TraceIcon>
|
|
2155
|
+
<IconWorld aria-hidden="true" className="agentkit-icon" />
|
|
2156
|
+
</TraceIcon>
|
|
2157
|
+
<span className="agentkit-disclosure-label">{label}</span>
|
|
2158
|
+
</TraceLine>
|
|
2159
|
+
</TraceCollapsibleTrigger>
|
|
2160
|
+
<TraceCollapsibleContent keepMounted>
|
|
2161
|
+
<ul className="agentkit-sources-list">
|
|
2162
|
+
{sources.map((source, index) => (
|
|
2163
|
+
<li key={`${index}:${source.url ?? source.title}`}>
|
|
2164
|
+
<AgentLink href={source.url}>
|
|
2165
|
+
<TraceLine>
|
|
2166
|
+
<TraceIcon>
|
|
2167
|
+
<IconWorld aria-hidden="true" className="agentkit-icon" />
|
|
2168
|
+
</TraceIcon>
|
|
2169
|
+
<TraceTitle>{sourceTitle(source)}</TraceTitle>
|
|
2170
|
+
</TraceLine>
|
|
2171
|
+
</AgentLink>
|
|
2172
|
+
</li>
|
|
2173
|
+
))}
|
|
2174
|
+
</ul>
|
|
2175
|
+
</TraceCollapsibleContent>
|
|
2176
|
+
</TraceCollapsible>
|
|
2177
|
+
</Trace>
|
|
2178
|
+
);
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
function sourceTitle(source: CitationPart): string {
|
|
2182
|
+
const title = readableText(source.title);
|
|
2183
|
+
if (title) return title;
|
|
2184
|
+
const href = safeAgentHref(source.url);
|
|
2185
|
+
if (!href) return source.title;
|
|
2186
|
+
try {
|
|
2187
|
+
return new URL(href).hostname;
|
|
2188
|
+
} catch {
|
|
2189
|
+
return href;
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
function traceStatus(status: AgentActivity["status"]): TraceStatus | undefined {
|
|
2194
|
+
return status === "running"
|
|
2195
|
+
? "running"
|
|
2196
|
+
: status === "failed"
|
|
2197
|
+
? "error"
|
|
2198
|
+
: undefined;
|
|
2199
|
+
}
|
|
2200
|
+
|
|
1857
2201
|
function ActivitySourceIcon({
|
|
1858
2202
|
activity,
|
|
1859
2203
|
threadId,
|
|
@@ -1973,29 +2317,6 @@ function workedSummaryLabel(
|
|
|
1973
2317
|
: labels.worked;
|
|
1974
2318
|
}
|
|
1975
2319
|
|
|
1976
|
-
export function formatAgentKitDuration(
|
|
1977
|
-
ms: number,
|
|
1978
|
-
units: { hour?: string; minute?: string; second?: string } = {},
|
|
1979
|
-
): string {
|
|
1980
|
-
const hour = units.hour ?? "h";
|
|
1981
|
-
const minute = units.minute ?? "m";
|
|
1982
|
-
const second = units.second ?? "s";
|
|
1983
|
-
const totalSeconds = Math.max(0, Math.round(ms / 1_000));
|
|
1984
|
-
if (totalSeconds < 60) return `${Math.max(1, totalSeconds)}${second}`;
|
|
1985
|
-
const minutes = Math.floor(totalSeconds / 60);
|
|
1986
|
-
const seconds = totalSeconds % 60;
|
|
1987
|
-
if (minutes < 60) {
|
|
1988
|
-
return seconds === 0
|
|
1989
|
-
? `${minutes}${minute}`
|
|
1990
|
-
: `${minutes}${minute} ${seconds}${second}`;
|
|
1991
|
-
}
|
|
1992
|
-
const hours = Math.floor(minutes / 60);
|
|
1993
|
-
const remainingMinutes = minutes % 60;
|
|
1994
|
-
return remainingMinutes === 0
|
|
1995
|
-
? `${hours}${hour}`
|
|
1996
|
-
: `${hours}${hour} ${remainingMinutes}${minute}`;
|
|
1997
|
-
}
|
|
1998
|
-
|
|
1999
2320
|
function isVisibleReasoningPart(part: AgentMessagePart): boolean {
|
|
2000
2321
|
return (
|
|
2001
2322
|
part.type === "reasoning" &&
|
|
@@ -2802,6 +3123,7 @@ export const AgentActivityGroup = memo(function AgentActivityGroup({
|
|
|
2802
3123
|
open={false}
|
|
2803
3124
|
onToggle={() => {}}
|
|
2804
3125
|
expandable={false}
|
|
3126
|
+
status={summaryLabel ? undefined : "running"}
|
|
2805
3127
|
icon={summaryIcon}
|
|
2806
3128
|
label={
|
|
2807
3129
|
summaryLabel ? (
|
|
@@ -3009,6 +3331,7 @@ function AgentWorkGroupRow({
|
|
|
3009
3331
|
open={open}
|
|
3010
3332
|
onToggle={() => setOpen((current) => !current)}
|
|
3011
3333
|
controls={contentId}
|
|
3334
|
+
status={group.failed ? "error" : group.running ? "running" : undefined}
|
|
3012
3335
|
icon={
|
|
3013
3336
|
group.failed ? (
|
|
3014
3337
|
<IconAlertTriangle aria-hidden="true" className="agentkit-icon" />
|
|
@@ -3166,6 +3489,7 @@ function AgentWorkStepItem({
|
|
|
3166
3489
|
className="agentkit-work-step-title"
|
|
3167
3490
|
open={open}
|
|
3168
3491
|
onToggle={() => setOpen((current) => !current)}
|
|
3492
|
+
status={step.active ? "running" : undefined}
|
|
3169
3493
|
expandable={hasBody}
|
|
3170
3494
|
as={hasBody ? "button" : "static"}
|
|
3171
3495
|
controls={hasBody ? bodyId : undefined}
|
|
@@ -3424,6 +3748,7 @@ function AgentWorkDisclosure({
|
|
|
3424
3748
|
rowClassName="agentkit-activities-row"
|
|
3425
3749
|
open={open}
|
|
3426
3750
|
onToggle={() => setOpen((value) => !value)}
|
|
3751
|
+
status={running ? "running" : undefined}
|
|
3427
3752
|
expandable={expandable}
|
|
3428
3753
|
controls={contentId}
|
|
3429
3754
|
style={workedForRowSummaryStyle(workedForRowLayout)}
|
|
@@ -3474,6 +3799,7 @@ function AgentReasoningPartDisclosure({
|
|
|
3474
3799
|
open={open}
|
|
3475
3800
|
onToggle={() => setOpen((value) => !value)}
|
|
3476
3801
|
controls={contentId}
|
|
3802
|
+
status={active ? "running" : undefined}
|
|
3477
3803
|
icon={activityIcon("reasoning")}
|
|
3478
3804
|
label={<span className="agentkit-reasoning-label">{label}</span>}
|
|
3479
3805
|
/>
|
|
@@ -3527,12 +3853,596 @@ function AgentReasoningSteps({
|
|
|
3527
3853
|
[active, message],
|
|
3528
3854
|
);
|
|
3529
3855
|
return (
|
|
3530
|
-
<AgentWorkStepList
|
|
3531
|
-
steps={steps}
|
|
3532
|
-
threadId={threadId}
|
|
3533
|
-
running={active}
|
|
3534
|
-
renderCall={() => null}
|
|
3535
|
-
/>
|
|
3856
|
+
<AgentWorkStepList
|
|
3857
|
+
steps={steps}
|
|
3858
|
+
threadId={threadId}
|
|
3859
|
+
running={active}
|
|
3860
|
+
renderCall={() => null}
|
|
3861
|
+
/>
|
|
3862
|
+
);
|
|
3863
|
+
}
|
|
3864
|
+
|
|
3865
|
+
/** `sequence` is the entry's latest event. */
|
|
3866
|
+
type AgentCollaborationEntry =
|
|
3867
|
+
| { type: "interaction"; value: AgentInteraction; sequence: number }
|
|
3868
|
+
| { type: "activity"; value: AgentActivity; sequence: number };
|
|
3869
|
+
|
|
3870
|
+
type AgentCollaborationItem =
|
|
3871
|
+
| { type: "entry"; key: string; entry: AgentCollaborationEntry }
|
|
3872
|
+
| {
|
|
3873
|
+
type: "subagent";
|
|
3874
|
+
key: string;
|
|
3875
|
+
agentId: string;
|
|
3876
|
+
entries: AgentCollaborationEntry[];
|
|
3877
|
+
};
|
|
3878
|
+
|
|
3879
|
+
function collaborationSubjectId(
|
|
3880
|
+
entry: AgentCollaborationEntry,
|
|
3881
|
+
thread: AgentThreadState,
|
|
3882
|
+
): string | undefined {
|
|
3883
|
+
const isPrimary = (id: string | undefined) =>
|
|
3884
|
+
id !== undefined && thread.agents[id]?.kind === "primary";
|
|
3885
|
+
if (entry.type === "activity") {
|
|
3886
|
+
const id = entry.value.agentId;
|
|
3887
|
+
return id && !isPrimary(id) ? id : undefined;
|
|
3888
|
+
}
|
|
3889
|
+
const { agentId, targetAgentId } = entry.value;
|
|
3890
|
+
if (isPrimary(agentId)) {
|
|
3891
|
+
return targetAgentId && !isPrimary(targetAgentId)
|
|
3892
|
+
? targetAgentId
|
|
3893
|
+
: undefined;
|
|
3894
|
+
}
|
|
3895
|
+
return agentId;
|
|
3896
|
+
}
|
|
3897
|
+
|
|
3898
|
+
/** Each delegated agent's entries fold into one item where it first appears. */
|
|
3899
|
+
function agentCollaborationItems(
|
|
3900
|
+
thread: AgentThreadState,
|
|
3901
|
+
runEvents: readonly AgentEvent[],
|
|
3902
|
+
labels: Partial<AgentKitLabels>,
|
|
3903
|
+
range: {
|
|
3904
|
+
afterSequence?: number | undefined;
|
|
3905
|
+
throughSequence?: number | undefined;
|
|
3906
|
+
},
|
|
3907
|
+
): AgentCollaborationItem[] {
|
|
3908
|
+
const entries = new Map<string, AgentCollaborationEntry>();
|
|
3909
|
+
const firstSequence = new Map<string, number>();
|
|
3910
|
+
const order: string[] = [];
|
|
3911
|
+
const delegatedAgentIds = new Map<string, string>();
|
|
3912
|
+
// A delegated agent's own tool calls leave the parent's work for its step.
|
|
3913
|
+
const delegatedToolAgentIds = new Map<string, string>();
|
|
3914
|
+
for (const event of runEvents) {
|
|
3915
|
+
if (
|
|
3916
|
+
event.type === "activity.started" ||
|
|
3917
|
+
event.type === "activity.updated" ||
|
|
3918
|
+
event.type === "activity.completed"
|
|
3919
|
+
) {
|
|
3920
|
+
const activity = thread.activities[event.activity.id] ?? event.activity;
|
|
3921
|
+
const agentId = event.activity.agentId ?? activity.agentId;
|
|
3922
|
+
if (agentId) delegatedAgentIds.set(event.activity.id, agentId);
|
|
3923
|
+
}
|
|
3924
|
+
if (event.type === "tool.started" || event.type === "tool.updated") {
|
|
3925
|
+
const agentId =
|
|
3926
|
+
event.toolCall.agentId ?? thread.tools[event.toolCall.id]?.agentId;
|
|
3927
|
+
if (agentId) delegatedToolAgentIds.set(event.toolCall.id, agentId);
|
|
3928
|
+
}
|
|
3929
|
+
}
|
|
3930
|
+
const remember = (
|
|
3931
|
+
key: string,
|
|
3932
|
+
sequence: number,
|
|
3933
|
+
entry:
|
|
3934
|
+
| { type: "interaction"; value: AgentInteraction }
|
|
3935
|
+
| { type: "activity"; value: AgentActivity },
|
|
3936
|
+
) => {
|
|
3937
|
+
if (!entries.has(key)) {
|
|
3938
|
+
order.push(key);
|
|
3939
|
+
firstSequence.set(key, sequence);
|
|
3940
|
+
}
|
|
3941
|
+
entries.set(key, { ...entry, sequence });
|
|
3942
|
+
};
|
|
3943
|
+
for (const event of runEvents) {
|
|
3944
|
+
if (event.type === "agent.interaction") {
|
|
3945
|
+
remember(`interaction:${event.interaction.id}`, event.sequence, {
|
|
3946
|
+
type: "interaction",
|
|
3947
|
+
value: event.interaction,
|
|
3948
|
+
});
|
|
3949
|
+
}
|
|
3950
|
+
if (
|
|
3951
|
+
event.type === "activity.started" ||
|
|
3952
|
+
event.type === "activity.updated" ||
|
|
3953
|
+
event.type === "activity.completed"
|
|
3954
|
+
) {
|
|
3955
|
+
const agentId = delegatedAgentIds.get(event.activity.id);
|
|
3956
|
+
if (!agentId) continue;
|
|
3957
|
+
const activity = thread.activities[event.activity.id] ?? event.activity;
|
|
3958
|
+
remember(`activity:${event.activity.id}`, event.sequence, {
|
|
3959
|
+
type: "activity",
|
|
3960
|
+
value: { ...activity, agentId },
|
|
3961
|
+
});
|
|
3962
|
+
}
|
|
3963
|
+
const toolId =
|
|
3964
|
+
event.type === "tool.started" || event.type === "tool.updated"
|
|
3965
|
+
? event.toolCall.id
|
|
3966
|
+
: event.type === "tool.delta"
|
|
3967
|
+
? event.toolCallId
|
|
3968
|
+
: undefined;
|
|
3969
|
+
const toolAgentId = toolId ? delegatedToolAgentIds.get(toolId) : undefined;
|
|
3970
|
+
if (toolId && toolAgentId) {
|
|
3971
|
+
const tool =
|
|
3972
|
+
toolId in thread.tools
|
|
3973
|
+
? thread.tools[toolId]
|
|
3974
|
+
: event.type === "tool.started" || event.type === "tool.updated"
|
|
3975
|
+
? event.toolCall
|
|
3976
|
+
: undefined;
|
|
3977
|
+
if (tool) {
|
|
3978
|
+
remember(`tool:${toolId}`, event.sequence, {
|
|
3979
|
+
type: "activity",
|
|
3980
|
+
value: { ...toolToActivity(tool, labels), agentId: toolAgentId },
|
|
3981
|
+
});
|
|
3982
|
+
}
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
const items: AgentCollaborationItem[] = [];
|
|
3986
|
+
const subagents = new Map<string, AgentCollaborationEntry[]>();
|
|
3987
|
+
for (const key of order) {
|
|
3988
|
+
const sequence = firstSequence.get(key);
|
|
3989
|
+
const entry = entries.get(key);
|
|
3990
|
+
if (!entry || sequence === undefined || !sequenceInRange(sequence, range)) {
|
|
3991
|
+
continue;
|
|
3992
|
+
}
|
|
3993
|
+
const agentId = collaborationSubjectId(entry, thread);
|
|
3994
|
+
if (!agentId) {
|
|
3995
|
+
items.push({ type: "entry", key, entry });
|
|
3996
|
+
continue;
|
|
3997
|
+
}
|
|
3998
|
+
const grouped = subagents.get(agentId);
|
|
3999
|
+
if (grouped) {
|
|
4000
|
+
grouped.push(entry);
|
|
4001
|
+
continue;
|
|
4002
|
+
}
|
|
4003
|
+
const created = [entry];
|
|
4004
|
+
subagents.set(agentId, created);
|
|
4005
|
+
items.push({
|
|
4006
|
+
type: "subagent",
|
|
4007
|
+
key: `subagent:${agentId}`,
|
|
4008
|
+
agentId,
|
|
4009
|
+
entries: created,
|
|
4010
|
+
});
|
|
4011
|
+
}
|
|
4012
|
+
return items;
|
|
4013
|
+
}
|
|
4014
|
+
|
|
4015
|
+
/** The one task a Subagent step shows: the agent's active task, else its first. */
|
|
4016
|
+
function subagentStepTask(
|
|
4017
|
+
thread: AgentThreadState,
|
|
4018
|
+
agentId: string,
|
|
4019
|
+
): AgentTask | undefined {
|
|
4020
|
+
const agent = agentId in thread.agents ? thread.agents[agentId] : undefined;
|
|
4021
|
+
return Object.values(thread.tasks).find(
|
|
4022
|
+
(candidate) =>
|
|
4023
|
+
candidate.assignedAgentId === agentId &&
|
|
4024
|
+
(agent?.activeTaskId === undefined ||
|
|
4025
|
+
candidate.id === agent.activeTaskId),
|
|
4026
|
+
);
|
|
4027
|
+
}
|
|
4028
|
+
|
|
4029
|
+
/** Tasks the Subagent steps in this range of a run show. */
|
|
4030
|
+
function subagentStepTaskIds(
|
|
4031
|
+
thread: AgentThreadState,
|
|
4032
|
+
runId: RunId | undefined,
|
|
4033
|
+
labels: Partial<AgentKitLabels>,
|
|
4034
|
+
range: {
|
|
4035
|
+
afterSequence?: number | undefined;
|
|
4036
|
+
throughSequence?: number | undefined;
|
|
4037
|
+
},
|
|
4038
|
+
): Set<string> {
|
|
4039
|
+
const ids = new Set<string>();
|
|
4040
|
+
for (const item of agentCollaborationItems(
|
|
4041
|
+
thread,
|
|
4042
|
+
agentRunEvents(thread, runId),
|
|
4043
|
+
labels,
|
|
4044
|
+
range,
|
|
4045
|
+
)) {
|
|
4046
|
+
if (item.type !== "subagent") continue;
|
|
4047
|
+
const task = subagentStepTask(thread, item.agentId);
|
|
4048
|
+
if (task) ids.add(task.id);
|
|
4049
|
+
}
|
|
4050
|
+
return ids;
|
|
4051
|
+
}
|
|
4052
|
+
|
|
4053
|
+
/** Set inside a Subagent step, so its rows do not repeat the agent's chip. */
|
|
4054
|
+
const AgentSubagentStepContext = createContext<string | undefined>(undefined);
|
|
4055
|
+
|
|
4056
|
+
const SUBAGENT_LIFECYCLE_KINDS = new Set([
|
|
4057
|
+
"started",
|
|
4058
|
+
"resumed",
|
|
4059
|
+
"delegated",
|
|
4060
|
+
"paused",
|
|
4061
|
+
"completed",
|
|
4062
|
+
"failed",
|
|
4063
|
+
"closed",
|
|
4064
|
+
]);
|
|
4065
|
+
|
|
4066
|
+
type SubagentState = "running" | "paused" | "completed" | "failed" | "closed";
|
|
4067
|
+
|
|
4068
|
+
function subagentState(
|
|
4069
|
+
agent: AgentParticipant | undefined,
|
|
4070
|
+
entries: readonly AgentCollaborationEntry[],
|
|
4071
|
+
): SubagentState {
|
|
4072
|
+
switch (agent?.status) {
|
|
4073
|
+
case "waiting":
|
|
4074
|
+
case "paused":
|
|
4075
|
+
return "paused";
|
|
4076
|
+
case "completed":
|
|
4077
|
+
return "completed";
|
|
4078
|
+
case "failed":
|
|
4079
|
+
return "failed";
|
|
4080
|
+
case "closed":
|
|
4081
|
+
return "closed";
|
|
4082
|
+
case "idle":
|
|
4083
|
+
case "working":
|
|
4084
|
+
case undefined:
|
|
4085
|
+
break;
|
|
4086
|
+
}
|
|
4087
|
+
// Status can stay "working" past its last event; a finished call is not a finished agent.
|
|
4088
|
+
const newestFirst = [...entries].sort((a, b) => b.sequence - a.sequence);
|
|
4089
|
+
for (const entry of newestFirst) {
|
|
4090
|
+
if (entry.type === "activity") {
|
|
4091
|
+
if (entry.value.status === "running") return "running";
|
|
4092
|
+
continue;
|
|
4093
|
+
}
|
|
4094
|
+
switch (entry.value.kind) {
|
|
4095
|
+
case "completed":
|
|
4096
|
+
return "completed";
|
|
4097
|
+
case "failed":
|
|
4098
|
+
return "failed";
|
|
4099
|
+
case "paused":
|
|
4100
|
+
return "paused";
|
|
4101
|
+
case "closed":
|
|
4102
|
+
return "closed";
|
|
4103
|
+
case "started":
|
|
4104
|
+
case "resumed":
|
|
4105
|
+
case "delegated":
|
|
4106
|
+
return "running";
|
|
4107
|
+
case "messaged":
|
|
4108
|
+
default:
|
|
4109
|
+
break;
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
if (agent?.status === "working") return "running";
|
|
4113
|
+
const latest = newestFirst.at(0);
|
|
4114
|
+
return latest?.type === "activity" && latest.value.status === "failed"
|
|
4115
|
+
? "failed"
|
|
4116
|
+
: latest?.type === "activity" && latest.value.status === "completed"
|
|
4117
|
+
? "completed"
|
|
4118
|
+
: "running";
|
|
4119
|
+
}
|
|
4120
|
+
|
|
4121
|
+
function metadataDurationMs(
|
|
4122
|
+
metadata: Record<string, unknown> | undefined,
|
|
4123
|
+
): number | undefined {
|
|
4124
|
+
const value = metadata?.durationMs;
|
|
4125
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0
|
|
4126
|
+
? value
|
|
4127
|
+
: undefined;
|
|
4128
|
+
}
|
|
4129
|
+
|
|
4130
|
+
function subagentDurationMs(
|
|
4131
|
+
agent: AgentParticipant | undefined,
|
|
4132
|
+
entries: readonly AgentCollaborationEntry[],
|
|
4133
|
+
): number | undefined {
|
|
4134
|
+
const fromAgent = metadataDurationMs(agent?.metadata);
|
|
4135
|
+
if (fromAgent !== undefined) return fromAgent;
|
|
4136
|
+
for (const entry of [...entries].sort((a, b) => b.sequence - a.sequence)) {
|
|
4137
|
+
const fromEntry = metadataDurationMs(entry.value.metadata);
|
|
4138
|
+
if (fromEntry !== undefined) return fromEntry;
|
|
4139
|
+
}
|
|
4140
|
+
const started = agent?.startedAt ? Date.parse(agent.startedAt) : Number.NaN;
|
|
4141
|
+
const completed = agent?.completedAt
|
|
4142
|
+
? Date.parse(agent.completedAt)
|
|
4143
|
+
: Number.NaN;
|
|
4144
|
+
return Number.isFinite(started) &&
|
|
4145
|
+
Number.isFinite(completed) &&
|
|
4146
|
+
completed >= started
|
|
4147
|
+
? completed - started
|
|
4148
|
+
: undefined;
|
|
4149
|
+
}
|
|
4150
|
+
|
|
4151
|
+
/** One row per delegated agent, with its own calls and its result inside. */
|
|
4152
|
+
export function AgentSubagentStep({
|
|
4153
|
+
agentId,
|
|
4154
|
+
entries,
|
|
4155
|
+
runId,
|
|
4156
|
+
threadId,
|
|
4157
|
+
}: {
|
|
4158
|
+
agentId: string;
|
|
4159
|
+
entries: readonly AgentCollaborationEntry[];
|
|
4160
|
+
runId: RunId | undefined;
|
|
4161
|
+
threadId: string;
|
|
4162
|
+
}) {
|
|
4163
|
+
const {
|
|
4164
|
+
slots,
|
|
4165
|
+
registry,
|
|
4166
|
+
labels,
|
|
4167
|
+
formatWorkedForDuration,
|
|
4168
|
+
formatAgentInteractionDetail,
|
|
4169
|
+
} = useAgentKit();
|
|
4170
|
+
const thread = useAgentThreadSlice(
|
|
4171
|
+
(previous, next) =>
|
|
4172
|
+
sameAgentsAndTasks(previous, next) &&
|
|
4173
|
+
previous.tools === next.tools &&
|
|
4174
|
+
previous.runs === next.runs,
|
|
4175
|
+
);
|
|
4176
|
+
const [open, setOpen] = useState(false);
|
|
4177
|
+
const agent = agentId in thread.agents ? thread.agents[agentId] : undefined;
|
|
4178
|
+
const AgentRenderer = agent
|
|
4179
|
+
? (registry.agents?.[agent.kind ?? ""] ??
|
|
4180
|
+
slots.agent ??
|
|
4181
|
+
AgentParticipantView)
|
|
4182
|
+
: undefined;
|
|
4183
|
+
const name = resolveAgentName(
|
|
4184
|
+
thread,
|
|
4185
|
+
agentId,
|
|
4186
|
+
entries.map((entry) => entry.value.source),
|
|
4187
|
+
labels.assistant,
|
|
4188
|
+
);
|
|
4189
|
+
const runStatus =
|
|
4190
|
+
runId === undefined ? undefined : thread.runs[runId]?.status;
|
|
4191
|
+
const liveState = subagentState(agent, entries);
|
|
4192
|
+
// Agents can outlive their run, so one still "working" after it settled has stopped.
|
|
4193
|
+
const settledStatus =
|
|
4194
|
+
liveState !== "running"
|
|
4195
|
+
? undefined
|
|
4196
|
+
: runStatus === "completed"
|
|
4197
|
+
? "completed"
|
|
4198
|
+
: runStatus === "failed"
|
|
4199
|
+
? "failed"
|
|
4200
|
+
: runStatus === "cancelled"
|
|
4201
|
+
? "closed"
|
|
4202
|
+
: undefined;
|
|
4203
|
+
const state: SubagentState = settledStatus ?? liveState;
|
|
4204
|
+
const durationMs =
|
|
4205
|
+
state === "running" ? undefined : subagentDurationMs(agent, entries);
|
|
4206
|
+
const duration =
|
|
4207
|
+
durationMs === undefined
|
|
4208
|
+
? undefined
|
|
4209
|
+
: (formatWorkedForDuration?.(durationMs) ??
|
|
4210
|
+
formatAgentKitDuration(durationMs, {
|
|
4211
|
+
hour: labels.durationHourShort,
|
|
4212
|
+
minute: labels.durationMinuteShort,
|
|
4213
|
+
second: labels.durationSecondShort,
|
|
4214
|
+
}));
|
|
4215
|
+
const task = subagentStepTask(thread, agentId);
|
|
4216
|
+
const taskTitle = task ? humanizeAgentLabel(task.title) : "";
|
|
4217
|
+
const shownTaskTitle =
|
|
4218
|
+
taskTitle && taskTitle.toLocaleLowerCase() !== name.toLocaleLowerCase()
|
|
4219
|
+
? taskTitle
|
|
4220
|
+
: "";
|
|
4221
|
+
const newestFirst = [...entries].sort((a, b) => b.sequence - a.sequence);
|
|
4222
|
+
const latest = <T,>(
|
|
4223
|
+
pick: (entry: AgentCollaborationEntry) => T | undefined,
|
|
4224
|
+
) => {
|
|
4225
|
+
for (const entry of newestFirst) {
|
|
4226
|
+
const value = pick(entry);
|
|
4227
|
+
if (value !== undefined) return value;
|
|
4228
|
+
}
|
|
4229
|
+
return undefined;
|
|
4230
|
+
};
|
|
4231
|
+
// A call's detail is its output, which is no headline.
|
|
4232
|
+
const progress =
|
|
4233
|
+
state === "running"
|
|
4234
|
+
? latest((entry) =>
|
|
4235
|
+
entry.type === "activity" &&
|
|
4236
|
+
entry.value.status === "running" &&
|
|
4237
|
+
!(entry.value.id in thread.tools) &&
|
|
4238
|
+
readableText(entry.value.detail)
|
|
4239
|
+
? entry.value
|
|
4240
|
+
: undefined,
|
|
4241
|
+
)
|
|
4242
|
+
: undefined;
|
|
4243
|
+
const progressDetail = progress ? readableText(progress.detail) : "";
|
|
4244
|
+
const failureDetail =
|
|
4245
|
+
state === "failed"
|
|
4246
|
+
? latest((entry) =>
|
|
4247
|
+
entry.type === "interaction" && entry.value.kind === "failed"
|
|
4248
|
+
? readableText(
|
|
4249
|
+
formatAgentInteractionDetail?.(entry.value) ??
|
|
4250
|
+
entry.value.detail,
|
|
4251
|
+
) || undefined
|
|
4252
|
+
: entry.type === "activity" && entry.value.status === "failed"
|
|
4253
|
+
? readableText(entry.value.detail) || undefined
|
|
4254
|
+
: undefined,
|
|
4255
|
+
)
|
|
4256
|
+
: undefined;
|
|
4257
|
+
const status =
|
|
4258
|
+
state === "running"
|
|
4259
|
+
? labels.agentWorking
|
|
4260
|
+
: state === "failed"
|
|
4261
|
+
? labels.agentFailed
|
|
4262
|
+
: state === "paused"
|
|
4263
|
+
? labels.agentPaused
|
|
4264
|
+
: state === "closed"
|
|
4265
|
+
? labels.agentClosed
|
|
4266
|
+
: duration
|
|
4267
|
+
? labels.agentWorkedFor.replace("{{duration}}", () => duration)
|
|
4268
|
+
: labels.agentCompleted;
|
|
4269
|
+
const content = entries.flatMap((entry): ReactNode[] => {
|
|
4270
|
+
const key = `${entry.type}:${entry.value.id}`;
|
|
4271
|
+
if (entry.type === "interaction") {
|
|
4272
|
+
const interaction = entry.value;
|
|
4273
|
+
const Custom =
|
|
4274
|
+
registry.agentInteractions?.[interaction.kind] ??
|
|
4275
|
+
slots.agentInteraction;
|
|
4276
|
+
if (Custom) {
|
|
4277
|
+
return [<Custom key={key} value={interaction} threadId={threadId} />];
|
|
4278
|
+
}
|
|
4279
|
+
// The row's own status, duration and error line already say these.
|
|
4280
|
+
if (
|
|
4281
|
+
SUBAGENT_LIFECYCLE_KINDS.has(interaction.kind) &&
|
|
4282
|
+
(interaction.kind === "failed" ||
|
|
4283
|
+
!(
|
|
4284
|
+
interaction.label ||
|
|
4285
|
+
interaction.detail ||
|
|
4286
|
+
interaction.object ||
|
|
4287
|
+
interaction.targetAgentId
|
|
4288
|
+
))
|
|
4289
|
+
) {
|
|
4290
|
+
return [];
|
|
4291
|
+
}
|
|
4292
|
+
return [
|
|
4293
|
+
<AgentInteractionItem
|
|
4294
|
+
key={key}
|
|
4295
|
+
value={interaction}
|
|
4296
|
+
threadId={threadId}
|
|
4297
|
+
/>,
|
|
4298
|
+
];
|
|
4299
|
+
}
|
|
4300
|
+
const activity = entry.value;
|
|
4301
|
+
const Custom = registry.activities?.[activity.kind] ?? slots.activity;
|
|
4302
|
+
if (Custom) {
|
|
4303
|
+
return [<Custom key={key} value={activity} threadId={threadId} />];
|
|
4304
|
+
}
|
|
4305
|
+
if (activity.id in thread.tools) {
|
|
4306
|
+
return [
|
|
4307
|
+
<AgentActivityItem key={key} value={activity} threadId={threadId} />,
|
|
4308
|
+
];
|
|
4309
|
+
}
|
|
4310
|
+
// The agent's report is the step's result, so it shows in place.
|
|
4311
|
+
const summary = (activity.summary ?? []).flatMap((part, partIndex) => {
|
|
4312
|
+
const safePart =
|
|
4313
|
+
part.type === "text"
|
|
4314
|
+
? {
|
|
4315
|
+
...part,
|
|
4316
|
+
text: part.text.trim()
|
|
4317
|
+
? formatActivityText(part.text, labels)
|
|
4318
|
+
: "",
|
|
4319
|
+
}
|
|
4320
|
+
: part;
|
|
4321
|
+
return safePart.type === "text" && !safePart.text
|
|
4322
|
+
? []
|
|
4323
|
+
: [
|
|
4324
|
+
<AgentMessagePartView
|
|
4325
|
+
key={`${key}:summary:${partIndex}`}
|
|
4326
|
+
value={safePart}
|
|
4327
|
+
threadId={threadId}
|
|
4328
|
+
/>,
|
|
4329
|
+
];
|
|
4330
|
+
});
|
|
4331
|
+
if (summary.length) return summary;
|
|
4332
|
+
// A summary-less "agent" activity is the status line the header carries.
|
|
4333
|
+
if (activity === progress || activity.kind === "agent") return [];
|
|
4334
|
+
return [
|
|
4335
|
+
<AgentActivityItem key={key} value={activity} threadId={threadId} />,
|
|
4336
|
+
];
|
|
4337
|
+
});
|
|
4338
|
+
const failed = state === "failed";
|
|
4339
|
+
const line = (
|
|
4340
|
+
<TraceLine
|
|
4341
|
+
status={state === "running" ? "running" : failed ? "error" : undefined}
|
|
4342
|
+
>
|
|
4343
|
+
<TraceIcon>
|
|
4344
|
+
{/* A failed agent's chip already carries the triangle. */}
|
|
4345
|
+
<IconArrowFork aria-hidden="true" className="agentkit-icon" />
|
|
4346
|
+
</TraceIcon>
|
|
4347
|
+
<span className="agentkit-disclosure-label">
|
|
4348
|
+
{agent && AgentRenderer ? (
|
|
4349
|
+
<AgentRenderer
|
|
4350
|
+
value={settledStatus ? { ...agent, status: settledStatus } : agent}
|
|
4351
|
+
threadId={threadId}
|
|
4352
|
+
/>
|
|
4353
|
+
) : (
|
|
4354
|
+
<AgentIdentityChip
|
|
4355
|
+
id={agentId}
|
|
4356
|
+
name={name}
|
|
4357
|
+
fallbackName={labels.assistant}
|
|
4358
|
+
{...(failed
|
|
4359
|
+
? { status: "failed" as const }
|
|
4360
|
+
: state === "running"
|
|
4361
|
+
? { status: "working" as const }
|
|
4362
|
+
: {})}
|
|
4363
|
+
/>
|
|
4364
|
+
)}
|
|
4365
|
+
<TraceTitle className="agentkit-activity-label">{status}</TraceTitle>
|
|
4366
|
+
</span>
|
|
4367
|
+
</TraceLine>
|
|
4368
|
+
);
|
|
4369
|
+
const detail = shownTaskTitle || progressDetail;
|
|
4370
|
+
const ObjectRenderer = slots.object ?? AgentObjectReferenceView;
|
|
4371
|
+
const taskObject = task
|
|
4372
|
+
? (task.object ??
|
|
4373
|
+
sourceObjectUnlessRepresented(task.source, thread, [agentId]))
|
|
4374
|
+
: undefined;
|
|
4375
|
+
const meta =
|
|
4376
|
+
detail || taskObject ? (
|
|
4377
|
+
<>
|
|
4378
|
+
{taskObject ? (
|
|
4379
|
+
<ObjectRenderer value={taskObject} threadId={threadId} />
|
|
4380
|
+
) : null}
|
|
4381
|
+
{detail ? (
|
|
4382
|
+
<TraceDescription
|
|
4383
|
+
{...SESSION_REPLAY_MASK_PROPS}
|
|
4384
|
+
className="agentkit-activity-detail"
|
|
4385
|
+
>
|
|
4386
|
+
{detail}
|
|
4387
|
+
</TraceDescription>
|
|
4388
|
+
) : null}
|
|
4389
|
+
</>
|
|
4390
|
+
) : null;
|
|
4391
|
+
const errorLine = failureDetail ? (
|
|
4392
|
+
<span {...SESSION_REPLAY_MASK_PROPS} className="agentkit-activity-error">
|
|
4393
|
+
{failureDetail}
|
|
4394
|
+
</span>
|
|
4395
|
+
) : null;
|
|
4396
|
+
const rootProps = {
|
|
4397
|
+
className: "agentkit-subagent-step",
|
|
4398
|
+
"data-agent-id": agentId,
|
|
4399
|
+
// Not data-state: the collapsible keeps its open state there.
|
|
4400
|
+
"data-status": state,
|
|
4401
|
+
};
|
|
4402
|
+
if (!content.length) {
|
|
4403
|
+
return (
|
|
4404
|
+
<div {...rootProps}>
|
|
4405
|
+
<div className="agentkit-disclosure-row agentkit-activity-row">
|
|
4406
|
+
<span className="agentkit-disclosure-summary" data-expandable="false">
|
|
4407
|
+
{line}
|
|
4408
|
+
</span>
|
|
4409
|
+
{meta}
|
|
4410
|
+
</div>
|
|
4411
|
+
{errorLine}
|
|
4412
|
+
</div>
|
|
4413
|
+
);
|
|
4414
|
+
}
|
|
4415
|
+
return (
|
|
4416
|
+
<TraceCollapsible {...rootProps} open={open} onOpenChange={setOpen}>
|
|
4417
|
+
<div
|
|
4418
|
+
className="agentkit-disclosure-row agentkit-activity-row"
|
|
4419
|
+
data-expandable="true"
|
|
4420
|
+
onClick={(event) => {
|
|
4421
|
+
const target = event.target;
|
|
4422
|
+
if (
|
|
4423
|
+
target instanceof Element &&
|
|
4424
|
+
target.closest(DISCLOSURE_ROW_INTERACTIVE)
|
|
4425
|
+
)
|
|
4426
|
+
return;
|
|
4427
|
+
if (globalThis.getSelection()?.toString()) return;
|
|
4428
|
+
setOpen((current) => !current);
|
|
4429
|
+
}}
|
|
4430
|
+
>
|
|
4431
|
+
<TraceCollapsibleTrigger>{line}</TraceCollapsibleTrigger>
|
|
4432
|
+
{meta}
|
|
4433
|
+
</div>
|
|
4434
|
+
{errorLine}
|
|
4435
|
+
<TraceCollapsibleContent
|
|
4436
|
+
key={open ? "open" : "closed"}
|
|
4437
|
+
keepMounted
|
|
4438
|
+
{...(failed || !open ? SESSION_REPLAY_BLOCK_PROPS : {})}
|
|
4439
|
+
className="agentkit-subagent-content"
|
|
4440
|
+
>
|
|
4441
|
+
<AgentSubagentStepContext.Provider value={agentId}>
|
|
4442
|
+
{content}
|
|
4443
|
+
</AgentSubagentStepContext.Provider>
|
|
4444
|
+
</TraceCollapsibleContent>
|
|
4445
|
+
</TraceCollapsible>
|
|
3536
4446
|
);
|
|
3537
4447
|
}
|
|
3538
4448
|
|
|
@@ -3545,77 +4455,32 @@ export function AgentCollaborationFeed({
|
|
|
3545
4455
|
const thread = useAgentThreadSlice((previous, next) =>
|
|
3546
4456
|
sameRunWork(previous, next, runId),
|
|
3547
4457
|
);
|
|
3548
|
-
const
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
for (const event of runEvents) {
|
|
3556
|
-
if (
|
|
3557
|
-
event.type === "activity.started" ||
|
|
3558
|
-
event.type === "activity.updated" ||
|
|
3559
|
-
event.type === "activity.completed"
|
|
3560
|
-
) {
|
|
3561
|
-
const activity = thread.activities[event.activity.id] ?? event.activity;
|
|
3562
|
-
const agentId = event.activity.agentId ?? activity.agentId;
|
|
3563
|
-
if (agentId) delegatedAgentIds.set(event.activity.id, agentId);
|
|
3564
|
-
}
|
|
3565
|
-
}
|
|
3566
|
-
const firstSequence = new Map<string, number>();
|
|
3567
|
-
const order: string[] = [];
|
|
3568
|
-
const remember = (
|
|
3569
|
-
key: string,
|
|
3570
|
-
sequence: number,
|
|
3571
|
-
entry:
|
|
3572
|
-
| { type: "interaction"; value: AgentInteraction }
|
|
3573
|
-
| { type: "activity"; value: AgentActivity },
|
|
3574
|
-
) => {
|
|
3575
|
-
if (!entries.has(key)) {
|
|
3576
|
-
order.push(key);
|
|
3577
|
-
firstSequence.set(key, sequence);
|
|
3578
|
-
}
|
|
3579
|
-
entries.set(key, entry);
|
|
3580
|
-
};
|
|
3581
|
-
for (const event of runEvents) {
|
|
3582
|
-
if (event.type === "agent.interaction") {
|
|
3583
|
-
remember(`interaction:${event.interaction.id}`, event.sequence, {
|
|
3584
|
-
type: "interaction",
|
|
3585
|
-
value: event.interaction,
|
|
3586
|
-
});
|
|
3587
|
-
}
|
|
3588
|
-
if (
|
|
3589
|
-
event.type === "activity.started" ||
|
|
3590
|
-
event.type === "activity.updated" ||
|
|
3591
|
-
event.type === "activity.completed"
|
|
3592
|
-
) {
|
|
3593
|
-
const agentId = delegatedAgentIds.get(event.activity.id);
|
|
3594
|
-
if (!agentId) continue;
|
|
3595
|
-
const activity = thread.activities[event.activity.id] ?? event.activity;
|
|
3596
|
-
remember(`activity:${event.activity.id}`, event.sequence, {
|
|
3597
|
-
type: "activity",
|
|
3598
|
-
value: { ...activity, agentId },
|
|
3599
|
-
});
|
|
3600
|
-
}
|
|
3601
|
-
}
|
|
3602
|
-
const selectedOrder = order.filter((key) => {
|
|
3603
|
-
const sequence = firstSequence.get(key);
|
|
3604
|
-
return (
|
|
3605
|
-
sequence !== undefined &&
|
|
3606
|
-
sequenceInRange(sequence, { afterSequence, throughSequence })
|
|
3607
|
-
);
|
|
3608
|
-
});
|
|
3609
|
-
if (selectedOrder.length === 0) return null;
|
|
4458
|
+
const items = agentCollaborationItems(
|
|
4459
|
+
thread,
|
|
4460
|
+
agentRunEvents(thread, runId),
|
|
4461
|
+
labels,
|
|
4462
|
+
{ afterSequence, throughSequence },
|
|
4463
|
+
);
|
|
4464
|
+
if (items.length === 0) return null;
|
|
3610
4465
|
return (
|
|
3611
4466
|
<section
|
|
3612
4467
|
className="agentkit-agent-feed"
|
|
3613
4468
|
aria-label={labels.agents}
|
|
3614
4469
|
data-agent-collaboration-feed="true"
|
|
3615
4470
|
>
|
|
3616
|
-
{
|
|
3617
|
-
|
|
3618
|
-
|
|
4471
|
+
{items.map((item) => {
|
|
4472
|
+
if (item.type === "subagent") {
|
|
4473
|
+
return (
|
|
4474
|
+
<AgentSubagentStep
|
|
4475
|
+
key={item.key}
|
|
4476
|
+
agentId={item.agentId}
|
|
4477
|
+
entries={item.entries}
|
|
4478
|
+
runId={runId}
|
|
4479
|
+
threadId={threadId}
|
|
4480
|
+
/>
|
|
4481
|
+
);
|
|
4482
|
+
}
|
|
4483
|
+
const { entry, key } = item;
|
|
3619
4484
|
if (entry.type === "interaction") {
|
|
3620
4485
|
const Renderer =
|
|
3621
4486
|
registry.agentInteractions?.[entry.value.kind] ??
|
|
@@ -3694,8 +4559,11 @@ export function AgentTaskItem({
|
|
|
3694
4559
|
slots.agent ??
|
|
3695
4560
|
AgentParticipantView)
|
|
3696
4561
|
: undefined;
|
|
3697
|
-
const
|
|
3698
|
-
|
|
4562
|
+
const taskProgress = task.progress;
|
|
4563
|
+
const progress = taskProgress
|
|
4564
|
+
? labels.tasksProgress
|
|
4565
|
+
.replace("{{completed}}", () => String(taskProgress.completed))
|
|
4566
|
+
.replace("{{total}}", () => String(taskProgress.total))
|
|
3699
4567
|
: undefined;
|
|
3700
4568
|
const title =
|
|
3701
4569
|
humanizeAgentLabel(task.title) ||
|
|
@@ -3709,8 +4577,18 @@ export function AgentTaskItem({
|
|
|
3709
4577
|
title.toLocaleLowerCase() !== agentName.toLocaleLowerCase();
|
|
3710
4578
|
const status = taskStatusPresentation(task.status, labels);
|
|
3711
4579
|
return (
|
|
3712
|
-
<
|
|
3713
|
-
|
|
4580
|
+
<TraceLine
|
|
4581
|
+
className="agentkit-task-row"
|
|
4582
|
+
data-status={task.status}
|
|
4583
|
+
status={
|
|
4584
|
+
task.status === "running"
|
|
4585
|
+
? "running"
|
|
4586
|
+
: task.status === "failed"
|
|
4587
|
+
? "error"
|
|
4588
|
+
: undefined
|
|
4589
|
+
}
|
|
4590
|
+
>
|
|
4591
|
+
<TraceIcon>{status.icon}</TraceIcon>
|
|
3714
4592
|
<span className="agentkit-visually-hidden">{status.label}</span>
|
|
3715
4593
|
{showAgent && agent && AgentRenderer ? (
|
|
3716
4594
|
<AgentRenderer value={agent} threadId={threadId} />
|
|
@@ -3721,7 +4599,7 @@ export function AgentTaskItem({
|
|
|
3721
4599
|
fallbackName={labels.assistant}
|
|
3722
4600
|
/>
|
|
3723
4601
|
) : null}
|
|
3724
|
-
<
|
|
4602
|
+
<TraceTitle className="agentkit-task-title">{title}</TraceTitle>
|
|
3725
4603
|
{object ? <ObjectRenderer value={object} threadId={threadId} /> : null}
|
|
3726
4604
|
{detail ? (
|
|
3727
4605
|
<span className="agentkit-task-detail" title={detail}>
|
|
@@ -3729,9 +4607,11 @@ export function AgentTaskItem({
|
|
|
3729
4607
|
</span>
|
|
3730
4608
|
) : null}
|
|
3731
4609
|
{progress ? (
|
|
3732
|
-
<
|
|
4610
|
+
<TraceDescription className="agentkit-task-progress">
|
|
4611
|
+
{progress}
|
|
4612
|
+
</TraceDescription>
|
|
3733
4613
|
) : null}
|
|
3734
|
-
</
|
|
4614
|
+
</TraceLine>
|
|
3735
4615
|
);
|
|
3736
4616
|
}
|
|
3737
4617
|
|
|
@@ -3763,14 +4643,23 @@ export function AgentTaskGroup({
|
|
|
3763
4643
|
}
|
|
3764
4644
|
tasks.set(event.task.id, thread.tasks[event.task.id] ?? event.task);
|
|
3765
4645
|
}
|
|
4646
|
+
// Only the task a Subagent step shows moves out of this list.
|
|
4647
|
+
const stepTaskIds = subagentStepTaskIds(thread, runId, labels, {
|
|
4648
|
+
afterSequence,
|
|
4649
|
+
throughSequence,
|
|
4650
|
+
});
|
|
3766
4651
|
const selectedIds = ids.filter((id) => {
|
|
3767
4652
|
const sequence = firstSequence.get(id);
|
|
3768
4653
|
return (
|
|
3769
4654
|
sequence !== undefined &&
|
|
3770
|
-
sequenceInRange(sequence, { afterSequence, throughSequence })
|
|
4655
|
+
sequenceInRange(sequence, { afterSequence, throughSequence }) &&
|
|
4656
|
+
!stepTaskIds.has(id)
|
|
3771
4657
|
);
|
|
3772
4658
|
});
|
|
3773
4659
|
if (selectedIds.length === 0) return null;
|
|
4660
|
+
const completedCount = selectedIds.filter(
|
|
4661
|
+
(id) => tasks.get(id)?.status === "completed",
|
|
4662
|
+
).length;
|
|
3774
4663
|
return (
|
|
3775
4664
|
<div className="agentkit-tasks" data-open={open ? "true" : "false"}>
|
|
3776
4665
|
<AgentKitDisclosureSummary
|
|
@@ -3782,7 +4671,11 @@ export function AgentTaskGroup({
|
|
|
3782
4671
|
label={
|
|
3783
4672
|
<>
|
|
3784
4673
|
<span>{labels.tasks}</span>
|
|
3785
|
-
<span className="agentkit-tasks-count">
|
|
4674
|
+
<span className="agentkit-tasks-count">
|
|
4675
|
+
{labels.tasksProgress
|
|
4676
|
+
.replace("{{completed}}", () => String(completedCount))
|
|
4677
|
+
.replace("{{total}}", () => String(selectedIds.length))}
|
|
4678
|
+
</span>
|
|
3786
4679
|
</>
|
|
3787
4680
|
}
|
|
3788
4681
|
/>
|
|
@@ -4194,8 +5087,8 @@ export function AgentConnectionRequestCard({
|
|
|
4194
5087
|
return (
|
|
4195
5088
|
<Surface
|
|
4196
5089
|
as="section"
|
|
4197
|
-
className="agentkit-connection-request
|
|
4198
|
-
elevation="
|
|
5090
|
+
className="agentkit-connection-request"
|
|
5091
|
+
elevation="none"
|
|
4199
5092
|
padding="none"
|
|
4200
5093
|
data-status={request.status}
|
|
4201
5094
|
aria-labelledby={`${request.id}-title`}
|
|
@@ -4249,7 +5142,7 @@ export function AgentConnectionRequestCard({
|
|
|
4249
5142
|
) : null}
|
|
4250
5143
|
{resolution.error ? (
|
|
4251
5144
|
<div className="agentkit-command-error" role="alert">
|
|
4252
|
-
<
|
|
5145
|
+
<IconAlertTriangle aria-hidden="true" className="agentkit-icon" />
|
|
4253
5146
|
<span {...SESSION_REPLAY_MASK_PROPS}>{resolution.error.message}</span>
|
|
4254
5147
|
</div>
|
|
4255
5148
|
) : null}
|
|
@@ -5214,39 +6107,100 @@ function AgentMessageTimestamp({
|
|
|
5214
6107
|
);
|
|
5215
6108
|
}
|
|
5216
6109
|
|
|
6110
|
+
/** Enter and Space fire a click with no pointer detail. */
|
|
6111
|
+
function pressedByKeyboard(event: { detail: number } | undefined): boolean {
|
|
6112
|
+
return event?.detail === 0;
|
|
6113
|
+
}
|
|
6114
|
+
|
|
6115
|
+
/** Keyboard presses only: focusing the composer on touch opens the on-screen keyboard. */
|
|
6116
|
+
function useRescueFocusToComposer(threadId: string): () => void {
|
|
6117
|
+
const { requestComposerFocus } = useAgentKit();
|
|
6118
|
+
return useCallback(() => {
|
|
6119
|
+
requestAnimationFrame(() => {
|
|
6120
|
+
const active = document.activeElement;
|
|
6121
|
+
if (!active || active === document.body) requestComposerFocus(threadId);
|
|
6122
|
+
});
|
|
6123
|
+
}, [requestComposerFocus, threadId]);
|
|
6124
|
+
}
|
|
6125
|
+
|
|
5217
6126
|
/**
|
|
5218
6127
|
* Names an icon-only message action on hover and keyboard focus. Below the
|
|
5219
6128
|
* button, so it never covers the message it acts on.
|
|
5220
6129
|
*/
|
|
5221
6130
|
function MessageActionButton({
|
|
5222
6131
|
title,
|
|
6132
|
+
busy = false,
|
|
6133
|
+
unavailable = false,
|
|
6134
|
+
icon,
|
|
6135
|
+
onPress,
|
|
6136
|
+
className,
|
|
5223
6137
|
...props
|
|
5224
|
-
}: ComponentPropsWithoutRef<typeof IconButton>
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
6138
|
+
}: ComponentPropsWithoutRef<typeof IconButton> & {
|
|
6139
|
+
/** Stays focusable: disabling the pressed button drops focus to the page. */
|
|
6140
|
+
busy?: boolean;
|
|
6141
|
+
/** Announced with aria-disabled and kept focusable. */
|
|
6142
|
+
unavailable?: boolean;
|
|
6143
|
+
}) {
|
|
6144
|
+
// Only a native title explains a disabled button, never also the tooltip. An
|
|
6145
|
+
// unavailable one stays focusable and a press opens its tooltip: taps never hover.
|
|
6146
|
+
const inactive = Boolean(props.disabled || props.pending || busy);
|
|
5229
6147
|
const reason = inactive ? undefined : title;
|
|
6148
|
+
const reasonId = useId();
|
|
6149
|
+
const [tooltipOpen, setTooltipOpen] = useState(false);
|
|
5230
6150
|
return (
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
6151
|
+
<>
|
|
6152
|
+
<Tooltip
|
|
6153
|
+
open={tooltipOpen}
|
|
6154
|
+
onOpenChange={setTooltipOpen}
|
|
6155
|
+
content={
|
|
6156
|
+
reason ? (
|
|
6157
|
+
<>
|
|
6158
|
+
{props.label}
|
|
6159
|
+
<span className="block text-muted-foreground">{reason}</span>
|
|
6160
|
+
</>
|
|
6161
|
+
) : (
|
|
6162
|
+
props.label
|
|
6163
|
+
)
|
|
6164
|
+
}
|
|
6165
|
+
placement="bottom"
|
|
6166
|
+
trigger={
|
|
6167
|
+
<IconButton
|
|
6168
|
+
{...props}
|
|
6169
|
+
icon={
|
|
6170
|
+
busy ? (
|
|
6171
|
+
<IconLoader2 aria-hidden="true" className="animate-spin" />
|
|
6172
|
+
) : (
|
|
6173
|
+
icon
|
|
6174
|
+
)
|
|
6175
|
+
}
|
|
6176
|
+
{...(unavailable
|
|
6177
|
+
? {
|
|
6178
|
+
className: `${className ?? ""} aria-disabled:cursor-default aria-disabled:opacity-50`,
|
|
6179
|
+
}
|
|
6180
|
+
: className
|
|
6181
|
+
? { className }
|
|
6182
|
+
: {})}
|
|
6183
|
+
{...(busy ? { "aria-busy": true } : {})}
|
|
6184
|
+
{...(unavailable ? { "aria-disabled": true } : {})}
|
|
6185
|
+
{...(reason ? { "aria-describedby": reasonId } : {})}
|
|
6186
|
+
onPress={(event) => {
|
|
6187
|
+
if (unavailable) {
|
|
6188
|
+
if (reason) setTooltipOpen(true);
|
|
6189
|
+
return;
|
|
6190
|
+
}
|
|
6191
|
+
if (busy) return;
|
|
6192
|
+
onPress?.(event);
|
|
6193
|
+
}}
|
|
6194
|
+
{...(inactive && title !== undefined ? { title } : {})}
|
|
6195
|
+
/>
|
|
6196
|
+
}
|
|
6197
|
+
/>
|
|
6198
|
+
{reason ? (
|
|
6199
|
+
<span id={reasonId} hidden>
|
|
6200
|
+
{reason}
|
|
6201
|
+
</span>
|
|
6202
|
+
) : null}
|
|
6203
|
+
</>
|
|
5250
6204
|
);
|
|
5251
6205
|
}
|
|
5252
6206
|
|
|
@@ -5262,6 +6216,7 @@ export const AgentMessageActions = memo(function AgentMessageActions({
|
|
|
5262
6216
|
onCopyMessage,
|
|
5263
6217
|
branchNavigation,
|
|
5264
6218
|
} = useAgentKit();
|
|
6219
|
+
const rescueFocusToComposer = useRescueFocusToComposer(threadId);
|
|
5265
6220
|
const feedbackCapability = useAgentCapability("feedback");
|
|
5266
6221
|
const forkingCapability = useAgentCapability("threadForking");
|
|
5267
6222
|
const control = useAgentKitControl(threadId);
|
|
@@ -5315,6 +6270,10 @@ export const AgentMessageActions = memo(function AgentMessageActions({
|
|
|
5315
6270
|
null,
|
|
5316
6271
|
);
|
|
5317
6272
|
const toolbarRef = useRef<HTMLDivElement>(null);
|
|
6273
|
+
const actionsRef = useRef<HTMLDivElement>(null);
|
|
6274
|
+
// One tab stop per message: the toolbar for a reply, the row for a sent message.
|
|
6275
|
+
useRovingFocus(toolbarRef, isAssistant);
|
|
6276
|
+
useRovingFocus(actionsRef, !isAssistant);
|
|
5318
6277
|
const moreToggleRef = useRef<HTMLButtonElement>(null);
|
|
5319
6278
|
const secondaryActionsRef = useRef<HTMLDivElement>(null);
|
|
5320
6279
|
const pendingFocusRef = useRef<"secondary" | "toggle" | null>(null);
|
|
@@ -5475,7 +6434,7 @@ export const AgentMessageActions = memo(function AgentMessageActions({
|
|
|
5475
6434
|
)
|
|
5476
6435
|
}
|
|
5477
6436
|
size="compact"
|
|
5478
|
-
className="size-6"
|
|
6437
|
+
className="size-6 rounded-[var(--agent-kit-radius-xs,0.25rem)]"
|
|
5479
6438
|
// Copies stay enabled while they run: disabling the pressed button drops
|
|
5480
6439
|
// focus to the page, where Escape can no longer close the toolbar.
|
|
5481
6440
|
aria-busy={copyAction.pending || undefined}
|
|
@@ -5498,11 +6457,9 @@ export const AgentMessageActions = memo(function AgentMessageActions({
|
|
|
5498
6457
|
label={labels.previousBranch}
|
|
5499
6458
|
icon={<IconChevronLeft aria-hidden="true" />}
|
|
5500
6459
|
size="compact"
|
|
5501
|
-
className="size-6"
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
}
|
|
5505
|
-
pending={branchNavigationAction.pending}
|
|
6460
|
+
className="size-6 rounded-[var(--agent-kit-radius-xs,0.25rem)]"
|
|
6461
|
+
unavailable={branchNavigation.index <= 1}
|
|
6462
|
+
busy={branchNavigationAction.pending}
|
|
5506
6463
|
onPress={() =>
|
|
5507
6464
|
void branchNavigationAction
|
|
5508
6465
|
.execute("previous")
|
|
@@ -5523,12 +6480,9 @@ export const AgentMessageActions = memo(function AgentMessageActions({
|
|
|
5523
6480
|
label={labels.nextBranch}
|
|
5524
6481
|
icon={<IconChevronRight aria-hidden="true" />}
|
|
5525
6482
|
size="compact"
|
|
5526
|
-
className="size-6"
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
branchNavigationAction.pending
|
|
5530
|
-
}
|
|
5531
|
-
pending={branchNavigationAction.pending}
|
|
6483
|
+
className="size-6 rounded-[var(--agent-kit-radius-xs,0.25rem)]"
|
|
6484
|
+
unavailable={branchNavigation.index >= branchNavigation.count}
|
|
6485
|
+
busy={branchNavigationAction.pending}
|
|
5532
6486
|
onPress={() =>
|
|
5533
6487
|
void branchNavigationAction.execute("next").catch(() => undefined)
|
|
5534
6488
|
}
|
|
@@ -5539,7 +6493,7 @@ export const AgentMessageActions = memo(function AgentMessageActions({
|
|
|
5539
6493
|
<div
|
|
5540
6494
|
ref={toolbarRef}
|
|
5541
6495
|
className="agentkit-message-toolbar"
|
|
5542
|
-
role="
|
|
6496
|
+
role="toolbar"
|
|
5543
6497
|
aria-label={labels.messageActions}
|
|
5544
6498
|
data-placement={placement}
|
|
5545
6499
|
data-latest={isLastAssistantMessage ? "true" : undefined}
|
|
@@ -5617,15 +6571,14 @@ export const AgentMessageActions = memo(function AgentMessageActions({
|
|
|
5617
6571
|
)
|
|
5618
6572
|
}
|
|
5619
6573
|
size="compact"
|
|
5620
|
-
className="size-6"
|
|
6574
|
+
className="size-6 rounded-[var(--agent-kit-radius-xs,0.25rem)]"
|
|
5621
6575
|
data-message-action={`feedback-${value}`}
|
|
5622
6576
|
aria-pressed={chosen}
|
|
5623
6577
|
aria-haspopup={toggle ? "dialog" : undefined}
|
|
5624
6578
|
aria-expanded={toggle ? open : undefined}
|
|
5625
6579
|
aria-controls={toggle && open ? contentId : undefined}
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
}
|
|
6580
|
+
unavailable={!feedbackCapability.enabled}
|
|
6581
|
+
busy={feedbackAction.pending}
|
|
5629
6582
|
{...(feedbackCapability.reason === undefined
|
|
5630
6583
|
? {}
|
|
5631
6584
|
: { title: feedbackCapability.reason })}
|
|
@@ -5664,20 +6617,23 @@ export const AgentMessageActions = memo(function AgentMessageActions({
|
|
|
5664
6617
|
label={labels.regenerateResponse}
|
|
5665
6618
|
icon={<IconRefresh aria-hidden="true" />}
|
|
5666
6619
|
size="compact"
|
|
5667
|
-
className="size-6"
|
|
6620
|
+
className="size-6 rounded-[var(--agent-kit-radius-xs,0.25rem)]"
|
|
5668
6621
|
data-message-action="retry"
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
regenerateAction.pending ||
|
|
5672
|
-
position.active ||
|
|
5673
|
-
!forkingCapability.enabled
|
|
5674
|
-
}
|
|
6622
|
+
busy={regenerateAction.pending}
|
|
6623
|
+
unavailable={position.active || !forkingCapability.enabled}
|
|
5675
6624
|
{...(forkingCapability.reason === undefined
|
|
5676
6625
|
? {}
|
|
5677
6626
|
: { title: forkingCapability.reason })}
|
|
5678
|
-
onPress={() =>
|
|
5679
|
-
|
|
5680
|
-
|
|
6627
|
+
onPress={(event) => {
|
|
6628
|
+
const keyboard = pressedByKeyboard(event);
|
|
6629
|
+
void regenerateAction
|
|
6630
|
+
.execute()
|
|
6631
|
+
.catch(() => undefined)
|
|
6632
|
+
// The new response replaces this toolbar.
|
|
6633
|
+
.finally(() => {
|
|
6634
|
+
if (keyboard) rescueFocusToComposer();
|
|
6635
|
+
});
|
|
6636
|
+
}}
|
|
5681
6637
|
/>
|
|
5682
6638
|
) : null}
|
|
5683
6639
|
{showFork ? (
|
|
@@ -5685,10 +6641,10 @@ export const AgentMessageActions = memo(function AgentMessageActions({
|
|
|
5685
6641
|
label={labels.forkFromHere}
|
|
5686
6642
|
icon={<IconArrowFork aria-hidden="true" />}
|
|
5687
6643
|
size="compact"
|
|
5688
|
-
className="size-6"
|
|
6644
|
+
className="size-6 rounded-[var(--agent-kit-radius-xs,0.25rem)]"
|
|
5689
6645
|
data-message-action="fork"
|
|
5690
|
-
|
|
5691
|
-
|
|
6646
|
+
busy={forkAction.pending}
|
|
6647
|
+
unavailable={!forkingCapability.enabled}
|
|
5692
6648
|
{...(forkingCapability.reason === undefined
|
|
5693
6649
|
? {}
|
|
5694
6650
|
: { title: forkingCapability.reason })}
|
|
@@ -5708,7 +6664,7 @@ export const AgentMessageActions = memo(function AgentMessageActions({
|
|
|
5708
6664
|
)
|
|
5709
6665
|
}
|
|
5710
6666
|
size="compact"
|
|
5711
|
-
className="size-6"
|
|
6667
|
+
className="size-6 rounded-[var(--agent-kit-radius-xs,0.25rem)]"
|
|
5712
6668
|
data-message-action="request-id"
|
|
5713
6669
|
aria-busy={requestIdAction.pending || undefined}
|
|
5714
6670
|
onPress={() => {
|
|
@@ -5728,7 +6684,7 @@ export const AgentMessageActions = memo(function AgentMessageActions({
|
|
|
5728
6684
|
size="compact"
|
|
5729
6685
|
// Hover is lighter than open, the toolkit's menu-trigger pattern,
|
|
5730
6686
|
// so pressing it reads as a change while the pointer is still on it.
|
|
5731
|
-
className="agentkit-message-actions-toggle size-6 hover:bg-accent/60 aria-expanded:bg-accent aria-expanded:text-foreground"
|
|
6687
|
+
className="agentkit-message-actions-toggle size-6 rounded-[var(--agent-kit-radius-xs,0.25rem)] hover:bg-accent/60 aria-expanded:bg-accent aria-expanded:text-foreground"
|
|
5732
6688
|
data-message-actions-toggle=""
|
|
5733
6689
|
aria-expanded={moreActionsOpen}
|
|
5734
6690
|
aria-controls={secondaryActionsId}
|
|
@@ -5743,7 +6699,7 @@ export const AgentMessageActions = memo(function AgentMessageActions({
|
|
|
5743
6699
|
<span className="agentkit-visually-hidden" aria-live="polite">
|
|
5744
6700
|
{feedbackConfirmed
|
|
5745
6701
|
? labels.feedbackSubmitted
|
|
5746
|
-
: requestIdCopied
|
|
6702
|
+
: requestIdCopied || copied
|
|
5747
6703
|
? labels.copied
|
|
5748
6704
|
: ""}
|
|
5749
6705
|
</span>
|
|
@@ -5751,6 +6707,7 @@ export const AgentMessageActions = memo(function AgentMessageActions({
|
|
|
5751
6707
|
) : null;
|
|
5752
6708
|
return (
|
|
5753
6709
|
<div
|
|
6710
|
+
ref={actionsRef}
|
|
5754
6711
|
className="agentkit-message-actions"
|
|
5755
6712
|
data-role={message.role}
|
|
5756
6713
|
data-placement={placement}
|
|
@@ -5758,6 +6715,9 @@ export const AgentMessageActions = memo(function AgentMessageActions({
|
|
|
5758
6715
|
data-streaming={message.status === "streaming" ? "true" : undefined}
|
|
5759
6716
|
data-expanded={moreActionsOpen ? "true" : undefined}
|
|
5760
6717
|
data-has-error={footerError ? "true" : undefined}
|
|
6718
|
+
{...(isAssistant
|
|
6719
|
+
? {}
|
|
6720
|
+
: { role: "toolbar", "aria-label": labels.messageActions })}
|
|
5761
6721
|
>
|
|
5762
6722
|
{isAssistant ? (
|
|
5763
6723
|
<>
|
|
@@ -5776,8 +6736,8 @@ export const AgentMessageActions = memo(function AgentMessageActions({
|
|
|
5776
6736
|
label={labels.editMessage}
|
|
5777
6737
|
icon={<IconPencil aria-hidden="true" />}
|
|
5778
6738
|
size="compact"
|
|
5779
|
-
className="size-6"
|
|
5780
|
-
|
|
6739
|
+
className="size-6 rounded-[var(--agent-kit-radius-xs,0.25rem)]"
|
|
6740
|
+
unavailable={position.active || !forkingCapability.enabled}
|
|
5781
6741
|
{...(forkingCapability.reason === undefined
|
|
5782
6742
|
? {}
|
|
5783
6743
|
: { title: forkingCapability.reason })}
|
|
@@ -5794,6 +6754,9 @@ export const AgentMessageActions = memo(function AgentMessageActions({
|
|
|
5794
6754
|
/>
|
|
5795
6755
|
) : null}
|
|
5796
6756
|
</div>
|
|
6757
|
+
<span className="agentkit-visually-hidden" aria-live="polite">
|
|
6758
|
+
{copied ? labels.copied : ""}
|
|
6759
|
+
</span>
|
|
5797
6760
|
</>
|
|
5798
6761
|
)}
|
|
5799
6762
|
{footerError ? (
|
|
@@ -5909,15 +6872,28 @@ export const AgentMessageView = memo(function AgentMessageView({
|
|
|
5909
6872
|
let hasSeenWidget = false;
|
|
5910
6873
|
const renderedContent: ReactNode[] = [];
|
|
5911
6874
|
let citationRun: ReactNode[] = [];
|
|
6875
|
+
let citationParts: CitationPart[] = [];
|
|
5912
6876
|
let citationRunKey = "";
|
|
5913
6877
|
const flushCitations = () => {
|
|
5914
6878
|
if (!citationRun.length) return;
|
|
5915
6879
|
renderedContent.push(
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
6880
|
+
// A host citation renderer keeps the inline run it was written for.
|
|
6881
|
+
slots.citation || registry.messageParts?.citation ? (
|
|
6882
|
+
<span
|
|
6883
|
+
key={`citations:${citationRunKey}`}
|
|
6884
|
+
className="agentkit-citations"
|
|
6885
|
+
>
|
|
6886
|
+
{citationRun}
|
|
6887
|
+
</span>
|
|
6888
|
+
) : (
|
|
6889
|
+
<AgentSources
|
|
6890
|
+
key={`citations:${citationRunKey}`}
|
|
6891
|
+
citations={citationParts}
|
|
6892
|
+
/>
|
|
6893
|
+
),
|
|
5919
6894
|
);
|
|
5920
6895
|
citationRun = [];
|
|
6896
|
+
citationParts = [];
|
|
5921
6897
|
};
|
|
5922
6898
|
orderedContent.forEach((item, index) => {
|
|
5923
6899
|
const isWidget = item.type === "widget" || item.part.type === "widget";
|
|
@@ -5944,6 +6920,7 @@ export const AgentMessageView = memo(function AgentMessageView({
|
|
|
5944
6920
|
if (item.type === "part" && item.part.type === "citation") {
|
|
5945
6921
|
if (!citationRun.length) citationRunKey = item.key;
|
|
5946
6922
|
citationRun.push(<Fragment key={item.key}>{content}</Fragment>);
|
|
6923
|
+
citationParts.push(item.part);
|
|
5947
6924
|
return;
|
|
5948
6925
|
}
|
|
5949
6926
|
flushCitations();
|
|
@@ -5970,6 +6947,10 @@ export const AgentMessageView = memo(function AgentMessageView({
|
|
|
5970
6947
|
aria-label={message.role === "user" ? labels.you : labels.assistant}
|
|
5971
6948
|
aria-busy={message.status === "streaming"}
|
|
5972
6949
|
>
|
|
6950
|
+
{/* Lets screen-reader users jump between turns by heading. */}
|
|
6951
|
+
<h2 className="agentkit-visually-hidden">
|
|
6952
|
+
{message.role === "user" ? labels.you : labels.assistant}
|
|
6953
|
+
</h2>
|
|
5973
6954
|
<div className="agentkit-message-content">
|
|
5974
6955
|
{renderedContent}
|
|
5975
6956
|
{attachedAnnotations.length ? (
|
|
@@ -6057,19 +7038,17 @@ export function AgentRunFailure({
|
|
|
6057
7038
|
setContinueFailed(true);
|
|
6058
7039
|
});
|
|
6059
7040
|
};
|
|
6060
|
-
//
|
|
6061
|
-
const tone = error.retryable === true ? "warning" : "danger";
|
|
6062
|
-
const ToneIcon = tone === "warning" ? IconAlertTriangle : IconAlertCircle;
|
|
7041
|
+
// Amber, not red, even when not retryable: a failed run destroys nothing.
|
|
6063
7042
|
return (
|
|
6064
7043
|
<div
|
|
6065
7044
|
className="agentkit-run-failure"
|
|
6066
|
-
data-tone=
|
|
7045
|
+
data-tone="warning"
|
|
6067
7046
|
data-error-code={error.code}
|
|
6068
7047
|
data-run-id={runId}
|
|
6069
7048
|
data-thread-id={threadId}
|
|
6070
7049
|
role={!superseded && latestFailure ? "alert" : undefined}
|
|
6071
7050
|
>
|
|
6072
|
-
<
|
|
7051
|
+
<IconAlertTriangle aria-hidden="true" className="agentkit-icon" />
|
|
6073
7052
|
<div className="agentkit-run-failure-copy">
|
|
6074
7053
|
<strong>{labels.runFailed}</strong>
|
|
6075
7054
|
<span {...SESSION_REPLAY_MASK_PROPS}>{error.message}</span>
|
|
@@ -6106,7 +7085,7 @@ export function AgentConnectionErrorView({
|
|
|
6106
7085
|
const { labels } = useAgentKit();
|
|
6107
7086
|
return (
|
|
6108
7087
|
<ComposerNotice
|
|
6109
|
-
tone=
|
|
7088
|
+
tone="warning"
|
|
6110
7089
|
role="alert"
|
|
6111
7090
|
noticeId="connection-error"
|
|
6112
7091
|
className="agentkit-connection-notice"
|
|
@@ -6622,7 +7601,7 @@ export function AgentKitComposer({
|
|
|
6622
7601
|
trigger={
|
|
6623
7602
|
<span
|
|
6624
7603
|
tabIndex={0}
|
|
6625
|
-
className="inline-flex min-w-0 items-center gap-2 rounded-
|
|
7604
|
+
className="inline-flex min-w-0 items-center gap-2 rounded-[var(--agent-kit-radius-xs,0.25rem)] outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
6626
7605
|
>
|
|
6627
7606
|
<IconPencil aria-hidden="true" className="size-4 shrink-0" />
|
|
6628
7607
|
<span id={editingLabelId} className="truncate">
|
|
@@ -6799,6 +7778,9 @@ export function AgentKitComposer({
|
|
|
6799
7778
|
threadId: forkedThread.id,
|
|
6800
7779
|
text: payload.text,
|
|
6801
7780
|
attachments: [...payload.attachments],
|
|
7781
|
+
...(payload.references.length
|
|
7782
|
+
? { references: [...payload.references] }
|
|
7783
|
+
: {}),
|
|
6802
7784
|
options: payload.options,
|
|
6803
7785
|
metadata: sendMetadata,
|
|
6804
7786
|
onLocalSubmit,
|
|
@@ -6841,6 +7823,9 @@ export function AgentKitComposer({
|
|
|
6841
7823
|
const message = {
|
|
6842
7824
|
text: payload.text,
|
|
6843
7825
|
attachments: [...payload.attachments],
|
|
7826
|
+
...(payload.references.length
|
|
7827
|
+
? { references: [...payload.references] }
|
|
7828
|
+
: {}),
|
|
6844
7829
|
options: payload.options,
|
|
6845
7830
|
metadata: sendMetadata,
|
|
6846
7831
|
queueWhileRunning,
|
|
@@ -7280,6 +8265,8 @@ export interface AgentKitChatProps {
|
|
|
7280
8265
|
emptyComposerPlacement?: "center" | "bottom";
|
|
7281
8266
|
/** Follow new output until the user deliberately scrolls away. */
|
|
7282
8267
|
autoScroll?: boolean;
|
|
8268
|
+
/** Hold a sent message near the top while its reply streams; `false` follows the bottom. */
|
|
8269
|
+
anchorTurns?: boolean;
|
|
7283
8270
|
/** Optional transcript geometry. Omitted values keep the shared defaults. */
|
|
7284
8271
|
transcriptLayout?: AgentChatTranscriptLayout;
|
|
7285
8272
|
/**
|
|
@@ -7408,6 +8395,8 @@ const AGENTKIT_HISTORY_ROW_CHUNK = 16;
|
|
|
7408
8395
|
const AGENTKIT_TRANSCRIPT_SCROLLBAR_IDLE_MS = 900;
|
|
7409
8396
|
/** Outlasts the reveal's bounded drain after a run ends. */
|
|
7410
8397
|
const AGENTKIT_TRANSCRIPT_GLIDE_AFTER_RUN_MS = 2_000;
|
|
8398
|
+
/** How much of the previous turn shows above an anchored message. */
|
|
8399
|
+
const AGENTKIT_TURN_ANCHOR_PEEK_PX = 32;
|
|
7411
8400
|
const AGENTKIT_TRANSCRIPT_SCROLL_KEYS = new Set([
|
|
7412
8401
|
"ArrowDown",
|
|
7413
8402
|
"ArrowUp",
|
|
@@ -7417,7 +8406,8 @@ const AGENTKIT_TRANSCRIPT_SCROLL_KEYS = new Set([
|
|
|
7417
8406
|
"PageUp",
|
|
7418
8407
|
" ",
|
|
7419
8408
|
]);
|
|
7420
|
-
|
|
8409
|
+
// Long enough for a screen reader to finish speaking between step names.
|
|
8410
|
+
const AGENTKIT_ACTIVITY_ANNOUNCE_INTERVAL_MS = 10_000;
|
|
7421
8411
|
|
|
7422
8412
|
export function isAgentKitTranscriptNearBottom(
|
|
7423
8413
|
element: Pick<HTMLElement, "clientHeight" | "scrollHeight" | "scrollTop">,
|
|
@@ -7445,7 +8435,7 @@ export function AgentKitTranscriptSkeleton({
|
|
|
7445
8435
|
>
|
|
7446
8436
|
<span className="agentkit-visually-hidden">{label}</span>
|
|
7447
8437
|
<div className="agentkit-transcript-skeleton-user" aria-hidden="true">
|
|
7448
|
-
<Skeleton className="agentkit-transcript-skeleton-bubble ms-auto h-
|
|
8438
|
+
<Skeleton className="agentkit-transcript-skeleton-bubble ms-auto h-11 w-2/5 rounded-[var(--agent-kit-radius-sm,0.5rem)]" />
|
|
7449
8439
|
</div>
|
|
7450
8440
|
<div
|
|
7451
8441
|
className="agentkit-transcript-skeleton-assistant"
|
|
@@ -7565,6 +8555,24 @@ function currentRunActivityLabel(
|
|
|
7565
8555
|
return labels.working;
|
|
7566
8556
|
}
|
|
7567
8557
|
|
|
8558
|
+
// A run that stops to wait on the user is as much news as one that ends.
|
|
8559
|
+
const RUN_STATUS_ANNOUNCEMENTS: Partial<
|
|
8560
|
+
Record<
|
|
8561
|
+
string,
|
|
8562
|
+
| "responseReady"
|
|
8563
|
+
| "responseFailed"
|
|
8564
|
+
| "runStopped"
|
|
8565
|
+
| "waitingForApproval"
|
|
8566
|
+
| "waitingForInput"
|
|
8567
|
+
>
|
|
8568
|
+
> = {
|
|
8569
|
+
completed: "responseReady",
|
|
8570
|
+
failed: "responseFailed",
|
|
8571
|
+
cancelled: "runStopped",
|
|
8572
|
+
awaiting_approval: "waitingForApproval",
|
|
8573
|
+
awaiting_input: "waitingForInput",
|
|
8574
|
+
};
|
|
8575
|
+
|
|
7568
8576
|
/**
|
|
7569
8577
|
* One polite status region for the whole chat. The transcript itself stays
|
|
7570
8578
|
* aria-live="off": streamed text would otherwise be read token by token.
|
|
@@ -7575,6 +8583,8 @@ function AgentKitLiveStatus({ threadId }: { threadId: string }) {
|
|
|
7575
8583
|
const runId = latestRunId(thread);
|
|
7576
8584
|
const run = runId ? thread.runs[runId] : undefined;
|
|
7577
8585
|
const status = run?.status;
|
|
8586
|
+
// A failure with an error has its own alert; announcing it here reads it twice.
|
|
8587
|
+
const failureHasAlert = status === "failed" && Boolean(run?.error);
|
|
7578
8588
|
const running = status === "running";
|
|
7579
8589
|
const activityText =
|
|
7580
8590
|
running && runId ? currentRunActivityLabel(thread, runId, labels) : "";
|
|
@@ -7599,15 +8609,14 @@ function AgentKitLiveStatus({ threadId }: { threadId: string }) {
|
|
|
7599
8609
|
if (!runId || previous.runId !== runId || previous.status === status) {
|
|
7600
8610
|
return;
|
|
7601
8611
|
}
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
}, [labels.responseFailed, labels.responseReady, runId, status]);
|
|
8612
|
+
const announced = status ? RUN_STATUS_ANNOUNCEMENTS[status] : undefined;
|
|
8613
|
+
if (!announced) return;
|
|
8614
|
+
if (pendingTimerRef.current) clearTimeout(pendingTimerRef.current);
|
|
8615
|
+
pendingTimerRef.current = undefined;
|
|
8616
|
+
lastAnnouncedAtRef.current = Date.now();
|
|
8617
|
+
// Cleared rather than left on the last step name, which is stale now.
|
|
8618
|
+
setAnnouncement(failureHasAlert ? "" : labels[announced]);
|
|
8619
|
+
}, [failureHasAlert, labels, runId, status]);
|
|
7611
8620
|
useEffect(() => {
|
|
7612
8621
|
if (!activityText) return;
|
|
7613
8622
|
const announce = () => {
|
|
@@ -7672,6 +8681,7 @@ export function AgentKitChat({
|
|
|
7672
8681
|
hasRenderedMessages = false,
|
|
7673
8682
|
emptyComposerPlacement = "center",
|
|
7674
8683
|
autoScroll = true,
|
|
8684
|
+
anchorTurns = true,
|
|
7675
8685
|
transcriptLayout,
|
|
7676
8686
|
expectExistingThread,
|
|
7677
8687
|
onStartNewChat,
|
|
@@ -7686,6 +8696,7 @@ export function AgentKitChat({
|
|
|
7686
8696
|
onThreadForked,
|
|
7687
8697
|
threadLoad,
|
|
7688
8698
|
formatWorkedForDuration,
|
|
8699
|
+
requestComposerFocus,
|
|
7689
8700
|
} = useAgentKit();
|
|
7690
8701
|
const formatReasoningDuration = (ms: number) =>
|
|
7691
8702
|
formatWorkedForDuration?.(ms) ??
|
|
@@ -7773,6 +8784,9 @@ export function AgentKitChat({
|
|
|
7773
8784
|
}, [threadId]);
|
|
7774
8785
|
useRealtimeVoiceTranscripts(controller, threadId, hasActiveRuns(thread));
|
|
7775
8786
|
const transcriptContentRef = useRef<HTMLDivElement>(null);
|
|
8787
|
+
const turnSpacerRef = useRef<HTMLDivElement>(null);
|
|
8788
|
+
// Set by a message sent in this thread; a thread that opens has no anchor.
|
|
8789
|
+
const turnAnchoredRef = useRef(false);
|
|
7776
8790
|
const fileDragDepthRef = useRef(0);
|
|
7777
8791
|
const followsTranscriptRef = useRef(true);
|
|
7778
8792
|
const programmaticScrollTopRef = useRef<number | undefined>(undefined);
|
|
@@ -7968,7 +8982,41 @@ export function AgentKitChat({
|
|
|
7968
8982
|
scheduleTranscriptScrollbarHide();
|
|
7969
8983
|
}, [scheduleTranscriptScrollbarHide]);
|
|
7970
8984
|
|
|
8985
|
+
// The spacer pads what the reply has not filled, so following the bottom holds
|
|
8986
|
+
// the sent message near the top. Sized imperatively so streaming never re-renders.
|
|
8987
|
+
const updateTurnSpacer = useCallback(() => {
|
|
8988
|
+
const transcript = transcriptRef.current;
|
|
8989
|
+
const content = transcriptContentRef.current;
|
|
8990
|
+
const spacer = turnSpacerRef.current;
|
|
8991
|
+
if (!transcript || !content || !spacer) return;
|
|
8992
|
+
// No stylesheet may size the spacer: its inline height is read back as-is.
|
|
8993
|
+
const current = Number.parseFloat(spacer.style.blockSize) || 0;
|
|
8994
|
+
const rows = content.querySelectorAll<HTMLElement>(
|
|
8995
|
+
'.agentkit-message[data-role="user"]',
|
|
8996
|
+
);
|
|
8997
|
+
const row = rows.length ? rows[rows.length - 1] : undefined;
|
|
8998
|
+
if (!anchorTurns || !turnAnchoredRef.current || !row) {
|
|
8999
|
+
if (current) spacer.style.blockSize = "";
|
|
9000
|
+
return;
|
|
9001
|
+
}
|
|
9002
|
+
const rowTop =
|
|
9003
|
+
row.getBoundingClientRect().top -
|
|
9004
|
+
transcript.getBoundingClientRect().top +
|
|
9005
|
+
transcript.scrollTop;
|
|
9006
|
+
const anchorTop = Math.max(0, rowTop - AGENTKIT_TURN_ANCHOR_PEEK_PX);
|
|
9007
|
+
const next = Math.max(
|
|
9008
|
+
0,
|
|
9009
|
+
Math.ceil(
|
|
9010
|
+
anchorTop +
|
|
9011
|
+
transcript.clientHeight -
|
|
9012
|
+
(transcript.scrollHeight - current),
|
|
9013
|
+
),
|
|
9014
|
+
);
|
|
9015
|
+
if (next !== current) spacer.style.blockSize = next ? `${next}px` : "";
|
|
9016
|
+
}, [anchorTurns]);
|
|
9017
|
+
|
|
7971
9018
|
const scrollTranscriptToBottom = useCallback(() => {
|
|
9019
|
+
updateTurnSpacer();
|
|
7972
9020
|
const transcript = transcriptRef.current;
|
|
7973
9021
|
if (!autoScroll || !transcript || !followsTranscriptRef.current) return;
|
|
7974
9022
|
const nextScrollTop = Math.max(
|
|
@@ -7977,7 +9025,7 @@ export function AgentKitChat({
|
|
|
7977
9025
|
);
|
|
7978
9026
|
if (Math.abs(transcript.scrollTop - nextScrollTop) <= 1) return;
|
|
7979
9027
|
writeTranscriptScrollTop(transcript, nextScrollTop);
|
|
7980
|
-
}, [autoScroll, writeTranscriptScrollTop]);
|
|
9028
|
+
}, [autoScroll, updateTurnSpacer, writeTranscriptScrollTop]);
|
|
7981
9029
|
|
|
7982
9030
|
const stopTranscriptGlide = useCallback(() => {
|
|
7983
9031
|
transcriptGlideRef.current?.glide.stop();
|
|
@@ -7986,6 +9034,7 @@ export function AgentKitChat({
|
|
|
7986
9034
|
// Streamed growth glides to the new bottom; anything else (opening a
|
|
7987
9035
|
// thread, a long jump) lands there at once instead of gliding through it.
|
|
7988
9036
|
const followTranscriptToBottom = useCallback(() => {
|
|
9037
|
+
updateTurnSpacer();
|
|
7989
9038
|
const transcript = transcriptRef.current;
|
|
7990
9039
|
if (!autoScroll || !transcript || !followsTranscriptRef.current) return;
|
|
7991
9040
|
const distance =
|
|
@@ -8004,7 +9053,22 @@ export function AgentKitChat({
|
|
|
8004
9053
|
transcriptGlideRef.current = {
|
|
8005
9054
|
element: transcript,
|
|
8006
9055
|
glide: new TranscriptGlide({
|
|
8007
|
-
|
|
9056
|
+
// Size the spacer first so an anchored message holds still as the reply grows.
|
|
9057
|
+
element: {
|
|
9058
|
+
get scrollTop() {
|
|
9059
|
+
return transcript.scrollTop;
|
|
9060
|
+
},
|
|
9061
|
+
set scrollTop(value: number) {
|
|
9062
|
+
transcript.scrollTop = value;
|
|
9063
|
+
},
|
|
9064
|
+
get scrollHeight() {
|
|
9065
|
+
updateTurnSpacer();
|
|
9066
|
+
return transcript.scrollHeight;
|
|
9067
|
+
},
|
|
9068
|
+
get clientHeight() {
|
|
9069
|
+
return transcript.clientHeight;
|
|
9070
|
+
},
|
|
9071
|
+
},
|
|
8008
9072
|
write: (scrollTop) => writeTranscriptScrollTop(transcript, scrollTop),
|
|
8009
9073
|
following: () => followsTranscriptRef.current,
|
|
8010
9074
|
}),
|
|
@@ -8015,6 +9079,7 @@ export function AgentKitChat({
|
|
|
8015
9079
|
autoScroll,
|
|
8016
9080
|
scrollTranscriptToBottom,
|
|
8017
9081
|
stopTranscriptGlide,
|
|
9082
|
+
updateTurnSpacer,
|
|
8018
9083
|
writeTranscriptScrollTop,
|
|
8019
9084
|
]);
|
|
8020
9085
|
|
|
@@ -8110,6 +9175,7 @@ export function AgentKitChat({
|
|
|
8110
9175
|
if (transcriptThreadIdRef.current !== threadId) {
|
|
8111
9176
|
transcriptThreadIdRef.current = threadId;
|
|
8112
9177
|
previousLastMessageIdRef.current = undefined;
|
|
9178
|
+
turnAnchoredRef.current = false;
|
|
8113
9179
|
transcriptGlidesUntilRef.current = 0;
|
|
8114
9180
|
jumps = true;
|
|
8115
9181
|
setTranscriptFollow(true);
|
|
@@ -8124,6 +9190,8 @@ export function AgentKitChat({
|
|
|
8124
9190
|
lastMessage.id !== previousLastMessageId
|
|
8125
9191
|
) {
|
|
8126
9192
|
jumps = true;
|
|
9193
|
+
// Only a send anchors: the first render of an opened thread is not one.
|
|
9194
|
+
if (previousLastMessageId !== undefined) turnAnchoredRef.current = true;
|
|
8127
9195
|
setTranscriptFollow(true);
|
|
8128
9196
|
hideTranscriptScrollbar();
|
|
8129
9197
|
}
|
|
@@ -8146,6 +9214,31 @@ export function AgentKitChat({
|
|
|
8146
9214
|
threadId,
|
|
8147
9215
|
]);
|
|
8148
9216
|
|
|
9217
|
+
// Anchor from the pending submission so the message never lands at the bottom first.
|
|
9218
|
+
const newestPendingSubmissionId = pendingSubmissions.at(-1)?.id;
|
|
9219
|
+
const previousPendingSubmissionIdRef = useRef(newestPendingSubmissionId);
|
|
9220
|
+
useLayoutEffect(() => {
|
|
9221
|
+
const previous = previousPendingSubmissionIdRef.current;
|
|
9222
|
+
previousPendingSubmissionIdRef.current = newestPendingSubmissionId;
|
|
9223
|
+
if (
|
|
9224
|
+
!autoScroll ||
|
|
9225
|
+
!newestPendingSubmissionId ||
|
|
9226
|
+
newestPendingSubmissionId === previous
|
|
9227
|
+
) {
|
|
9228
|
+
return;
|
|
9229
|
+
}
|
|
9230
|
+
turnAnchoredRef.current = true;
|
|
9231
|
+
setTranscriptFollow(true);
|
|
9232
|
+
stopTranscriptGlide();
|
|
9233
|
+
scrollTranscriptToBottom();
|
|
9234
|
+
}, [
|
|
9235
|
+
autoScroll,
|
|
9236
|
+
newestPendingSubmissionId,
|
|
9237
|
+
scrollTranscriptToBottom,
|
|
9238
|
+
setTranscriptFollow,
|
|
9239
|
+
stopTranscriptGlide,
|
|
9240
|
+
]);
|
|
9241
|
+
|
|
8149
9242
|
useLayoutEffect(() => {
|
|
8150
9243
|
const transcript = transcriptRef.current;
|
|
8151
9244
|
const content = transcriptContentRef.current;
|
|
@@ -9164,13 +10257,29 @@ export function AgentKitChat({
|
|
|
9164
10257
|
) : null}
|
|
9165
10258
|
{toolbar ? (
|
|
9166
10259
|
<AgentKitRegionSlot slot={Toolbar} threadId={threadId}>
|
|
9167
|
-
<div
|
|
10260
|
+
<div
|
|
10261
|
+
// Host buttons carry the primitive's own radius
|
|
10262
|
+
// utility, which beats layered CSS.
|
|
10263
|
+
className="agentkit-chat-toolbar [&_:is(button,[role=button])]:rounded-[var(--agentkit-radius-xs)]"
|
|
10264
|
+
>
|
|
10265
|
+
{toolbar}
|
|
10266
|
+
</div>
|
|
9168
10267
|
</AgentKitRegionSlot>
|
|
9169
10268
|
) : null}
|
|
9170
10269
|
</header>
|
|
9171
10270
|
</AgentKitRegionSlot>
|
|
9172
10271
|
</AgentKitSurfaceBoundary>
|
|
9173
10272
|
) : null}
|
|
10273
|
+
{composer && !showEmptyState ? (
|
|
10274
|
+
<button
|
|
10275
|
+
type="button"
|
|
10276
|
+
className="agentkit-skip-link"
|
|
10277
|
+
data-agentkit-skip-link="composer"
|
|
10278
|
+
onClick={() => requestComposerFocus(threadId)}
|
|
10279
|
+
>
|
|
10280
|
+
{labels.skipToComposer}
|
|
10281
|
+
</button>
|
|
10282
|
+
) : null}
|
|
9174
10283
|
<div
|
|
9175
10284
|
ref={transcriptRef}
|
|
9176
10285
|
className="agentkit-transcript"
|
|
@@ -9296,6 +10405,11 @@ export function AgentKitChat({
|
|
|
9296
10405
|
</AgentKitSurfaceBoundary>
|
|
9297
10406
|
) : null}
|
|
9298
10407
|
</AgentKitRegionSlot>
|
|
10408
|
+
<div
|
|
10409
|
+
ref={turnSpacerRef}
|
|
10410
|
+
className="agentkit-turn-spacer"
|
|
10411
|
+
aria-hidden="true"
|
|
10412
|
+
/>
|
|
9299
10413
|
</div>
|
|
9300
10414
|
</div>
|
|
9301
10415
|
{/* Outside the scroller so its shadow is never clipped and it
|
|
@@ -9310,8 +10424,14 @@ export function AgentKitChat({
|
|
|
9310
10424
|
label={labels.scrollToBottom}
|
|
9311
10425
|
icon={<IconArrowDown aria-hidden="true" />}
|
|
9312
10426
|
size="compact"
|
|
9313
|
-
className="rounded-
|
|
9314
|
-
onPress={
|
|
10427
|
+
className="rounded-[var(--agent-kit-radius-sm,0.5rem)] border border-[color:var(--agentkit-border)] bg-(--agentkit-surface) text-[color:var(--agentkit-text)] shadow-(--agent-kit-overlay-elevation) hover:bg-(--agentkit-subtle) hover:text-[color:var(--agentkit-text)]"
|
|
10428
|
+
onPress={(event) => {
|
|
10429
|
+
returnTranscriptToBottom();
|
|
10430
|
+
// The control goes inert at the bottom, which would drop
|
|
10431
|
+
// a keyboard user's focus to the page.
|
|
10432
|
+
if (pressedByKeyboard(event))
|
|
10433
|
+
requestComposerFocus(threadId);
|
|
10434
|
+
}}
|
|
9315
10435
|
/>
|
|
9316
10436
|
</div>
|
|
9317
10437
|
<AgentKitLiveStatus threadId={threadId} />
|